usps-imis-api 0.11.26 → 0.11.28

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: a3e12835817725b251105021e66e0119f4a934bcce5e13d648b611875aff33dd
4
- data.tar.gz: b01c567700f486cebd6cfabab0b7017fbcd351cd83bb47bfa38528ad3ed2a9f8
3
+ metadata.gz: 386dc2ca30c8b2c640872931b83d9105e7e189d56c85edb2b8d75e02e6b830e8
4
+ data.tar.gz: 1353457f06768558a7cf4491ba0dbe221e072679e4c7f0164616743464700259
5
5
  SHA512:
6
- metadata.gz: 763bd5cb2d6227333d54e135a66bfbe3a51abe236809c1cbd4a7252f3d4cfce3198e6a3d7235e88ee456bff1a7203ec161c62478abacdedac065643718a5ea66
7
- data.tar.gz: 66784814074fb357d0b9afc665ba47c9f19091f0e537b85b351ad215e69960568d8ae73e19606b8e813dd1af04d261513b3f3573d4cead995202d97e64c54e57
6
+ metadata.gz: 9ef7330684ebcb7c169f285a2c4e44e85307df27b8426e9e25889dbd58c3338732cab7199461e3e6c3797d62a6afe915213261fdac08319dbf957cc652a073a3
7
+ data.tar.gz: 75e262ce1acec88980b072588747032be8be2faf9aa3bc6bf017e8911d0d728b8a6398e029729c715e63cd00e5844bfddbb7e27804607e23c4160dad2cb5f555
data/lib/usps/imis/api.rb CHANGED
@@ -53,8 +53,9 @@ module Usps
53
53
  #
54
54
  # @param imis_id [Integer, String] iMIS ID to select immediately on initialization
55
55
  #
56
- def initialize(imis_id: nil)
56
+ def initialize(imis_id: nil, record_id: nil)
57
57
  self.imis_id = imis_id if imis_id
58
+ self.record_id = record_id if record_id
58
59
  @logger ||= Imis.logger('Api')
59
60
  Imis.config.validate!
60
61
  end
@@ -71,6 +72,26 @@ module Usps
71
72
  @imis_id = id&.to_i
72
73
  end
73
74
 
75
+ # Manually set the current record ID
76
+ #
77
+ # @param id [Integer, String] Record ID to select for future requests
78
+ #
79
+ # @return [Integer] Record ID
80
+ #
81
+ def record_id=(id)
82
+ return if id.nil?
83
+
84
+ raise Errors::LockedIdError if lock_imis_id
85
+
86
+ @record_id = id.to_i
87
+ end
88
+
89
+ # Currently selected Record ID for API requests
90
+ #
91
+ # Defaults to the iMIS ID
92
+ #
93
+ def record_id = @record_id || imis_id
94
+
74
95
  # Convert a member's certificate number into an iMIS ID number
75
96
  #
76
97
  # @param certificate [String] Certificate number to lookup the corresponding iMIS ID for
@@ -101,6 +122,7 @@ module Usps
101
122
  #
102
123
  # @param id [Integer, String] iMIS ID to select for requests within the block
103
124
  # @param certificate [String] Certificate number to convert to iMIS ID and select for requests within the block
125
+ # @param record_id [Integer] Record ID to select for requests within the block
104
126
  #
105
127
  # @example
106
128
  # with(12345) do
@@ -109,12 +131,14 @@ module Usps
109
131
  #
110
132
  # @return [Usps::Imis::Api]
111
133
  #
112
- def with(id = nil, certificate: nil, &)
134
+ def with(id = nil, certificate: nil, record_id: nil, &)
113
135
  raise ArgumentError, 'Must provide id or certificate' unless id || certificate
114
136
 
115
137
  old_id = imis_id
138
+ old_record_id = @record_id
116
139
 
117
140
  id.nil? ? imis_id_for(certificate) : self.imis_id = id
