usps-imis-api 1.0.0.pre.rc.1 → 1.0.0.pre.rc.2
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/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/lib/usps/imis/api.rb +81 -7
- data/lib/usps/imis/config.rb +8 -0
- data/lib/usps/imis/error/api.rb +18 -0
- data/lib/usps/imis/error/mapper.rb +2 -0
- data/lib/usps/imis/error/response.rb +30 -0
- data/lib/usps/imis/mapper.rb +14 -2
- data/lib/usps/imis/panel/base_panel.rb +21 -0
- data/lib/usps/imis/panel/education.rb +2 -0
- data/lib/usps/imis/panel/vsc.rb +2 -0
- data/lib/usps/imis/version.rb +1 -1
- data/lib/usps/imis.rb +10 -8
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd103cb82cf705d717bd293bef46390aee8a82dca66ec103fa28510bb67b921c
|
|
4
|
+
data.tar.gz: 4d1dee0de5129874d612b8b888d57b24918582925b61ffac91ef7722a5cbc9cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 30acc54f0082681a9c38ba36bdfe6252331a0199416e131557352a880df66b465cd30de07fb3c3ab0d66366df97b096210e58be649387a3929609c09b9293010
|
|
7
|
+
data.tar.gz: 729b191b9d78e62f0acc659730d3761e4dc8a60f72304dd5b584a6ac9a63853c659c0fc335ceb7cbd35d903bc2c85656c0642259b2ee8c2f318669741d0d31fe
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/lib/usps/imis/api.rb
CHANGED
|
@@ -2,14 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
module Usps
|
|
4
4
|
module Imis
|
|
5
|
+
# The core API wrapper
|
|
6
|
+
#
|
|
5
7
|
class Api
|
|
8
|
+
# Endpoint for (re-)authentication requests
|
|
9
|
+
#
|
|
6
10
|
AUTHENTICATION_PATH = 'Token'
|
|
11
|
+
|
|
12
|
+
# Endpoint for general API requests
|
|
13
|
+
#
|
|
7
14
|
API_PATH = 'api'
|
|
15
|
+
|
|
16
|
+
# Endpoint for IQA query requests
|
|
17
|
+
#
|
|
8
18
|
QUERY_PATH = 'api/Query'
|
|
9
|
-
PANELS = Struct.new(:vsc, :education)
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
# API bearer token
|
|
21
|
+
#
|
|
22
|
+
attr_reader :token
|
|
23
|
+
|
|
24
|
+
# Expiration time for the API bearer token
|
|
25
|
+
#
|
|
26
|
+
# Used to automatically re-authenticate as needed
|
|
27
|
+
#
|
|
28
|
+
attr_reader :token_expiration
|
|
29
|
+
|
|
30
|
+
# Currently selected iMIS ID for API requests
|
|
31
|
+
#
|
|
32
|
+
attr_reader :imis_id
|
|
12
33
|
|
|
34
|
+
# A new instance of +Api+
|
|
35
|
+
#
|
|
36
|
+
# @param skip_authentication [bool] Skip authentication on initialization (used for tests)
|
|
37
|
+
# @param imis_id [Integer, String] iMIS ID to select immediately on initialization
|
|
38
|
+
#
|
|
13
39
|
def initialize(skip_authentication: false, imis_id: nil)
|
|
14
40
|
authenticate unless skip_authentication
|
|
15
41
|
self.imis_id = imis_id if imis_id
|
|
@@ -17,12 +43,18 @@ module Usps
|
|
|
17
43
|
|
|
18
44
|
# Manually set the current ID, if you already have it for a given member
|
|
19
45
|
#
|
|
46
|
+
# @param id [Integer, String] iMIS ID to select for future requests
|
|
47
|
+
#
|
|
20
48
|
def imis_id=(id)
|
|
21
49
|
@imis_id = id.to_i.to_s
|
|
22
50
|
end
|
|
23
51
|
|
|
24
52
|
# Convert a member's certificate number into an iMIS ID number
|
|
25
53
|
#
|
|
54
|
+
# @param certificate [String] Certificate number to lookup the corresponding iMIS ID for
|
|
55
|
+
#
|
|
56
|
+
# @return [String] Corresponding iMIS ID
|
|
57
|
+
#
|
|
26
58
|
def imis_id_for(certificate)
|
|
27
59
|
result = query(Imis.configuration.imis_id_query_name, { certificate: })
|
|
28
60
|
@imis_id = result['Items']['$values'][0]['ID']
|
|
@@ -34,6 +66,13 @@ module Usps
|
|
|
34
66
|
#
|
|
35
67
|
# This should be used with methods that do not change the value of `imis_id`
|
|
36
68
|
#
|
|
69
|
+
# @param id [Integer, String] iMIS ID to select for requests within the block
|
|
70
|
+
#
|
|
71
|
+
# @example
|
|
72
|
+
# with(12345) do
|
|
73
|
+
# update(mm: 15)
|
|
74
|
+
# end
|
|
75
|
+
#
|
|
37
76
|
def with(id, &)
|
|
38
77
|
old_id = imis_id
|
|
39
78
|
self.imis_id = id
|
|
@@ -44,6 +83,11 @@ module Usps
|
|
|
44
83
|
|
|
45
84
|
# Get a business object for the current member
|
|
46
85
|
#
|
|
86
|
+
# @param business_object_name [String] Name of the business object
|
|
87
|
+
# @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
|
|
88
|
+
#
|
|
89
|
+
# @return [Hash] Response data from the API
|
|
90
|
+
#
|
|
47
91
|
def get(business_object_name, url_id: nil)
|
|
48
92
|
uri = uri_for(business_object_name, url_id:)
|
|
49
93
|
request = Net::HTTP::Get.new(uri)
|
|
@@ -53,7 +97,11 @@ module Usps
|
|
|
53
97
|
|
|
54
98
|
# Update only specific fields on a business object for the current member
|
|
55
99
|
#
|
|
56
|
-
#
|
|
100
|
+
# @param business_object_name [String] Name of the business object
|
|
101
|
+
# @param fields [Hash] Conforms to pattern +{ field_key => value }+
|
|
102
|
+
# @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
|
|
103
|
+
#
|
|
104
|
+
# @return [Hash] Response data from the API
|
|
57
105
|
#
|
|
58
106
|
def put_fields(business_object_name, fields, url_id: nil)
|
|
59
107
|
updated = filter_fields(business_object_name, fields)
|
|
@@ -62,6 +110,12 @@ module Usps
|
|
|
62
110
|
|
|
63
111
|
# Update a business object for the current member
|
|
64
112
|
#
|
|
113
|
+
# @param business_object_name [String] Name of the business object
|
|
114
|
+
# @param body [Hash] Full raw API object data
|
|
115
|
+
# @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
|
|
116
|
+
#
|
|
117
|
+
# @return [Hash] Response data from the API
|
|
118
|
+
#
|
|
65
119
|
def put(business_object_name, body, url_id: nil)
|
|
66
120
|
uri = uri_for(business_object_name, url_id:)
|
|
67
121
|
request = Net::HTTP::Put.new(uri)
|
|
@@ -72,6 +126,12 @@ module Usps
|
|
|
72
126
|
|
|
73
127
|
# Create a business object for the current member
|
|
74
128
|
#
|
|
129
|
+
# @param business_object_name [String] Name of the business object
|
|
130
|
+
# @param body [Hash] Full raw API object data
|
|
131
|
+
# @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
|
|
132
|
+
#
|
|
133
|
+
# @return [Hash] Response data from the API
|
|
134
|
+
#
|
|
75
135
|
def post(business_object_name, body, url_id: nil)
|
|
76
136
|
uri = uri_for(business_object_name, url_id:)
|
|
77
137
|
request = Net::HTTP::Post.new(uri)
|
|
@@ -82,7 +142,10 @@ module Usps
|
|
|
82
142
|
|
|
83
143
|
# Remove a business object for the current member
|
|
84
144
|
#
|
|
85
|
-
#
|
|
145
|
+
# @param business_object_name [String] Name of the business object
|
|
146
|
+
# @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
|
|
147
|
+
#
|
|
148
|
+
# @return [String] Error response body from the API, or empty string on success
|
|
86
149
|
#
|
|
87
150
|
def delete(business_object_name, url_id: nil)
|
|
88
151
|
uri = uri_for(business_object_name, url_id:)
|
|
@@ -93,8 +156,10 @@ module Usps
|
|
|
93
156
|
|
|
94
157
|
# Run an IQA Query
|
|
95
158
|
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
159
|
+
# @param query_name [String] Full path of the query in IQA, e.g. +$/_ABC/Fiander/iMIS_ID+
|
|
160
|
+
# @query_params [Hash] Conforms to pattern +{ param_name => param_value }+
|
|
161
|
+
#
|
|
162
|
+
# @return [Hash] Response data from the API
|
|
98
163
|
#
|
|
99
164
|
def query(query_name, query_params = {})
|
|
100
165
|
query_params[:QueryName] = query_name
|
|
@@ -105,21 +170,30 @@ module Usps
|
|
|
105
170
|
JSON.parse(result.body)
|
|
106
171
|
end
|
|
107
172
|
|
|
173
|
+
# An instance of Mapper, using this instance as its parent +Api+
|
|
174
|
+
#
|
|
108
175
|
def mapper
|
|
109
176
|
@mapper ||= Mapper.new(self)
|
|
110
177
|
end
|
|
111
178
|
|
|
179
|
+
# Convenience accessor for available Panel objects, each using this instance as its parent
|
|
180
|
+
# +Api+
|
|
181
|
+
#
|
|
112
182
|
def panels
|
|
113
|
-
@panels ||=
|
|
183
|
+
@panels ||= Struct.new(:vsc, :education).new(
|
|
114
184
|
Panel::Vsc.new(self),
|
|
115
185
|
Panel::Education.new(self)
|
|
116
186
|
)
|
|
117
187
|
end
|
|
118
188
|
|
|
189
|
+
# Convenience alias for updating mapped fields
|
|
190
|
+
#
|
|
119
191
|
def update(data)
|
|
120
192
|
mapper.update(data)
|
|
121
193
|
end
|
|
122
194
|
|
|
195
|
+
# Ruby 3.5 instance variable filter
|
|
196
|
+
#
|
|
123
197
|
def instance_variables_to_inspect = %i[@token_expiration @imis_id]
|
|
124
198
|
|
|
125
199
|
private
|
data/lib/usps/imis/config.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Usps
|
|
4
4
|
module Imis
|
|
5
|
+
# API Configuration
|
|
6
|
+
#
|
|
5
7
|
class Config
|
|
6
8
|
IMIS_ROOT_URL_PROD = 'https://portal.americasboatingclub.org'
|
|
7
9
|
IMIS_ROOT_URL_DEV = 'https://abcdev.imiscloud.com'
|
|
@@ -12,6 +14,10 @@ module Usps
|
|
|
12
14
|
yield self if block_given?
|
|
13
15
|
end
|
|
14
16
|
|
|
17
|
+
# Environment-specific API endpoint hostname
|
|
18
|
+
#
|
|
19
|
+
# @return The API hostname for the current environment
|
|
20
|
+
#
|
|
15
21
|
def hostname
|
|
16
22
|
case environment.to_sym
|
|
17
23
|
when :production
|
|
@@ -23,6 +29,8 @@ module Usps
|
|
|
23
29
|
end
|
|
24
30
|
end
|
|
25
31
|
|
|
32
|
+
# Ruby 3.5 instance variable filter
|
|
33
|
+
#
|
|
26
34
|
def instance_variables_to_inspect = %i[@environment @imis_id_query_name @username]
|
|
27
35
|
end
|
|
28
36
|
end
|
data/lib/usps/imis/error/api.rb
CHANGED
|
@@ -3,14 +3,32 @@
|
|
|
3
3
|
module Usps
|
|
4
4
|
module Imis
|
|
5
5
|
module Error
|
|
6
|
+
# Base error class for all internal exceptions
|
|
7
|
+
#
|
|
6
8
|
class Api < StandardError
|
|
9
|
+
# Additional call-specific metadata to pass through to Bugsnag
|
|
10
|
+
#
|
|
7
11
|
attr_accessor :metadata
|
|
8
12
|
|
|
13
|
+
# A new instance of +Error::Api+
|
|
14
|
+
#
|
|
15
|
+
# @param message [String] The base exception message
|
|
16
|
+
# @param metadata [Hash] Additional call-specific metadata to pass through to Bugsnag
|
|
17
|
+
#
|
|
9
18
|
def initialize(message, metadata = {})
|
|
10
19
|
super(message)
|
|
11
20
|
@metadata = metadata
|
|
12
21
|
end
|
|
13
22
|
|
|
23
|
+
# Additional metadata to include in Bugsnag reports
|
|
24
|
+
#
|
|
25
|
+
# Can include fields at the top level, which will be shows on the custom tab
|
|
26
|
+
#
|
|
27
|
+
# Can include fields nested under a top-level key, which will be shown on a tab with the
|
|
28
|
+
# top-level key as its name
|
|
29
|
+
#
|
|
30
|
+
# @return [Hash]
|
|
31
|
+
#
|
|
14
32
|
def bugsnag_meta_data
|
|
15
33
|
metadata == {} ? {} : base_metadata
|
|
16
34
|
end
|
|
@@ -3,23 +3,53 @@
|
|
|
3
3
|
module Usps
|
|
4
4
|
module Imis
|
|
5
5
|
module Error
|
|
6
|
+
# Exception raised due to receiving an error response from the API
|
|
7
|
+
#
|
|
6
8
|
class Response < Api
|
|
9
|
+
# [Net::HTTPResponse] The response received from the API
|
|
10
|
+
#
|
|
7
11
|
attr_reader :response
|
|
12
|
+
|
|
13
|
+
# [Hash] Additional call-specific metadata to pass through to Bugsnag
|
|
14
|
+
#
|
|
8
15
|
attr_accessor :metadata
|
|
9
16
|
|
|
17
|
+
# Create a new instance of +Error::Response+ from an API response
|
|
18
|
+
#
|
|
19
|
+
# @param response [Net::HTTPResponse] The response received from the API
|
|
20
|
+
#
|
|
10
21
|
def self.from(response)
|
|
11
22
|
new(nil, response)
|
|
12
23
|
end
|
|
13
24
|
|
|
25
|
+
# Create a new instance of +Error::Response+
|
|
26
|
+
#
|
|
27
|
+
# @param _message Ignored
|
|
28
|
+
# @param response [Net::HTTPResponse] The response received from the API
|
|
29
|
+
# @param metadata [Hash] Additional call-specific metadata to pass through to Bugsnag
|
|
30
|
+
#
|
|
14
31
|
def initialize(_message, response, metadata = {})
|
|
15
32
|
@response = response
|
|
16
33
|
super(message, metadata)
|
|
17
34
|
end
|
|
18
35
|
|
|
36
|
+
# Additional metadata to include in Bugsnag reports
|
|
37
|
+
#
|
|
38
|
+
# Can include fields at the top level, which will be shows on the +custom+ tab
|
|
39
|
+
#
|
|
40
|
+
# Can include fields nested under a top-level key, which will be shown on a tab with the
|
|
41
|
+
# top-level key as its name
|
|
42
|
+
#
|
|
43
|
+
# @return [Hash]
|
|
44
|
+
#
|
|
19
45
|
def bugsnag_meta_data
|
|
20
46
|
base_metadata.tap { |m| m[:api].merge!(metadata) }
|
|
21
47
|
end
|
|
22
48
|
|
|
49
|
+
# Auto-formatted exception message, based on the provided API response
|
|
50
|
+
#
|
|
51
|
+
# @return [String] The exception message
|
|
52
|
+
#
|
|
23
53
|
def message
|
|
24
54
|
[
|
|
25
55
|
"#{self.class.name}: [#{status.to_s.upcase}] The iMIS API returned an error.",
|
data/lib/usps/imis/mapper.rb
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Usps
|
|
4
4
|
module Imis
|
|
5
|
+
# Specific known fields mapping to facilitate updating across multiple
|
|
6
|
+
# business objects.
|
|
7
|
+
#
|
|
5
8
|
class Mapper
|
|
9
|
+
# List of known mapped fields
|
|
10
|
+
#
|
|
6
11
|
FIELD_MAPPING = {
|
|
7
12
|
grade: %w[ABC_ASC_Individual_Demog Grade],
|
|
8
13
|
edpro: %w[ABC_ASC_Individual_Demog Educ_Proficiency],
|
|
@@ -12,19 +17,26 @@ module Usps
|
|
|
12
17
|
mm_updated: %w[ABC_ASC_Individual_Demog MMS_Updated]
|
|
13
18
|
}.freeze
|
|
14
19
|
|
|
20
|
+
# The parent +Api+ object
|
|
21
|
+
#
|
|
15
22
|
attr_reader :api
|
|
16
23
|
|
|
24
|
+
# A new instance of +Mapper+
|
|
25
|
+
#
|
|
17
26
|
def initialize(api = nil, imis_id: nil)
|
|
18
27
|
@api = api || Api.new
|
|
19
28
|
@api.imis_id = imis_id if imis_id
|
|
20
29
|
end
|
|
21
30
|
|
|
22
31
|
# Update a member's data on multiple affected business objects by arbitrary field names
|
|
32
|
+
#
|
|
23
33
|
# Does not require knowing which business object / iMIS-specific field name to use
|
|
24
34
|
#
|
|
25
|
-
# Only available for
|
|
35
|
+
# Only available for fields defined in +FIELD_MAPPING+
|
|
36
|
+
#
|
|
37
|
+
# @param data [Hash] Conforms to pattern +{ field_key => value }+
|
|
26
38
|
#
|
|
27
|
-
#
|
|
39
|
+
# @return [Array<Hash>] Response data from the API for each internal update request
|
|
28
40
|
#
|
|
29
41
|
def update(data)
|
|
30
42
|
updates = data.each_with_object({}) do |(field_key, value), hash|
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
module Usps
|
|
4
4
|
module Imis
|
|
5
5
|
module Panel
|
|
6
|
+
# Base class for configuring Panels
|
|
7
|
+
#
|
|
6
8
|
class BasePanel
|
|
9
|
+
# The parent +Api+ object
|
|
10
|
+
#
|
|
7
11
|
attr_reader :api
|
|
8
12
|
|
|
9
13
|
def initialize(api = nil, imis_id: nil)
|
|
@@ -11,18 +15,35 @@ module Usps
|
|
|
11
15
|
@api.imis_id = imis_id if imis_id
|
|
12
16
|
end
|
|
13
17
|
|
|
18
|
+
# Get a specific object from the Panel
|
|
19
|
+
#
|
|
20
|
+
# @param ordinal [Integer] The ordinal identifier for the desired object
|
|
21
|
+
#
|
|
14
22
|
def get(ordinal)
|
|
15
23
|
api.get(business_object, url_id: "~#{api.imis_id}|#{ordinal}")
|
|
16
24
|
end
|
|
17
25
|
|
|
26
|
+
# Create a new object in the Panel
|
|
27
|
+
#
|
|
28
|
+
# @param data [Hash] The record data for the desired object
|
|
29
|
+
#
|
|
18
30
|
def create(data)
|
|
19
31
|
api.post(business_object, payload(data), url_id: '')
|
|
20
32
|
end
|
|
21
33
|
|
|
34
|
+
# Update an existing object in the Panel
|
|
35
|
+
#
|
|
36
|
+
# @param data [Hash] The record data for the desired object -- including the required
|
|
37
|
+
# +ordinal+ identifier
|
|
38
|
+
#
|
|
22
39
|
def update(data)
|
|
23
40
|
api.put(business_object, payload(data), url_id: "~#{api.imis_id}|#{data[:ordinal]}")
|
|
24
41
|
end
|
|
25
42
|
|
|
43
|
+
# Remove a specific object from the Panel
|
|
44
|
+
#
|
|
45
|
+
# @param ordinal [Integer] The ordinal identifier for the desired object
|
|
46
|
+
#
|
|
26
47
|
def destroy(ordinal)
|
|
27
48
|
api.delete(business_object, url_id: "~#{api.imis_id}|#{ordinal}")
|
|
28
49
|
end
|
data/lib/usps/imis/panel/vsc.rb
CHANGED
data/lib/usps/imis/version.rb
CHANGED
data/lib/usps/imis.rb
CHANGED
|
@@ -23,24 +23,26 @@ require_relative 'imis/panel/vsc'
|
|
|
23
23
|
require_relative 'imis/panel/education'
|
|
24
24
|
|
|
25
25
|
module Usps
|
|
26
|
+
# API wrapper for interacting with iMIS
|
|
27
|
+
#
|
|
26
28
|
module Imis
|
|
27
29
|
class << self
|
|
30
|
+
# Accessor for configuration values
|
|
31
|
+
#
|
|
32
|
+
# @return The configuration object
|
|
33
|
+
#
|
|
28
34
|
def configuration
|
|
29
35
|
@configuration ||= Config.new
|
|
30
36
|
end
|
|
31
37
|
|
|
38
|
+
# Used to define a block of configuration settings
|
|
39
|
+
#
|
|
40
|
+
# @return The updated configuration object
|
|
41
|
+
#
|
|
32
42
|
def configure
|
|
33
43
|
yield(configuration) if block_given?
|
|
34
44
|
configuration
|
|
35
45
|
end
|
|
36
|
-
|
|
37
|
-
# def mock!(value = true)
|
|
38
|
-
# @mock = value
|
|
39
|
-
# end
|
|
40
|
-
|
|
41
|
-
# def mock
|
|
42
|
-
# @mock || false
|
|
43
|
-
# end
|
|
44
46
|
end
|
|
45
47
|
end
|
|
46
48
|
end
|