usps-imis-api 0.11.23 → 0.11.24

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f2842977f938199479a756b99cdf0a9849fa4b006940debfb08a576b92e389e
4
- data.tar.gz: 485c9ed5663f2f538896e563d15991f59a1f8d810728d99b9465078321ecc657
3
+ metadata.gz: 547836afa31a2c997a8494b75185b98d6102750d83c210c81bb1026dfec76074
4
+ data.tar.gz: 37ee57c08ff274b829d5cfb17c19ecb1f7110f854900706f2740734925d97405
5
5
  SHA512:
6
- metadata.gz: 0ad85e740f53e024400bb491c9d818d48185d6dd2bd8dfdbc2a6befc09096034d57d580d96461cd552f5967b5c4b861db733ea352316c29ef8124cb14a7fa5d5
7
- data.tar.gz: 0cbc1a0d52f8dd83bd1a7e2ec04db2963446b804c717cdcd6fbb820ad1bb1e02cb21de49bbfcae05bdd66eb789604213f482e66dd14fa91966d8f79895e7aaf0
6
+ metadata.gz: e043dae5556f22f9d903d6a96b0101b3a0516bc424792720b1f394315f88e1c45f4092f63a7010e4027c32e289ab1228d702a5e775e13e4dc4473723a118de22
7
+ data.tar.gz: 2cf61c236f9d3f6415e09607925ce196876fda35073927c20c890f06769fe4c17e0149a4ae265256666a463827bdf7f363895e2dd532030619e4b970ff45068b
data/lib/usps/imis/api.rb CHANGED
@@ -135,7 +135,7 @@ module Usps
135
135
  #
136
136
  # @return [Usps::Imis::Query] Query wrapper
137
137
  #
138
- def query(query_name, query_params = {}) = Query.new(self, query_name, **query_params)
138
+ def query(query_name, query_params = nil) = Query.new(self, query_name, **query_params)
139
139
 
140
140
  # Run requests as DSL, with specific +BusinessObject+ only maintained for this scope
141
141
  #
@@ -202,7 +202,7 @@ module Usps
202
202
  @panels ||= Panels.all(self)
203
203
  end
204
204
 
205
- # Used by the PHP Wrapper to reduce authentication requests
205
+ # Used by the CLI wrappers to reduce authentication requests
206
206
  #
207
207
  def auth_token
208
208
  authenticate
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pp'
4
+ require 'stringio'
5
+
6
+ module Usps
7
+ module Imis
8
+ # Base class for API data response wrappers
9
+ #
10
+ class BaseData < 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
+ # Access the iMIS ID property
20
+ #
21
+ def imis_id
22
+ raise Errors::ApiError, "#{self.class} must implement #imis_id"
23
+ end
24
+ alias id imis_id
25
+
26
+ # Access an individual property value by name
27
+ #
28
+ def [](_property_name)
29
+ raise Errors::ApiError, "#{self.class} must implement #[](property_name)"
30
+ end
31
+
32
+ # Hash of all property names to values
33
+ #
34
+ def properties(...)
35
+ raise Errors::ApiError, "#{self.class} must implement #properties"
36
+ end
37
+
38
+ def []=(...)
39
+ raise(
40
+ Errors::ApiError,
41
+ "#{self.class} does not support setting values. If you need to modify it, call `.raw` on it."
42
+ )
43
+ end
44
+
45
+ def inspect
46
+ stringio = StringIO.new
47
+ PP.pp(self, stringio)
48
+ stringio.string.delete("\n")
49
+ end
50
+
51
+ def pretty_print(pp)
52
+ pp.group(1, "#<#{self.class}", '>') do
53
+ pretty_print_data.each do |key, value|
54
+ pp.breakable
55
+ pp.text "#{key}="
56
+ pp.pp value
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'requests'
4
4
  require_relative 'data'
5
+ require_relative 'party_data'
5
6
 
6
7
  module Usps
7
8
  module Imis
@@ -160,6 +161,8 @@ module Usps
160
161
  # Manually assemble the matching data structure, with fields in the correct order
161
162
  #
162
163
  def filter_fields(fields)
