usps-imis-api 0.11.22 → 1.0.0.pre.rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +57 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +88 -0
- data/.ruby-version +1 -0
- data/.simplecov +8 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +95 -0
- data/Rakefile +12 -0
- data/Readme.md +191 -19
- data/bin/console +21 -0
- data/bin/setup +8 -0
- data/lib/ext/hash.rb +10 -0
- data/lib/usps/imis/api.rb +138 -177
- data/lib/usps/imis/config.rb +10 -68
- data/lib/usps/imis/error/api.rb +26 -0
- data/lib/usps/imis/error/mapper.rb +9 -0
- data/lib/usps/imis/{errors/response_error.rb → error/response.rb} +7 -34
- data/lib/usps/imis/mapper.rb +21 -90
- data/lib/usps/imis/panel/base_panel.rb +42 -0
- data/lib/usps/imis/panel/education.rb +111 -0
- data/lib/usps/imis/panel/vsc.rb +109 -0
- data/lib/usps/imis/version.rb +1 -1
- data/lib/usps/imis.rb +17 -32
- data/spec/lib/usps/imis/api_spec.rb +143 -0
- data/spec/lib/usps/imis/config_spec.rb +33 -0
- data/spec/lib/usps/imis/error/api_spec.rb +17 -0
- data/spec/lib/usps/imis/error/response_spec.rb +107 -0
- data/spec/lib/usps/imis/mapper_spec.rb +31 -0
- data/spec/lib/usps/imis/panel/base_panel_spec.rb +32 -0
- data/spec/lib/usps/imis/panel/education_spec.rb +55 -0
- data/spec/lib/usps/imis/panel/vsc_spec.rb +38 -0
- data/spec/lib/usps/imis_spec.rb +11 -0
- data/spec/spec_helper.rb +35 -0
- data/usps-imis-api.gemspec +18 -0
- metadata +33 -94
- data/bin/imis +0 -8
- data/lib/usps/imis/business_object.rb +0 -209
- data/lib/usps/imis/command_line/interface.rb +0 -205
- data/lib/usps/imis/command_line/options_parser.rb +0 -132
- data/lib/usps/imis/command_line.rb +0 -15
- data/lib/usps/imis/data.rb +0 -76
- data/lib/usps/imis/error.rb +0 -55
- data/lib/usps/imis/errors/api_error.rb +0 -11
- data/lib/usps/imis/errors/command_line_error.rb +0 -11
- data/lib/usps/imis/errors/config_error.rb +0 -11
- data/lib/usps/imis/errors/locked_id_error.rb +0 -15
- data/lib/usps/imis/errors/mapper_error.rb +0 -29
- data/lib/usps/imis/errors/missing_id_error.rb +0 -15
- data/lib/usps/imis/errors/not_found_error.rb +0 -11
- data/lib/usps/imis/errors/panel_unimplemented_error.rb +0 -34
- data/lib/usps/imis/errors/unexpected_property_type_error.rb +0 -31
- data/lib/usps/imis/logger.rb +0 -19
- data/lib/usps/imis/logger_formatter.rb +0 -32
- data/lib/usps/imis/logger_helpers.rb +0 -17
- data/lib/usps/imis/mocks/business_object.rb +0 -47
- data/lib/usps/imis/mocks.rb +0 -11
- data/lib/usps/imis/panels/base_panel.rb +0 -158
- data/lib/usps/imis/panels/education.rb +0 -33
- data/lib/usps/imis/panels/vsc.rb +0 -32
- data/lib/usps/imis/panels.rb +0 -25
- data/lib/usps/imis/properties.rb +0 -50
- data/lib/usps/imis/query.rb +0 -153
- data/lib/usps/imis/requests.rb +0 -68
- data/spec/support/usps/vcr/config.rb +0 -47
- data/spec/support/usps/vcr/filters.rb +0 -89
- data/spec/support/usps/vcr.rb +0 -8
data/lib/usps/imis/mapper.rb
CHANGED
|
@@ -2,12 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Usps
|
|
4
4
|
module Imis
|
|
5
|
-
# Specific known fields mapping to facilitate updating across multiple
|
|
6
|
-
# business objects.
|
|
7
|
-
#
|
|
8
5
|
class Mapper
|
|
9
|
-
# List of known mapped fields
|
|
10
|
-
#
|
|
11
6
|
FIELD_MAPPING = {
|
|
12
7
|
grade: %w[ABC_ASC_Individual_Demog Grade],
|
|
13
8
|
edpro: %w[ABC_ASC_Individual_Demog Educ_Proficiency],
|
|
@@ -17,85 +12,19 @@ module Usps
|
|
|
17
12
|
mm_updated: %w[ABC_ASC_Individual_Demog MMS_Updated]
|
|
18
13
|
}.freeze
|
|
19
14
|
|
|
20
|
-
# The parent +Api+ object
|
|
21
|
-
#
|
|
22
15
|
attr_reader :api
|
|
23
16
|
|
|
24
|
-
# Tagged logger
|
|
25
|
-
#
|
|
26
|
-
attr_reader :logger
|
|
27
|
-
|
|
28
|
-
# A new instance of +Mapper+
|
|
29
|
-
#
|
|
30
17
|
def initialize(api = nil, imis_id: nil)
|
|
31
18
|
@api = api || Api.new
|
|
32
19
|
@api.imis_id = imis_id if imis_id
|
|
33
|
-
@logger ||= Imis.logger('Mapper')
|
|
34
20
|
end
|
|
35
21
|
|
|
36
|
-
# Get a member's data for a specific field by arbitrary field name
|
|
37
|
-
#
|
|
38
|
-
# Does not require knowing which business object / iMIS-specific field name to use
|
|
39
|
-
#
|
|
40
|
-
# Only available for fields defined in +FIELD_MAPPING+
|
|
41
|
-
#
|
|
42
|
-
# @param field_key [Symbol] Internal name of the field
|
|
43
|
-
#
|
|
44
|
-
# @return Value of the field
|
|
45
|
-
#
|
|
46
|
-
def get_field(field_key)
|
|
47
|
-
missing_mapping!(field_key) unless FIELD_MAPPING.key?(field_key.to_sym)
|
|
48
|
-
|
|
49
|
-
business_object_name, field = FIELD_MAPPING[field_key]
|
|
50
|
-
api.on(business_object_name)[field]
|
|
51
|
-
end
|
|
52
|
-
alias fetch get_field
|
|
53
|
-
alias [] get_field
|
|
54
|
-
|
|
55
|
-
# Get specific fields by arbitrary field names
|
|
56
|
-
#
|
|
57
|
-
# @param names [Array<String>] Internal name of the fields
|
|
58
|
-
#
|
|
59
|
-
# @return [Array] Values of the field
|
|
60
|
-
#
|
|
61
|
-
def get_fields(*fields)
|
|
62
|
-
business_objects = {}
|
|
63
|
-
|
|
64
|
-
fields.map do |field_key|
|
|
65
|
-
business_object_name, field = FIELD_MAPPING[field_key.to_sym]
|
|
66
|
-
|
|
67
|
-
business_objects[business_object_name] ||= []
|
|
68
|
-
business_objects[business_object_name] << field
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
business_objects.flat_map do |business_object_name, fields|
|
|
72
|
-
api.on(business_object_name).get_fields(*fields)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
alias fetch_all get_fields
|
|
76
|
-
|
|
77
|
-
# Update a member's data for a specific field by arbitrary field name
|
|
78
|
-
#
|
|
79
|
-
# Does not require knowing which business object / iMIS-specific field name to use
|
|
80
|
-
#
|
|
81
|
-
# Only available for fields defined in +FIELD_MAPPING+
|
|
82
|
-
#
|
|
83
|
-
# @param field_key [Symbol] Internal name of the field
|
|
84
|
-
#
|
|
85
|
-
# @return Value of the field
|
|
86
|
-
#
|
|
87
|
-
def put_field(field_key, value) = update(field_key => value)
|
|
88
|
-
alias []= put_field
|
|
89
|
-
|
|
90
22
|
# Update a member's data on multiple affected business objects by arbitrary field names
|
|
91
|
-
#
|
|
92
23
|
# Does not require knowing which business object / iMIS-specific field name to use
|
|
93
24
|
#
|
|
94
|
-
# Only available for fields
|
|
95
|
-
#
|
|
96
|
-
# @param data [Hash] Conforms to pattern +{ field_key => value }+
|
|
25
|
+
# Only available for previously-mapped fields
|
|
97
26
|
#
|
|
98
|
-
#
|
|
27
|
+
# `data` is a hash of shape { field_key => value }
|
|
99
28
|
#
|
|
100
29
|
def update(data)
|
|
101
30
|
updates = data.each_with_object({}) do |(field_key, value), hash|
|
|
@@ -106,35 +35,37 @@ module Usps
|
|
|
106
35
|
end
|
|
107
36
|
|
|
108
37
|
updates.map do |business_object_name, field_updates|
|
|
109
|
-
api.
|
|
38
|
+
api.put_fields(business_object_name, field_updates)
|
|
110
39
|
end
|
|
111
40
|
end
|
|
112
41
|
|
|
113
|
-
# Ruby 3.5 instance variable filter
|
|
114
|
-
#
|
|
115
|
-
def instance_variables_to_inspect = instance_variables - %i[@api @logger]
|
|
116
|
-
|
|
117
42
|
private
|
|
118
43
|
|
|
119
|
-
def map_update(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
44
|
+
def map_update(field_name)
|
|
45
|
+
if FIELD_MAPPING.key?(field_name.to_sym)
|
|
46
|
+
business_object_name, field = FIELD_MAPPING[field_name.to_sym]
|
|
47
|
+
yield(business_object_name, field)
|
|
48
|
+
else
|
|
49
|
+
missing_mapping(field_name)
|
|
50
|
+
end
|
|
124
51
|
end
|
|
125
52
|
|
|
126
|
-
def missing_mapping
|
|
53
|
+
def missing_mapping(field_name)
|
|
127
54
|
unless ENV['TESTING']
|
|
128
55
|
# :nocov:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
56
|
+
warn(
|
|
57
|
+
"Mapper does not know how to handle field \"#{field_name}\".\n\n" \
|
|
58
|
+
'You can use api.put_fields(business_object_name, { field_name => value }) ' \
|
|
59
|
+
"if you know the business object and iMIS-specific field name.\n\n"
|
|
60
|
+
)
|
|
134
61
|
# :nocov:
|
|
135
62
|
end
|
|
136
63
|
|
|
137
|
-
raise
|
|
64
|
+
raise(
|
|
65
|
+
Error::Mapper,
|
|
66
|
+
"Unrecognized field: \"#{field_name}\". " \
|
|
67
|
+
'Please report what data you are attempting to work with to ITCom leadership.'
|
|
68
|
+
)
|
|
138
69
|
end
|
|
139
70
|
end
|
|
140
71
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
module Panel
|
|
6
|
+
class BasePanel
|
|
7
|
+
attr_reader :api
|
|
8
|
+
|
|
9
|
+
def initialize(api = nil, imis_id: nil)
|
|
10
|
+
@api = api || Api.new
|
|
11
|
+
@api.imis_id = imis_id if imis_id
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(ordinal)
|
|
15
|
+
api.get(business_object, url_id: "~#{api.imis_id}|#{ordinal}")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create(data)
|
|
19
|
+
api.post(business_object, payload(data), url_id: '')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update(data)
|
|
23
|
+
api.put(business_object, payload(data), url_id: "~#{api.imis_id}|#{data[:ordinal]}")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def destroy(ordinal)
|
|
27
|
+
api.delete(business_object, url_id: "~#{api.imis_id}|#{ordinal}")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def business_object
|
|
33
|
+
raise Error::Api, "#{self.class.name} must implement #business_object"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def payload(_data)
|
|
37
|
+
raise Error::Api, "#{self.class.name} must implement #payload(data)"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
module Panel
|
|
6
|
+
class Education < BasePanel
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def business_object
|
|
10
|
+
'ABC_ASC_Educ'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# rubocop:disable Metrics/MethodLength
|
|
14
|
+
def payload(data)
|
|
15
|
+
identity_type =
|
|
16
|
+
'System.Collections.ObjectModel.Collection`1[[System.String, mscorlib]], mscorlib'
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericEntityData, Asi.Contracts',
|
|
20
|
+
'EntityTypeName' => 'ABC_ASC_EDUC',
|
|
21
|
+
'PrimaryParentEntityTypeName' => 'Party',
|
|
22
|
+
'Identity' => {
|
|
23
|
+
'$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
|
|
24
|
+
'EntityTypeName' => 'ABC_ASC_EDUC',
|
|
25
|
+
'IdentityElements' => {
|
|
26
|
+
'$type' => identity_type,
|
|
27
|
+
'$values' => [api.imis_id, data[:ordinal]&.to_s].compact
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
'PrimaryParentIdentity' => {
|
|
31
|
+
'$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
|
|
32
|
+
'EntityTypeName' => 'Party',
|
|
33
|
+
'IdentityElements' => {
|
|
34
|
+
'$type' => identity_type,
|
|
35
|
+
'$values' => [api.imis_id]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
'Properties' => {
|
|
39
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyDataCollection, Asi.Contracts',
|
|
40
|
+
'$values' => [
|
|
41
|
+
{
|
|
42
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
43
|
+
'Name' => 'ID',
|
|
44
|
+
'Value' => api.imis_id
|
|
45
|
+
},
|
|
46
|
+
(
|
|
47
|
+
if data[:ordinal]
|
|
48
|
+
{
|
|
49
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
50
|
+
'Name' => 'Ordinal',
|
|
51
|
+
'Value' => {
|
|
52
|
+
'$type' => 'System.Int32',
|
|
53
|
+
'$value' => data[:ordinal]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
),
|
|
58
|
+
{
|
|
59
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
60
|
+
'Name' => 'ABC_EDUC_THRU_DATE',
|
|
61
|
+
'Value' => data[:thru_date] || '0001-01-01T00:00:00'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
65
|
+
'Name' => 'ABC_ECertificate',
|
|
66
|
+
'Value' => data[:certificate]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
70
|
+
'Name' => 'ABC_Educ_Description',
|
|
71
|
+
'Value' => data[:description]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
75
|
+
'Name' => 'ABC_Educ_Effective_Date',
|
|
76
|
+
'Value' => data[:effective_date]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
80
|
+
'Name' => 'ABC_Educ_Source_System',
|
|
81
|
+
'Value' => data[:source]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
85
|
+
'Name' => 'ABC_Educ_Transaction_Date',
|
|
86
|
+
'Value' => Time.now.strftime('%Y-%m-%dT%H:%I:%S')
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
90
|
+
'Name' => 'ABC_Other_Code',
|
|
91
|
+
'Value' => data[:code]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
95
|
+
'Name' => 'ABC_Product_Code',
|
|
96
|
+
'Value' => data[:type_code]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
100
|
+
'Name' => 'ABC_TYPE',
|
|
101
|
+
'Value' => data[:abc_type_code] || 'EDUC'
|
|
102
|
+
}
|
|
103
|
+
].compact
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
# rubocop:enable Metrics/MethodLength
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
module Panel
|
|
6
|
+
class Vsc < BasePanel
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def business_object
|
|
10
|
+
'ABC_ASC_Vessel_Safety_Checks'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# rubocop:disable Metrics/MethodLength
|
|
14
|
+
def payload(data)
|
|
15
|
+
identity_type =
|
|
16
|
+
'System.Collections.ObjectModel.Collection`1[[System.String, mscorlib]], mscorlib'
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericEntityData, Asi.Contracts',
|
|
20
|
+
'EntityTypeName' => 'ABC_ASC_Vessel_Safety_Checks',
|
|
21
|
+
'PrimaryParentEntityTypeName' => 'Party',
|
|
22
|
+
'Identity' => {
|
|
23
|
+
'$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
|
|
24
|
+
'EntityTypeName' => 'ABC_ASC_Vessel_Safety_Checks',
|
|
25
|
+
'IdentityElements' => {
|
|
26
|
+
'$type' => identity_type,
|
|
27
|
+
'$values' => [api.imis_id, data[:ordinal]&.to_s].compact
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
'PrimaryParentIdentity' => {
|
|
31
|
+
'$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
|
|
32
|
+
'EntityTypeName' => 'Party',
|
|
33
|
+
'IdentityElements' => {
|
|
34
|
+
'$type' => identity_type,
|
|
35
|
+
'$values' => [api.imis_id]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
'Properties' => {
|
|
39
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyDataCollection, Asi.Contracts',
|
|
40
|
+
'$values' => [
|
|
41
|
+
{
|
|
42
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
43
|
+
'Name' => 'ID',
|
|
44
|
+
'Value' => api.imis_id
|
|
45
|
+
},
|
|
46
|
+
(
|
|
47
|
+
if data[:ordinal]
|
|
48
|
+
{
|
|
49
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
50
|
+
'Name' => 'Ordinal',
|
|
51
|
+
'Value' => {
|
|
52
|
+
'$type' => 'System.Int32',
|
|
53
|
+
'$value' => data[:ordinal]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
),
|
|
58
|
+
{
|
|
59
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
60
|
+
'Name' => 'Source_System',
|
|
61
|
+
'Value' => 'Manual ITCom Entry'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
65
|
+
'Name' => 'ABC_ECertificate',
|
|
66
|
+
'Value' => data[:certificate]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
70
|
+
'Name' => 'Activity_Type',
|
|
71
|
+
'Value' => 'VSC'
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
75
|
+
'Name' => 'Description',
|
|
76
|
+
'Value' => 'Vessel Safety Checks'
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
80
|
+
'Name' => 'Effective_Date',
|
|
81
|
+
'Value' => "#{data[:year]}-12-01T00:00:00"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
85
|
+
'Name' => 'Quantity',
|
|
86
|
+
'Value' => {
|
|
87
|
+
'$type' => 'System.Int32',
|
|
88
|
+
'$value' => data[:count]
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
93
|
+
'Name' => 'Thru_Date',
|
|
94
|
+
'Value' => "#{data[:year]}-12-31T00:00:00"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
98
|
+
'Name' => 'Transaction_Date',
|
|
99
|
+
'Value' => Time.now.strftime('%Y-%m-%dT%H:%M:%S')
|
|
100
|
+
}
|
|
101
|
+
].compact
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
# rubocop:enable Metrics/MethodLength
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/usps/imis/version.rb
CHANGED
data/lib/usps/imis.rb
CHANGED
|
@@ -3,59 +3,44 @@
|
|
|
3
3
|
# Core dependencies
|
|
4
4
|
require 'net/https'
|
|
5
5
|
require 'json'
|
|
6
|
-
require 'yaml'
|
|
7
6
|
require 'time'
|
|
8
7
|
require 'cgi'
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
require '
|
|
13
|
-
|
|
14
|
-
require 'logger'
|
|
15
|
-
require 'active_support/isolated_execution_state' # Fix costant loading issue with TaggedLogging
|
|
16
|
-
require 'active_support/tagged_logging'
|
|
17
|
-
|
|
18
|
-
require 'dotenv/load'
|
|
9
|
+
# Extensions
|
|
10
|
+
# :nocov:
|
|
11
|
+
require 'ext/hash' unless defined?(Rails)
|
|
12
|
+
# :nocov:
|
|
19
13
|
|
|
20
14
|
# Internal requires
|
|
21
15
|
require_relative 'imis/config'
|
|
22
|
-
require_relative 'imis/error'
|
|
16
|
+
require_relative 'imis/error/api'
|
|
17
|
+
require_relative 'imis/error/mapper'
|
|
18
|
+
require_relative 'imis/error/response'
|
|
23
19
|
require_relative 'imis/api'
|
|
24
|
-
require_relative 'imis/
|
|
25
|
-
require_relative 'imis/
|
|
26
|
-
require_relative 'imis/
|
|
27
|
-
require_relative 'imis/
|
|
20
|
+
require_relative 'imis/mapper'
|
|
21
|
+
require_relative 'imis/panel/base_panel'
|
|
22
|
+
require_relative 'imis/panel/vsc'
|
|
23
|
+
require_relative 'imis/panel/education'
|
|
28
24
|
|
|
29
25
|
module Usps
|
|
30
|
-
# API wrapper for interacting with iMIS
|
|
31
|
-
#
|
|
32
26
|
module Imis
|
|
33
27
|
class << self
|
|
34
|
-
# Accessor for configuration values
|
|
35
|
-
#
|
|
36
|
-
# @return The configuration object
|
|
37
|
-
#
|
|
38
28
|
def configuration
|
|
39
29
|
@configuration ||= Config.new
|
|
40
30
|
end
|
|
41
|
-
alias config configuration
|
|
42
31
|
|
|
43
|
-
# Used to define a block of configuration settings
|
|
44
|
-
#
|
|
45
|
-
# @return The updated configuration object
|
|
46
|
-
#
|
|
47
32
|
def configure
|
|
48
33
|
yield(configuration) if block_given?
|
|
49
34
|
configuration
|
|
50
35
|
end
|
|
51
36
|
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
|
|
37
|
+
# def mock!(value = true)
|
|
38
|
+
# @mock = value
|
|
39
|
+
# end
|
|
55
40
|
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
|
|
41
|
+
# def mock
|
|
42
|
+
# @mock || false
|
|
43
|
+
# end
|
|
59
44
|
end
|
|
60
45
|
end
|
|
61
46
|
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Usps::Imis::Api do
|
|
6
|
+
let(:api) { described_class.new }
|
|
7
|
+
|
|
8
|
+
describe '#initialize' do
|
|
9
|
+
it 'skips authentication' do
|
|
10
|
+
# rubocop:disable RSpec/AnyInstance
|
|
11
|
+
expect_any_instance_of(described_class).not_to receive(:authenticate)
|
|
12
|
+
# rubocop:enable RSpec/AnyInstance
|
|
13
|
+
|
|
14
|
+
described_class.new(skip_authentication: true)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'stores the initial imis_id' do
|
|
18
|
+
api = described_class.new(imis_id: 42)
|
|
19
|
+
|
|
20
|
+
expect(api.imis_id).to eq('42')
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe '#imis_id_for' do
|
|
25
|
+
it 'gets the iMIS ID' do
|
|
26
|
+
expect(api.imis_id_for('E231625')).to eq('31092')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
context 'with a query error' do
|
|
30
|
+
before { allow(api).to receive(:query).and_raise('Stub') }
|
|
31
|
+
|
|
32
|
+
it 'wraps errors' do
|
|
33
|
+
expect { api.imis_id_for('E231625') }.to raise_error(
|
|
34
|
+
Usps::Imis::Error::Api, 'Member not found'
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe '#put' do
|
|
41
|
+
before { api.imis_id = 31092 }
|
|
42
|
+
|
|
43
|
+
it 'sends an update' do
|
|
44
|
+
expect(api.put_fields('ABC_ASC_Individual_Demog', { 'TotMMS' => 15 })).to be_a(Hash)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context 'when receiving a response error' do
|
|
48
|
+
let(:warning_text) do
|
|
49
|
+
<<~WARNING.chomp
|
|
50
|
+
Usps::Imis::Error::Response: [INTERNAL_SERVER_ERROR] The iMIS API returned an error.
|
|
51
|
+
Something went wrong
|
|
52
|
+
WARNING
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
before do
|
|
56
|
+
error = Struct.new(:code, :body).new('500', 'Something went wrong')
|
|
57
|
+
|
|
58
|
+
# rubocop:disable RSpec/AnyInstance
|
|
59
|
+
allow_any_instance_of(Net::HTTP).to receive(:request).and_return(error)
|
|
60
|
+
# rubocop:enable RSpec/AnyInstance
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'wraps the error' do
|
|
64
|
+
expect { api.put_fields('ABC_ASC_Individual_Demog', { 'TotMMS' => 15 }) }.to raise_error(
|
|
65
|
+
Usps::Imis::Error::Api, warning_text
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#with' do
|
|
72
|
+
it 'sends an update from put' do
|
|
73
|
+
expect(
|
|
74
|
+
api.with(31092) { put_fields('ABC_ASC_Individual_Demog', { 'TotMMS' => 15 }) }
|
|
75
|
+
).to be_a(Hash)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'sends an update from update' do
|
|
79
|
+
expect(api.with(31092) { update(mm: 15) }.first).to be_a(Hash)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'uses a panel correctly' do
|
|
83
|
+
expect(api.with(6374) { panels.vsc.get(1433) }).to be_a(Hash)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe '#inspect' do
|
|
88
|
+
it 'is configured to exclude the token instance variable' do
|
|
89
|
+
expect(api.instance_variables_to_inspect).not_to include(:@token)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'does not show the token instance variable' do
|
|
93
|
+
pending 'Requires Ruby 3.5' if Gem::Version.new(RUBY_VERSION) < Gem::Version.new(3.5)
|
|
94
|
+
|
|
95
|
+
expect(api.inspect).not_to match(/ @token="/)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe '#authorize' do
|
|
100
|
+
before { allow(api).to receive(:authenticate) }
|
|
101
|
+
|
|
102
|
+
it 'automatically refreshes an expired token' do
|
|
103
|
+
api.instance_variable_set(:@token_expiration, Time.now - 60)
|
|
104
|
+
|
|
105
|
+
request = Net::HTTP::Put.new('/')
|
|
106
|
+
api.send(:authorize, request)
|
|
107
|
+
|
|
108
|
+
expect(api).to have_received(:authenticate)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
describe '#filter_fields' do
|
|
113
|
+
let(:expected) do
|
|
114
|
+
{
|
|
115
|
+
'Properties' => {
|
|
116
|
+
'$values' => [
|
|
117
|
+
{ 'Name' => 'Stub iMIS ID', 'Value' => { '$value' => '31092' } },
|
|
118
|
+
{ 'Name' => 'Stub Integer', 'Value' => { '$value' => 43 } },
|
|
119
|
+
{ 'Name' => 'Stub String', 'Value' => 'other' }
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
before do
|
|
126
|
+
allow(api).to receive(:get).and_return({
|
|
127
|
+
'Properties' => {
|
|
128
|
+
'$values' => [
|
|
129
|
+
{ 'Name' => 'Stub iMIS ID', 'Value' => { '$value' => '31092' } },
|
|
130
|
+
{ 'Name' => 'Stub Integer', 'Value' => { '$value' => 42 } },
|
|
131
|
+
{ 'Name' => 'Stub String', 'Value' => 'something' }
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it 'formats fields correctly' do
|
|
138
|
+
updated = api.send(:filter_fields, 'Stub', { 'Stub Integer' => 43, 'Stub String' => 'other' })
|
|
139
|
+
|
|
140
|
+
expect(updated).to eq(expected)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Usps::Imis::Config do
|
|
6
|
+
let(:config) { described_class.new }
|
|
7
|
+
|
|
8
|
+
it 'sets values on initialize' do
|
|
9
|
+
config = described_class.new { |c| c.environment = 'test' }
|
|
10
|
+
|
|
11
|
+
expect(config.environment).to eq('test')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#hostname' do
|
|
15
|
+
context 'with production environment' do
|
|
16
|
+
before { config.environment = 'production' }
|
|
17
|
+
|
|
18
|
+
it 'returns the production hostname' do
|
|
19
|
+
expect(config.hostname).to eq(described_class::IMIS_ROOT_URL_PROD)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context 'with unrecognized environment' do
|
|
24
|
+
before { config.environment = 'nothing' }
|
|
25
|
+
|
|
26
|
+
it 'raises an error' do
|
|
27
|
+
expect { config.hostname }.to raise_error(
|
|
28
|
+
Usps::Imis::Error::Api, 'Unexpected API environment: nothing'
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|