usps-imis-api 0.11.23 → 1.0.0.pre.rc.1
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/.github/workflows/main.yml +57 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +88 -0
- data/.ruby-version +1 -0
- data/.simplecov +8 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +95 -0
- data/Rakefile +12 -0
- data/Readme.md +191 -19
- data/bin/console +21 -0
- data/bin/setup +8 -0
- data/lib/ext/hash.rb +10 -0
- data/lib/usps/imis/api.rb +138 -177
- data/lib/usps/imis/config.rb +10 -68
- data/lib/usps/imis/error/api.rb +26 -0
- data/lib/usps/imis/error/mapper.rb +9 -0
- data/lib/usps/imis/{errors/response_error.rb → error/response.rb} +7 -34
- data/lib/usps/imis/mapper.rb +21 -90
- data/lib/usps/imis/panel/base_panel.rb +42 -0
- data/lib/usps/imis/panel/education.rb +111 -0
- data/lib/usps/imis/panel/vsc.rb +109 -0
- data/lib/usps/imis/version.rb +1 -1
- data/lib/usps/imis.rb +17 -32
- data/spec/lib/usps/imis/api_spec.rb +143 -0
- data/spec/lib/usps/imis/config_spec.rb +33 -0
- data/spec/lib/usps/imis/error/api_spec.rb +17 -0
- data/spec/lib/usps/imis/error/response_spec.rb +107 -0
- data/spec/lib/usps/imis/mapper_spec.rb +31 -0
- data/spec/lib/usps/imis/panel/base_panel_spec.rb +32 -0
- data/spec/lib/usps/imis/panel/education_spec.rb +55 -0
- data/spec/lib/usps/imis/panel/vsc_spec.rb +38 -0
- data/spec/lib/usps/imis_spec.rb +11 -0
- data/spec/spec_helper.rb +35 -0
- data/usps-imis-api.gemspec +18 -0
- metadata +33 -94
- data/bin/imis +0 -8
- data/lib/usps/imis/business_object.rb +0 -209
- data/lib/usps/imis/command_line/interface.rb +0 -204
- data/lib/usps/imis/command_line/options_parser.rb +0 -136
- data/lib/usps/imis/command_line.rb +0 -15
- data/lib/usps/imis/data.rb +0 -76
- data/lib/usps/imis/error.rb +0 -55
- data/lib/usps/imis/errors/api_error.rb +0 -11
- data/lib/usps/imis/errors/command_line_error.rb +0 -11
- data/lib/usps/imis/errors/config_error.rb +0 -11
- data/lib/usps/imis/errors/locked_id_error.rb +0 -15
- data/lib/usps/imis/errors/mapper_error.rb +0 -29
- data/lib/usps/imis/errors/missing_id_error.rb +0 -15
- data/lib/usps/imis/errors/not_found_error.rb +0 -11
- data/lib/usps/imis/errors/panel_unimplemented_error.rb +0 -34
- data/lib/usps/imis/errors/unexpected_property_type_error.rb +0 -31
- data/lib/usps/imis/logger.rb +0 -19
- data/lib/usps/imis/logger_formatter.rb +0 -32
- data/lib/usps/imis/logger_helpers.rb +0 -17
- data/lib/usps/imis/mocks/business_object.rb +0 -47
- data/lib/usps/imis/mocks.rb +0 -11
- data/lib/usps/imis/panels/base_panel.rb +0 -158
- data/lib/usps/imis/panels/education.rb +0 -33
- data/lib/usps/imis/panels/vsc.rb +0 -32
- data/lib/usps/imis/panels.rb +0 -25
- data/lib/usps/imis/properties.rb +0 -50
- data/lib/usps/imis/query.rb +0 -153
- data/lib/usps/imis/requests.rb +0 -68
- data/spec/support/usps/vcr/config.rb +0 -47
- data/spec/support/usps/vcr/filters.rb +0 -89
- data/spec/support/usps/vcr.rb +0 -8
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'requests'
|
|
4
|
-
require_relative 'data'
|
|
5
|
-
|
|
6
|
-
module Usps
|
|
7
|
-
module Imis
|
|
8
|
-
# DEV
|
|
9
|
-
class BusinessObject
|
|
10
|
-
include Requests
|
|
11
|
-
|
|
12
|
-
# Endpoint for general API requests
|
|
13
|
-
#
|
|
14
|
-
API_PATH = 'api'
|
|
15
|
-
|
|
16
|
-
# The parent +Api+ object
|
|
17
|
-
#
|
|
18
|
-
attr_reader :api
|
|
19
|
-
|
|
20
|
-
# Name of the iMIS Business Object
|
|
21
|
-
#
|
|
22
|
-
attr_reader :business_object_name
|
|
23
|
-
|
|
24
|
-
# Ordinal to build override ID param of the URL (e.g. used for Panels)
|
|
25
|
-
#
|
|
26
|
-
attr_reader :ordinal
|
|
27
|
-
|
|
28
|
-
# Tagged logger
|
|
29
|
-
#
|
|
30
|
-
attr_reader :logger
|
|
31
|
-
|
|
32
|
-
# A new instance of +BusinessObject+
|
|
33
|
-
#
|
|
34
|
-
def initialize(api, business_object_name, ordinal: nil)
|
|
35
|
-
@api = api
|
|
36
|
-
@business_object_name = business_object_name
|
|
37
|
-
@ordinal = ordinal
|
|
38
|
-
@logger ||= Imis.logger('BusinessObject')
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Run a query on the entire business object
|
|
42
|
-
#
|
|
43
|
-
# @return [Usps::Imis::Query] Query wrapper
|
|
44
|
-
#
|
|
45
|
-
def query = api.query(business_object_name)
|
|
46
|
-
|
|
47
|
-
# Support passthrough for Api#with
|
|
48
|
-
#
|
|
49
|
-
def with(imis_id, &)
|
|
50
|
-
# Bring into local scope
|
|
51
|
-
wrapper_business_object_name = business_object_name
|
|
52
|
-
wrapper_ordinal = ordinal
|
|
53
|
-
|
|
54
|
-
api.with(imis_id) do
|
|
55
|
-
on(wrapper_business_object_name, ordinal: wrapper_ordinal, &)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Get a business object for the current member
|
|
60
|
-
#
|
|
61
|
-
# If +fields+ is provided, will return only those field values
|
|
62
|
-
#
|
|
63
|
-
# @param fields [String] Field names to return
|
|
64
|
-
#
|
|
65
|
-
# @return [Usps::Imis::Data, Array<Usps::Imis::Data>] Response data from the API
|
|
66
|
-
#
|
|
67
|
-
def get(*fields) = fields.any? ? get_fields(*fields) : raw_object
|
|
68
|
-
alias read get
|
|
69
|
-
|
|
70
|
-
# Get a single named field from a business object for the current member
|
|
71
|
-
#
|
|
72
|
-
# @param field [String] Field name to return
|
|
73
|
-
#
|
|
74
|
-
# @return Response data field value from the API
|
|
75
|
-
#
|
|
76
|
-
def get_field(field) = raw_object[field]
|
|
77
|
-
alias fetch get_field
|
|
78
|
-
alias [] get_field
|
|
79
|
-
|
|
80
|
-
# Get named fields from a business object for the current member
|
|
81
|
-
#
|
|
82
|
-
# @param names [Array<String>] Field names to return
|
|
83
|
-
#
|
|
84
|
-
# @return [Array] Response data from the API
|
|
85
|
-
#
|
|
86
|
-
def get_fields(*fields)
|
|
87
|
-
values = raw_object
|
|
88
|
-
fields.map { values[it] }
|
|
89
|
-
end
|
|
90
|
-
alias fetch_all get_fields
|
|
91
|
-
|
|
92
|
-
# Update a single named field on a business object for the current member
|
|
93
|
-
#
|
|
94
|
-
# @param field [String] Name of the field
|
|
95
|
-
# @param value Value of the field
|
|
96
|
-
#
|
|
97
|
-
# @return [Usps::Imis::Data] Response data from the API
|
|
98
|
-
#
|
|
99
|
-
def put_field(field, value) = put(filter_fields(field => value))
|
|
100
|
-
alias []= put_field
|
|
101
|
-
|
|
102
|
-
# Update only specific fields on a business object for the current member
|
|
103
|
-
#
|
|
104
|
-
# @param fields [Hash] Conforms to pattern +{ field_key => value }+
|
|
105
|
-
#
|
|
106
|
-
# @return [Usps::Imis::Data] Response data from the API
|
|
107
|
-
#
|
|
108
|
-
def put_fields(fields) = put(filter_fields(fields))
|
|
109
|
-
alias patch put_fields
|
|
110
|
-
|
|
111
|
-
# Update a business object for the current member
|
|
112
|
-
#
|
|
113
|
-
# Any properties not included will be left unmodified
|
|
114
|
-
#
|
|
115
|
-
# @param body [Hash] Full raw API object data
|
|
116
|
-
#
|
|
117
|
-
# @return [Usps::Imis::Data] Response data from the API
|
|
118
|
-
#
|
|
119
|
-
def put(body) = put_object(http_put, body)
|
|
120
|
-
alias update put
|
|
121
|
-
|
|
122
|
-
# Create a business object for the current member
|
|
123
|
-
#
|
|
124
|
-
# @param body [Hash] Full raw API object data
|
|
125
|
-
#
|
|
126
|
-
# @return [Usps::Imis::Data] Response data from the API
|
|
127
|
-
#
|
|
128
|
-
def post(body) = put_object(http_post, body)
|
|
129
|
-
alias create post
|
|
130
|
-
|
|
131
|
-
# Remove a business object for the current member
|
|
132
|
-
#
|
|
133
|
-
# @return [true] Only on success response (i.e. blank string from the API)
|
|
134
|
-
#
|
|
135
|
-
def delete = submit(uri, authorize(http_delete)).body == '' # rubocop:disable Naming/PredicateMethod
|
|
136
|
-
alias destroy delete
|
|
137
|
-
|
|
138
|
-
# Ruby 3.5 instance variable filter
|
|
139
|
-
#
|
|
140
|
-
def instance_variables_to_inspect = instance_variables - %i[@api @logger]
|
|
141
|
-
|
|
142
|
-
private
|
|
143
|
-
|
|
144
|
-
# Construct a business object API endpoint address
|
|
145
|
-
#
|
|
146
|
-
def uri(id: nil)
|
|
147
|
-
full_path = "#{API_PATH}/#{business_object_name}/#{id_for_uri(id)}"
|
|
148
|
-
URI(File.join(Imis.configuration.hostname, full_path))
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# Select the correct ID to use in the URI
|
|
152
|
-
#
|
|
153
|
-
def id_for_uri(id = nil)
|
|
154
|
-
return CGI.escape(id) unless id.nil?
|
|
155
|
-
return CGI.escape("~#{api.imis_id}|#{ordinal}") if ordinal
|
|
156
|
-
|
|
157
|
-
api.imis_id.to_s
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# Manually assemble the matching data structure, with fields in the correct order
|
|
161
|
-
#
|
|
162
|
-
def filter_fields(fields)
|
|
163
|
-
existing = get
|
|
164
|
-
|
|
165
|
-
JSON.parse(JSON.dump(existing)).tap do |updated|
|
|
166
|
-
# Preserve the iMIS ID, as well as the Ordinal (if present)
|
|
167
|
-
updated['Properties']['$values'], properties =
|
|
168
|
-
existing.raw['Properties']['$values'].partition { %w[ID Ordinal].include?(it['Name']) }
|
|
169
|
-
|
|
170
|
-
# Iterate through all existing fields
|
|
171
|
-
properties.each do |value|
|
|
172
|
-
# Skip unmodified fields
|
|
173
|
-
next unless fields.keys.include?(value['Name'])
|
|
174
|
-
|
|
175
|
-
value['Value'] = Properties.wrap(fields[value['Name']])
|
|
176
|
-
|
|
177
|
-
# Add the completed field with the updated value
|
|
178
|
-
updated['Properties']['$values'] << value
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
# Get a raw object response from the API
|
|
184
|
-
#
|
|
185
|
-
# Useful for stubbing data in tests
|
|
186
|
-
#
|
|
187
|
-
def raw_object
|
|
188
|
-
raise Errors::MissingIdError if api.imis_id.nil?
|
|
189
|
-
|
|
190
|
-
response = submit(uri, authorize(http_get))
|
|
191
|
-
result = Data.from_json(response.body)
|
|
192
|
-
logger.json result
|
|
193
|
-
result
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
# Upload an object to the API
|
|
197
|
-
#
|
|
198
|
-
def put_object(request, body)
|
|
199
|
-
raise Errors::MissingIdError if api.imis_id.nil?
|
|
200
|
-
|
|
201
|
-
request.body = JSON.dump(body)
|
|
202
|
-
response = submit(uri, authorize(request))
|
|
203
|
-
result = Data.from_json(response.body)
|
|
204
|
-
logger.json result
|
|
205
|
-
result
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
end
|
|
@@ -1,204 +0,0 @@
|
|
|
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
|
-
# CLI options that indicate the response is a raw Hash rather than a Data object,
|
|
12
|
-
# and should not be simplified
|
|
13
|
-
#
|
|
14
|
-
RAW_HASH_RESPONSE_OPTIONS = %i[business_objects auth_token].freeze
|
|
15
|
-
|
|
16
|
-
# Options passed in from the command line
|
|
17
|
-
#
|
|
18
|
-
attr_reader :options
|
|
19
|
-
|
|
20
|
-
# Tagged logger
|
|
21
|
-
#
|
|
22
|
-
attr_reader :logger
|
|
23
|
-
|
|
24
|
-
def self.run(...) = new(...).run
|
|
25
|
-
|
|
26
|
-
def initialize(**)
|
|
27
|
-
@options = input_options.merge(**)
|
|
28
|
-
options[:version] = true if default_options?
|
|
29
|
-
|
|
30
|
-
configure! if options[:config]
|
|
31
|
-
logging!
|
|
32
|
-
@logger ||= Imis.logger('CommandLine')
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def run
|
|
36
|
-
logger.info 'Running'
|
|
37
|
-
logger.debug 'CLI Options:'
|
|
38
|
-
logger.json(options.dup.tap { it[:token] = '[FILTERED]' if it[:token] })
|
|
39
|
-
|
|
40
|
-
set_member
|
|
41
|
-
|
|
42
|
-
result = simplify(perform!)
|
|
43
|
-
|
|
44
|
-
output { result }
|
|
45
|
-
|
|
46
|
-
result
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
private
|
|
50
|
-
|
|
51
|
-
# :nocov:
|
|
52
|
-
def input_options
|
|
53
|
-
if ENV.fetch('CI', false) || ENV.fetch('TESTING', false)
|
|
54
|
-
defaults = {
|
|
55
|
-
raw: false,
|
|
56
|
-
quiet: false,
|
|
57
|
-
log: false,
|
|
58
|
-
log_level: 'info',
|
|
59
|
-
version: false,
|
|
60
|
-
help: false
|
|
61
|
-
}
|
|
62
|
-
return defaults
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
OptionsParser.new.options
|
|
66
|
-
end
|
|
67
|
-
# :nocov:
|
|
68
|
-
|
|
69
|
-
def api
|
|
70
|
-
@api ||=
|
|
71
|
-
if options[:token]
|
|
72
|
-
Usps::Imis::Api.with_token(options[:token])
|
|
73
|
-
else
|
|
74
|
-
Usps::Imis::Api.new
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def set_member
|
|
79
|
-
case options
|
|
80
|
-
in certificate: then api.imis_id_for(certificate)
|
|
81
|
-
in id: then api.imis_id = id
|
|
82
|
-
else
|
|
83
|
-
# Query
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
logger.debug "iMIS ID: #{api.imis_id}" if api.imis_id
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# rubocop:disable Metrics
|
|
90
|
-
def perform!
|
|
91
|
-
case convert(options)
|
|
92
|
-
in mapper:, field:, data: then mapper.put_field(field.to_sym, data)
|
|
93
|
-
in mapper:, fields: then mapper.get_fields(*fields)
|
|
94
|
-
in mapper:, field: then mapper.get_field(field.to_sym)
|
|
95
|
-
in mapper:, data: then mapper.update(data)
|
|
96
|
-
|
|
97
|
-
in on:, delete: true then on.delete
|
|
98
|
-
in on:, create: true, data: then on.post(data)
|
|
99
|
-
in on:, data:, field: then on.put_field(field, data)
|
|
100
|
-
in on:, data: then on.put_fields(data)
|
|
101
|
-
in on:, fields: then on.get_fields(*fields)
|
|
102
|
-
in on:, field: then on.get_field(field)
|
|
103
|
-
in on: then on.get
|
|
104
|
-
|
|
105
|
-
in panel:, delete: true, ordinal: then panel.delete(ordinal)
|
|
106
|
-
in panel:, create: true, data: then panel.post(data)
|
|
107
|
-
in panel:, ordinal:, data:, field: then panel.put_field(ordinal, field, data)
|
|
108
|
-
in panel:, ordinal:, data: then panel.put_fields(ordinal, data)
|
|
109
|
-
in panel:, ordinal:, fields: then panel.get_fields(ordinal, *fields)
|
|
110
|
-
in panel:, ordinal:, field: then panel.get_field(ordinal, field)
|
|
111
|
-
in panel:, ordinal: then panel.get(ordinal)
|
|
112
|
-
|
|
113
|
-
in query:, data: then api.query(query, data)
|
|
114
|
-
in query: then api.query(query)
|
|
115
|
-
|
|
116
|
-
in business_objects: true then api.business_objects
|
|
117
|
-
|
|
118
|
-
in auth_token: true then api.auth_token
|
|
119
|
-
|
|
120
|
-
in certificate: then api.imis_id
|
|
121
|
-
|
|
122
|
-
in version: true then "#{Interface::NAME} (v#{Imis::VERSION})"
|
|
123
|
-
end
|
|
124
|
-
rescue NoMatchingPatternError => e
|
|
125
|
-
raise Errors::CommandLineError, "Unable to match pattern from options: #{e.message}"
|
|
126
|
-
end
|
|
127
|
-
# rubocop:enable Metrics
|
|
128
|
-
|
|
129
|
-
def convert(options)
|
|
130
|
-
options.dup.tap do |converted|
|
|
131
|
-
case converted
|
|
132
|
-
in map: then converted.merge!(mapper: api.mapper, field: map)
|
|
133
|
-
in mapper: true then converted[:mapper] = api.mapper
|
|
134
|
-
in on: then converted[:on] = api.on(on)
|
|
135
|
-
in panel: then converted[:panel] = api.panels.public_send(panel)
|
|
136
|
-
else
|
|
137
|
-
# Nothing to convert
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Remove mapper flag when false
|
|
141
|
-
converted.delete(:mapper) unless converted[:mapper]
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def simplify(data)
|
|
146
|
-
return data.to_a if data.is_a?(Query)
|
|
147
|
-
return data if options[:raw] || RAW_HASH_RESPONSE_OPTIONS.any? { options[it] }
|
|
148
|
-
|
|
149
|
-
# Hash includes Usps::Imis::Data
|
|
150
|
-
if data.is_a?(Hash)
|
|
151
|
-
logger.debug 'Returning simplified Data#properties'
|
|
152
|
-
data.properties(include_ids: options[:include_ids])
|
|
153
|
-
elsif data.is_a?(Array) && data.all? { it.is_a?(Hash) }
|
|
154
|
-
logger.debug 'Returning simplified Array<Data#properties>'
|
|
155
|
-
data.map { it.properties(include_ids: options[:include_ids]) }
|
|
156
|
-
else
|
|
157
|
-
data
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# :nocov:
|
|
162
|
-
def output(&block)
|
|
163
|
-
if ENV.fetch('SUPPRESS_OUTPUT', false)
|
|
164
|
-
logger.debug 'Output suppressed'
|
|
165
|
-
return
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
puts JSON.dump(block.call) if block_given?
|
|
169
|
-
end
|
|
170
|
-
# :nocov:
|
|
171
|
-
|
|
172
|
-
# Supports YAML or JSON config data
|
|
173
|
-
#
|
|
174
|
-
def configure!
|
|
175
|
-
config_data = YAML.safe_load_file(options[:config])
|
|
176
|
-
|
|
177
|
-
Usps::Imis.configure do |config|
|
|
178
|
-
config_data.each do |key, value|
|
|
179
|
-
config.public_send("#{key}=", value)
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def logging!
|
|
185
|
-
Usps::Imis.configure do |config|
|
|
186
|
-
# :nocov:
|
|
187
|
-
if options[:quiet] || ENV.fetch('SUPPRESS_OUTPUT', false)
|
|
188
|
-
config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil))
|
|
189
|
-
elsif options[:log] || config.logger_file
|
|
190
|
-
# Use default, or configured file
|
|
191
|
-
else
|
|
192
|
-
config.logger = ActiveSupport::TaggedLogging.new(Logger.new($stderr))
|
|
193
|
-
end
|
|
194
|
-
# :nocov:
|
|
195
|
-
|
|
196
|
-
config.logger.level = options[:log_level]
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
def default_options? = options[:log_level] == 'info' && options.except(:log_level).values.none?
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
end
|
|
@@ -1,136 +0,0 @@
|
|
|
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
|
-
# IDs
|
|
11
|
-
certificate: ['Member certificate number', { type: :string }],
|
|
12
|
-
id: ['Member iMIS ID', { type: :integer }],
|
|
13
|
-
|
|
14
|
-
# Primary interactions
|
|
15
|
-
on: ['Business Object name', { type: :string }],
|
|
16
|
-
panel: ['Panel name', { type: :string }],
|
|
17
|
-
query: ['IQA Query or Business Object name to query', { type: :string, short: :Q }],
|
|
18
|
-
mapper: ['Interact with mapped fields', { short: :M }],
|
|
19
|
-
map: ["Shorthand for #{'-Mf'.green} to access a single mapped field", { type: :string }],
|
|
20
|
-
business_objects: ['List available Business Objects'],
|
|
21
|
-
|
|
22
|
-
# Alternate verbs
|
|
23
|
-
create: ["Send a #{'POST'.cyan} request", { short: :P }],
|
|
24
|
-
delete: ["Send a #{'DELETE'.cyan} request", { short: :D }],
|
|
25
|
-
|
|
26
|
-
# Data
|
|
27
|
-
ordinal: ['Ordinal ID within a Panel', { type: :integer }],
|
|
28
|
-
field: ['Specific field to return or update', { type: :string }],
|
|
29
|
-
fields: ['Specific field(s) to return', { type: :strings, short: :F }],
|
|
30
|
-
data: ['JSON string input', { type: :string }],
|
|
31
|
-
|
|
32
|
-
# Iteractions for supporting other language wrappers
|
|
33
|
-
auth_token: ['Return an auth token for other language wrappers', { short: :T }],
|
|
34
|
-
token: ['Provide an existing auth token', { type: :string }],
|
|
35
|
-
|
|
36
|
-
# General config
|
|
37
|
-
config: ['Path to the JSON/YAML config file to use', { type: :string, short: :C }],
|
|
38
|
-
raw: ['Return raw JSON output, rather than simplified data', { short: :R }],
|
|
39
|
-
include_ids: ["Include any #{'iMIS ID'.yellow} and #{'Ordinal'.yellow} properties in returned data"],
|
|
40
|
-
quiet: ["Suppress logging to #{'STDERR'.red}"],
|
|
41
|
-
log: ["Redirect logging to #{'STDOUT'.red}"],
|
|
42
|
-
log_level: ['Set the logging level', { type: :string, default: 'info', short: :L }]
|
|
43
|
-
}.freeze
|
|
44
|
-
|
|
45
|
-
CONFLICTING_OPTION_GROUPS = [
|
|
46
|
-
%i[certificate id],
|
|
47
|
-
%i[on panel query mapper map business_objects auth_token],
|
|
48
|
-
%i[field fields map query],
|
|
49
|
-
%i[raw include_ids],
|
|
50
|
-
%i[quiet log_level],
|
|
51
|
-
%i[quiet log],
|
|
52
|
-
|
|
53
|
-
%i[create delete],
|
|
54
|
-
|
|
55
|
-
%i[create mapper],
|
|
56
|
-
%i[create query],
|
|
57
|
-
%i[create map],
|
|
58
|
-
%i[create field],
|
|
59
|
-
%i[create fields],
|
|
60
|
-
|
|
61
|
-
%i[delete mapper],
|
|
62
|
-
%i[delete query],
|
|
63
|
-
%i[delete map],
|
|
64
|
-
%i[delete field],
|
|
65
|
-
%i[delete fields],
|
|
66
|
-
%i[delete data],
|
|
67
|
-
%i[delete raw]
|
|
68
|
-
].freeze
|
|
69
|
-
|
|
70
|
-
attr_reader :arguments, :options
|
|
71
|
-
|
|
72
|
-
def self.banner_header(version)
|
|
73
|
-
<<~BANNER
|
|
74
|
-
#{version.bold.blue}
|
|
75
|
-
#{'P/R/C Julian Fiander, SN'.gray}\n \n
|
|
76
|
-
BANNER
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def self.banner_contents
|
|
80
|
-
<<~BANNER
|
|
81
|
-
#{'Usage'.underline}
|
|
82
|
-
|
|
83
|
-
#{'imis'.bold} #{'[options]'.gray}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
#{'Further Help'.underline}
|
|
87
|
-
|
|
88
|
-
For an explanation of how to provide API configuration, more details on the options,
|
|
89
|
-
and usage examples, please refer to the wiki:
|
|
90
|
-
|
|
91
|
-
https://github.com/unitedstatespowersquadrons/imis-api-ruby/wiki/Command-Line
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
#{'Options'.underline}
|
|
95
|
-
BANNER
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def initialize
|
|
99
|
-
@options = parse_options.compact
|
|
100
|
-
@arguments = ARGV # Not currently used
|
|
101
|
-
|
|
102
|
-
Optimist.educate if ARGV.empty? && defaults? # DEV: This shadows setting the --version flag by default
|
|
103
|
-
|
|
104
|
-
# :nocov:
|
|
105
|
-
@options[:data] = read_stdin if stdin?
|
|
106
|
-
# :nocov:
|
|
107
|
-
|
|
108
|
-
@options[:data] = JSON.parse(@options[:data]) if @options[:data]
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
private
|
|
112
|
-
|
|
113
|
-
def parse_options
|
|
114
|
-
Optimist.options do
|
|
115
|
-
version "#{Interface::NAME} (v#{Usps::Imis::VERSION})"
|
|
116
|
-
|
|
117
|
-
banner OptionsParser.banner_header(version)
|
|
118
|
-
banner OptionsParser.banner_contents
|
|
119
|
-
|
|
120
|
-
OPTIONS.each { |option, data| opt(option, *data) }
|
|
121
|
-
CONFLICTING_OPTION_GROUPS.each { |group| conflicts(*group) }
|
|
122
|
-
|
|
123
|
-
educate_on_error
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# :nocov:
|
|
128
|
-
def stdin? = $stdin.wait_readable(0)
|
|
129
|
-
def read_stdin = $stdin.read.chomp
|
|
130
|
-
# :nocov:
|
|
131
|
-
|
|
132
|
-
def defaults? = options[:log_level] == 'info' && options.except(:log_level).values.none?
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
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/data.rb
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'pp'
|
|
4
|
-
require 'stringio'
|
|
5
|
-
|
|
6
|
-
module Usps
|
|
7
|
-
module Imis
|
|
8
|
-
# Convenience wrapper for accessing specific properties within an API data response
|
|
9
|
-
#
|
|
10
|
-
class Data < Hash
|
|
11
|
-
# Load raw API response JSON to access properties
|
|
12
|
-
#
|
|
13
|
-
# @param json [String] Raw API response JSON
|
|
14
|
-
#
|
|
15
|
-
def self.from_json(json) = self[JSON.parse(json)]
|
|
16
|
-
|
|
17
|
-
alias raw to_h
|
|
18
|
-
|
|
19
|
-
# The Business Object or Panel name
|
|
20
|
-
#
|
|
21
|
-
def entity = raw['EntityTypeName']
|
|
22
|
-
|
|
23
|
-
# Access the iMIS ID property
|
|
24
|
-
#
|
|
25
|
-
def imis_id = self['ID'].to_i
|
|
26
|
-
alias id imis_id
|
|
27
|
-
|
|
28
|
-
# Access the Ordinal identifier property (if present)
|
|
29
|
-
#
|
|
30
|
-
def ordinal = self['Ordinal']&.to_i
|
|
31
|
-
|
|
32
|
-
# Access an individual property value by name
|
|
33
|
-
#
|
|
34
|
-
def [](property_name)
|
|
35
|
-
property = raw['Properties']['$values'].find { it['Name'] == property_name }
|
|
36
|
-
return if property.nil?
|
|
37
|
-
|
|
38
|
-
value = property['Value']
|
|
39
|
-
value.nil? || value.is_a?(String) ? value : value['$value']
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Hash of all property names to values
|
|
43
|
-
#
|
|
44
|
-
# @param include_ids [Boolean] Whether to include the iMIS ID and Ordinal
|
|
45
|
-
#
|
|
46
|
-
def properties(include_ids: false)
|
|
47
|
-
raw['Properties']['$values']
|
|
48
|
-
.map { it['Name'] }
|
|
49
|
-
.select { include_ids || !%w[ID Ordinal].include?(it) }
|
|
50
|
-
.index_with { self[it] }
|
|
51
|
-
end
|
|
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
|
-
|
|
57
|
-
def inspect
|
|
58
|
-
stringio = StringIO.new
|
|
59
|
-
PP.pp(self, stringio)
|
|
60
|
-
stringio.string.delete("\n")
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def pretty_print(pp)
|
|
64
|
-
data = { entity:, imis_id:, ordinal: }.compact
|
|
65
|
-
|
|
66
|
-
pp.group(1, "#<#{self.class}", '>') do
|
|
67
|
-
data.each do |key, value|
|
|
68
|
-
pp.breakable
|
|
69
|
-
pp.text "#{key}="
|
|
70
|
-
pp.pp value
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
data/lib/usps/imis/error.rb
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Usps
|
|
4
|
-
module Imis
|
|
5
|
-
# Base error class for all internal exceptions
|
|
6
|
-
#
|
|
7
|
-
class Error < StandardError
|
|
8
|
-
# Additional call-specific metadata to pass through to Bugsnag
|
|
9
|
-
#
|
|
10
|
-
attr_accessor :metadata
|
|
11
|
-
|
|
12
|
-
# A new instance of +ApiError+
|
|
13
|
-
#
|
|
14
|
-
# @param message [String] The base exception message
|
|
15
|
-
# @param metadata [Hash] Additional call-specific metadata to pass through to Bugsnag
|
|
16
|
-
#
|
|
17
|
-
def initialize(message, metadata = {})
|
|
18
|
-
super(message)
|
|
19
|
-
@metadata = metadata
|
|
20
|
-
|
|
21
|
-
Imis.logger(self.class.name).error self
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Additional metadata to include in Bugsnag reports
|
|
25
|
-
#
|
|
26
|
-
# Can include fields at the top level, which will be shows on the custom tab
|
|
27
|
-
#
|
|
28
|
-
# Can include fields nested under a top-level key, which will be shown on a tab with the
|
|
29
|
-
# top-level key as its name
|
|
30
|
-
#
|
|
31
|
-
# @return [Hash]
|
|
32
|
-
#
|
|
33
|
-
def bugsnag_meta_data
|
|
34
|
-
metadata == {} ? {} : base_metadata
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
def base_metadata
|
|
40
|
-
{ api: metadata }
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
require_relative 'errors/api_error'
|
|
47
|
-
require_relative 'errors/config_error'
|
|
48
|
-
require_relative 'errors/locked_id_error'
|
|
49
|
-
require_relative 'errors/mapper_error'
|
|
50
|
-
require_relative 'errors/missing_id_error'
|
|
51
|
-
require_relative 'errors/not_found_error'
|
|
52
|
-
require_relative 'errors/response_error'
|
|
53
|
-
require_relative 'errors/panel_unimplemented_error'
|
|
54
|
-
require_relative 'errors/unexpected_property_type_error'
|
|
55
|
-
require_relative 'errors/command_line_error'
|