164
+ block_not_supported!
165
+
163
166
  existing = get
164
167
 
165
168
  JSON.parse(JSON.dump(existing)).tap do |updated|
@@ -188,7 +191,8 @@ module Usps
188
191
  raise Errors::MissingIdError if api.imis_id.nil?
189
192
 
190
193
  response = submit(uri, authorize(http_get))
191
- result = Data.from_json(response.body)
194
+ klass = business_object_name == 'Party' ? PartyData : Data
195
+ result = klass.from_json(response.body)
192
196
  logger.json result
193
197
  result
194
198
  end
@@ -200,10 +204,17 @@ module Usps
200
204
 
201
205
  request.body = JSON.dump(body)
202
206
  response = submit(uri, authorize(request))
203
- result = Data.from_json(response.body)
207
+ klass = business_object_name == 'Party' ? PartyData : Data
208
+ result = klass.from_json(response.body)
204
209
  logger.json result
205
210
  result
206
211
  end
212
+
213
+ def block_not_supported!
214
+ return unless business_object_name == 'Party'
215
+
216
+ raise Errors::ApiError, 'The Party business object does not support updating specific fields'
217
+ end
207
218
  end
208
219
  end
209
220
  end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'performers'
4
+
3
5
  module Usps
4
6
  module Imis
5
7
  module CommandLine
6
8
  # Command line interface for the Api
7
9
  #
8
10
  class Interface
11
+ include Performers
12
+
9
13
  NAME = 'USPS iMIS API - Ruby'
10
14
 
11
15
  # CLI options that indicate the response is a raw Hash rather than a Data object,
@@ -21,8 +25,16 @@ module Usps
21
25
  #
22
26
  attr_reader :logger
23
27
 
28
+ # Initialize an +Interface+ and run it with the provided options
29
+ #
30
+ # @param [**Hash] Options from the CLI options parser
31
+ #
24
32
  def self.run(...) = new(...).run
25
33
 
34
+ # A new instance of +Interface+
35
+ #
36
+ # @param [**Hash] Options from the CLI options parser
37
+ #
26
38
  def initialize(**)
27
39
  @options = input_options.merge(**)
28
40
  options[:version] = true if default_options?
@@ -32,6 +44,8 @@ module Usps
32
44
  @logger ||= Imis.logger('CommandLine')
33
45
  end
34
46
 
47
+ # Run the configured action on the API
48
+ #
35
49
  def run
36
50
  logger.info 'Running'
37
51
  logger.debug 'CLI Options:'
@@ -50,19 +64,15 @@ module Usps
50
64
 
51
65
  # :nocov:
52
66
  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
67
+ return OptionsParser.new.options unless ENV.fetch('CI', false) || ENV.fetch('TESTING', false)
68
+
69
+ {
70
+ raw: false,
71
+ quiet: false,
72
+ log: false,
73
+ log_level: 'info',
74
+ version: false
75
+ }
66
76
  end
67
77
  # :nocov:
68
78
 
@@ -86,62 +96,6 @@ module Usps
86
96
  logger.debug "iMIS ID: #{api.imis_id}" if api.imis_id
87
97
  end
88
98
 
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
99
  def simplify(data)
146
100
  return data.to_a if data.is_a?(Query)
147
101
  return data if options[:raw] || RAW_HASH_RESPONSE_OPTIONS.any? { options[it] }
@@ -159,13 +113,13 @@ module Usps
159
113
  end
160
114
 
161
115
  # :nocov:
162
- def output(&block)
116
+ def output(&)
163
117
  if ENV.fetch('SUPPRESS_OUTPUT', false)
164
118
  logger.debug 'Output suppressed'
165
119
  return
166
120
  end
167
121
 
168
- puts JSON.dump(block.call) if block_given?
122
+ puts JSON.dump(yield) if block_given?
169
123
  end
170
124
  # :nocov:
