usps-imis-api 1.0.0.pre.rc.3 → 1.0.0.pre.rc.5
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 +0 -2
- data/Gemfile.lock +29 -1
- data/Readme.md +44 -14
- data/lib/usps/imis/api.rb +35 -141
- data/lib/usps/imis/business_object.rb +144 -0
- data/lib/usps/imis/config.rb +15 -9
- data/lib/usps/imis/mapper.rb +1 -1
- data/lib/usps/imis/panel/base_panel.rb +42 -4
- data/lib/usps/imis/panel/education.rb +13 -93
- data/lib/usps/imis/panel/panel_properties.rb +52 -0
- data/lib/usps/imis/panel/vsc.rb +12 -91
- data/lib/usps/imis/requests.rb +33 -0
- data/lib/usps/imis/version.rb +1 -1
- data/lib/usps/imis.rb +5 -0
- data/spec/lib/usps/imis/api_spec.rb +39 -36
- data/spec/lib/usps/imis/business_object_spec.rb +50 -0
- data/spec/lib/usps/imis/config_spec.rb +29 -3
- data/spec/lib/usps/imis/panel/panel_properties_spec.rb +19 -0
- data/spec/spec_helper.rb +1 -0
- data/usps-imis-api.gemspec +2 -0
- metadata +21 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c0abf715f3c6019016bc6b8bdf867ce9d83234ba093c6150a4885997e9156f4
|
|
4
|
+
data.tar.gz: bcd2727e5f74b7a8681ae8ea1c510539fc24b62085719b439a01a331989a59c9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e0f98c36fd93ae643a949980e590fbb327cc40e1947ef5c8b530156e3961073082702dd38916fa27da4db10c642316c3c3db9f97d82d2f7c616032917cea22f1
|
|
7
|
+
data.tar.gz: 3195ecb427cff0fea1c6d1cef0d877507c2bb7baaa3a3db6d669f281048a03de7fb06c15e3b5f1cfe36625ece30d2e890f05202accfaf56148de40dda0fdce4d
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,17 +1,39 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
usps-imis-api (1.0.0.pre.rc.
|
|
4
|
+
usps-imis-api (1.0.0.pre.rc.5)
|
|
5
|
+
activesupport (~> 8.0)
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
8
9
|
specs:
|
|
10
|
+
activesupport (8.0.3)
|
|
11
|
+
base64
|
|
12
|
+
benchmark (>= 0.3)
|
|
13
|
+
bigdecimal
|
|
14
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
15
|
+
connection_pool (>= 2.2.5)
|
|
16
|
+
drb
|
|
17
|
+
i18n (>= 1.6, < 2)
|
|
18
|
+
logger (>= 1.4.2)
|
|
19
|
+
minitest (>= 5.1)
|
|
20
|
+
securerandom (>= 0.3)
|
|
21
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
22
|
+
uri (>= 0.13.1)
|
|
9
23
|
ast (2.4.2)
|
|
24
|
+
base64 (0.3.0)
|
|
25
|
+
benchmark (0.4.1)
|
|
26
|
+
bigdecimal (3.3.1)
|
|
27
|
+
concurrent-ruby (1.3.5)
|
|
28
|
+
connection_pool (2.5.4)
|
|
10
29
|
date (3.4.1)
|
|
11
30
|
diff-lcs (1.5.1)
|
|
12
31
|
docile (1.4.1)
|
|
13
32
|
dotenv (3.1.4)
|
|
33
|
+
drb (2.2.3)
|
|
14
34
|
erb (5.0.3)
|
|
35
|
+
i18n (1.14.7)
|
|
36
|
+
concurrent-ruby (~> 1.0)
|
|
15
37
|
io-console (0.8.1)
|
|
16
38
|
irb (1.15.2)
|
|
17
39
|
pp (>= 0.6.0)
|
|
@@ -19,6 +41,8 @@ GEM
|
|
|
19
41
|
reline (>= 0.4.2)
|
|
20
42
|
json (2.7.2)
|
|
21
43
|
language_server-protocol (3.17.0.3)
|
|
44
|
+
logger (1.7.0)
|
|
45
|
+
minitest (5.26.0)
|
|
22
46
|
parallel (1.26.3)
|
|
23
47
|
parser (3.3.5.0)
|
|
24
48
|
ast (~> 2.4.1)
|
|
@@ -67,6 +91,7 @@ GEM
|
|
|
67
91
|
rubocop-rspec (3.1.0)
|
|
68
92
|
rubocop (~> 1.61)
|
|
69
93
|
ruby-progressbar (1.13.0)
|
|
94
|
+
securerandom (0.4.1)
|
|
70
95
|
simplecov (0.22.0)
|
|
71
96
|
docile (~> 1.1)
|
|
72
97
|
simplecov-html (~> 0.11)
|
|
@@ -75,7 +100,10 @@ GEM
|
|
|
75
100
|
simplecov_json_formatter (0.1.4)
|
|
76
101
|
stringio (3.1.7)
|
|
77
102
|
tsort (0.2.0)
|
|
103
|
+
tzinfo (2.0.6)
|
|
104
|
+
concurrent-ruby (~> 1.0)
|
|
78
105
|
unicode-display_width (2.6.0)
|
|
106
|
+
uri (1.0.4)
|
|
79
107
|
|
|
80
108
|
PLATFORMS
|
|
81
109
|
arm64-darwin-23
|
data/Readme.md
CHANGED
|
@@ -13,7 +13,7 @@ gem install usps-imis-api
|
|
|
13
13
|
or add this line to your Gemfile:
|
|
14
14
|
|
|
15
15
|
```ruby
|
|
16
|
-
gem 'usps-imis-api', '
|
|
16
|
+
gem 'usps-imis-api', '~> 0.6.3'
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Setup
|
|
@@ -25,9 +25,11 @@ require 'dotenv/load' # Optionally load environment variables from `.env` file
|
|
|
25
25
|
require 'usps/imis'
|
|
26
26
|
|
|
27
27
|
Usps::Imis.configure do |config|
|
|
28
|
-
|
|
29
|
-
config.
|
|
28
|
+
# This will default to `Rails.env` if available.
|
|
29
|
+
config.environment = :development
|
|
30
30
|
|
|
31
|
+
# These options will default to the listed `ENV` variable if available.
|
|
32
|
+
config.imis_id_query_name = ENV['IMIS_ID_QUERY_NAME']
|
|
31
33
|
config.username = ENV['IMIS_USERNAME']
|
|
32
34
|
config.password = ENV['IMIS_PASSWORD']
|
|
33
35
|
end
|
|
@@ -82,7 +84,17 @@ To fetch member data, run e.g.:
|
|
|
82
84
|
```ruby
|
|
83
85
|
api.imis_id = 31092
|
|
84
86
|
|
|
85
|
-
data = api.
|
|
87
|
+
data = api.on('ABC_ASC_Individual_Demog').get
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### GET Field
|
|
91
|
+
|
|
92
|
+
To fetch a specific field from member data, run e.g.:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
api.imis_id = 31092
|
|
96
|
+
|
|
97
|
+
tot_mms = api.on('ABC_ASC_Individual_Demog').get_field('TotMMS')
|
|
86
98
|
```
|
|
87
99
|
|
|
88
100
|
### PUT Fields
|
|
@@ -93,7 +105,7 @@ To update member data, run e.g.:
|
|
|
93
105
|
api.imis_id = 31092
|
|
94
106
|
|
|
95
107
|
data = { 'MMS_Updated' => Time.now.strftime('%Y-%m-%dT%H:%M:%S'), 'TotMMS' => new_total }
|
|
96
|
-
update = api.
|
|
108
|
+
update = api.on('ABC_ASC_Individual_Demog').put_fields(data)
|
|
97
109
|
```
|
|
98
110
|
|
|
99
111
|
This method fetches the current data structure, and filters it down to just what you want to
|
|
@@ -106,7 +118,7 @@ To update member data, run e.g.:
|
|
|
106
118
|
```ruby
|
|
107
119
|
api.imis_id = 31092
|
|
108
120
|
|
|
109
|
-
update = api.
|
|
121
|
+
update = api.on('ABC_ASC_Individual_Demog').put(complete_imis_object)
|
|
110
122
|
```
|
|
111
123
|
|
|
112
124
|
This method requires a complete iMIS data structure.
|
|
@@ -116,7 +128,7 @@ This method requires a complete iMIS data structure.
|
|
|
116
128
|
To create new member data, run e.g.:
|
|
117
129
|
|
|
118
130
|
```ruby
|
|
119
|
-
created = api.
|
|
131
|
+
created = api.on('ABC_ASC_Individual_Demog').post(complete_imis_object)
|
|
120
132
|
```
|
|
121
133
|
|
|
122
134
|
This method requires a complete iMIS data structure.
|
|
@@ -128,7 +140,7 @@ To remove member data, run e.g.:
|
|
|
128
140
|
```ruby
|
|
129
141
|
api.imis_id = 31092
|
|
130
142
|
|
|
131
|
-
api.
|
|
143
|
+
api.on('ABC_ASC_Individual_Demog').delete
|
|
132
144
|
```
|
|
133
145
|
|
|
134
146
|
This returns a blank string on success.
|
|
@@ -202,9 +214,11 @@ previous value.
|
|
|
202
214
|
|
|
203
215
|
```ruby
|
|
204
216
|
api.with(31092) do
|
|
205
|
-
# These
|
|
217
|
+
# These requests are identical:
|
|
206
218
|
|
|
207
|
-
|
|
219
|
+
on('ABC_ASC_Individual_Demog') { put('TotMMS' => 15) }
|
|
220
|
+
|
|
221
|
+
on('ABC_ASC_Individual_Demog').put('TotMMS' => 15)
|
|
208
222
|
|
|
209
223
|
mapper.update(mm: 15)
|
|
210
224
|
|
|
@@ -218,12 +232,26 @@ api.with(6374) do
|
|
|
218
232
|
end
|
|
219
233
|
```
|
|
220
234
|
|
|
221
|
-
|
|
235
|
+
```ruby
|
|
236
|
+
api.with(31092) do
|
|
237
|
+
# These requests are identical:
|
|
238
|
+
|
|
239
|
+
on('ABC_ASC_Individual_Demog') do
|
|
240
|
+
get['Properties']['$values'].find { |hash| hash['Name'] == 'TotMMS' }['Value']['$value']
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
on('ABC_ASC_Individual_Demog') { get_field('TotMMS') }
|
|
244
|
+
|
|
245
|
+
on('ABC_ASC_Individual_Demog').get_field('TotMMS')
|
|
246
|
+
end
|
|
222
247
|
|
|
223
|
-
|
|
248
|
+
# This request fetches the same data, but leaves the iMIS ID selected
|
|
249
|
+
api.with(31092).on('ABC_ASC_Individual_Demog').get_field('TotMMS')
|
|
250
|
+
```
|
|
224
251
|
|
|
225
|
-
|
|
226
|
-
|
|
252
|
+
## Exception Handling
|
|
253
|
+
|
|
254
|
+
All internal exceptions inherit from `Usps::Imis::ApiError`.
|
|
227
255
|
|
|
228
256
|
## Automated Testing and Linting
|
|
229
257
|
|
|
@@ -239,6 +267,8 @@ Linting is available by running:
|
|
|
239
267
|
bundle exec rubocop
|
|
240
268
|
```
|
|
241
269
|
|
|
270
|
+
100% branch coverage is enforced on the test suite.
|
|
271
|
+
|
|
242
272
|
### GitHub Actions
|
|
243
273
|
|
|
244
274
|
Testing and linting are automatically run on every push.
|
data/lib/usps/imis/api.rb
CHANGED
|
@@ -5,14 +5,12 @@ module Usps
|
|
|
5
5
|
# The core API wrapper
|
|
6
6
|
#
|
|
7
7
|
class Api
|
|
8
|
+
include Requests
|
|
9
|
+
|
|
8
10
|
# Endpoint for (re-)authentication requests
|
|
9
11
|
#
|
|
10
12
|
AUTHENTICATION_PATH = 'Token'
|
|
11
13
|
|
|
12
|
-
# Endpoint for general API requests
|
|
13
|
-
#
|
|
14
|
-
API_PATH = 'api'
|
|
15
|
-
|
|
16
14
|
# Endpoint for IQA query requests
|
|
17
15
|
#
|
|
18
16
|
QUERY_PATH = 'api/Query'
|
|
@@ -52,7 +50,7 @@ module Usps
|
|
|
52
50
|
def imis_id=(id)
|
|
53
51
|
raise Error::ApiError, 'Cannot change iMIS ID while locked' if lock_imis_id
|
|
54
52
|
|
|
55
|
-
@imis_id = id
|
|
53
|
+
@imis_id = id&.to_i&.to_s
|
|
56
54
|
end
|
|
57
55
|
|
|
58
56
|
# Convert a member's certificate number into an iMIS ID number
|
|
@@ -76,6 +74,8 @@ module Usps
|
|
|
76
74
|
#
|
|
77
75
|
# While in this block, changes to the value of +imis_id+ are not allowed
|
|
78
76
|
#
|
|
77
|
+
# If no block is given, this sets the iMIS ID and returns self.
|
|
78
|
+
#
|
|
79
79
|
# @param id [Integer, String] iMIS ID to select for requests within the block
|
|
80
80
|
#
|
|
81
81
|
# @example
|
|
@@ -86,107 +86,68 @@ module Usps
|
|
|
86
86
|
def with(id, &)
|
|
87
87
|
old_id = imis_id
|
|
88
88
|
self.imis_id = id
|
|
89
|
+
return self unless block_given?
|
|
89
90
|
|
|
90
91
|
@lock_imis_id = true
|
|
91
92
|
instance_eval(&)
|
|
92
93
|
ensure
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
if block_given?
|
|
95
|
+
@lock_imis_id = false
|
|
96
|
+
self.imis_id = old_id
|
|
97
|
+
end
|
|
95
98
|
end
|
|
96
99
|
|
|
97
|
-
#
|
|
100
|
+
# Run an IQA Query
|
|
98
101
|
#
|
|
99
|
-
# @param
|
|
100
|
-
# @
|
|
102
|
+
# @param query_name [String] Full path of the query in IQA, e.g. +$/_ABC/Fiander/iMIS_ID+
|
|
103
|
+
# @query_params [Hash] Conforms to pattern +{ param_name => param_value }+
|
|
101
104
|
#
|
|
102
105
|
# @return [Hash] Response data from the API
|
|
103
106
|
#
|
|
104
|
-
def
|
|
105
|
-
|
|
107
|
+
def query(query_name, query_params = {})
|
|
108
|
+
query_params[:QueryName] = query_name
|
|
109
|
+
path = "#{QUERY_PATH}?#{query_params.to_query}"
|
|
110
|
+
uri = URI(File.join(Imis.configuration.hostname, path))
|
|
106
111
|
request = Net::HTTP::Get.new(uri)
|
|
107
112
|
result = submit(uri, authorize(request))
|
|
108
113
|
JSON.parse(result.body)
|
|
109
114
|
end
|
|
110
115
|
|
|
111
|
-
#
|
|
116
|
+
# An instance of +BusinessObject+, using this instance as its parent +Api+
|
|
112
117
|
#
|
|
113
118
|
# @param business_object_name [String] Name of the business object
|
|
114
|
-
# @param fields [Hash] Conforms to pattern +{ field_key => value }+
|
|
115
119
|
# @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
|
|
116
120
|
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def put_fields(business_object_name, fields, url_id: nil)
|
|
120
|
-
updated = filter_fields(business_object_name, fields)
|
|
121
|
-
put(business_object_name, updated, url_id:)
|
|
121
|
+
def business_object(business_object_name, url_id: nil)
|
|
122
|
+
BusinessObject.new(self, business_object_name, url_id:)
|
|
122
123
|
end
|
|
123
124
|
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
# @param business_object_name [String] Name of the business object
|
|
127
|
-
# @param body [Hash] Full raw API object data
|
|
128
|
-
# @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
|
|
125
|
+
# Run requests as DSL, with specific +BusinessObject+ only maintained for this scope
|
|
129
126
|
#
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
def put(business_object_name, body, url_id: nil)
|
|
133
|
-
uri = uri_for(business_object_name, url_id:)
|
|
134
|
-
request = Net::HTTP::Put.new(uri)
|
|
135
|
-
request.body = JSON.dump(body)
|
|
136
|
-
result = submit(uri, authorize(request))
|
|
137
|
-
JSON.parse(result.body)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Create a business object for the current member
|
|
127
|
+
# If no block is given, this returns the specified +BusinessObject+.
|
|
141
128
|
#
|
|
142
129
|
# @param business_object_name [String] Name of the business object
|
|
143
|
-
# @param body [Hash] Full raw API object data
|
|
144
130
|
# @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
|
|
145
131
|
#
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
uri = uri_for(business_object_name, url_id:)
|
|
150
|
-
request = Net::HTTP::Post.new(uri)
|
|
151
|
-
request.body = JSON.dump(body)
|
|
152
|
-
result = submit(uri, authorize(request))
|
|
153
|
-
JSON.parse(result.body)
|
|
154
|
-
end
|
|
132
|
+
def on(business_object_name, url_id: nil, &)
|
|
133
|
+
object = business_object(business_object_name, url_id:)
|
|
134
|
+
return object unless block_given?
|
|
155
135
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
# @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
|
|
160
|
-
#
|
|
161
|
-
# @return [String] Error response body from the API, or empty string on success
|
|
162
|
-
#
|
|
163
|
-
def delete(business_object_name, url_id: nil)
|
|
164
|
-
uri = uri_for(business_object_name, url_id:)
|
|
165
|
-
request = Net::HTTP::Delete.new(uri)
|
|
166
|
-
result = submit(uri, authorize(request))
|
|
167
|
-
result.body
|
|
136
|
+
result = nil
|
|
137
|
+
object.tap { |obj| result = obj.instance_eval(&) }
|
|
138
|
+
result
|
|
168
139
|
end
|
|
169
140
|
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
-
# @param query_name [String] Full path of the query in IQA, e.g. +$/_ABC/Fiander/iMIS_ID+
|
|
173
|
-
# @query_params [Hash] Conforms to pattern +{ param_name => param_value }+
|
|
141
|
+
# An instance of +Mapper+, using this instance as its parent +Api+
|
|
174
142
|
#
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
def query(query_name, query_params = {})
|
|
178
|
-
query_params[:QueryName] = query_name
|
|
179
|
-
path = "#{QUERY_PATH}?#{query_params.to_query}"
|
|
180
|
-
uri = URI(File.join(imis_hostname, path))
|
|
181
|
-
request = Net::HTTP::Get.new(uri)
|
|
182
|
-
result = submit(uri, authorize(request))
|
|
183
|
-
JSON.parse(result.body)
|
|
143
|
+
def mapper
|
|
144
|
+
@mapper ||= Mapper.new(self)
|
|
184
145
|
end
|
|
185
146
|
|
|
186
|
-
#
|
|
147
|
+
# Convenience alias for updating mapped fields
|
|
187
148
|
#
|
|
188
|
-
def
|
|
189
|
-
|
|
149
|
+
def update(data)
|
|
150
|
+
mapper.update(data)
|
|
190
151
|
end
|
|
191
152
|
|
|
192
153
|
# Convenience accessor for available Panel objects, each using this instance as its parent
|
|
@@ -199,56 +160,16 @@ module Usps
|
|
|
199
160
|
)
|
|
200
161
|
end
|
|
201
162
|
|
|
202
|
-
# Convenience alias for updating mapped fields
|
|
203
|
-
#
|
|
204
|
-
def update(data)
|
|
205
|
-
mapper.update(data)
|
|
206
|
-
end
|
|
207
|
-
|
|
208
163
|
# Ruby 3.5 instance variable filter
|
|
209
164
|
#
|
|
210
165
|
def instance_variables_to_inspect = %i[@token_expiration @imis_id]
|
|
211
166
|
|
|
212
167
|
private
|
|
213
168
|
|
|
214
|
-
def client(uri)
|
|
215
|
-
Net::HTTP.new(uri.host, uri.port).tap do |http|
|
|
216
|
-
http.use_ssl = true
|
|
217
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
def imis_hostname
|
|
222
|
-
Imis.configuration.hostname
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# Authorize a request prior to submitting
|
|
226
|
-
#
|
|
227
|
-
# If the current token is missing/expired, request a new one
|
|
228
|
-
#
|
|
229
|
-
def authorize(request)
|
|
230
|
-
authenticate if token_expiration < Time.now
|
|
231
|
-
request.tap { |r| r.add_field('Authorization', "Bearer #{token}") }
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
# Construct a business object API endpoint address
|
|
235
|
-
#
|
|
236
|
-
def uri_for(business_object_name, url_id: nil)
|
|
237
|
-
url_id ||= imis_id
|
|
238
|
-
url_id = CGI.escape(url_id)
|
|
239
|
-
URI(File.join(imis_hostname, "#{API_PATH}/#{business_object_name}/#{url_id}"))
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def submit(uri, request)
|
|
243
|
-
client(uri).request(request).tap do |result|
|
|
244
|
-
raise Error::ResponseError.from(result) unless result.is_a?(Net::HTTPSuccess)
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
|
|
248
169
|
# Authenticate to the iMIS API, and store the access token and expiration time
|
|
249
170
|
#
|
|
250
171
|
def authenticate
|
|
251
|
-
uri = URI(File.join(
|
|
172
|
+
uri = URI(File.join(Imis.configuration.hostname, AUTHENTICATION_PATH))
|
|
252
173
|
req = Net::HTTP::Post.new(uri)
|
|
253
174
|
authentication_data = {
|
|
254
175
|
grant_type: 'password',
|
|
@@ -262,33 +183,6 @@ module Usps
|
|
|
262
183
|
@token = json['access_token']
|
|
263
184
|
@token_expiration = Time.parse(json['.expires'])
|
|
264
185
|
end
|
|
265
|
-
|
|
266
|
-
# Manually assemble the matching data structure, with fields in the correct order
|
|
267
|
-
#
|
|
268
|
-
def filter_fields(business_object_name, fields)
|
|
269
|
-
existing = get(business_object_name)
|
|
270
|
-
|
|
271
|
-
JSON.parse(JSON.dump(existing)).tap do |updated|
|
|
272
|
-
# The first property is always the iMIS ID again
|
|
273
|
-
updated['Properties']['$values'] = [existing['Properties']['$values'][0]]
|
|
274
|
-
|
|
275
|
-
# Iterate through all existing fields
|
|
276
|
-
existing['Properties']['$values'].each do |value|
|
|
277
|
-
next unless fields.keys.include?(value['Name'])
|
|
278
|
-
|
|
279
|
-
# Strings are not wrapped in the type definition structure
|
|
280
|
-
new_value = fields[value['Name']]
|
|
281
|
-
if new_value.is_a?(String)
|
|
282
|
-
value['Value'] = new_value
|
|
283
|
-
else
|
|
284
|
-
value['Value']['$value'] = new_value
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
# Add the completed field with the updated value
|
|
288
|
-
updated['Properties']['$values'] << value
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
end
|
|
292
186
|
end
|
|
293
187
|
end
|
|
294
188
|
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
# DEV
|
|
6
|
+
class BusinessObject
|
|
7
|
+
include Requests
|
|
8
|
+
|
|
9
|
+
# Endpoint for general API requests
|
|
10
|
+
#
|
|
11
|
+
API_PATH = 'api'
|
|
12
|
+
|
|
13
|
+
# The parent +Api+ object
|
|
14
|
+
#
|
|
15
|
+
attr_reader :api
|
|
16
|
+
|
|
17
|
+
# Name of the iMIS Business Object
|
|
18
|
+
#
|
|
19
|
+
attr_reader :business_object_name
|
|
20
|
+
|
|
21
|
+
# Override ID param of the URL (e.g. used for Panels)
|
|
22
|
+
#
|
|
23
|
+
attr_reader :url_id
|
|
24
|
+
|
|
25
|
+
# A new instance of +BusinessObject+
|
|
26
|
+
#
|
|
27
|
+
def initialize(api, business_object_name, url_id: nil)
|
|
28
|
+
@api = api
|
|
29
|
+
@business_object_name = business_object_name
|
|
30
|
+
@url_id = url_id
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Get a business object for the current member
|
|
34
|
+
#
|
|
35
|
+
# @return [Hash] Response data from the API
|
|
36
|
+
#
|
|
37
|
+
def get
|
|
38
|
+
request = Net::HTTP::Get.new(uri)
|
|
39
|
+
result = submit(uri, authorize(request))
|
|
40
|
+
JSON.parse(result.body)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Get a single named field from a business object for the current member
|
|
44
|
+
#
|
|
45
|
+
# @param name [String] Field name to return
|
|
46
|
+
#
|
|
47
|
+
# @return [Hash] Response data from the API
|
|
48
|
+
#
|
|
49
|
+
def get_field(name)
|
|
50
|
+
values = get['Properties']['$values']
|
|
51
|
+
value = values.find { |hash| hash['Name'] == name }['Value']
|
|
52
|
+
|
|
53
|
+
value.is_a?(String) ? value : value['$value']
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Update only specific fields on a business object for the current member
|
|
57
|
+
#
|
|
58
|
+
# @param fields [Hash] Conforms to pattern +{ field_key => value }+
|
|
59
|
+
#
|
|
60
|
+
# @return [Hash] Response data from the API
|
|
61
|
+
#
|
|
62
|
+
def put_fields(fields)
|
|
63
|
+
updated = filter_fields(fields)
|
|
64
|
+
put(updated)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Update a business object for the current member
|
|
68
|
+
#
|
|
69
|
+
# @param body [Hash] Full raw API object data
|
|
70
|
+
#
|
|
71
|
+
# @return [Hash] Response data from the API
|
|
72
|
+
#
|
|
73
|
+
def put(body)
|
|
74
|
+
request = Net::HTTP::Put.new(uri)
|
|
75
|
+
request.body = JSON.dump(body)
|
|
76
|
+
result = submit(uri, authorize(request))
|
|
77
|
+
JSON.parse(result.body)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Create a business object for the current member
|
|
81
|
+
#
|
|
82
|
+
# @param body [Hash] Full raw API object data
|
|
83
|
+
#
|
|
84
|
+
# @return [Hash] Response data from the API
|
|
85
|
+
#
|
|
86
|
+
def post(body)
|
|
87
|
+
request = Net::HTTP::Post.new(uri)
|
|
88
|
+
request.body = JSON.dump(body)
|
|
89
|
+
result = submit(uri, authorize(request))
|
|
90
|
+
JSON.parse(result.body)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Remove a business object for the current member
|
|
94
|
+
#
|
|
95
|
+
# @return [String] Error response body from the API, or empty string on success
|
|
96
|
+
#
|
|
97
|
+
def delete
|
|
98
|
+
request = Net::HTTP::Delete.new(uri)
|
|
99
|
+
result = submit(uri, authorize(request))
|
|
100
|
+
result.body
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def token = api.token
|
|
106
|
+
def token_expiration = api.token_expiration
|
|
107
|
+
|
|
108
|
+
# Construct a business object API endpoint address
|
|
109
|
+
#
|
|
110
|
+
def uri
|
|
111
|
+
id_for_url = url_id ? CGI.escape(url_id) : api.imis_id
|
|
112
|
+
full_path = "#{API_PATH}/#{business_object_name}/#{id_for_url}"
|
|
113
|
+
URI(File.join(Imis.configuration.hostname, full_path))
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Manually assemble the matching data structure, with fields in the correct order
|
|
117
|
+
#
|
|
118
|
+
def filter_fields(fields)
|
|
119
|
+
existing = get
|
|
120
|
+
|
|
121
|
+
JSON.parse(JSON.dump(existing)).tap do |updated|
|
|
122
|
+
# The first property is always the iMIS ID again
|
|
123
|
+
updated['Properties']['$values'] = [existing['Properties']['$values'][0]]
|
|
124
|
+
|
|
125
|
+
# Iterate through all existing fields
|
|
126
|
+
existing['Properties']['$values'].each do |value|
|
|
127
|
+
next unless fields.keys.include?(value['Name'])
|
|
128
|
+
|
|
129
|
+
# Strings are not wrapped in the type definition structure
|
|
130
|
+
new_value = fields[value['Name']]
|
|
131
|
+
if new_value.is_a?(String)
|
|
132
|
+
value['Value'] = new_value
|
|
133
|
+
else
|
|
134
|
+
value['Value']['$value'] = new_value
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Add the completed field with the updated value
|
|
138
|
+
updated['Properties']['$values'] << value
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
data/lib/usps/imis/config.rb
CHANGED
|
@@ -8,25 +8,31 @@ module Usps
|
|
|
8
8
|
IMIS_ROOT_URL_PROD = 'https://portal.americasboatingclub.org'
|
|
9
9
|
IMIS_ROOT_URL_DEV = 'https://abcdev.imiscloud.com'
|
|
10
10
|
|
|
11
|
-
attr_accessor :
|
|
11
|
+
attr_accessor :imis_id_query_name, :username, :password
|
|
12
|
+
attr_reader :environment
|
|
12
13
|
|
|
13
14
|
def initialize
|
|
15
|
+
@environment = defined?(Rails) ? Rails.env : ActiveSupport::StringInquirer.new('development')
|
|
16
|
+
@imis_id_query_name = ENV.fetch('IMIS_ID_QUERY_NAME', nil)
|
|
17
|
+
@username = ENV.fetch('IMIS_USERNAME', nil)
|
|
18
|
+
@password = ENV.fetch('IMIS_PASSWORD', nil)
|
|
19
|
+
|
|
14
20
|
yield self if block_given?
|
|
15
21
|
end
|
|
16
22
|
|
|
23
|
+
def environment=(env)
|
|
24
|
+
@environment = ActiveSupport::StringInquirer.new(env.to_s)
|
|
25
|
+
end
|
|
26
|
+
|
|
17
27
|
# Environment-specific API endpoint hostname
|
|
18
28
|
#
|
|
19
29
|
# @return The API hostname for the current environment
|
|
20
30
|
#
|
|
21
31
|
def hostname
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
IMIS_ROOT_URL_DEV
|
|
27
|
-
else
|
|
28
|
-
raise Error::ApiError, "Unexpected API environment: #{environment}"
|
|
29
|
-
end
|
|
32
|
+
return IMIS_ROOT_URL_PROD if environment.production?
|
|
33
|
+
return IMIS_ROOT_URL_DEV if environment.development?
|
|
34
|
+
|
|
35
|
+
raise Error::ApiError, "Unexpected API environment: #{environment}"
|
|
30
36
|
end
|
|
31
37
|
|
|
32
38
|
# Ruby 3.5 instance variable filter
|