usps-imis-api 1.0.0.pre.rc.5 → 1.0.0.pre.rc.8
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/.rubocop.yml +4 -1
- data/Gemfile.lock +36 -30
- data/Readme.md +149 -33
- data/lib/usps/imis/api.rb +32 -39
- data/lib/usps/imis/business_object.rb +97 -47
- data/lib/usps/imis/config.rb +14 -3
- data/lib/usps/imis/data.rb +72 -0
- data/lib/usps/imis/error.rb +53 -0
- data/lib/usps/imis/errors/api_error.rb +11 -0
- data/lib/usps/imis/errors/config_error.rb +11 -0
- data/lib/usps/imis/errors/locked_id_error.rb +15 -0
- data/lib/usps/imis/errors/mapper_error.rb +29 -0
- data/lib/usps/imis/errors/not_found_error.rb +11 -0
- data/lib/usps/imis/errors/panel_unimplemented_error.rb +34 -0
- data/lib/usps/imis/{error → errors}/response_error.rb +5 -8
- data/lib/usps/imis/errors/unexpected_property_type_error.rb +31 -0
- data/lib/usps/imis/mapper.rb +51 -20
- data/lib/usps/imis/mocks/business_object.rb +47 -0
- data/lib/usps/imis/mocks.rb +11 -0
- data/lib/usps/imis/panels/base_panel.rb +154 -0
- data/lib/usps/imis/{panel → panels}/education.rb +2 -2
- data/lib/usps/imis/{panel → panels}/vsc.rb +2 -2
- data/lib/usps/imis/panels.rb +25 -0
- data/lib/usps/imis/properties.rb +50 -0
- data/lib/usps/imis/query.rb +94 -0
- data/lib/usps/imis/requests.rb +27 -3
- data/lib/usps/imis/version.rb +1 -1
- data/lib/usps/imis.rb +20 -15
- data/spec/lib/usps/imis/api_spec.rb +26 -13
- data/spec/lib/usps/imis/business_object_spec.rb +70 -33
- data/spec/lib/usps/imis/config_spec.rb +2 -2
- data/spec/lib/usps/imis/data_spec.rb +66 -0
- data/spec/lib/usps/imis/{error/api_error_spec.rb → error_spec.rb} +1 -1
- data/spec/lib/usps/imis/{error → errors}/response_error_spec.rb +4 -4
- data/spec/lib/usps/imis/mapper_spec.rb +27 -3
- data/spec/lib/usps/imis/mocks/business_object_spec.rb +65 -0
- data/spec/lib/usps/imis/panels/base_panel_spec.rb +33 -0
- data/spec/lib/usps/imis/panels/education_spec.rb +70 -0
- data/spec/lib/usps/imis/{panel → panels}/vsc_spec.rb +6 -7
- data/spec/lib/usps/imis/properties_spec.rb +19 -0
- data/spec/spec_helper.rb +2 -0
- data/usps-imis-api.gemspec +1 -1
- metadata +28 -16
- data/lib/ext/hash.rb +0 -10
- data/lib/usps/imis/error/api_error.rb +0 -44
- data/lib/usps/imis/error/mapper_error.rb +0 -11
- data/lib/usps/imis/panel/base_panel.rb +0 -101
- data/lib/usps/imis/panel/panel_properties.rb +0 -52
- data/spec/lib/usps/imis/panel/base_panel_spec.rb +0 -32
- data/spec/lib/usps/imis/panel/education_spec.rb +0 -55
- data/spec/lib/usps/imis/panel/panel_properties_spec.rb +0 -19
data/lib/usps/imis/mapper.rb
CHANGED
|
@@ -28,6 +28,37 @@ module Usps
|
|
|
28
28
|
@api.imis_id = imis_id if imis_id
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
# Get a member's data for a specific field by arbitrary field name
|
|
32
|
+
#
|
|
33
|
+
# Does not require knowing which business object / iMIS-specific field name to use
|
|
34
|
+
#
|
|
35
|
+
# Only available for fields defined in +FIELD_MAPPING+
|
|
36
|
+
#
|
|
37
|
+
# @param field_key [Symbol] Internal name of the field
|
|
38
|
+
#
|
|
39
|
+
# @return Value of the field
|
|
40
|
+
#
|
|
41
|
+
def fetch(field_key)
|
|
42
|
+
missing_mapping!(field_key) unless FIELD_MAPPING.key?(field_key.to_sym)
|
|
43
|
+
|
|
44
|
+
business_object_name, field = FIELD_MAPPING[field_key]
|
|
45
|
+
api.on(business_object_name)[field]
|
|
46
|
+
end
|
|
47
|
+
alias [] fetch
|
|
48
|
+
|
|
49
|
+
# Update a member's data for a specific field by arbitrary field name
|
|
50
|
+
#
|
|
51
|
+
# Does not require knowing which business object / iMIS-specific field name to use
|
|
52
|
+
#
|
|
53
|
+
# Only available for fields defined in +FIELD_MAPPING+
|
|
54
|
+
#
|
|
55
|
+
# @param field_key [Symbol] Internal name of the field
|
|
56
|
+
#
|
|
57
|
+
# @return Value of the field
|
|
58
|
+
#
|
|
59
|
+
def put_field(field_key, value) = update(field_key => value)
|
|
60
|
+
alias []= put_field
|
|
61
|
+
|
|
31
62
|
# Update a member's data on multiple affected business objects by arbitrary field names
|
|
32
63
|
#
|
|
33
64
|
# Does not require knowing which business object / iMIS-specific field name to use
|
|
@@ -36,7 +67,7 @@ module Usps
|
|
|
36
67
|
#
|
|
37
68
|
# @param data [Hash] Conforms to pattern +{ field_key => value }+
|
|
38
69
|
#
|
|
39
|
-
# @return [Array<
|
|
70
|
+
# @return [Array<Usps::Imis::Data>] Response data from the API for each internal update request
|
|
40
71
|
#
|
|
41
72
|
def update(data)
|
|
42
73
|
updates = data.each_with_object({}) do |(field_key, value), hash|
|
|
@@ -47,37 +78,37 @@ module Usps
|
|
|
47
78
|
end
|
|
48
79
|
|
|
49
80
|
updates.map do |business_object_name, field_updates|
|
|
50
|
-
api.
|
|
81
|
+
api.on(business_object_name).put_fields(field_updates)
|
|
51
82
|
end
|
|
52
83
|
end
|
|
53
84
|
|
|
85
|
+
# Ruby 3.5 instance variable filter
|
|
86
|
+
#
|
|
87
|
+
def instance_variables_to_inspect = instance_variables - %i[@api]
|
|
88
|
+
|
|
54
89
|
private
|
|
55
90
|
|
|
56
|
-
def
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
91
|
+
def logger = Imis.logger('Mapper')
|
|
92
|
+
|
|
93
|
+
def map_update(field_key)
|
|
94
|
+
missing_mapping!(field_key) unless FIELD_MAPPING.key?(field_key.to_sym)
|
|
95
|
+
|
|
96
|
+
business_object_name, field = FIELD_MAPPING[field_key.to_sym]
|
|
97
|
+
yield(business_object_name, field)
|
|
63
98
|
end
|
|
64
99
|
|
|
65
|
-
def missing_mapping(
|
|
100
|
+
def missing_mapping!(field_key)
|
|
66
101
|
unless ENV['TESTING']
|
|
67
102
|
# :nocov:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"
|
|
72
|
-
|
|
103
|
+
"Mapper does not know how to handle field \"#{field_key}\".\n\n" \
|
|
104
|
+
'You can use api.put_fields(business_object_name, { field_name => value }) ' \
|
|
105
|
+
"if you know the business object and iMIS-specific field name.\n\n"
|
|
106
|
+
.split("\n")
|
|
107
|
+
.each { logger.warn(it) }
|
|
73
108
|
# :nocov:
|
|
74
109
|
end
|
|
75
110
|
|
|
76
|
-
raise(
|
|
77
|
-
Error::MapperError,
|
|
78
|
-
"Unrecognized field: \"#{field_name}\". " \
|
|
79
|
-
'Please report what data you are attempting to work with to ITCom leadership.'
|
|
80
|
-
)
|
|
111
|
+
raise Errors::MapperError.new({ field_key: })
|
|
81
112
|
end
|
|
82
113
|
end
|
|
83
114
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
module Mocks
|
|
6
|
+
# Mock data response for testing
|
|
7
|
+
#
|
|
8
|
+
class BusinessObject
|
|
9
|
+
attr_reader :fields
|
|
10
|
+
|
|
11
|
+
def initialize(**fields)
|
|
12
|
+
@fields = fields.transform_keys(&:to_s)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def get
|
|
16
|
+
Usps::Imis::Properties.build do |props|
|
|
17
|
+
fields.each { |name, value| props.add(name, value) }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
alias read get
|
|
21
|
+
|
|
22
|
+
def get_field(name) = fields[name]
|
|
23
|
+
alias fetch get_field
|
|
24
|
+
alias [] get_field
|
|
25
|
+
|
|
26
|
+
def get_fields(*field_names) = field_names.map { fields[it] }
|
|
27
|
+
alias fetch_all get_fields
|
|
28
|
+
|
|
29
|
+
def put_fields(data)
|
|
30
|
+
Usps::Imis::Properties.build do |props|
|
|
31
|
+
fields.merge(data.transform_keys(&:to_s)).each { |name, value| props.add(name, value) }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
alias patch put_fields
|
|
35
|
+
|
|
36
|
+
def put(data) = data
|
|
37
|
+
alias update put
|
|
38
|
+
|
|
39
|
+
def post(data) = data
|
|
40
|
+
alias create post
|
|
41
|
+
|
|
42
|
+
def delete = ''
|
|
43
|
+
alias destroy delete
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
module Panels
|
|
6
|
+
# Base class for configuring Panels
|
|
7
|
+
#
|
|
8
|
+
class BasePanel
|
|
9
|
+
# The parent +Api+ object
|
|
10
|
+
#
|
|
11
|
+
attr_reader :api
|
|
12
|
+
|
|
13
|
+
def initialize(api = nil, imis_id: nil)
|
|
14
|
+
@api = api || Api.new
|
|
15
|
+
@api.imis_id = imis_id if imis_id
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Get a specific object from the Panel
|
|
19
|
+
#
|
|
20
|
+
# If +fields+ is provided, will return only those field values
|
|
21
|
+
#
|
|
22
|
+
# @param fields [String] Field names to return
|
|
23
|
+
#
|
|
24
|
+
# @param ordinal [Integer] The ordinal identifier for the desired object
|
|
25
|
+
#
|
|
26
|
+
# @return [Usps::Imis::Data, Array<Usps::Imis::Data>] Response data from the API
|
|
27
|
+
#
|
|
28
|
+
def get(ordinal, *fields) = api.on(business_object, ordinal:).get(*fields)
|
|
29
|
+
alias read get
|
|
30
|
+
|
|
31
|
+
# Get a single named field from a Panel for the current member
|
|
32
|
+
#
|
|
33
|
+
# @param ordinal [Integer] The ordinal identifier for the desired object
|
|
34
|
+
# @param field [String] Field name to return
|
|
35
|
+
#
|
|
36
|
+
# @return Response data field value from the API
|
|
37
|
+
#
|
|
38
|
+
def get_field(ordinal, field) = api.on(business_object, ordinal:).get_field(field)
|
|
39
|
+
alias fetch get_field
|
|
40
|
+
alias [] get_field
|
|
41
|
+
|
|
42
|
+
# Get named fields from a Panel for the current member
|
|
43
|
+
#
|
|
44
|
+
# @param ordinal [Integer] The ordinal identifier for the desired object
|
|
45
|
+
# @param fields [Array<String>] Field names to return
|
|
46
|
+
#
|
|
47
|
+
# @return [Array] Response data from the API
|
|
48
|
+
#
|
|
49
|
+
def get_fields(ordinal, *fields) = api.on(business_object, ordinal:).get_fields(*fields)
|
|
50
|
+
alias fetch_all get_fields
|
|
51
|
+
|
|
52
|
+
# Update a single named field on a business object for the current member
|
|
53
|
+
#
|
|
54
|
+
# @param field [String] Name of the field
|
|
55
|
+
# @param value Value of the field
|
|
56
|
+
#
|
|
57
|
+
# @return [Usps::Imis::Data] Response data from the API
|
|
58
|
+
#
|
|
59
|
+
def put_field(field, value) = api.on(business_object, ordinal:).put_field(field, value)
|
|
60
|
+
alias []= put_field
|
|
61
|
+
|
|
62
|
+
# Update only specific fields on a Panel for the current member
|
|
63
|
+
#
|
|
64
|
+
# @param ordinal [Integer] The ordinal identifier for the desired object
|
|
65
|
+
# @param fields [Hash] Conforms to pattern +{ field_key => value }+
|
|
66
|
+
#
|
|
67
|
+
# @return [Usps::Imis::Data] Response data from the API
|
|
68
|
+
#
|
|
69
|
+
def put_fields(ordinal, fields) = api.on(business_object, ordinal:).put_fields(fields)
|
|
70
|
+
alias patch put_fields
|
|
71
|
+
|
|
72
|
+
# Update an existing object in the Panel
|
|
73
|
+
#
|
|
74
|
+
# @param data [Hash] The record data for the desired object -- including the required
|
|
75
|
+
# +ordinal+ identifier
|
|
76
|
+
#
|
|
77
|
+
# @return [Usps::Imis::Data] Response data from the API
|
|
78
|
+
#
|
|
79
|
+
def put(data) = api.on(business_object, ordinal: data[:ordinal]).put(payload(data))
|
|
80
|
+
alias update put
|
|
81
|
+
|
|
82
|
+
# Create a new object in the Panel
|
|
83
|
+
#
|
|
84
|
+
# @param data [Hash] The record data for the desired object
|
|
85
|
+
#
|
|
86
|
+
# @return [Usps::Imis::Data] Response data from the API
|
|
87
|
+
#
|
|
88
|
+
def post(data) = api.on(business_object).post(payload(data))
|
|
89
|
+
alias create post
|
|
90
|
+
|
|
91
|
+
# Remove a specific object from the Panel
|
|
92
|
+
#
|
|
93
|
+
# @param ordinal [Integer] The ordinal identifier for the desired object
|
|
94
|
+
#
|
|
95
|
+
# @return [true] Only on success response (i.e. blank string from the API)
|
|
96
|
+
#
|
|
97
|
+
def delete(ordinal) = api.on(business_object, ordinal:).delete
|
|
98
|
+
alias destroy delete
|
|
99
|
+
|
|
100
|
+
# Ruby 3.5 instance variable filter
|
|
101
|
+
#
|
|
102
|
+
def instance_variables_to_inspect = instance_variables - %i[@api]
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def logger = Imis.logger('Panel')
|
|
107
|
+
|
|
108
|
+
def business_object
|
|
109
|
+
raise Errors::PanelUnimplementedError.from(self.class.name, 'business_object')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def payload(_data)
|
|
113
|
+
raise Errors::PanelUnimplementedError.from(self.class.name, 'payload(data)')
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def payload_header(data)
|
|
117
|
+
{
|
|
118
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericEntityData, Asi.Contracts',
|
|
119
|
+
'EntityTypeName' => business_object,
|
|
120
|
+
'PrimaryParentEntityTypeName' => 'Party',
|
|
121
|
+
'Identity' => identity(data[:ordinal]),
|
|
122
|
+
'PrimaryParentIdentity' => primary_parent_identity
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def identity(ordinal = nil)
|
|
127
|
+
{
|
|
128
|
+
'$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
|
|
129
|
+
'EntityTypeName' => business_object,
|
|
130
|
+
'IdentityElements' => {
|
|
131
|
+
'$type' => identity_type,
|
|
132
|
+
'$values' => [api.imis_id.to_s, ordinal&.to_s].compact
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def primary_parent_identity
|
|
138
|
+
{
|
|
139
|
+
'$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
|
|
140
|
+
'EntityTypeName' => 'Party',
|
|
141
|
+
'IdentityElements' => {
|
|
142
|
+
'$type' => identity_type,
|
|
143
|
+
'$values' => [api.imis_id.to_s]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def identity_type = 'System.Collections.ObjectModel.Collection`1[[System.String, mscorlib]], mscorlib'
|
|
149
|
+
|
|
150
|
+
def build_payload(data, &) = payload_header(data).merge(Properties.build(&))
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Usps
|
|
4
4
|
module Imis
|
|
5
|
-
module
|
|
5
|
+
module Panels
|
|
6
6
|
# Panel for accessing the Educational completions business object
|
|
7
7
|
#
|
|
8
8
|
class Education < BasePanel
|
|
@@ -14,7 +14,7 @@ module Usps
|
|
|
14
14
|
|
|
15
15
|
def payload(data)
|
|
16
16
|
build_payload(data) do |props|
|
|
17
|
-
props.add 'ID', api.imis_id
|
|
17
|
+
props.add 'ID', api.imis_id.to_s
|
|
18
18
|
props.add 'Ordinal', data[:ordinal] if data[:ordinal]
|
|
19
19
|
props.add 'ABC_EDUC_THRU_DATE', data[:thru_date] || '0001-01-01T00:00:00'
|
|
20
20
|
props.add 'ABC_ECertificate', data[:certificate]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Usps
|
|
4
4
|
module Imis
|
|
5
|
-
module
|
|
5
|
+
module Panels
|
|
6
6
|
# Panel for accessing the annual VSC completed counts business object
|
|
7
7
|
#
|
|
8
8
|
class Vsc < BasePanel
|
|
@@ -14,7 +14,7 @@ module Usps
|
|
|
14
14
|
|
|
15
15
|
def payload(data)
|
|
16
16
|
build_payload(data) do |props|
|
|
17
|
-
props.add 'ID', api.imis_id
|
|
17
|
+
props.add 'ID', api.imis_id.to_s
|
|
18
18
|
props.add 'Ordinal', data[:ordinal] if data[:ordinal]
|
|
19
19
|
props.add 'Source_System', 'Manual ITCom Entry'
|
|
20
20
|
props.add 'ABC_ECertificate', data[:certificate]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'panels/base_panel'
|
|
4
|
+
require_relative 'panels/vsc'
|
|
5
|
+
require_relative 'panels/education'
|
|
6
|
+
|
|
7
|
+
module Usps
|
|
8
|
+
module Imis
|
|
9
|
+
# Namespace for all Panels
|
|
10
|
+
#
|
|
11
|
+
module Panels
|
|
12
|
+
# Convenience accessor for available Panel objects
|
|
13
|
+
#
|
|
14
|
+
# @param api [Api] Parent to use for making requests
|
|
15
|
+
#
|
|
16
|
+
def self.all(api = Api.new)
|
|
17
|
+
panels = constants.reject { it == :BasePanel }
|
|
18
|
+
|
|
19
|
+
Struct
|
|
20
|
+
.new(*panels.map { it.to_s.underscore.to_sym })
|
|
21
|
+
.new(*panels.map { const_get(it).new(api) })
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
# Constructor for the Properties field
|
|
6
|
+
#
|
|
7
|
+
class Properties
|
|
8
|
+
# Build the data for a new Properties field
|
|
9
|
+
#
|
|
10
|
+
def self.build(&) = new.build(&)
|
|
11
|
+
|
|
12
|
+
# Build the data for the Properties field
|
|
13
|
+
#
|
|
14
|
+
def build
|
|
15
|
+
yield(self)
|
|
16
|
+
|
|
17
|
+
{
|
|
18
|
+
'Properties' => {
|
|
19
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyDataCollection, Asi.Contracts',
|
|
20
|
+
'$values' => @properties
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Add an individual property to the field
|
|
26
|
+
#
|
|
27
|
+
def add(name, value)
|
|
28
|
+
@properties ||= []
|
|
29
|
+
@properties << {
|
|
30
|
+
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
31
|
+
'Name' => name,
|
|
32
|
+
'Value' => wrap(value)
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def wrap(value)
|
|
39
|
+
case value
|
|
40
|
+
when String then value
|
|
41
|
+
when Time, DateTime then value.strftime('%Y-%m-%dT%H:%I:%S')
|
|
42
|
+
when Integer then { '$type' => 'System.Int32', '$value' => value }
|
|
43
|
+
when true, false then { '$type' => 'System.Boolean', '$value' => value }
|
|
44
|
+
else
|
|
45
|
+
raise Errors::UnexpectedPropertyTypeError.from(value)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
# API wrapper for IQA Queries
|
|
6
|
+
#
|
|
7
|
+
class Query
|
|
8
|
+
include Enumerable
|
|
9
|
+
include Requests
|
|
10
|
+
|
|
11
|
+
# Endpoint for IQA query requests
|
|
12
|
+
#
|
|
13
|
+
QUERY_PATH = 'api/Query'
|
|
14
|
+
|
|
15
|
+
# The parent +Api+ object
|
|
16
|
+
#
|
|
17
|
+
attr_reader :api
|
|
18
|
+
|
|
19
|
+
# Name of the Query to run
|
|
20
|
+
#
|
|
21
|
+
attr_reader :query_name
|
|
22
|
+
|
|
23
|
+
# Parameters for the Query
|
|
24
|
+
#
|
|
25
|
+
attr_reader :query_params
|
|
26
|
+
|
|
27
|
+
# Current offset for paging through the Query
|
|
28
|
+
#
|
|
29
|
+
attr_reader :offset
|
|
30
|
+
|
|
31
|
+
# A new instance of +Query+
|
|
32
|
+
#
|
|
33
|
+
# @param api [Api] Parent to use for making requests
|
|
34
|
+
# @param query_name [String] Full path of the query in IQA, e.g. +$/_ABC/Fiander/iMIS_ID+
|
|
35
|
+
# @query_params [Hash] Conforms to pattern +{ param_name => param_value }+
|
|
36
|
+
#
|
|
37
|
+
def initialize(api, query_name, query_params)
|
|
38
|
+
@api = api
|
|
39
|
+
@query_name = query_name
|
|
40
|
+
@query_params = query_params
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Iterate through all results from the query
|
|
44
|
+
#
|
|
45
|
+
def each(&)
|
|
46
|
+
logger.info 'Running IQA Query on iMIS'
|
|
47
|
+
|
|
48
|
+
items = []
|
|
49
|
+
find_each { items << it }
|
|
50
|
+
items.each(&)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Iterate through all results from the query, fetching one page at a time
|
|
54
|
+
#
|
|
55
|
+
def find_each(&)
|
|
56
|
+
result = { 'HasNext' => true }
|
|
57
|
+
count = 0
|
|
58
|
+
|
|
59
|
+
while result['HasNext']
|
|
60
|
+
logger.info 'Fetching IQA Query page'
|
|
61
|
+
|
|
62
|
+
result = fetch
|
|
63
|
+
|
|
64
|
+
count += result['Count'] || 0
|
|
65
|
+
logger.info " -> #{count} / #{result['TotalCount']} #{'item'.pluralize(count)}"
|
|
66
|
+
logger.debug ' -> Query page data:'
|
|
67
|
+
JSON.pretty_generate(result).split("\n").each { logger.debug " -> #{it}" }
|
|
68
|
+
|
|
69
|
+
items = result['Items']['$values'].map { it.except('$type') }
|
|
70
|
+
@offset = result['NextOffset']
|
|
71
|
+
|
|
72
|
+
items.each(&)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Ruby 3.5 instance variable filter
|
|
79
|
+
#
|
|
80
|
+
def instance_variables_to_inspect = instance_variables - %i[@api]
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def token = api.token
|
|
85
|
+
def token_expiration = api.token_expiration
|
|
86
|
+
|
|
87
|
+
def path = "#{QUERY_PATH}?#{query_params.merge(QueryName: query_name, Offset: offset).to_query}"
|
|
88
|
+
def uri = URI(File.join(Imis.configuration.hostname, path))
|
|
89
|
+
def fetch = JSON.parse(submit(uri, authorize(Net::HTTP::Get.new(uri))).body)
|
|
90
|
+
|
|
91
|
+
def logger = Imis.logger('Query')
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/usps/imis/requests.rb
CHANGED
|
@@ -7,6 +7,8 @@ module Usps
|
|
|
7
7
|
module Requests
|
|
8
8
|
private
|
|
9
9
|
|
|
10
|
+
def logger = Imis.logger
|
|
11
|
+
|
|
10
12
|
def client(uri)
|
|
11
13
|
Net::HTTP.new(uri.host, uri.port).tap do |http|
|
|
12
14
|
http.use_ssl = true
|
|
@@ -19,15 +21,37 @@ module Usps
|
|
|
19
21
|
# If the current token is missing/expired, request a new one
|
|
20
22
|
#
|
|
21
23
|
def authorize(request)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
if token_expiration < Time.now
|
|
25
|
+
logger.debug 'Token expired: re-authenticating with iMIS'
|
|
26
|
+
authenticate
|
|
27
|
+
end
|
|
28
|
+
request.tap { it.add_field('Authorization', "Bearer #{token}") }
|
|
24
29
|
end
|
|
25
30
|
|
|
26
31
|
def submit(uri, request)
|
|
32
|
+
logger.info 'Submitting request to iMIS'
|
|
33
|
+
logger.debug " -> #{request.class.name.demodulize.upcase} #{uri}"
|
|
34
|
+
sanitized_request_body(request).split("\n").each { logger.debug " -> #{it}" }
|
|
27
35
|
client(uri).request(request).tap do |result|
|
|
28
|
-
raise
|
|
36
|
+
raise Errors::ResponseError.from(result) unless result.is_a?(Net::HTTPSuccess)
|
|
37
|
+
|
|
38
|
+
logger.info 'Request succeeded'
|
|
29
39
|
end
|
|
30
40
|
end
|
|
41
|
+
|
|
42
|
+
def sanitized_request_body(request)
|
|
43
|
+
return '*** empty request body ***' if request.body.nil?
|
|
44
|
+
|
|
45
|
+
body = request.body.dup
|
|
46
|
+
|
|
47
|
+
Imis.config.filtered_parameters.each do |parameter|
|
|
48
|
+
body =
|
|
49
|
+
body.gsub(Imis.config.public_send(parameter), '[FILTERED]')
|
|
50
|
+
.gsub(CGI.escape(Imis.config.public_send(parameter)), '[FILTERED]')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
body
|
|
54
|
+
end
|
|
31
55
|
end
|
|
32
56
|
end
|
|
33
57
|
end
|
data/lib/usps/imis/version.rb
CHANGED
data/lib/usps/imis.rb
CHANGED
|
@@ -6,26 +6,22 @@ require 'json'
|
|
|
6
6
|
require 'time'
|
|
7
7
|
require 'cgi'
|
|
8
8
|
|
|
9
|
+
require 'active_support/core_ext/string/inflections'
|
|
10
|
+
require 'active_support/core_ext/object/to_query'
|
|
11
|
+
require 'active_support/core_ext/enumerable'
|
|
9
12
|
require 'active_support/string_inquirer'
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
require 'ext/hash' unless defined?(Rails)
|
|
14
|
-
# :nocov:
|
|
13
|
+
require 'logger'
|
|
14
|
+
require 'active_support/isolated_execution_state' # Fix costant loading issue with TaggedLogging
|
|
15
|
+
require 'active_support/tagged_logging'
|
|
15
16
|
|
|
16
17
|
# Internal requires
|
|
17
18
|
require_relative 'imis/config'
|
|
18
|
-
require_relative 'imis/error
|
|
19
|
-
require_relative 'imis/error/mapper_error'
|
|
20
|
-
require_relative 'imis/error/response_error'
|
|
21
|
-
require_relative 'imis/requests'
|
|
22
|
-
require_relative 'imis/business_object'
|
|
19
|
+
require_relative 'imis/error'
|
|
23
20
|
require_relative 'imis/api'
|
|
24
|
-
require_relative 'imis/
|
|
25
|
-
require_relative 'imis/
|
|
26
|
-
require_relative 'imis/
|
|
27
|
-
require_relative 'imis/
|
|
28
|
-
require_relative 'imis/panel/education'
|
|
21
|
+
require_relative 'imis/properties'
|
|
22
|
+
require_relative 'imis/panels'
|
|
23
|
+
require_relative 'imis/mocks'
|
|
24
|
+
require_relative 'imis/version'
|
|
29
25
|
|
|
30
26
|
module Usps
|
|
31
27
|
# API wrapper for interacting with iMIS
|
|
@@ -39,6 +35,7 @@ module Usps
|
|
|
39
35
|
def configuration
|
|
40
36
|
@configuration ||= Config.new
|
|
41
37
|
end
|
|
38
|
+
alias config configuration
|
|
42
39
|
|
|
43
40
|
# Used to define a block of configuration settings
|
|
44
41
|
#
|
|
@@ -48,6 +45,14 @@ module Usps
|
|
|
48
45
|
yield(configuration) if block_given?
|
|
49
46
|
configuration
|
|
50
47
|
end
|
|
48
|
+
|
|
49
|
+
# Logger (with optional nested tags) to write to
|
|
50
|
+
#
|
|
51
|
+
def logger(*tags) = configuration.logger.tagged('iMIS', *tags)
|
|
52
|
+
|
|
53
|
+
# Gem version
|
|
54
|
+
#
|
|
55
|
+
def version = Gem::Version.new(VERSION)
|
|
51
56
|
end
|
|
52
57
|
end
|
|
53
58
|
end
|