171
125
 
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Usps
4
+ module Imis
5
+ module CommandLine
6
+ # Command line interface perform matchers
7
+ #
8
+ # @private
9
+ #
10
+ module Performers
11
+ private
12
+
13
+ def perform!
14
+ case convert(options)
15
+ in mapper:, **extra then perform_mapper(mapper, **extra)
16
+ in on:, **extra then perform_on(on, **extra)
17
+ in panel:, **extra then perform_panel(panel, **extra)
18
+ in query:, **extra then api.query(query, extra[:data])
19
+ in business_objects: true then api.business_objects
20
+ in auth_token: true then api.auth_token
21
+ in certificate: then api.imis_id
22
+ in version: true then "#{Interface::NAME} (v#{Imis::VERSION})"
23
+ end
24
+ rescue NoMatchingPatternError => e
25
+ raise Errors::CommandLineError, "Unable to match pattern from options: #{e.message}"
26
+ end
27
+
28
+ def convert(options)
29
+ options.dup.tap do |converted|
30
+ case converted
31
+ in map: then converted.merge!(mapper: api.mapper, field: map)
32
+ in mapper: true then converted[:mapper] = api.mapper
33
+ in on: then converted[:on] = api.on(on)
34
+ in panel: then converted[:panel] = api.panels.public_send(panel)
35
+ else
36
+ # Nothing to convert
37
+ end
38
+ end
39
+ end
40
+
41
+ def perform_mapper(mapper, **options)
42
+ case options
43
+ in field:, data: then mapper.put_field(field.to_sym, data)
44
+ in fields: then mapper.get_fields(*fields)
45
+ in field: then mapper.get_field(field.to_sym)
46
+ in data: then mapper.update(data)
47
+ end
48
+ end
49
+
50
+ def perform_on(on, **options)
51
+ case options
52
+ in delete: true then on.delete
53
+ in create: true, data: then on.post(data)
54
+ in data:, field: then on.put_field(field, data)
55
+ in data: then on.put_fields(data)
56
+ in fields: then on.get_fields(*fields)
57
+ in field: then on.get_field(field)
58
+ else
59
+ on.get
60
+ end
61
+ end
62
+
63
+ def perform_panel(panel, **options)
64
+ case options
65
+ in delete: true, ordinal: then panel.delete(ordinal)
66
+ in create: true, data: then panel.post(data)
67
+ in ordinal:, data:, field: then panel.put_field(ordinal, field, data)
68
+ in ordinal:, data: then panel.put_fields(ordinal, data)
69
+ in ordinal:, fields: then panel.get_fields(ordinal, *fields)
70
+ in ordinal:, field: then panel.get_field(ordinal, field)
71
+ in ordinal: then panel.get(ordinal)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -3,19 +3,13 @@
3
3
  require 'pp'
4
4
  require 'stringio'
5
5
 
6
+ require_relative 'base_data'
7
+
6
8
  module Usps
7
9
  module Imis
8
10
  # Convenience wrapper for accessing specific properties within an API data response
9
11
  #
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
-
12
+ class Data < BaseData
19
13
  # The Business Object or Panel name
20
14
  #
21
15
  def entity = raw['EntityTypeName']
@@ -32,7 +26,7 @@ module Usps
32
26
  # Access an individual property value by name
33
27
  #
34
28
  def [](property_name)
35
- property = raw['Properties']['$values'].find { it['Name'] == property_name }
29
+ property = property_values.find { it['Name'] == property_name }
36
30
  return if property.nil?
37
31
 
38
32
  value = property['Value']
@@ -44,33 +38,19 @@ module Usps
44
38
  # @param include_ids [Boolean] Whether to include the iMIS ID and Ordinal
45
39
  #
46
40
  def properties(include_ids: false)
47
- raw['Properties']['$values']
41
+ property_values
48
42
  .map { it['Name'] }
49
43
  .select { include_ids || !%w[ID Ordinal].include?(it) }
50
44
  .index_with { self[it] }
51
45
  end
52
46
 
53
- def []=(...)
54
- raise Errors::ApiError, '`Data` does not support setting values. If you need to modify it, call `.raw` on it.'
55
- end
47
+ private
56
48
 
57
- def inspect
58
- stringio = StringIO.new
59
- PP.pp(self, stringio)
60
- stringio.string.delete("\n")
49
+ def pretty_print_data
50
+ { entity:, imis_id:, ordinal: }.compact
61
51
  end