141
+ self.record_id = record_id
118
142
  return self unless block_given?
119
143
 
120
144
  @lock_imis_id = true
@@ -123,6 +147,7 @@ module Usps
123
147
  if block_given?
124
148
  @lock_imis_id = false
125
149
  self.imis_id = old_id
150
+ self.record_id = old_record_id
126
151
  end
127
152
  end
128
153
 
@@ -23,6 +23,12 @@ module Usps
23
23
  end
24
24
  alias id imis_id
25
25
 
26
+ # Access the Record ID property
27
+ #
28
+ def record_id
29
+ raise Errors::ApiError, "#{self.class} must implement #record_id"
30
+ end
31
+
26
32
  # Access an individual property value by name
27
33
  #
28
34
  def [](_property_name)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'requests'
4
+ require_relative 'data'
5
+ require_relative 'party_data'
6
+
7
+ module Usps
8
+ module Imis
9
+ # Constructor for blank BusinessObject and BasePanel objects
10
+ #
11
+ class BlankObject
12
+ # The parent object
13
+ #
14
+ attr_reader :parent
15
+
16
+ def initialize(parent, ordinal = nil)
17
+ @parent = parent
18
+ @ordinal = ordinal
19
+ end
20
+
21
+ # Build a blank object for the current iMIS ID
22
+ #
23
+ def build(&) = payload_header.merge(Properties.build(parent.api.record_id.to_s, @ordinal&.to_s, &))
24
+
25
+ private
26
+
27
+ def identity_type = 'System.Collections.ObjectModel.Collection`1[[System.String, mscorlib]], mscorlib'
28
+
29
+ def payload_header
30
+ {
31
+ '$type' => 'Asi.Soa.Core.DataContracts.GenericEntityData, Asi.Contracts',
32
+ 'EntityTypeName' => parent.business_object_name,
33
+ 'PrimaryParentEntityTypeName' => 'Party',
34
+ 'Identity' => identity,
35
+ 'PrimaryParentIdentity' => primary_parent_identity
36
+ }
37
+ end
38
+
39
+ def identity
40
+ {
41
+ '$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
42
+ 'EntityTypeName' => parent.business_object_name,
43
+ 'IdentityElements' => {
44
+ '$type' => identity_type,
45
+ '$values' => [parent.api.record_id.to_s, @ordinal&.to_s].compact
46
+ }
47
+ }
48
+ end
49
+
50
+ def primary_parent_identity
51
+ {
52
+ '$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
53
+ 'EntityTypeName' => 'Party',
54
+ 'IdentityElements' => {
55
+ '$type' => identity_type,
56
+ '$values' => [parent.api.imis_id.to_s]
57
+ }
58
+ }
59
+ end
60
+ end
61
+ end
62
+ end
@@ -6,7 +6,8 @@ require_relative 'party_data'
6
6
 
7
7
  module Usps
8
8
  module Imis
9
- # DEV
9
+ # Configured accessor for a specific Business Object
10
+ #
10
11
  class BusinessObject
11
12
  include Requests
12
13
 
@@ -47,12 +48,12 @@ module Usps
47
48
 
48
49
  # Support passthrough for Api#with
49
50
  #
50
- def with(imis_id, &)
51
+ def with(*, **, &)
51
52
  # Bring into local scope
52
53
  wrapper_business_object_name = business_object_name
53
54
  wrapper_ordinal = ordinal
54
55
 
55
- api.with(imis_id) do
56
+ api.with(*, **) do
56
57
  on(wrapper_business_object_name, ordinal: wrapper_ordinal, &)
57
58
  end
58
59
  end
@@ -136,6 +137,15 @@ module Usps
136
137
  def delete = submit(uri, authorize(http_delete)).body == '' # rubocop:disable Naming/PredicateMethod
137
138
  alias destroy delete
138
139
 
