usps-imis-api 0.10.3 → 0.11.0
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 +1 -372
- data/lib/usps/imis/business_object.rb +12 -0
- data/lib/usps/imis/command_line/interface.rb +149 -0
- data/lib/usps/imis/command_line/options_parser.rb +93 -0
- data/lib/usps/imis/command_line.rb +15 -0
- data/lib/usps/imis/config.rb +9 -1
- data/lib/usps/imis/data.rb +1 -1
- data/lib/usps/imis/error.rb +1 -0
- data/lib/usps/imis/errors/command_line_error.rb +11 -0
- data/lib/usps/imis/version.rb +1 -1
- data/lib/usps/imis.rb +8 -0
- metadata +33 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a938a3621c6e575ac4d205495bbdccf39b4579a986a4431730d366bee510824
|
|
4
|
+
data.tar.gz: 7cfbacbf135e1f9899510fc54ff05aa079bf70f24b1c93b81de0bcd78fc26218
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 679178e131a5fa45915738be179b82173fa8383c0109ba6fe5e2d73b9f85c1ecdb6bc160e94ed385866371ed10e465de17a05409b55de64e015f8d44d4b12c0a
|
|
7
|
+
data.tar.gz: 66466bcfca8c602e00a04a3f52599cf96f0bae5d64065542e615bbb8acb9cab27fd5b44552e9367ecdb8ef3004ac9a40e3417a5ec8bc6a46b8ffb04cff7aed96
|
data/Readme.md
CHANGED
|
@@ -39,380 +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 raise an exception.
|
|
87
|
-
|
|
88
|
-
If you want to query the entire Business Object, use the query interface instead:
|
|
89
|
-
|
|
90
|
-
```ruby
|
|
91
|
-
api = Usps::Imis::Api.new # No iMIS ID set
|
|
92
|
-
|
|
93
|
-
# These requests are identical:
|
|
94
|
-
|
|
95
|
-
Usps::Imis::Query.new(api, 'ABC_ASC_Individual_Demog')
|
|
96
|
-
|
|
97
|
-
api.on('ABC_ASC_Individual_Demog').query
|
|
98
|
-
|
|
99
|
-
api.query('ABC_ASC_Individual_Demog')
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Business Object and Panel Actions
|
|
103
|
-
|
|
104
|
-
Business Objects and Panels support the following actions.
|
|
105
|
-
|
|
106
|
-
Panels require passing in the ordinal identifier as an argument, except for `POST`.
|
|
107
|
-
|
|
108
|
-
#### GET
|
|
109
|
-
|
|
110
|
-
To fetch member data, run e.g.:
|
|
111
|
-
|
|
112
|
-
```ruby
|
|
113
|
-
data = api.on('ABC_ASC_Individual_Demog').get
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
You can also pass in specific field names to filter the returned member data, e.g.:
|
|
117
|
-
|
|
118
|
-
```ruby
|
|
119
|
-
data = api.on('ABC_ASC_Individual_Demog').get('TotMMS', 'MMS_Updated')
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
The response from `get` behaves like a Hash, but directly accesses property values by name.
|
|
123
|
-
If you need to access the rest of the underlying data, use the `raw` method:
|
|
124
|
-
|
|
125
|
-
```ruby
|
|
126
|
-
data = api.on('ABC_ASC_Individual_Demog').get
|
|
127
|
-
data['TotMMS']
|
|
128
|
-
data.raw['EntityTypeName']
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Alias: `read`
|
|
132
|
-
|
|
133
|
-
#### GET Field
|
|
134
|
-
|
|
135
|
-
To fetch a specific field from member data, run e.g.:
|
|
136
|
-
|
|
137
|
-
```ruby
|
|
138
|
-
tot_mms = api.on('ABC_ASC_Individual_Demog').get_field('TotMMS')
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
You can also access fields directly on the Business Object or Panel like a Hash:
|
|
142
|
-
|
|
143
|
-
```ruby
|
|
144
|
-
tot_mms = api.on('ABC_ASC_Individual_Demog')['TotMMS']
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Alias: `fetch`
|
|
148
|
-
|
|
149
|
-
#### GET Fields
|
|
150
|
-
|
|
151
|
-
To fetch multiple specific fields from member data, run e.g.:
|
|
152
|
-
|
|
153
|
-
```ruby
|
|
154
|
-
data = api.on('ABC_ASC_Individual_Demog').get_fields('TotMMS', 'MMS_Updated')
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
Alias: `fetch_all`
|
|
158
|
-
|
|
159
|
-
#### PUT Fields
|
|
160
|
-
|
|
161
|
-
To update member data, run e.g.:
|
|
162
|
-
|
|
163
|
-
```ruby
|
|
164
|
-
data = { 'MMS_Updated' => Time.now.strftime('%Y-%m-%dT%H:%M:%S'), 'TotMMS' => new_total }
|
|
165
|
-
update = api.on('ABC_ASC_Individual_Demog').put_fields(data)
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
This method fetches the current data structure, and filters it down to just what you want to
|
|
169
|
-
update, to reduce the likelihood of update collisions or type validation failures.
|
|
170
|
-
|
|
171
|
-
Alias: `patch`
|
|
172
|
-
|
|
173
|
-
#### PUT
|
|
174
|
-
|
|
175
|
-
To update member data, run e.g.:
|
|
176
|
-
|
|
177
|
-
```ruby
|
|
178
|
-
update = api.on('ABC_ASC_Individual_Demog').put(complete_imis_object)
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
This method requires a complete iMIS data structure. However, any properties not included will be
|
|
182
|
-
left unmodified (meaning this also effectively handles `PATCH`, though iMIS does not accept that
|
|
183
|
-
HTTP verb).
|
|
184
|
-
|
|
185
|
-
Alias: `update`
|
|
186
|
-
|
|
187
|
-
#### POST
|
|
188
|
-
|
|
189
|
-
To create new member data, run e.g.:
|
|
190
|
-
|
|
191
|
-
```ruby
|
|
192
|
-
created = api.on('ABC_ASC_Individual_Demog').post(complete_imis_object)
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
This method requires a complete iMIS data structure.
|
|
196
|
-
|
|
197
|
-
Alias: `create`
|
|
198
|
-
|
|
199
|
-
#### DELETE
|
|
200
|
-
|
|
201
|
-
To remove member data, run e.g.:
|
|
202
|
-
|
|
203
|
-
```ruby
|
|
204
|
-
api.on('ABC_ASC_Individual_Demog').delete
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
Alias: `destroy`
|
|
208
|
-
|
|
209
|
-
### QUERY
|
|
210
|
-
|
|
211
|
-
Run an IQA Query
|
|
212
|
-
|
|
213
|
-
`query_params` is a hash of shape: `{ param_name => param_value }`
|
|
214
|
-
|
|
215
|
-
```ruby
|
|
216
|
-
query = api.query(query_name, query_params)
|
|
217
|
-
|
|
218
|
-
query.each do |item|
|
|
219
|
-
# Download all pages of the query, then iterate on the results
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
query.find_each do |item|
|
|
223
|
-
# Iterate one page at a time, fetching new pages automatically
|
|
224
|
-
end
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### Field Mapper
|
|
228
|
-
|
|
229
|
-
For fields that have already been mapped between the ITCom database and iMIS, you can use the
|
|
230
|
-
Mapper class to further simplify the `fetch` / `update` interfaces:
|
|
231
|
-
|
|
232
|
-
```ruby
|
|
233
|
-
mm = api.mapper.fetch(:mm)
|
|
234
|
-
mm = api.mapper[:mm]
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
```ruby
|
|
238
|
-
api.mapper.update(mm: 15)
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
For simplicity, you can also call `fetch` (or simply use Hash access syntax) and `update` on the
|
|
242
|
-
`Api` class directly:
|
|
243
|
-
|
|
244
|
-
```ruby
|
|
245
|
-
api.fetch(:mm)
|
|
246
|
-
api[:mm]
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
```ruby
|
|
250
|
-
api.update(mm: 15)
|
|
251
|
-
api[:mm] = 15
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
If there is no known mapping for the requested field, the Mapper will give up, but will provide
|
|
255
|
-
you with the standard API call syntax, and will suggest you inform ITCom leadership of the new
|
|
256
|
-
mapping you need.
|
|
257
|
-
|
|
258
|
-
### Panels
|
|
259
|
-
|
|
260
|
-
For supported panels (usually, business objects with composite identity keys), you can interact
|
|
261
|
-
with them in the same general way:
|
|
262
|
-
|
|
263
|
-
```ruby
|
|
264
|
-
vsc = Usps::Imis::Panels::Vsc.new(imis_id: 6374)
|
|
265
|
-
|
|
266
|
-
vsc.get(1417)
|
|
267
|
-
|
|
268
|
-
# All of these options are identical
|
|
269
|
-
#
|
|
270
|
-
vsc.get(1417, 'Quantity').first
|
|
271
|
-
vsc.get(1417)['Quantity']
|
|
272
|
-
vsc[1417, 'Quantity']
|
|
273
|
-
vsc.get(1417).raw['Properties']['$values'].find { it['Name'] == 'Quantity' }['Value']['$value']
|
|
274
|
-
vsc.get_field(1417, 'Quantity')
|
|
275
|
-
|
|
276
|
-
created = vsc.create(certificate: 'E136924', year: 2024, count: 42)
|
|
277
|
-
|
|
278
|
-
# Get the Ordinal identifier from the response
|
|
279
|
-
#
|
|
280
|
-
# All of these options are identical
|
|
281
|
-
#
|
|
282
|
-
ordinal = created.ordinal
|
|
283
|
-
ordinal = created['Ordinal']
|
|
284
|
-
ordinal = created.raw['Properties']['$values'].find { it['Name'] == 'Ordinal' }['Value']['$value']
|
|
285
|
-
ordinal = created.raw['Identity']['IdentityElements']['$values'][1].to_i # Value is duplicated here
|
|
286
|
-
|
|
287
|
-
vsc.update(certificate: 'E136924', year: 2024, count: 43, ordinal: ordinal)
|
|
288
|
-
|
|
289
|
-
vsc.put_fields(ordinal, 'Quantity' => 44)
|
|
290
|
-
vsc[ordinal, 'Quantity'] = 44
|
|
291
|
-
|
|
292
|
-
vsc.destroy(ordinal)
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
If you already have an iMIS ID to work with, you can pass that in immediately:
|
|
296
|
-
|
|
297
|
-
```ruby
|
|
298
|
-
vsc = Usps::Imis::Panels::Vsc.new(imis_id: imis_id)
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
Panels are also accessible directly from the API object:
|
|
302
|
-
|
|
303
|
-
```ruby
|
|
304
|
-
api.panels.vsc.get(1417)
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
### DSL Mode
|
|
308
|
-
|
|
309
|
-
Instead of manually setting the current iMIS ID, then running individual queries, you can instead
|
|
310
|
-
run queries in DSL mode. This specifies the iMIS ID for the scope of the block, then reverts to the
|
|
311
|
-
previous value.
|
|
312
|
-
|
|
313
|
-
```ruby
|
|
314
|
-
api.with(31092) do
|
|
315
|
-
# These requests are identical:
|
|
316
|
-
|
|
317
|
-
on('ABC_ASC_Individual_Demog') { put_fields('TotMMS' => 15) }
|
|
318
|
-
|
|
319
|
-
on('ABC_ASC_Individual_Demog').put_fields('TotMMS' => 15)
|
|
320
|
-
|
|
321
|
-
mapper.update(mm: 15)
|
|
322
|
-
|
|
323
|
-
update(mm: 15)
|
|
324
|
-
|
|
325
|
-
mapper[:mm] = 15
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
# This request fetches the same data, but leaves the iMIS ID selected
|
|
329
|
-
api.with(31092)[:mm] = 15
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
```ruby
|
|
333
|
-
api.with(6374) do
|
|
334
|
-
panels.vsc.get(1417)
|
|
335
|
-
end
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
```ruby
|
|
339
|
-
api.with(31092) do
|
|
340
|
-
# These requests are identical:
|
|
341
|
-
|
|
342
|
-
on('ABC_ASC_Individual_Demog') do
|
|
343
|
-
get.raw['Properties']['$values'].find { it['Name'] == 'TotMMS' }['Value']['$value']
|
|
344
|
-
|
|
345
|
-
get['TotMMS']
|
|
346
|
-
|
|
347
|
-
get_field('TotMMS')
|
|
348
|
-
|
|
349
|
-
get_fields('TotMMS').first
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
on('ABC_ASC_Individual_Demog').get_field('TotMMS')
|
|
353
|
-
|
|
354
|
-
on('ABC_ASC_Individual_Demog')['TotMMS']
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
# These requests fetch the same data, but leave the iMIS ID selected
|
|
358
|
-
api.with(31092).on('ABC_ASC_Individual_Demog').get_field('TotMMS')
|
|
359
|
-
api.with(31092).on('ABC_ASC_Individual_Demog')['TotMMS']
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
### Data Methods
|
|
363
|
-
|
|
364
|
-
Data responses from the API can be handled as a standard Hash using the `raw` method.
|
|
365
|
-
|
|
366
|
-
If you need to access all of the property values, you can use the `properties` method.
|
|
367
|
-
By default, this will exclude the `ID` and `Ordinal` properties; they can be included with
|
|
368
|
-
`properties(include_ids: true)`.
|
|
369
|
-
|
|
370
|
-
## Test Data Mocking
|
|
371
|
-
|
|
372
|
-
You can use the provided Business Object Mock to generate stub data for rspec:
|
|
373
|
-
|
|
374
|
-
```ruby
|
|
375
|
-
allow(api).to(
|
|
376
|
-
receive(:on).with('ABC_ASC_Individual_Demog').and_return(
|
|
377
|
-
Usps::Imis::Mocks::BusinessObject.new(TotMMS: 2)
|
|
378
|
-
)
|
|
379
|
-
)
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
### VCR
|
|
383
|
-
|
|
384
|
-
If you would like to use the included VCR config, make sure your Gemfile includes the required gems:
|
|
385
|
-
|
|
386
|
-
```ruby
|
|
387
|
-
gem 'cgi' # 2025-10-27 - Currently required for Ruby 3.5
|
|
388
|
-
gem 'vcr'
|
|
389
|
-
gem 'webmock'
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
In `spec_helper.rb`, add the following:
|
|
393
|
-
|
|
394
|
-
```ruby
|
|
395
|
-
require 'usps/vcr'
|
|
396
|
-
Usps::Vcr::Config.configure!
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
You can also pass additional VCR config options through:
|
|
400
|
-
|
|
401
|
-
```ruby
|
|
402
|
-
Usps::Vcr::Config.configure! do |config|
|
|
403
|
-
config.ignore_localhost = true
|
|
404
|
-
end
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
If you have any tests that are dependent on ordering to (re-)generate cassettes, a helper is also
|
|
408
|
-
available to support adding example metadata, which will use a fixed order when the environment
|
|
409
|
-
varialbe `VCR=all` is set:
|
|
410
|
-
|
|
411
|
-
```ruby
|
|
412
|
-
describe 'examples that care about order', order: Usps::Vcr::Config.vcr_record_ordered do
|
|
413
|
-
# ...
|
|
414
|
-
end
|
|
415
|
-
```
|
|
44
|
+
For more details and examples, refer to the [Wiki](https://github.com/unitedstatespowersquadrons/imis-api-ruby/wiki).
|
|
416
45
|
|
|
417
46
|
## Exception Handling
|
|
418
47
|
|
|
@@ -39,6 +39,18 @@ module Usps
|
|
|
39
39
|
#
|
|
40
40
|
def query = api.query(business_object_name)
|
|
41
41
|
|
|
42
|
+
# Support passthrough for Api#with
|
|
43
|
+
#
|
|
44
|
+
def with(imis_id, &)
|
|
45
|
+
# Bring into local scope
|
|
46
|
+
wrapper_business_object_name = business_object_name
|
|
47
|
+
wrapper_ordinal = ordinal
|
|
48
|
+
|
|
49
|
+
api.with(imis_id) do
|
|
50
|
+
on(wrapper_business_object_name, ordinal: wrapper_ordinal, &)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
42
54
|
# Get a business object for the current member
|
|
43
55
|
#
|
|
44
56
|
# If +fields+ is provided, will return only those field values
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
module CommandLine
|
|
6
|
+
# Command line interface for the Api
|
|
7
|
+
#
|
|
8
|
+
class Interface
|
|
9
|
+
NAME = 'USPS iMIS API - Ruby'
|
|
10
|
+
|
|
11
|
+
attr_reader :options
|
|
12
|
+
|
|
13
|
+
def self.run(...) = new(...).run
|
|
14
|
+
|
|
15
|
+
def initialize(**)
|
|
16
|
+
@options = input_options.merge(**)
|
|
17
|
+
validate_options!
|
|
18
|
+
configure! if options[:config]
|
|
19
|
+
logging!
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run
|
|
23
|
+
set_member
|
|
24
|
+
|
|
25
|
+
result = simplify(perform!)
|
|
26
|
+
|
|
27
|
+
output { result }
|
|
28
|
+
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# :nocov:
|
|
35
|
+
def input_options
|
|
36
|
+
if ENV.fetch('CI', false)
|
|
37
|
+
defaults = {
|
|
38
|
+
raw: false,
|
|
39
|
+
quiet: false,
|
|
40
|
+
log: false,
|
|
41
|
+
log_level: 'info',
|
|
42
|
+
version: false,
|
|
43
|
+
help: false
|
|
44
|
+
}
|
|
45
|
+
return defaults
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
OptionsParser.new.options
|
|
49
|
+
end
|
|
50
|
+
# :nocov:
|
|
51
|
+
|
|
52
|
+
def api
|
|
53
|
+
@api ||= Usps::Imis::Api.new
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def set_member
|
|
57
|
+
case options
|
|
58
|
+
in certificate: then api.imis_id_for(certificate)
|
|
59
|
+
in id: then api.imis_id = id
|
|
60
|
+
else
|
|
61
|
+
# Query
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# rubocop:disable Metrics
|
|
66
|
+
def perform!
|
|
67
|
+
case options
|
|
68
|
+
in map:, data: then api.mapper[map.to_sym] = data
|
|
69
|
+
in map: then api.mapper[map.to_sym]
|
|
70
|
+
|
|
71
|
+
in on:, delete: true then api.on(on).delete
|
|
72
|
+
in on:, data:, post: true then api.on(on).post(data)
|
|
73
|
+
in on:, data:, field: then api.on(on).put_field(field, data)
|
|
74
|
+
in on:, data: then api.on(on).put_fields(data)
|
|
75
|
+
in on:, fields: then api.on(on).get_fields(*fields)
|
|
76
|
+
in on:, field: then api.on(on).get_field(field)
|
|
77
|
+
in on: then api.on(on).get
|
|
78
|
+
|
|
79
|
+
in panel:, ordinal:, delete: true then api.panels.public_send(panel).delete(ordinal)
|
|
80
|
+
in panel:, data:, post: true then api.panels.public_send(panel).post(data)
|
|
81
|
+
in panel:, ordinal:, data:, field: then api.panels.public_send(panel).put_field(ordinal, field, data)
|
|
82
|
+
in panel:, ordinal:, data: then api.panels.public_send(panel).put_fields(ordinal, data)
|
|
83
|
+
in panel:, ordinal:, fields: then api.panels.public_send(panel).get_fields(ordinal, *fields)
|
|
84
|
+
in panel:, ordinal:, field: then api.panels.public_send(panel).get_field(ordinal, field)
|
|
85
|
+
in panel:, ordinal: then api.panels.public_send(panel).get(ordinal)
|
|
86
|
+
|
|
87
|
+
in query:, data: then api.query(query, data)
|
|
88
|
+
in query: then api.query(query)
|
|
89
|
+
end
|
|
90
|
+
rescue NoMatchingPatternError => e
|
|
91
|
+
raise Errors::CommandLineError, "Unable to match pattern from options: #{e.message}"
|
|
92
|
+
end
|
|
93
|
+
# rubocop:enable Metrics
|
|
94
|
+
|
|
95
|
+
def simplify(data)
|
|
96
|
+
return data.to_a if data.is_a?(Query)
|
|
97
|
+
return data if options[:raw]
|
|
98
|
+
return data unless data.is_a?(Hash) # Includes Usps::Imis::Data
|
|
99
|
+
|
|
100
|
+
data.properties(include_ids: options[:include_ids])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# :nocov:
|
|
104
|
+
def output(&block)
|
|
105
|
+
return if ENV.fetch('SUPPRESS_OUTPUT', false)
|
|
106
|
+
|
|
107
|
+
puts JSON.dump(block.call) if block_given?
|
|
108
|
+
end
|
|
109
|
+
# :nocov:
|
|
110
|
+
|
|
111
|
+
def configure!
|
|
112
|
+
json_config = JSON.parse(File.read(options[:config]))
|
|
113
|
+
|
|
114
|
+
Usps::Imis.configure do |config|
|
|
115
|
+
json_config.each do |key, value|
|
|
116
|
+
config.public_send("#{key}=", value)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def logging!
|
|
122
|
+
Usps::Imis.configure do |config|
|
|
123
|
+
# :nocov:
|
|
124
|
+
if options[:quiet] || ENV.fetch('SUPPRESS_OUTPUT', false)
|
|
125
|
+
config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil))
|
|
126
|
+
elsif options[:log]
|
|
127
|
+
# Use default
|
|
128
|
+
else
|
|
129
|
+
config.logger = ActiveSupport::TaggedLogging.new(Logger.new($stderr))
|
|
130
|
+
end
|
|
131
|
+
# :nocov:
|
|
132
|
+
|
|
133
|
+
config.logger.level = options[:log_level]
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def validate_options!
|
|
138
|
+
if options[:log_level] == 'info' && options.except(:log_level, :quiet).values.none?
|
|
139
|
+
raise Errors::CommandLineError, 'No options provided'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
return if options[:query].nil? || options[:query].start_with?('$/')
|
|
143
|
+
|
|
144
|
+
raise Errors::CommandLineError, 'Invalid IQA Query name'
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Usps
|
|
4
|
+
module Imis
|
|
5
|
+
module CommandLine
|
|
6
|
+
# Command line options parser
|
|
7
|
+
#
|
|
8
|
+
class OptionsParser
|
|
9
|
+
OPTIONS = {
|
|
10
|
+
certificate: ['Member certificate number', { type: :string }],
|
|
11
|
+
id: ['Member iMIS ID', { type: :integer }],
|
|
12
|
+
on: ['Business Object name', { type: :string }],
|
|
13
|
+
panel: ['Panel name', { type: :string }],
|
|
14
|
+
ordinal: ['Ordinal ID within a Panel', { type: :integer }],
|
|
15
|
+
query: ['IQA Query name', { type: :string, short: :Q }],
|
|
16
|
+
post: ["Send a #{'POST'.cyan} request", { short: :P }],
|
|
17
|
+
delete: ["Send a #{'DELETE'.cyan} request", { short: :D }],
|
|
18
|
+
field: ['Specific field to return or update', { type: :string }],
|
|
19
|
+
fields: ['Specific field(s) to return', { type: :strings, short: :F }],
|
|
20
|
+
map: ['Mapped field name to return or update', { type: :string }],
|
|
21
|
+
data: ['JSON string input', { type: :string }],
|
|
22
|
+
config: ['Path to the JSON config file to use', { type: :string, short: :C }],
|
|
23
|
+
raw: ['Return raw JSON output, rather than simplified data', { short: :R }],
|
|
24
|
+
include_ids: ['Include ID properties in returned data'],
|
|
25
|
+
quiet: ['Suppress logging to STDERR'],
|
|
26
|
+
log: ['Redirect logging to STDOUT'],
|
|
27
|
+
log_level: ['Set the logging level', { type: :string, default: 'info', short: :L }]
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
CONFLICTING_OPTION_GROUPS = [
|
|
31
|
+
%i[certificate id],
|
|
32
|
+
%i[on panel query map],
|
|
33
|
+
%i[field fields map query]
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
attr_reader :arguments, :options
|
|
37
|
+
|
|
38
|
+
def self.banner_contents
|
|
39
|
+
<<~BANNER
|
|
40
|
+
Usage:
|
|
41
|
+
imis.rb #{'[options]'.gray}
|
|
42
|
+
|
|
43
|
+
The default HTTP verb is #{'GET'.cyan}.
|
|
44
|
+
If #{'--data'.green}/#{'-d'.green} is provided, the default HTTP verb is #{'PUT'.cyan}.
|
|
45
|
+
|
|
46
|
+
#{'--data'.green}/#{'-d'.green} is used to provide field(s) data for #{'PUT'.cyan}
|
|
47
|
+
requests and mapper updates, object data for #{'POST'.cyan},
|
|
48
|
+
and to provide any query params.
|
|
49
|
+
|
|
50
|
+
Configuration can be specified with #{'--config'.green}/#{'-C'.green}, or by setting
|
|
51
|
+
the following environment variables:
|
|
52
|
+
#{'IMIS_ENVIRONMENT'.yellow}
|
|
53
|
+
#{'IMIS_USERNAME'.yellow}
|
|
54
|
+
#{'IMIS_PASSWORD'.yellow}
|
|
55
|
+
#{'IMIS_ID_QUERY_NAME'.yellow}
|
|
56
|
+
|
|
57
|
+
Options:
|
|
58
|
+
BANNER
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def initialize
|
|
62
|
+
@options = parse_options.compact
|
|
63
|
+
@arguments = ARGV # Not currently used
|
|
64
|
+
|
|
65
|
+
# :nocov:
|
|
66
|
+
@options[:data] = JSON.parse(read_stdin) if stdin?
|
|
67
|
+
# :nocov:
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def parse_options
|
|
73
|
+
Optimist.options do
|
|
74
|
+
version "#{Interface::NAME} (v#{Usps::Imis::VERSION})"
|
|
75
|
+
|
|
76
|
+
banner "#{version.bold.blue}\n \n"
|
|
77
|
+
banner OptionsParser.banner_contents
|
|
78
|
+
|
|
79
|
+
OPTIONS.each { |option, data| opt(option, *data) }
|
|
80
|
+
CONFLICTING_OPTION_GROUPS.each { |group| conflicts(*group) }
|
|
81
|
+
|
|
82
|
+
educate_on_error
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# :nocov:
|
|
87
|
+
def stdin? = $stdin.wait_readable(0)
|
|
88
|
+
def read_stdin = $stdin.read.chomp
|
|
89
|
+
# :nocov:
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optimist'
|
|
4
|
+
require 'colorize'
|
|
5
|
+
|
|
6
|
+
module Usps
|
|
7
|
+
module Imis
|
|
8
|
+
# Wrapper for the command line interface
|
|
9
|
+
#
|
|
10
|
+
module CommandLine; end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require_relative 'command_line/options_parser'
|
|
15
|
+
require_relative 'command_line/interface'
|
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 =
|
|
15
|
+
@environment = default_environment
|
|
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)
|
|
@@ -53,6 +53,14 @@ module Usps
|
|
|
53
53
|
# Parameters to filter out of logging
|
|
54
54
|
#
|
|
55
55
|
def filtered_parameters = %i[password]
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def default_environment
|
|
60
|
+
return ::Rails.env if defined?(::Rails)
|
|
61
|
+
|
|
62
|
+
ActiveSupport::StringInquirer.new(ENV.fetch('IMIS_ENVIRONMENT', 'development'))
|
|
63
|
+
end
|
|
56
64
|
end
|
|
57
65
|
end
|
|
58
66
|
end
|
data/lib/usps/imis/data.rb
CHANGED
data/lib/usps/imis/error.rb
CHANGED
data/lib/usps/imis/version.rb
CHANGED
data/lib/usps/imis.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
# Core dependencies
|
|
@@ -20,6 +21,7 @@ require_relative 'imis/error'
|
|
|
20
21
|
require_relative 'imis/api'
|
|
21
22
|
require_relative 'imis/properties'
|
|
22
23
|
require_relative 'imis/panels'
|
|
24
|
+
require_relative 'imis/command_line'
|
|
23
25
|
require_relative 'imis/mocks'
|
|
24
26
|
require_relative 'imis/version'
|
|
25
27
|
|
|
@@ -56,3 +58,9 @@ module Usps
|
|
|
56
58
|
end
|
|
57
59
|
end
|
|
58
60
|
end
|
|
61
|
+
|
|
62
|
+
# Invoke CLI, only if invoked from the command line
|
|
63
|
+
#
|
|
64
|
+
# :nocov:
|
|
65
|
+
Usps::Imis::CommandLine::Interface.run if __FILE__ == $PROGRAM_NAME
|
|
66
|
+
# :nocov:
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: usps-imis-api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Julian Fiander
|
|
@@ -23,6 +23,34 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '8.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: colorize
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 1.1.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 1.1.0
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: optimist
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 3.2.1
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 3.2.1
|
|
26
54
|
description: A wrapper for the iMIS API.
|
|
27
55
|
email: jsfiander@gmail.com
|
|
28
56
|
executables: []
|
|
@@ -33,10 +61,14 @@ files:
|
|
|
33
61
|
- lib/usps/imis.rb
|
|
34
62
|
- lib/usps/imis/api.rb
|
|
35
63
|
- lib/usps/imis/business_object.rb
|
|
64
|
+
- lib/usps/imis/command_line.rb
|
|
65
|
+
- lib/usps/imis/command_line/interface.rb
|
|
66
|
+
- lib/usps/imis/command_line/options_parser.rb
|
|
36
67
|
- lib/usps/imis/config.rb
|
|
37
68
|
- lib/usps/imis/data.rb
|
|
38
69
|
- lib/usps/imis/error.rb
|
|
39
70
|
- lib/usps/imis/errors/api_error.rb
|
|
71
|
+
- lib/usps/imis/errors/command_line_error.rb
|
|
40
72
|
- lib/usps/imis/errors/config_error.rb
|
|
41
73
|
- lib/usps/imis/errors/locked_id_error.rb
|
|
42
74
|
- lib/usps/imis/errors/mapper_error.rb
|