62
52
 
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
53
+ def property_values = raw['Properties']['$values']
74
54
  end
75
55
  end
76
56
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pp'
4
+ require 'stringio'
5
+
6
+ require_relative 'base_data'
7
+
8
+ module Usps
9
+ module Imis
10
+ # Convenience wrapper for accessing specific properties within an API data response for the
11
+ # Party Business Object, which has a different internal structure than others
12
+ #
13
+ class PartyData < BaseData
14
+ # The Business Object name
15
+ #
16
+ def entity = 'Party'
17
+
18
+ # Access the iMIS ID property
19
+ #
20
+ def imis_id = self['Id'].to_i
21
+ alias id imis_id
22
+
23
+ # Access the certificate number
24
+ #
25
+ def certificate = self['AlternateIds'].find { it['IdType'] == 'MajorKey' }['Id']
26
+
27
+ # Access an individual property value by name
28
+ #
29
+ def [](property_name) = properties[property_name]
30
+
31
+ # Hash of all property names to values
32
+ #
33
+ def properties
34
+ @properties ||= format_extracted(extract_hash(raw))
35
+ end
36
+
37
+ private
38
+
39
+ def pretty_print_data
40
+ { entity:, imis_id: }.compact
41
+ end
42
+
43
+ def extract_hash(hash)
44
+ hash.filter_map do |key, value|
45
+ next if key == '$type'
46
+
47
+ [key, extract_value(value)]
48
+ end
49
+ end
50
+
51
+ def extract_value(value)
52
+ case value
53
+ when String, Integer, true, false, nil then value
54
+ when Array then value.map { extract_value(it) }
55
+ when Hash then extract_hash_value(value)
56
+ else
57
+ raise Errors::ApiError, "Unrecognized value type: #{value.inspect}"
58
+ end
59
+ end
60
+
61
+ def extract_hash_value(value)
62
+ return value['$value'] if value.key?('$value')
63
+ return extract_value(value['$values']) if value.key?('$values')
64
+ return [value['Name'], extract_value(value['Value'])] if value.key?('Name') && value.key?('Value')
65
+
66
+ extract_hash(value)
67
+ end
68
+
69
+ def format_extracted(data)
70
+ hash = data.to_h
71
+
72
+ hash.each do |key, value|
73
+ case value
74
+ when String, Integer, true, false then next
75
+ end
76
+
77
+ if %w[Addresses AlternateIds CommunicationPreferences Emails Salutations].include?(key)
78
+ hash[key] = value.map { format_extracted(it.to_h) }
79
+ elsif value.all? { it.is_a?(String) }
80
+ # Do nothing
81
+ else
82
+ hash[key] = format_extracted(value.to_h)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Usps
4
4
  module Imis
5
- VERSION = '0.11.23'
5
+ VERSION = '0.11.24'
6
6
  end
7
7
  end
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.11.23
4
+ version: 0.11.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julian Fiander
@@ -76,10 +76,12 @@ files:
76
76
  - bin/imis
77
77
  - lib/usps/imis.rb
78
78
  - lib/usps/imis/api.rb
79
+ - lib/usps/imis/base_data.rb
79
80
  - lib/usps/imis/business_object.rb
80
81
  - lib/usps/imis/command_line.rb
81
82
  - lib/usps/imis/command_line/interface.rb
82
83
  - lib/usps/imis/command_line/options_parser.rb
84
+ - lib/usps/imis/command_line/performers.rb
83
85
  - lib/usps/imis/config.rb
84
86
  - lib/usps/imis/data.rb
85
87
  - lib/usps/imis/error.rb
@@ -103,6 +105,7 @@ files:
103
105
  - lib/usps/imis/panels/base_panel.rb
104
106
  - lib/usps/imis/panels/education.rb
105
107
  - lib/usps/imis/panels/vsc.rb
108
+ - lib/usps/imis/party_data.rb
106
109
  - lib/usps/imis/properties.rb
107
110
  - lib/usps/imis/query.rb
108
111
  - lib/usps/imis/requests.rb