140
+ # Build a blank object for the current iMIS ID
141
+ #
142
+ def blank_object(&) = BlankObject.new(self).build(&)
143
+
144
+ # Create a business object for the current member, starting with a blank object and building properties
145
+ #
146
+ def post_from_blank(&) = post(blank_object(&))
147
+ alias create_from_blank post_from_blank
148
+
139
149
  # Ruby 3.5 instance variable filter
140
150
  #
141
151
  def instance_variables_to_inspect = instance_variables - %i[@api @logger]
@@ -153,9 +163,9 @@ module Usps
153
163
  #
154
164
  def id_for_uri(id = nil)
155
165
  return CGI.escape(id) unless id.nil?
156
- return CGI.escape("~#{api.imis_id}|#{ordinal}") if ordinal
166
+ return CGI.escape("~#{api.record_id}|#{ordinal}") if ordinal
157
167
 
158
- api.imis_id.to_s
168
+ api.record_id.to_s
159
169
  end
160
170
 
161
171
  # Manually assemble the matching data structure, with fields in the correct order
@@ -14,11 +14,15 @@ module Usps
14
14
  #
15
15
  def entity = raw['EntityTypeName']
16
16
 
17
- # Access the iMIS ID property
17
+ # Access the iMIS ID (i.e. Party ID) property
18
18
  #
19
- def imis_id = self['ID'].to_i
19
+ def imis_id = raw['PrimaryParentIdentity']['IdentityElements']['$values'].first.to_i
20
20
  alias id imis_id
21
21
 
22
+ # Access the Record ID property
23
+ #
24
+ def record_id = raw['Identity']['IdentityElements']['$values'].first.to_i
25
+
22
26
  # Access the Ordinal identifier property (if present)
23
27
  #
24
28
  def ordinal = self['Ordinal']&.to_i
@@ -47,7 +51,7 @@ module Usps
47
51
  private
48
52
 
49
53
  def pretty_print_data
50
- { entity:, imis_id:, ordinal: }.compact
54
+ { entity:, imis_id:, record_id: (record_id unless record_id == imis_id), ordinal: }.compact
51
55
  end
52
56
 
53
57
  def property_values = raw['Properties']['$values']
@@ -18,7 +18,7 @@ module Usps
18
18
  super(message)
19
19
  @metadata = metadata
20
20
 
21
- Imis.logger(self.class.name).error self
21
+ Imis.logger(self.class.name).multiline(self.message, level: :error)
22
22
  end
23
23
 
24
24
  # Additional metadata to include in Bugsnag reports
@@ -3,12 +3,12 @@
3
3
  module Usps
4
4
  module Imis
5
5
  module Errors
6
- # Exception raised when attempting to change the iMIS ID while it is locked
6
+ # Exception raised when attempting to change the iMIS ID or Record ID while it is locked
7
7
  #
8
8
  class LockedIdError < Error
9
9
  def initialize = super(message)
10
10
 
11
- def message = 'Cannot change iMIS ID while locked'
11
+ def message = 'Cannot change iMIS or Record ID while locked'
12
12
  end
13
13
  end
14
14
  end
@@ -49,8 +49,8 @@ module Usps
49
49
  #
50
50
  def message
51
51
  [
52
- "#{self.class.name}: [#{status.to_s.upcase}] The iMIS API returned an error.",
53
- (metadata.inspect if metadata != {}),
52
+ "[#{status.to_s.upcase}] The iMIS API returned an error.",
53
+ (metadata.inspect if metadata?),
54
54
  body
55
55
  ].compact.join("\n")
56
56
  end
@@ -61,6 +61,8 @@ module Usps
61
61
  { api: { status:, body: } }
62
62
  end
63
63
 
64
+ def metadata? = metadata != {} && !metadata.nil?
65
+
64
66
  def status
65
67
  @status ||=
66
68
  case response.code
@@ -93,7 +95,7 @@ module Usps
93
95
  response_body['error_description']
94
96
  else
95
97
  # Unknown error type: just use the raw response
