usps-imis-api 0.11.23.pre.5 → 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: 0ff5cc6b3fffdbe890fd9798970406cb25f24c86701bea9e9c0f5db0c52f2589
4
- data.tar.gz: 5101c13613feaf5ba7c4636154e9e8bee097e22f485c5e00c4205f69cee48d7e
3
+ metadata.gz: 547836afa31a2c997a8494b75185b98d6102750d83c210c81bb1026dfec76074
4
+ data.tar.gz: 37ee57c08ff274b829d5cfb17c19ecb1f7110f854900706f2740734925d97405
5
5
  SHA512:
6
- metadata.gz: aa67cb37519dc9048c6307d816ba97b7e9818a31a963e6a41781b00484b70c7dab36f9166cbf0023555d4ef14940224e7768aaa93fa71687e917d6557f15f873
7
- data.tar.gz: 5e705606bcd8721197fcb69705692f4f2dc50e08f58ca0e45a95b2102cf410e256ccd191daf1e37f1a5ecd4e7d73969c553bf7035d6f6fd7e1483249664b89a0
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,16 +25,27 @@ 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
- validate_options!
40
+ options[:version] = true if default_options?
41
+
29
42
  configure! if options[:config]
30
43
  logging!
31
44
  @logger ||= Imis.logger('CommandLine')
32
45
  end
33
46
 
47
+ # Run the configured action on the API
48
+ #
34
49
  def run
35
50
  logger.info 'Running'
36
51
  logger.debug 'CLI Options:'
@@ -49,19 +64,15 @@ module Usps
49
64
 
50
65
  # :nocov:
51
66
  def input_options
52
- if ENV.fetch('CI', false) || ENV.fetch('TESTING', false)
53
- defaults = {
54
- raw: false,
55
- quiet: false,
56
- log: false,
57
- log_level: 'info',
58
- version: false,
59
- help: false
60
- }
61
- return defaults
62
- end
63
-
64
- 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
+ }
65
76
  end
66
77
  # :nocov:
67
78
 
@@ -85,62 +96,6 @@ module Usps
85
96
  logger.debug "iMIS ID: #{api.imis_id}" if api.imis_id
86
97
  end
87
98
 
88
- # rubocop:disable Metrics
89
- def perform!
90
- case convert(options)
91
- in mapper:, field:, data: then mapper.put_field(field.to_sym, data)
92
- in mapper:, fields: then mapper.get_fields(*fields)
93
- in mapper:, field: then mapper.get_field(field.to_sym)
94
- in mapper:, data: then mapper.update(data)
95
-
96
- in on:, delete: true then on.delete
97
- in on:, create: true, data: then on.post(data)
98
- in on:, data:, field: then on.put_field(field, data)
99
- in on:, data: then on.put_fields(data)
100
- in on:, fields: then on.get_fields(*fields)
101
- in on:, field: then on.get_field(field)
102
- in on: then on.get
103
-
104
- in panel:, delete: true, ordinal: then panel.delete(ordinal)
105
- in panel:, create: true, data: then panel.post(data)
106
- in panel:, ordinal:, data:, field: then panel.put_field(ordinal, field, data)
107
- in panel:, ordinal:, data: then panel.put_fields(ordinal, data)
108
- in panel:, ordinal:, fields: then panel.get_fields(ordinal, *fields)
109
- in panel:, ordinal:, field: then panel.get_field(ordinal, field)
110
- in panel:, ordinal: then panel.get(ordinal)
111
-
112
- in query:, data: then api.query(query, data)
113
- in query: then api.query(query)
114
-
115
- in business_objects: true then api.business_objects
116
-
117
- in auth_token: true then api.auth_token
118
-
119
- in certificate: then api.imis_id
120
-
121
- in version: true then Imis::VERSION
122
- end
123
- rescue NoMatchingPatternError => e
124
- raise Errors::CommandLineError, "Unable to match pattern from options: #{e.message}"
125
- end
126
- # rubocop:enable Metrics
127
-
128
- def convert(options)
129
- options.dup.tap do |converted|
130
- case converted
131
- in map: then converted.merge!(mapper: api.mapper, field: map)
132
- in mapper: true then converted[:mapper] = api.mapper
133
- in on: then converted[:on] = api.on(on)
134
- in panel: then converted[:panel] = api.panels.public_send(panel)
135
- else
136
- # Nothing to convert
137
- end
138
-
139
- # Remove mapper flag when false
140
- converted.delete(:mapper) unless converted[:mapper]
141
- end
142
- end
143
-
144
99
  def simplify(data)
145
100
  return data.to_a if data.is_a?(Query)
146
101
  return data if options[:raw] || RAW_HASH_RESPONSE_OPTIONS.any? { options[it] }
@@ -158,13 +113,13 @@ module Usps
158
113
  end
159
114
 
160
115
  # :nocov:
161
- def output(&block)
116
+ def output(&)
162
117
  if ENV.fetch('SUPPRESS_OUTPUT', false)
163
118
  logger.debug 'Output suppressed'
164
119
  return
165
120
  end
166
121
 
167
- puts JSON.dump(block.call) if block_given?
122
+ puts JSON.dump(yield) if block_given?
168
123
  end
169
124
  # :nocov:
170
125
 
@@ -196,11 +151,7 @@ module Usps
196
151
  end
197
152
  end
198
153
 
199
- def validate_options!
200
- return unless options[:log_level] == 'info' && options.except(:log_level, :quiet).values.none?
201
-
202
- options[:version] = true
203
- end
154
+ def default_options? = options[:log_level] == 'info' && options.except(:log_level).values.none?
204
155
  end
205
156
  end
206
157
  end
@@ -99,7 +99,7 @@ module Usps
99
99
  @options = parse_options.compact
100
100
  @arguments = ARGV # Not currently used
101
101
 
102
- Optimist.educate if ARGV.empty? && defaults?
102
+ Optimist.educate if ARGV.empty? && defaults? # DEV: This shadows setting the --version flag by default
103
103
 
104
104
  # :nocov:
105
105
  @options[:data] = read_stdin if stdin?
@@ -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'
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.pre.5
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