usps-imis-api 1.0.0.pre.rc.8 → 1.0.0.pre.rc.10
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/Readme.md +3 -323
- data/lib/usps/imis/api.rb +21 -17
- data/lib/usps/imis/business_object.rb +18 -18
- data/lib/usps/imis/config.rb +6 -2
- data/lib/usps/imis/data.rb +4 -0
- data/lib/usps/imis/error.rb +2 -1
- data/lib/usps/imis/errors/missing_id_error.rb +15 -0
- data/lib/usps/imis/panels/base_panel.rb +2 -1
- data/lib/usps/imis/properties.rb +14 -14
- data/lib/usps/imis/query.rb +78 -25
- data/lib/usps/imis/requests.rb +12 -4
- data/lib/usps/imis/version.rb +1 -1
- data/spec/support/usps/vcr/config.rb +47 -0
- data/spec/support/usps/vcr/filters.rb +89 -0
- data/spec/support/usps/vcr.rb +8 -0
- metadata +6 -27
- data/.github/workflows/main.yml +0 -57
- data/.gitignore +0 -5
- data/.rspec +0 -2
- data/.rubocop.yml +0 -89
- data/.ruby-version +0 -1
- data/.simplecov +0 -8
- data/Gemfile +0 -12
- data/Gemfile.lock +0 -129
- data/Rakefile +0 -12
- data/bin/console +0 -21
- data/bin/setup +0 -8
- data/spec/lib/usps/imis/api_spec.rb +0 -171
- data/spec/lib/usps/imis/business_object_spec.rb +0 -87
- data/spec/lib/usps/imis/config_spec.rb +0 -59
- data/spec/lib/usps/imis/data_spec.rb +0 -66
- data/spec/lib/usps/imis/error_spec.rb +0 -17
- data/spec/lib/usps/imis/errors/response_error_spec.rb +0 -107
- data/spec/lib/usps/imis/mapper_spec.rb +0 -55
- data/spec/lib/usps/imis/mocks/business_object_spec.rb +0 -65
- data/spec/lib/usps/imis/panels/base_panel_spec.rb +0 -33
- data/spec/lib/usps/imis/panels/education_spec.rb +0 -70
- data/spec/lib/usps/imis/panels/vsc_spec.rb +0 -37
- data/spec/lib/usps/imis/properties_spec.rb +0 -19
- data/spec/lib/usps/imis_spec.rb +0 -11
- data/spec/spec_helper.rb +0 -38
- data/usps-imis-api.gemspec +0 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ebdfba18c4fc6164bd3e5fbf4fe366d2ca81dbe37420bca3035d705b2309319f
|
|
4
|
+
data.tar.gz: 0244bef394a72715ddf7a6e2219f37bcbc93f025ae87c70fe3928fcd45201b0e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cd525cf6c237a7d13addbbd1faeed94b0c8a2cdecc7392d116447e19b91ba69a6d21064c517ce8b1885152eb55d918e129f681238e7b7912d20b0b408a09e441
|
|
7
|
+
data.tar.gz: 7c56a167b87a406f23e89d1c4bc0fb964e19e05b89c36eb180b2deae3f157c47f25c69a8c95cc7eff0f32fb975872edcf4b8cab28ca11516b7b96be430e63e32
|
data/Readme.md
CHANGED
|
@@ -39,331 +39,9 @@ Usps::Imis.configure do |config|
|
|
|
39
39
|
end
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
When using `bin/console`, this configuration will be run by default.
|
|
43
|
-
|
|
44
|
-
Instantiate the API object:
|
|
45
|
-
|
|
46
|
-
```ruby
|
|
47
|
-
api = Usps::Imis::Api.new
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
If you already have an iMIS ID to work with, you can pass that in immediately:
|
|
51
|
-
|
|
52
|
-
```ruby
|
|
53
|
-
api = Usps::Imis::Api.new(imis_id: imis_id)
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Authentication
|
|
57
|
-
|
|
58
|
-
If a token is not available, this will automatically fetch one when needed. As long as that token
|
|
59
|
-
should still be valid, it will reuse the same token. If the token should expire, this will
|
|
60
|
-
automatically request a new token.
|
|
61
|
-
|
|
62
42
|
## Usage
|
|
63
43
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
To act on member data, you need to have the iMIS ID. If you already have access to that from the
|
|
67
|
-
database, you can skip this step.
|
|
68
|
-
|
|
69
|
-
To convert a member's certificate number into their iMIS ID, run the following method:
|
|
70
|
-
|
|
71
|
-
```ruby
|
|
72
|
-
api.imis_id_for(certificate)
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
This will both return the ID, and store it for use with other requests. If you need to change which
|
|
76
|
-
member you are working with, just run this method again with the new certificate number.
|
|
77
|
-
|
|
78
|
-
You can also manually set the current ID, if you already have it for a given member
|
|
79
|
-
|
|
80
|
-
```ruby
|
|
81
|
-
api.imis_id = imis_id
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
#### Without an iMIS ID
|
|
85
|
-
|
|
86
|
-
Running requests without an iMIS ID set will result in query results returned from the API.
|
|
87
|
-
|
|
88
|
-
### Business Object and Panel Actions
|
|
89
|
-
|
|
90
|
-
Business Objects and Panels support the following actions.
|
|
91
|
-
|
|
92
|
-
Panels require passing in the ordinal identifier as an argument, except for `POST`.
|
|
93
|
-
|
|
94
|
-
#### GET
|
|
95
|
-
|
|
96
|
-
To fetch member data, run e.g.:
|
|
97
|
-
|
|
98
|
-
```ruby
|
|
99
|
-
data = api.on('ABC_ASC_Individual_Demog').get
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
You can also pass in specific field names to filter the returned member data, e.g.:
|
|
103
|
-
|
|
104
|
-
```ruby
|
|
105
|
-
data = api.on('ABC_ASC_Individual_Demog').get('TotMMS', 'MMS_Updated')
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
The response from `get` behaves like a Hash, but directly accesses property values by name.
|
|
109
|
-
If you need to access the rest of the underlying data, use the `raw` method:
|
|
110
|
-
|
|
111
|
-
```ruby
|
|
112
|
-
data = api.on('ABC_ASC_Individual_Demog').get
|
|
113
|
-
data['TotMMS']
|
|
114
|
-
data.raw['EntityTypeName']
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Alias: `read`
|
|
118
|
-
|
|
119
|
-
#### GET Field
|
|
120
|
-
|
|
121
|
-
To fetch a specific field from member data, run e.g.:
|
|
122
|
-
|
|
123
|
-
```ruby
|
|
124
|
-
tot_mms = api.on('ABC_ASC_Individual_Demog').get_field('TotMMS')
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
You can also access fields directly on the Business Object or Panel like a Hash:
|
|
128
|
-
|
|
129
|
-
```ruby
|
|
130
|
-
tot_mms = api.on('ABC_ASC_Individual_Demog')['TotMMS']
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Alias: `fetch`
|
|
134
|
-
|
|
135
|
-
#### GET Fields
|
|
136
|
-
|
|
137
|
-
To fetch multiple specific fields from member data, run e.g.:
|
|
138
|
-
|
|
139
|
-
```ruby
|
|
140
|
-
data = api.on('ABC_ASC_Individual_Demog').get_fields('TotMMS', 'MMS_Updated')
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Alias: `fetch_all`
|
|
144
|
-
|
|
145
|
-
#### PUT Fields
|
|
146
|
-
|
|
147
|
-
To update member data, run e.g.:
|
|
148
|
-
|
|
149
|
-
```ruby
|
|
150
|
-
data = { 'MMS_Updated' => Time.now.strftime('%Y-%m-%dT%H:%M:%S'), 'TotMMS' => new_total }
|
|
151
|
-
update = api.on('ABC_ASC_Individual_Demog').put_fields(data)
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
This method fetches the current data structure, and filters it down to just what you want to
|
|
155
|
-
update, to reduce the likelihood of update collisions or type validation failures.
|
|
156
|
-
|
|
157
|
-
Alias: `patch`
|
|
158
|
-
|
|
159
|
-
#### PUT
|
|
160
|
-
|
|
161
|
-
To update member data, run e.g.:
|
|
162
|
-
|
|
163
|
-
```ruby
|
|
164
|
-
update = api.on('ABC_ASC_Individual_Demog').put(complete_imis_object)
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
This method requires a complete iMIS data structure. However, any properties not included will be
|
|
168
|
-
left unmodified (meaning this also effectively handles `PATCH`, though iMIS does not accept that
|
|
169
|
-
HTTP verb).
|
|
170
|
-
|
|
171
|
-
Alias: `update`
|
|
172
|
-
|
|
173
|
-
#### POST
|
|
174
|
-
|
|
175
|
-
To create new member data, run e.g.:
|
|
176
|
-
|
|
177
|
-
```ruby
|
|
178
|
-
created = api.on('ABC_ASC_Individual_Demog').post(complete_imis_object)
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
This method requires a complete iMIS data structure.
|
|
182
|
-
|
|
183
|
-
Alias: `create`
|
|
184
|
-
|
|
185
|
-
#### DELETE
|
|
186
|
-
|
|
187
|
-
To remove member data, run e.g.:
|
|
188
|
-
|
|
189
|
-
```ruby
|
|
190
|
-
api.on('ABC_ASC_Individual_Demog').delete
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Alias: `destroy`
|
|
194
|
-
|
|
195
|
-
### QUERY
|
|
196
|
-
|
|
197
|
-
Run an IQA Query
|
|
198
|
-
|
|
199
|
-
`query_params` is a hash of shape: `{ param_name => param_value }`
|
|
200
|
-
|
|
201
|
-
```ruby
|
|
202
|
-
query = api.query(query_name, query_params)
|
|
203
|
-
|
|
204
|
-
query.each do |item|
|
|
205
|
-
# Download all pages of the query, then iterate on the results
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
query.find_each do |item|
|
|
209
|
-
# Iterate one page at a time, fetching new pages automatically
|
|
210
|
-
end
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Field Mapper
|
|
214
|
-
|
|
215
|
-
For fields that have already been mapped between the ITCom database and iMIS, you can use the
|
|
216
|
-
Mapper class to further simplify the `fetch` / `update` interfaces:
|
|
217
|
-
|
|
218
|
-
```ruby
|
|
219
|
-
mm = api.mapper.fetch(:mm)
|
|
220
|
-
mm = api.mapper[:mm]
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
```ruby
|
|
224
|
-
api.mapper.update(mm: 15)
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
For simplicity, you can also call `fetch` (or simply use Hash access syntax) and `update` on the
|
|
228
|
-
`Api` class directly:
|
|
229
|
-
|
|
230
|
-
```ruby
|
|
231
|
-
api.fetch(:mm)
|
|
232
|
-
api[:mm]
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
```ruby
|
|
236
|
-
api.update(mm: 15)
|
|
237
|
-
api[:mm] = 15
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
If there is no known mapping for the requested field, the Mapper will give up, but will provide
|
|
241
|
-
you with the standard API call syntax, and will suggest you inform ITCom leadership of the new
|
|
242
|
-
mapping you need.
|
|
243
|
-
|
|
244
|
-
### Panels
|
|
245
|
-
|
|
246
|
-
For supported panels (usually, business objects with composite identity keys), you can interact
|
|
247
|
-
with them in the same general way:
|
|
248
|
-
|
|
249
|
-
```ruby
|
|
250
|
-
vsc = Usps::Imis::Panels::Vsc.new(imis_id: 6374)
|
|
251
|
-
|
|
252
|
-
vsc.get(1417)
|
|
253
|
-
|
|
254
|
-
# All of these options are identical
|
|
255
|
-
#
|
|
256
|
-
vsc.get(1417, 'Quantity').first
|
|
257
|
-
vsc.get(1417)['Quantity']
|
|
258
|
-
vsc[1417, 'Quantity']
|
|
259
|
-
vsc.get(1417).raw['Properties']['$values'].find { it['Name'] == 'Quantity' }['Value']['$value']
|
|
260
|
-
vsc.get_field(1417, 'Quantity')
|
|
261
|
-
|
|
262
|
-
created = vsc.create(certificate: 'E136924', year: 2024, count: 42)
|
|
263
|
-
|
|
264
|
-
# Get the Ordinal identifier from the response
|
|
265
|
-
#
|
|
266
|
-
# All of these options are identical
|
|
267
|
-
#
|
|
268
|
-
ordinal = created.ordinal
|
|
269
|
-
ordinal = created['Ordinal']
|
|
270
|
-
ordinal = created.raw['Properties']['$values'].find { it['Name'] == 'Ordinal' }['Value']['$value']
|
|
271
|
-
ordinal = created.raw['Identity']['IdentityElements']['$values'][1].to_i # Value is duplicated here
|
|
272
|
-
|
|
273
|
-
vsc.update(certificate: 'E136924', year: 2024, count: 43, ordinal: ordinal)
|
|
274
|
-
|
|
275
|
-
vsc.put_fields(ordinal, 'Quantity' => 44)
|
|
276
|
-
vsc['Quantity'] = 44
|
|
277
|
-
|
|
278
|
-
vsc.destroy(ordinal)
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
If you already have an iMIS ID to work with, you can pass that in immediately:
|
|
282
|
-
|
|
283
|
-
```ruby
|
|
284
|
-
vsc = Usps::Imis::Panels::Vsc.new(imis_id: imis_id)
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
Panels are also accessible directly from the API object:
|
|
288
|
-
|
|
289
|
-
```ruby
|
|
290
|
-
api.panels.vsc.get(1417)
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### DSL Mode
|
|
294
|
-
|
|
295
|
-
Instead of manually setting the current iMIS ID, then running individual queries, you can instead
|
|
296
|
-
run queries in DSL mode. This specifies the iMIS ID for the scope of the block, then reverts to the
|
|
297
|
-
previous value.
|
|
298
|
-
|
|
299
|
-
```ruby
|
|
300
|
-
api.with(31092) do
|
|
301
|
-
# These requests are identical:
|
|
302
|
-
|
|
303
|
-
on('ABC_ASC_Individual_Demog') { put_fields('TotMMS' => 15) }
|
|
304
|
-
|
|
305
|
-
on('ABC_ASC_Individual_Demog').put_fields('TotMMS' => 15)
|
|
306
|
-
|
|
307
|
-
mapper.update(mm: 15)
|
|
308
|
-
|
|
309
|
-
update(mm: 15)
|
|
310
|
-
|
|
311
|
-
mapper[:mm] = 15
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
# This request fetches the same data, but leaves the iMIS ID selected
|
|
315
|
-
api.with(31092)[:mm] = 15
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
```ruby
|
|
319
|
-
api.with(6374) do
|
|
320
|
-
panels.vsc.get(1417)
|
|
321
|
-
end
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
```ruby
|
|
325
|
-
api.with(31092) do
|
|
326
|
-
# These requests are identical:
|
|
327
|
-
|
|
328
|
-
on('ABC_ASC_Individual_Demog') do
|
|
329
|
-
get.raw['Properties']['$values'].find { it['Name'] == 'TotMMS' }['Value']['$value']
|
|
330
|
-
|
|
331
|
-
get['TotMMS']
|
|
332
|
-
|
|
333
|
-
get_field('TotMMS')
|
|
334
|
-
|
|
335
|
-
get_fields('TotMMS').first
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
on('ABC_ASC_Individual_Demog').get_field('TotMMS')
|
|
339
|
-
|
|
340
|
-
on('ABC_ASC_Individual_Demog')['TotMMS']
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
# These requests fetch the same data, but leave the iMIS ID selected
|
|
344
|
-
api.with(31092).on('ABC_ASC_Individual_Demog').get_field('TotMMS')
|
|
345
|
-
api.with(31092).on('ABC_ASC_Individual_Demog')['TotMMS']
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### Data Methods
|
|
349
|
-
|
|
350
|
-
Data responses from the API can be handled as a standard Hash using the `raw` method.
|
|
351
|
-
|
|
352
|
-
If you need to access all of the property values, you can use the `properties` method.
|
|
353
|
-
By default, this will exclude the `ID` and `Ordinal` properties; they can be included with
|
|
354
|
-
`properties(include_ids: true)`.
|
|
355
|
-
|
|
356
|
-
## Test Data Mocking
|
|
357
|
-
|
|
358
|
-
You can use the provided Business Object Mock to generate stub data for rspec:
|
|
359
|
-
|
|
360
|
-
```ruby
|
|
361
|
-
allow(api).to(
|
|
362
|
-
receive(:on).with('ABC_ASC_Individual_Demog').and_return(
|
|
363
|
-
Usps::Imis::Mocks::BusinessObject.new(TotMMS: 2)
|
|
364
|
-
)
|
|
365
|
-
)
|
|
366
|
-
```
|
|
44
|
+
For more details and examples, refer to the [Wiki](https://github.com/unitedstatespowersquadrons/imis-api-ruby/wiki).
|
|
367
45
|
|
|
368
46
|
## Exception Handling
|
|
369
47
|
|
|
@@ -377,6 +55,8 @@ Testing is available by running:
|
|
|
377
55
|
bundle exec rspec
|
|
378
56
|
```
|
|
379
57
|
|
|
58
|
+
API web requests are sanitized and recorded using VCR.
|
|
59
|
+
|
|
380
60
|
Linting is available by running:
|
|
381
61
|
|
|
382
62
|
```ruby
|
data/lib/usps/imis/api.rb
CHANGED
|
@@ -36,11 +36,9 @@ module Usps
|
|
|
36
36
|
|
|
37
37
|
# A new instance of +Api+
|
|
38
38
|
#
|
|
39
|
-
# @param skip_authentication [bool] Skip authentication on initialization (used for tests)
|
|
40
39
|
# @param imis_id [Integer, String] iMIS ID to select immediately on initialization
|
|
41
40
|
#
|
|
42
|
-
def initialize(
|
|
43
|
-
authenticate unless skip_authentication
|
|
41
|
+
def initialize(imis_id: nil)
|
|
44
42
|
self.imis_id = imis_id if imis_id
|
|
45
43
|
end
|
|
46
44
|
|
|
@@ -63,8 +61,12 @@ module Usps
|
|
|
63
61
|
def imis_id_for(certificate)
|
|
64
62
|
raise Errors::LockedIdError if lock_imis_id
|
|
65
63
|
|
|
64
|
+
logger.debug "Fetching iMIS ID for #{certificate}"
|
|
65
|
+
|
|
66
66
|
begin
|
|
67
|
-
|
|
67
|
+
result = query(Imis.configuration.imis_id_query_name, { certificate: }).tap { logger.debug it }
|
|
68
|
+
page = result.page.tap { logger.debug it }
|
|
69
|
+
self.imis_id = page.first['ID'].to_i
|
|
68
70
|
rescue StandardError
|
|
69
71
|
raise Errors::NotFoundError, 'Member not found'
|
|
70
72
|
end
|
|
@@ -97,16 +99,16 @@ module Usps
|
|
|
97
99
|
end
|
|
98
100
|
end
|
|
99
101
|
|
|
100
|
-
# Build
|
|
102
|
+
# Build a Query interface
|
|
103
|
+
#
|
|
104
|
+
# Works with IQA queries and Business Objects
|
|
101
105
|
#
|
|
102
|
-
# @param query_name [String] Full path of the query
|
|
106
|
+
# @param query_name [String] Full path of the query, e.g. +$/_ABC/Fiander/iMIS_ID+
|
|
103
107
|
# @query_params [Hash] Conforms to pattern +{ param_name => param_value }+
|
|
104
108
|
#
|
|
105
|
-
# @return [
|
|
109
|
+
# @return [Usps::Imis::Query] Query wrapper
|
|
106
110
|
#
|
|
107
|
-
def query(query_name, query_params = {})
|
|
108
|
-
Query.new(self, query_name, query_params)
|
|
109
|
-
end
|
|
111
|
+
def query(query_name, query_params = {}) = Query.new(self, query_name, **query_params)
|
|
110
112
|
|
|
111
113
|
# Run requests as DSL, with specific +BusinessObject+ only maintained for this scope
|
|
112
114
|
#
|
|
@@ -162,20 +164,22 @@ module Usps
|
|
|
162
164
|
def authenticate
|
|
163
165
|
logger.debug 'Authenticating with iMIS'
|
|
164
166
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
authentication_data = {
|
|
167
|
+
request = http_post
|
|
168
|
+
request.body = URI.encode_www_form(
|
|
168
169
|
grant_type: 'password',
|
|
169
170
|
username: Imis.configuration.username,
|
|
170
171
|
password: Imis.configuration.password
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
result = submit(uri, req)
|
|
172
|
+
)
|
|
173
|
+
result = submit(uri, request)
|
|
174
174
|
json = JSON.parse(result.body)
|
|
175
175
|
|
|
176
176
|
@token = json['access_token']
|
|
177
|
-
@token_expiration = Time.
|
|
177
|
+
@token_expiration = Time.now - json['expires_in'] - 60
|
|
178
178
|
end
|
|
179
|
+
|
|
180
|
+
# URI for the authentication endpoint
|
|
181
|
+
#
|
|
182
|
+
def uri(...) = URI(File.join(Imis.configuration.hostname, AUTHENTICATION_PATH))
|
|
179
183
|
end
|
|
180
184
|
end
|
|
181
185
|
end
|
|
@@ -33,6 +33,12 @@ module Usps
|
|
|
33
33
|
@ordinal = ordinal
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Run a query on the entire business object
|
|
37
|
+
#
|
|
38
|
+
# @return [Usps::Imis::Query] Query wrapper
|
|
39
|
+
#
|
|
40
|
+
def query = api.query(business_object_name)
|
|
41
|
+
|
|
36
42
|
# Get a business object for the current member
|
|
37
43
|
#
|
|
38
44
|
# If +fields+ is provided, will return only those field values
|
|
@@ -93,7 +99,7 @@ module Usps
|
|
|
93
99
|
#
|
|
94
100
|
# @return [Usps::Imis::Data] Response data from the API
|
|
95
101
|
#
|
|
96
|
-
def put(body) = put_object(
|
|
102
|
+
def put(body) = put_object(http_put, body)
|
|
97
103
|
alias update put
|
|
98
104
|
|
|
99
105
|
# Create a business object for the current member
|
|
@@ -102,14 +108,14 @@ module Usps
|
|
|
102
108
|
#
|
|
103
109
|
# @return [Usps::Imis::Data] Response data from the API
|
|
104
110
|
#
|
|
105
|
-
def post(body) = put_object(
|
|
111
|
+
def post(body) = put_object(http_post, body)
|
|
106
112
|
alias create post
|
|
107
113
|
|
|
108
114
|
# Remove a business object for the current member
|
|
109
115
|
#
|
|
110
116
|
# @return [true] Only on success response (i.e. blank string from the API)
|
|
111
117
|
#
|
|
112
|
-
def delete = submit(uri, authorize(
|
|
118
|
+
def delete = submit(uri, authorize(http_delete)).body == '' # rubocop:disable Naming/PredicateMethod
|
|
113
119
|
alias destroy delete
|
|
114
120
|
|
|
115
121
|
# Ruby 3.5 instance variable filter
|
|
@@ -118,9 +124,6 @@ module Usps
|
|
|
118
124
|
|
|
119
125
|
private
|
|
120
126
|
|
|
121
|
-
def token = api.token
|
|
122
|
-
def token_expiration = api.token_expiration
|
|
123
|
-
|
|
124
127
|
def logger = Imis.logger('BusinessObject')
|
|
125
128
|
|
|
126
129
|
# Construct a business object API endpoint address
|
|
@@ -154,13 +157,7 @@ module Usps
|
|
|
154
157
|
# Skip unmodified fields
|
|
155
158
|
next unless fields.keys.include?(value['Name'])
|
|
156
159
|
|
|
157
|
-
|
|
158
|
-
new_value = fields[value['Name']]
|
|
159
|
-
if new_value.is_a?(String)
|
|
160
|
-
value['Value'] = new_value
|
|
161
|
-
else
|
|
162
|
-
value['Value']['$value'] = new_value
|
|
163
|
-
end
|
|
160
|
+
value['Value'] = Properties.wrap(fields[value['Name']])
|
|
164
161
|
|
|
165
162
|
# Add the completed field with the updated value
|
|
166
163
|
updated['Properties']['$values'] << value
|
|
@@ -173,9 +170,10 @@ module Usps
|
|
|
173
170
|
# Useful for stubbing data in tests
|
|
174
171
|
#
|
|
175
172
|
def raw_object
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
173
|
+
raise Errors::MissingIdError if api.imis_id.nil?
|
|
174
|
+
|
|
175
|
+
response = submit(uri, authorize(http_get))
|
|
176
|
+
result = Data.from_json(response.body)
|
|
179
177
|
JSON.pretty_generate(result).split("\n").each { logger.debug " -> #{it}" }
|
|
180
178
|
result
|
|
181
179
|
end
|
|
@@ -183,9 +181,11 @@ module Usps
|
|
|
183
181
|
# Upload an object to the API
|
|
184
182
|
#
|
|
185
183
|
def put_object(request, body)
|
|
184
|
+
raise Errors::MissingIdError if api.imis_id.nil?
|
|
185
|
+
|
|
186
186
|
request.body = JSON.dump(body)
|
|
187
|
-
|
|
188
|
-
result = Data.from_json(
|
|
187
|
+
response = submit(uri, authorize(request))
|
|
188
|
+
result = Data.from_json(response.body)
|
|
189
189
|
JSON.pretty_generate(result).split("\n").each { logger.debug " -> #{it}" }
|
|
190
190
|
result
|
|
191
191
|
end
|
data/lib/usps/imis/config.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Usps
|
|
|
12
12
|
attr_reader :environment, :logger, :logger_level
|
|
13
13
|
|
|
14
14
|
def initialize
|
|
15
|
-
@environment = defined?(Rails) ? Rails.env : ActiveSupport::StringInquirer.new('development')
|
|
15
|
+
@environment = defined?(::Rails) ? ::Rails.env : ActiveSupport::StringInquirer.new('development')
|
|
16
16
|
@imis_id_query_name = ENV.fetch('IMIS_ID_QUERY_NAME', nil)
|
|
17
17
|
@username = ENV.fetch('IMIS_USERNAME', nil)
|
|
18
18
|
@password = ENV.fetch('IMIS_PASSWORD', nil)
|
|
@@ -31,6 +31,10 @@ module Usps
|
|
|
31
31
|
@logger = ActiveSupport::TaggedLogging.new(logger)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
def silence!
|
|
35
|
+
self.logger = Logger.new(nil)
|
|
36
|
+
end
|
|
37
|
+
|
|
34
38
|
# Environment-specific API endpoint hostname
|
|
35
39
|
#
|
|
36
40
|
# @return The API hostname for the current environment
|
|
@@ -44,7 +48,7 @@ module Usps
|
|
|
44
48
|
|
|
45
49
|
# Ruby 3.5 instance variable filter
|
|
46
50
|
#
|
|
47
|
-
def instance_variables_to_inspect = %i[@
|
|
51
|
+
def instance_variables_to_inspect = instance_variables - %i[@password @logger]
|
|
48
52
|
|
|
49
53
|
# Parameters to filter out of logging
|
|
50
54
|
#
|
data/lib/usps/imis/data.rb
CHANGED
|
@@ -50,6 +50,10 @@ module Usps
|
|
|
50
50
|
.index_with { self[it] }
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def []=(...)
|
|
54
|
+
raise Errors::ApiError, '`Data` does not support setting values. If you need to modify it, call `.raw` on it.'
|
|
55
|
+
end
|
|
56
|
+
|
|
53
57
|
def inspect
|
|
54
58
|
stringio = StringIO.new
|
|
55
59
|
PP.pp(self, stringio)
|
data/lib/usps/imis/error.rb
CHANGED
|
@@ -18,7 +18,7 @@ module Usps
|
|
|
18
18
|
super(message)
|
|
19
19
|
@metadata = metadata
|
|
20
20
|
|
|
21
|
-
Imis.logger.
|
|
21
|
+
Imis.logger(self.class.name).error self
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# Additional metadata to include in Bugsnag reports
|
|
@@ -47,6 +47,7 @@ require_relative 'errors/api_error'
|
|
|
47
47
|
require_relative 'errors/config_error'
|
|
48
48
|
require_relative 'errors/locked_id_error'
|
|
49
49
|
require_relative 'errors/mapper_error'
|
|
50
|
+
require_relative 'errors/missing_id_error'
|
|
50
51
|
require_relative 'errors/not_found_error'
|
|
51
52
|
require_relative 'errors/response_error'
|
|
52
53
|
require_relative 'errors/panel_unimplemented_error'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
module Errors
|
|
6
|
+
# Exception raised when attempting to access a +BusinessObject+ without an iMIS ID
|
|
7
|
+
#
|
|
8
|
+
class MissingIdError < Error
|
|
9
|
+
def initialize = super(message)
|
|
10
|
+
|
|
11
|
+
def message = 'Cannot access an individual Business Object without an iMIS ID'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -51,12 +51,13 @@ module Usps
|
|
|
51
51
|
|
|
52
52
|
# Update a single named field on a business object for the current member
|
|
53
53
|
#
|
|
54
|
+
# @param ordinal [Integer] The ordinal identifier for the desired object
|
|
54
55
|
# @param field [String] Name of the field
|
|
55
56
|
# @param value Value of the field
|
|
56
57
|
#
|
|
57
58
|
# @return [Usps::Imis::Data] Response data from the API
|
|
58
59
|
#
|
|
59
|
-
def put_field(field, value) = api.on(business_object, ordinal:).put_field(field, value)
|
|
60
|
+
def put_field(ordinal, field, value) = api.on(business_object, ordinal:).put_field(field, value)
|
|
60
61
|
alias []= put_field
|
|
61
62
|
|
|
62
63
|
# Update only specific fields on a Panel for the current member
|
data/lib/usps/imis/properties.rb
CHANGED
|
@@ -9,6 +9,19 @@ module Usps
|
|
|
9
9
|
#
|
|
10
10
|
def self.build(&) = new.build(&)
|
|
11
11
|
|
|
12
|
+
# Wrap value in the API-internal type structure
|
|
13
|
+
#
|
|
14
|
+
def self.wrap(value)
|
|
15
|
+
case value
|
|
16
|
+
when String then value
|
|
17
|
+
when Time, DateTime then value.strftime('%Y-%m-%dT%H:%I:%S')
|
|
18
|
+
when Integer then { '$type' => 'System.Int32', '$value' => value }
|
|
19
|
+
when true, false then { '$type' => 'System.Boolean', '$value' => value }
|
|
20
|
+
else
|
|
21
|
+
raise Errors::UnexpectedPropertyTypeError.from(value)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
12
25
|
# Build the data for the Properties field
|
|
13
26
|
#
|
|
14
27
|
def build
|
|
@@ -29,22 +42,9 @@ module Usps
|
|
|
29
42
|
@properties << {
|
|
30
43
|
'$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
|
|
31
44
|
'Name' => name,
|
|
32
|
-
'Value' => wrap(value)
|
|
45
|
+
'Value' => self.class.wrap(value)
|
|
33
46
|
}
|
|
34
47
|
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
48
|
end
|
|
49
49
|
end
|
|
50
50
|
end
|