96
- response.body
98
+ JSON.pretty_generate(response.body)
97
99
  end
98
100
  end
99
101
  end
@@ -5,12 +5,15 @@ module Usps
5
5
  # Formatted logger helpers
6
6
  #
7
7
  module LoggerHelpers
8
- def multiline(string)
9
- string.split("\n").each { |line| debug(line) }
8
+ def multiline(string, level: :debug)
9
+ string.split("\n").each { public_send(level, it) }
10
10
  end
11
11
 
12
12
  def json(data)
13
- tagged('JSON') { multiline(JSON.pretty_generate(data)) }
13
+ hash = data.is_a?(String) ? JSON.parse(data) : data
14
+ tagged('JSON') { multiline(JSON.pretty_generate(hash)) }
15
+ rescue StandardError
16
+ multiline(data)
14
17
  end
15
18
  end
16
19
  end
@@ -14,9 +14,10 @@ module Usps
14
14
  #
15
15
  attr_reader :logger
16
16
 
17
- def initialize(api = nil, imis_id: nil)
17
+ def initialize(api = nil, imis_id: nil, record_id: nil)
18
18
  @api = api || Api.new
19
19
  @api.imis_id = imis_id if imis_id
20
+ @api.record_id = record_id if record_id
20
21
  @logger ||= Imis.logger('Panel')
21
22
  end
22
23
 
@@ -30,7 +31,7 @@ module Usps
30
31
  #
31
32
  # @return [Usps::Imis::Data, Array<Usps::Imis::Data>] Response data from the API
32
33
  #
33
- def get(ordinal, *fields) = api.on(business_object, ordinal:).get(*fields)
34
+ def get(ordinal, *fields) = api.on(business_object_name, ordinal:).get(*fields)
34
35
  alias read get
35
36
 
36
37
  # Get a single named field from a Panel for the current member
@@ -40,7 +41,7 @@ module Usps
40
41
  #
41
42
  # @return Response data field value from the API
42
43
  #
43
- def get_field(ordinal, field) = api.on(business_object, ordinal:).get_field(field)
44
+ def get_field(ordinal, field) = api.on(business_object_name, ordinal:).get_field(field)
44
45
  alias fetch get_field
45
46
  alias [] get_field
46
47
 
@@ -51,7 +52,7 @@ module Usps
51
52
  #
52
53
  # @return [Array] Response data from the API
53
54
  #
54
- def get_fields(ordinal, *fields) = api.on(business_object, ordinal:).get_fields(*fields)
55
+ def get_fields(ordinal, *fields) = api.on(business_object_name, ordinal:).get_fields(*fields)
55
56
  alias fetch_all get_fields
56
57
 
57
58
  # Update a single named field on a business object for the current member
@@ -62,7 +63,7 @@ module Usps
62
63
  #
63
64
  # @return [Usps::Imis::Data] Response data from the API
64
65
  #
65
- def put_field(ordinal, field, value) = api.on(business_object, ordinal:).put_field(field, value)
66
+ def put_field(ordinal, field, value) = api.on(business_object_name, ordinal:).put_field(field, value)
66
67
  alias []= put_field
67
68
 
68
69
  # Update only specific fields on a Panel for the current member
@@ -72,7 +73,7 @@ module Usps
72
73
  #
73
74
  # @return [Usps::Imis::Data] Response data from the API
74
75
  #
75
- def put_fields(ordinal, fields) = api.on(business_object, ordinal:).put_fields(fields)
76
+ def put_fields(ordinal, fields) = api.on(business_object_name, ordinal:).put_fields(fields)
76
77
  alias patch put_fields
77
78
 
78
79
  # Update an existing object in the Panel
@@ -82,7 +83,7 @@ module Usps
82
83
  #
83
84
  # @return [Usps::Imis::Data] Response data from the API
84
85
  #
85
- def put(data) = api.on(business_object, ordinal: data[:ordinal]).put(payload(data))
86
+ def put(data) = api.on(business_object_name, ordinal: data[:ordinal]).put(payload(data))
86
87
  alias update put
87
88
 
88
89
  # Create a new object in the Panel
@@ -91,7 +92,7 @@ module Usps
91
92
  #
92
93
  # @return [Usps::Imis::Data] Response data from the API
93
94
  #
94
- def post(data) = api.on(business_object).post(payload(data))
95
+ def post(data) = api.on(business_object_name).post(payload(data))
95
96
  alias create post
96
97
 
97
98
  # Remove a specific object from the Panel
@@ -100,58 +101,24 @@ module Usps
100
101
  #
101
102
  # @return [true] Only on success response (i.e. blank string from the API)
102
103
  #
103
- def delete(ordinal) = api.on(business_object, ordinal:).delete
104
+ def delete(ordinal) = api.on(business_object_name, ordinal:).delete
104
105
  alias destroy delete
105
106
 
107
+ def business_object_name
108
+ raise Errors::PanelUnimplementedError.from(self.class.name, 'business_object_name')
109
+ end
110
+
106
111
  # Ruby 3.5 instance variable filter
107
112
  #
108
113
  def instance_variables_to_inspect = instance_variables - %i[@api @logger]
109
114
 
110
115
  private
111
116
 
112
- def business_object
113
- raise Errors::PanelUnimplementedError.from(self.class.name, 'business_object')
114
- end
115
-
116
117
  def payload(_data)
117
118
  raise Errors::PanelUnimplementedError.from(self.class.name, 'payload(data)')
118
119
  end
119
120
 
120
- def payload_header(data)
121
- {
122
- '$type' => 'Asi.Soa.Core.DataContracts.GenericEntityData, Asi.Contracts',
123
- 'EntityTypeName' => business_object,
124
- 'PrimaryParentEntityTypeName' => 'Party',
125
- 'Identity' => identity(data[:ordinal]),
126
- 'PrimaryParentIdentity' => primary_parent_identity
127
- }
128
- end
129
-
130
- def identity(ordinal = nil)
131
- {
132
- '$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
133
- 'EntityTypeName' => business_object,
134
- 'IdentityElements' => {
135
- '$type' => identity_type,
136
- '$values' => [api.imis_id.to_s, ordinal&.to_s].compact
137
- }
138
- }
139
- end
140
-
141
- def primary_parent_identity
142
- {
143
- '$type' => 'Asi.Soa.Core.DataContracts.IdentityData, Asi.Contracts',
144
- 'EntityTypeName' => 'Party',
145
- 'IdentityElements' => {
146
- '$type' => identity_type,
147
- '$values' => [api.imis_id.to_s]
148
- }
149
- }
150
- end
151
-
152
- def identity_type = 'System.Collections.ObjectModel.Collection`1[[System.String, mscorlib]], mscorlib'
153
-
154
- def build_payload(data, &) = payload_header(data).merge(Properties.build(&))
121
+ def build_payload(data, &) = BlankObject.new(self, data[:ordinal]).build(&)
155
122
  end
156
123
  end
157
124
  end
@@ -6,16 +6,12 @@ module Usps
6
6
  # Panel for accessing the Educational completions business object
7
7
  #
8
8
  class Education < BasePanel
9
- private
9
+ def business_object_name = 'ABC_ASC_Educ'
10
10
 
11
- def business_object
12
- 'ABC_ASC_Educ'
13
- end
11
+ private
14
12
 
15
13
  def payload(data)
16
14
  build_payload(data) do |props|
17
- props.add 'ID', api.imis_id.to_s
18
- props.add 'Ordinal', data[:ordinal] if data[:ordinal]
19
15
  props.add 'ABC_EDUC_THRU_DATE', data[:thru_date] || '0001-01-01T00:00:00'
20
16
  props.add 'ABC_ECertificate', data[:certificate]
21
17
  props.add 'ABC_Educ_Description', data[:description]
@@ -6,16 +6,12 @@ module Usps
6
6
  # Panel for accessing the annual VSC completed counts business object
7
7
  #
8
8
  class Vsc < BasePanel
9
- private
9
+ def business_object_name = 'ABC_ASC_Vessel_Safety_Checks'
10
10
 
11
- def business_object
12
- 'ABC_ASC_Vessel_Safety_Checks'
13
- end
11
+ private
14
12
 
15
13
  def payload(data)
16
14
  build_payload(data) do |props|
17
- props.add 'ID', api.imis_id.to_s
18
- props.add 'Ordinal', data[:ordinal] if data[:ordinal]
19
15
  props.add 'Source_System', 'Manual ITCom Entry'
20
16
  props.add 'ABC_ECertificate', data[:certificate]
21
17
  props.add 'Activity_Type', 'VSC'
@@ -7,7 +7,10 @@ module Usps
7
7
  class Properties
8
8
  # Build the data for a new Properties field
9
9
  #
10
- def self.build(&) = new.build(&)
10
+ # @param id [String] iMIS ID to include in the properties before running the block
11
+ # @param ordinal [String] Ordinal to include in the properties before running the block
12
+ #
13
+ def self.build(id = nil, ordinal = nil, &) = new.build(id, ordinal, &)
11
14
 
12
15
  # Wrap value in the API-internal type structure
13
16
  #
@@ -24,7 +27,15 @@ module Usps
24
27
 
25
28
  # Build the data for the Properties field
26
29
  #
27
- def build
30
+ # @param id [String] iMIS ID to include in the properties before running the block
31
+ # @param ordinal [String] Ordinal to include in the properties before running the block
32
+ #
33
+ def build(id = nil, ordinal = nil)
34
+ @properties ||= []
35
+
36
+ add('ID', id) if id
37
+ add('Ordinal', ordinal) if ordinal
38
+
28
39
  yield(self)
29
40
 
30
41
  {
@@ -38,7 +49,6 @@ module Usps
38
49
  # Add an individual property to the field
39
50
  #
40
51
  def add(name, value)
41
- @properties ||= []
42
52
  @properties << {
43
53
  '$type' => 'Asi.Soa.Core.DataContracts.GenericPropertyData, Asi.Contracts',
44
54
  'Name' => name,
@@ -41,7 +41,7 @@ module Usps
41
41
  logger.info 'Submitting request to iMIS'
42
42
  logger.tagged('Request') do
43
43
  logger.debug "#{request.class.name.demodulize.upcase} #{uri}"
44
- logger.multiline sanitized_request_body(request)
44
+ logger.json sanitized_request_body(request)
45
45
 
46
46
  client(uri).request(request).tap do |result|
47
47
  raise Errors::ResponseError.from(result) unless result.is_a?(Net::HTTPSuccess)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Usps
4
4
  module Imis
5
- VERSION = '0.11.26'
5
+ VERSION = '0.11.28'
6
6
  end
7
7
  end
data/lib/usps/imis.rb CHANGED
@@ -22,6 +22,7 @@ require_relative 'imis/config'
22
22
  require_relative 'imis/error'
23
23
  require_relative 'imis/api'
24
24
  require_relative 'imis/properties'
25
+ require_relative 'imis/blank_object'
25
26
  require_relative 'imis/panels'
26
27
  require_relative 'imis/mocks'
27
28
  require_relative 'imis/version'
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.26
4
+ version: 0.11.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julian Fiander
@@ -77,6 +77,7 @@ files:
77
77
  - lib/usps/imis.rb
78
78
  - lib/usps/imis/api.rb
79
79
  - lib/usps/imis/base_data.rb
80
+ - lib/usps/imis/blank_object.rb
80
81
  - lib/usps/imis/business_object.rb
81
82
  - lib/usps/imis/command_line.rb
82
83
  - lib/usps/imis/command_line/interface.rb