usps-imis-api 1.0.0.pre.rc.3 → 1.0.0.pre.rc.5

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: 927a6d408196ed631d013daac7854beb5b0c27b7b138a454c0078af710cad204
4
- data.tar.gz: 8d747f8023487b5fa1d2e4fc50a3aa5d26676cc99c818849b6fa9af205a8e50b
3
+ metadata.gz: 1c0abf715f3c6019016bc6b8bdf867ce9d83234ba093c6150a4885997e9156f4
4
+ data.tar.gz: bcd2727e5f74b7a8681ae8ea1c510539fc24b62085719b439a01a331989a59c9
5
5
  SHA512:
6
- metadata.gz: b87668b7e37cfa951b729ef0c01b580055b0d48076f6ffab10bff81e0d47ad255df3718e3c63daece6c0a2fc256506eb9ceefec0bed564ce09b1c5bc4e8ba116
7
- data.tar.gz: e0c08859e7e890887267d13166023ef903355ed70416bd83e856d143de0eab6753d78c0b976acdcc3356144bd3ae92a95ebef43233c6b40a89b17315bb29de0e
6
+ metadata.gz: e0f98c36fd93ae643a949980e590fbb327cc40e1947ef5c8b530156e3961073082702dd38916fa27da4db10c642316c3c3db9f97d82d2f7c616032917cea22f1
7
+ data.tar.gz: 3195ecb427cff0fea1c6d1cef0d877507c2bb7baaa3a3db6d669f281048a03de7fb06c15e3b5f1cfe36625ece30d2e890f05202accfaf56148de40dda0fdce4d
data/.rubocop.yml CHANGED
@@ -32,8 +32,6 @@ Layout/SpaceInsideHashLiteralBraces:
32
32
  EnforcedStyleForEmptyBraces: no_space
33
33
  Layout/SpaceInsideArrayLiteralBrackets:
34
34
  EnforcedStyle: no_space
35
- Layout/LineLength:
36
- Max: 100
37
35
 
38
36
  Lint/UnusedMethodArgument:
39
37
  Enabled: true
data/Gemfile.lock CHANGED
@@ -1,17 +1,39 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- usps-imis-api (1.0.0.pre.rc.3)
4
+ usps-imis-api (1.0.0.pre.rc.5)
5
+ activesupport (~> 8.0)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ activesupport (8.0.3)
11
+ base64
12
+ benchmark (>= 0.3)
13
+ bigdecimal
14
+ concurrent-ruby (~> 1.0, >= 1.3.1)
15
+ connection_pool (>= 2.2.5)
16
+ drb
17
+ i18n (>= 1.6, < 2)
18
+ logger (>= 1.4.2)
19
+ minitest (>= 5.1)
20
+ securerandom (>= 0.3)
21
+ tzinfo (~> 2.0, >= 2.0.5)
22
+ uri (>= 0.13.1)
9
23
  ast (2.4.2)
24
+ base64 (0.3.0)
25
+ benchmark (0.4.1)
26
+ bigdecimal (3.3.1)
27
+ concurrent-ruby (1.3.5)
28
+ connection_pool (2.5.4)
10
29
  date (3.4.1)
11
30
  diff-lcs (1.5.1)
12
31
  docile (1.4.1)
13
32
  dotenv (3.1.4)
33
+ drb (2.2.3)
14
34
  erb (5.0.3)
35
+ i18n (1.14.7)
36
+ concurrent-ruby (~> 1.0)
15
37
  io-console (0.8.1)
16
38
  irb (1.15.2)
17
39
  pp (>= 0.6.0)
@@ -19,6 +41,8 @@ GEM
19
41
  reline (>= 0.4.2)
20
42
  json (2.7.2)
21
43
  language_server-protocol (3.17.0.3)
44
+ logger (1.7.0)
45
+ minitest (5.26.0)
22
46
  parallel (1.26.3)
23
47
  parser (3.3.5.0)
24
48
  ast (~> 2.4.1)
@@ -67,6 +91,7 @@ GEM
67
91
  rubocop-rspec (3.1.0)
68
92
  rubocop (~> 1.61)
69
93
  ruby-progressbar (1.13.0)
94
+ securerandom (0.4.1)
70
95
  simplecov (0.22.0)
71
96
  docile (~> 1.1)
72
97
  simplecov-html (~> 0.11)
@@ -75,7 +100,10 @@ GEM
75
100
  simplecov_json_formatter (0.1.4)
76
101
  stringio (3.1.7)
77
102
  tsort (0.2.0)
103
+ tzinfo (2.0.6)
104
+ concurrent-ruby (~> 1.0)
78
105
  unicode-display_width (2.6.0)
106
+ uri (1.0.4)
79
107
 
80
108
  PLATFORMS
81
109
  arm64-darwin-23
data/Readme.md CHANGED
@@ -13,7 +13,7 @@ gem install usps-imis-api
13
13
  or add this line to your Gemfile:
14
14
 
15
15
  ```ruby
16
- gem 'usps-imis-api', '>= 0.4.0'
16
+ gem 'usps-imis-api', '~> 0.6.3'
17
17
  ```
18
18
 
19
19
  ## Setup
@@ -25,9 +25,11 @@ require 'dotenv/load' # Optionally load environment variables from `.env` file
25
25
  require 'usps/imis'
26
26
 
27
27
  Usps::Imis.configure do |config|
28
- config.environment = :development # Rails.env
29
- config.imis_id_query_name = ENV['IMIS_ID_QUERY_NAME']
28
+ # This will default to `Rails.env` if available.
29
+ config.environment = :development
30
30
 
31
+ # These options will default to the listed `ENV` variable if available.
32
+ config.imis_id_query_name = ENV['IMIS_ID_QUERY_NAME']
31
33
  config.username = ENV['IMIS_USERNAME']
32
34
  config.password = ENV['IMIS_PASSWORD']
33
35
  end
@@ -82,7 +84,17 @@ To fetch member data, run e.g.:
82
84
  ```ruby
83
85
  api.imis_id = 31092
84
86
 
85
- data = api.get('ABC_ASC_Individual_Demog')
87
+ data = api.on('ABC_ASC_Individual_Demog').get
88
+ ```
89
+
90
+ ### GET Field
91
+
92
+ To fetch a specific field from member data, run e.g.:
93
+
94
+ ```ruby
95
+ api.imis_id = 31092
96
+
97
+ tot_mms = api.on('ABC_ASC_Individual_Demog').get_field('TotMMS')
86
98
  ```
87
99
 
88
100
  ### PUT Fields
@@ -93,7 +105,7 @@ To update member data, run e.g.:
93
105
  api.imis_id = 31092
94
106
 
95
107
  data = { 'MMS_Updated' => Time.now.strftime('%Y-%m-%dT%H:%M:%S'), 'TotMMS' => new_total }
96
- update = api.put_fields('ABC_ASC_Individual_Demog', data)
108
+ update = api.on('ABC_ASC_Individual_Demog').put_fields(data)
97
109
  ```
98
110
 
99
111
  This method fetches the current data structure, and filters it down to just what you want to
@@ -106,7 +118,7 @@ To update member data, run e.g.:
106
118
  ```ruby
107
119
  api.imis_id = 31092
108
120
 
109
- update = api.put('ABC_ASC_Individual_Demog', complete_imis_object)
121
+ update = api.on('ABC_ASC_Individual_Demog').put(complete_imis_object)
110
122
  ```
111
123
 
112
124
  This method requires a complete iMIS data structure.
@@ -116,7 +128,7 @@ This method requires a complete iMIS data structure.
116
128
  To create new member data, run e.g.:
117
129
 
118
130
  ```ruby
119
- created = api.post('ABC_ASC_Individual_Demog', complete_imis_object)
131
+ created = api.on('ABC_ASC_Individual_Demog').post(complete_imis_object)
120
132
  ```
121
133
 
122
134
  This method requires a complete iMIS data structure.
@@ -128,7 +140,7 @@ To remove member data, run e.g.:
128
140
  ```ruby
129
141
  api.imis_id = 31092
130
142
 
131
- api.delete('ABC_ASC_Individual_Demog')
143
+ api.on('ABC_ASC_Individual_Demog').delete
132
144
  ```
133
145
 
134
146
  This returns a blank string on success.
@@ -202,9 +214,11 @@ previous value.
202
214
 
203
215
  ```ruby
204
216
  api.with(31092) do
205
- # These three requests are identical:
217
+ # These requests are identical:
206
218
 
207
- put('ABC_ASC_Individual_Demog', { 'TotMMS' => 15 })
219
+ on('ABC_ASC_Individual_Demog') { put('TotMMS' => 15) }
220
+
221
+ on('ABC_ASC_Individual_Demog').put('TotMMS' => 15)
208
222
 
209
223
  mapper.update(mm: 15)
210
224
 
@@ -218,12 +232,26 @@ api.with(6374) do
218
232
  end
219
233
  ```
220
234
 
221
- ## Exception Handling
235
+ ```ruby
236
+ api.with(31092) do
237
+ # These requests are identical:
238
+
239
+ on('ABC_ASC_Individual_Demog') do
240
+ get['Properties']['$values'].find { |hash| hash['Name'] == 'TotMMS' }['Value']['$value']
241
+ end
242
+
243
+ on('ABC_ASC_Individual_Demog') { get_field('TotMMS') }
244
+
245
+ on('ABC_ASC_Individual_Demog').get_field('TotMMS')
246
+ end
222
247
 
223
- Exception and error response handling will be added later.
248
+ # This request fetches the same data, but leaves the iMIS ID selected
249
+ api.with(31092).on('ABC_ASC_Individual_Demog').get_field('TotMMS')
250
+ ```
224
251
 
225
- To print exception information to STDERR when raising, set the environment
226
- variable `IMIS_ERROR_LOG_TO_STDERR=true`.
252
+ ## Exception Handling
253
+
254
+ All internal exceptions inherit from `Usps::Imis::ApiError`.
227
255
 
228
256
  ## Automated Testing and Linting
229
257
 
@@ -239,6 +267,8 @@ Linting is available by running:
239
267
  bundle exec rubocop
240
268
  ```
241
269
 
270
+ 100% branch coverage is enforced on the test suite.
271
+
242
272
  ### GitHub Actions
243
273
 
244
274
  Testing and linting are automatically run on every push.
data/lib/usps/imis/api.rb CHANGED
@@ -5,14 +5,12 @@ module Usps
5
5
  # The core API wrapper
6
6
  #
7
7
  class Api
8
+ include Requests
9
+
8
10
  # Endpoint for (re-)authentication requests
9
11
  #
10
12
  AUTHENTICATION_PATH = 'Token'
11
13
 
12
- # Endpoint for general API requests
13
- #
14
- API_PATH = 'api'
15
-
16
14
  # Endpoint for IQA query requests
17
15
  #
18
16
  QUERY_PATH = 'api/Query'
@@ -52,7 +50,7 @@ module Usps
52
50
  def imis_id=(id)
53
51
  raise Error::ApiError, 'Cannot change iMIS ID while locked' if lock_imis_id
54
52
 
55
- @imis_id = id.to_i.to_s
53
+ @imis_id = id&.to_i&.to_s
56
54
  end
57
55
 
58
56
  # Convert a member's certificate number into an iMIS ID number
@@ -76,6 +74,8 @@ module Usps
76
74
  #
77
75
  # While in this block, changes to the value of +imis_id+ are not allowed
78
76
  #
77
+ # If no block is given, this sets the iMIS ID and returns self.
78
+ #
79
79
  # @param id [Integer, String] iMIS ID to select for requests within the block
80
80
  #
81
81
  # @example
@@ -86,107 +86,68 @@ module Usps
86
86
  def with(id, &)
87
87
  old_id = imis_id
88
88
  self.imis_id = id
89
+ return self unless block_given?
89
90
 
90
91
  @lock_imis_id = true
91
92
  instance_eval(&)
92
93
  ensure
93
- @lock_imis_id = false
94
- self.imis_id = old_id
94
+ if block_given?
95
+ @lock_imis_id = false
96
+ self.imis_id = old_id
97
+ end
95
98
  end
96
99
 
97
- # Get a business object for the current member
100
+ # Run an IQA Query
98
101
  #
99
- # @param business_object_name [String] Name of the business object
100
- # @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
102
+ # @param query_name [String] Full path of the query in IQA, e.g. +$/_ABC/Fiander/iMIS_ID+
103
+ # @query_params [Hash] Conforms to pattern +{ param_name => param_value }+
101
104
  #
102
105
  # @return [Hash] Response data from the API
103
106
  #
104
- def get(business_object_name, url_id: nil)
105
- uri = uri_for(business_object_name, url_id:)
107
+ def query(query_name, query_params = {})
108
+ query_params[:QueryName] = query_name
109
+ path = "#{QUERY_PATH}?#{query_params.to_query}"
110
+ uri = URI(File.join(Imis.configuration.hostname, path))
106
111
  request = Net::HTTP::Get.new(uri)
107
112
  result = submit(uri, authorize(request))
108
113
  JSON.parse(result.body)
109
114
  end
110
115
 
111
- # Update only specific fields on a business object for the current member
116
+ # An instance of +BusinessObject+, using this instance as its parent +Api+
112
117
  #
113
118
  # @param business_object_name [String] Name of the business object
114
- # @param fields [Hash] Conforms to pattern +{ field_key => value }+
115
119
  # @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
116
120
  #
117
- # @return [Hash] Response data from the API
118
- #
119
- def put_fields(business_object_name, fields, url_id: nil)
120
- updated = filter_fields(business_object_name, fields)
121
- put(business_object_name, updated, url_id:)
121
+ def business_object(business_object_name, url_id: nil)
122
+ BusinessObject.new(self, business_object_name, url_id:)
122
123
  end
123
124
 
124
- # Update a business object for the current member
125
- #
126
- # @param business_object_name [String] Name of the business object
127
- # @param body [Hash] Full raw API object data
128
- # @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
125
+ # Run requests as DSL, with specific +BusinessObject+ only maintained for this scope
129
126
  #
130
- # @return [Hash] Response data from the API
131
- #
132
- def put(business_object_name, body, url_id: nil)
133
- uri = uri_for(business_object_name, url_id:)
134
- request = Net::HTTP::Put.new(uri)
135
- request.body = JSON.dump(body)
136
- result = submit(uri, authorize(request))
137
- JSON.parse(result.body)
138
- end
139
-
140
- # Create a business object for the current member
127
+ # If no block is given, this returns the specified +BusinessObject+.
141
128
  #
142
129
  # @param business_object_name [String] Name of the business object
143
- # @param body [Hash] Full raw API object data
144
130
  # @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
145
131
  #
146
- # @return [Hash] Response data from the API
147
- #
148
- def post(business_object_name, body, url_id: nil)
149
- uri = uri_for(business_object_name, url_id:)
150
- request = Net::HTTP::Post.new(uri)
151
- request.body = JSON.dump(body)
152
- result = submit(uri, authorize(request))
153
- JSON.parse(result.body)
154
- end
132
+ def on(business_object_name, url_id: nil, &)
133
+ object = business_object(business_object_name, url_id:)
134
+ return object unless block_given?
155
135
 
156
- # Remove a business object for the current member
157
- #
158
- # @param business_object_name [String] Name of the business object
159
- # @param url_id [String] Override the ID param of the URL (e.g. used for Panels)
160
- #
161
- # @return [String] Error response body from the API, or empty string on success
162
- #
163
- def delete(business_object_name, url_id: nil)
164
- uri = uri_for(business_object_name, url_id:)
165
- request = Net::HTTP::Delete.new(uri)
166
- result = submit(uri, authorize(request))
167
- result.body
136
+ result = nil
137
+ object.tap { |obj| result = obj.instance_eval(&) }
138
+ result
168
139
  end
169
140
 
170
- # Run an IQA Query
171
- #
172
- # @param query_name [String] Full path of the query in IQA, e.g. +$/_ABC/Fiander/iMIS_ID+
173
- # @query_params [Hash] Conforms to pattern +{ param_name => param_value }+
141
+ # An instance of +Mapper+, using this instance as its parent +Api+
174
142
  #
175
- # @return [Hash] Response data from the API
176
- #
177
- def query(query_name, query_params = {})
178
- query_params[:QueryName] = query_name
179
- path = "#{QUERY_PATH}?#{query_params.to_query}"
180
- uri = URI(File.join(imis_hostname, path))
181
- request = Net::HTTP::Get.new(uri)
182
- result = submit(uri, authorize(request))
183
- JSON.parse(result.body)
143
+ def mapper
144
+ @mapper ||= Mapper.new(self)
184
145
  end
185
146
 
186
- # An instance of Mapper, using this instance as its parent +Api+
147
+ # Convenience alias for updating mapped fields
187
148
  #
188
- def mapper
189
- @mapper ||= Mapper.new(self)
149
+ def update(data)
150
+ mapper.update(data)
190
151
  end
191
152
 
192
153
  # Convenience accessor for available Panel objects, each using this instance as its parent
@@ -199,56 +160,16 @@ module Usps
199
160
  )
200
161
  end
201
162
 
202
- # Convenience alias for updating mapped fields
203
- #
204
- def update(data)
205
- mapper.update(data)
206
- end
207
-
208
163
  # Ruby 3.5 instance variable filter
209
164
  #
210
165
  def instance_variables_to_inspect = %i[@token_expiration @imis_id]
211
166
 
212
167
  private
213
168
 
214
- def client(uri)
215
- Net::HTTP.new(uri.host, uri.port).tap do |http|
216
- http.use_ssl = true
217
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
218
- end
219
- end
220
-
221
- def imis_hostname
222
- Imis.configuration.hostname
223
- end
224
-
225
- # Authorize a request prior to submitting
226
- #
227
- # If the current token is missing/expired, request a new one
228
- #
229
- def authorize(request)
230
- authenticate if token_expiration < Time.now
231
- request.tap { |r| r.add_field('Authorization', "Bearer #{token}") }
232
- end
233
-
234
- # Construct a business object API endpoint address
235
- #
236
- def uri_for(business_object_name, url_id: nil)
237
- url_id ||= imis_id
238
- url_id = CGI.escape(url_id)
239
- URI(File.join(imis_hostname, "#{API_PATH}/#{business_object_name}/#{url_id}"))
240
- end
241
-
242
- def submit(uri, request)
243
- client(uri).request(request).tap do |result|
244
- raise Error::ResponseError.from(result) unless result.is_a?(Net::HTTPSuccess)
245
- end
246
- end
247
-
248
169
  # Authenticate to the iMIS API, and store the access token and expiration time
249
170
  #
250
171
  def authenticate
251
- uri = URI(File.join(imis_hostname, AUTHENTICATION_PATH))
172
+ uri = URI(File.join(Imis.configuration.hostname, AUTHENTICATION_PATH))
252
173
  req = Net::HTTP::Post.new(uri)
253
174
  authentication_data = {
254
175
  grant_type: 'password',
@@ -262,33 +183,6 @@ module Usps
262
183
  @token = json['access_token']
263
184
  @token_expiration = Time.parse(json['.expires'])
264
185
  end
265
-
266
- # Manually assemble the matching data structure, with fields in the correct order
267
- #
268
- def filter_fields(business_object_name, fields)
269
- existing = get(business_object_name)
270
-
271
- JSON.parse(JSON.dump(existing)).tap do |updated|
272
- # The first property is always the iMIS ID again
273
- updated['Properties']['$values'] = [existing['Properties']['$values'][0]]
274
-
275
- # Iterate through all existing fields
276
- existing['Properties']['$values'].each do |value|
277
- next unless fields.keys.include?(value['Name'])
278
-
279
- # Strings are not wrapped in the type definition structure
280
- new_value = fields[value['Name']]
281
- if new_value.is_a?(String)
282
- value['Value'] = new_value
283
- else
284
- value['Value']['$value'] = new_value
285
- end
286
-
287
- # Add the completed field with the updated value
288
- updated['Properties']['$values'] << value
289
- end
290
- end
291
- end
292
186
  end
293
187
  end
294
188
  end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Usps
4
+ module Imis
5
+ # DEV
6
+ class BusinessObject
7
+ include Requests
8
+
9
+ # Endpoint for general API requests
10
+ #
11
+ API_PATH = 'api'
12
+
13
+ # The parent +Api+ object
14
+ #
15
+ attr_reader :api
16
+
17
+ # Name of the iMIS Business Object
18
+ #
19
+ attr_reader :business_object_name
20
+
21
+ # Override ID param of the URL (e.g. used for Panels)
22
+ #
23
+ attr_reader :url_id
24
+
25
+ # A new instance of +BusinessObject+
26
+ #
27
+ def initialize(api, business_object_name, url_id: nil)
28
+ @api = api
29
+ @business_object_name = business_object_name
30
+ @url_id = url_id
31
+ end
32
+
33
+ # Get a business object for the current member
34
+ #
35
+ # @return [Hash] Response data from the API
36
+ #
37
+ def get
38
+ request = Net::HTTP::Get.new(uri)
39
+ result = submit(uri, authorize(request))
40
+ JSON.parse(result.body)
41
+ end
42
+
43
+ # Get a single named field from a business object for the current member
44
+ #
45
+ # @param name [String] Field name to return
46
+ #
47
+ # @return [Hash] Response data from the API
48
+ #
49
+ def get_field(name)
50
+ values = get['Properties']['$values']
51
+ value = values.find { |hash| hash['Name'] == name }['Value']
52
+
53
+ value.is_a?(String) ? value : value['$value']
54
+ end
55
+
56
+ # Update only specific fields on a business object for the current member
57
+ #
58
+ # @param fields [Hash] Conforms to pattern +{ field_key => value }+
59
+ #
60
+ # @return [Hash] Response data from the API
61
+ #
62
+ def put_fields(fields)
63
+ updated = filter_fields(fields)
64
+ put(updated)
65
+ end
66
+
67
+ # Update a business object for the current member
68
+ #
69
+ # @param body [Hash] Full raw API object data
70
+ #
71
+ # @return [Hash] Response data from the API
72
+ #
73
+ def put(body)
74
+ request = Net::HTTP::Put.new(uri)
75
+ request.body = JSON.dump(body)
76
+ result = submit(uri, authorize(request))
77
+ JSON.parse(result.body)
78
+ end
79
+
80
+ # Create a business object for the current member
81
+ #
82
+ # @param body [Hash] Full raw API object data
83
+ #
84
+ # @return [Hash] Response data from the API
85
+ #
86
+ def post(body)
87
+ request = Net::HTTP::Post.new(uri)
88
+ request.body = JSON.dump(body)
89
+ result = submit(uri, authorize(request))
90
+ JSON.parse(result.body)
91
+ end
92
+
93
+ # Remove a business object for the current member
94
+ #
95
+ # @return [String] Error response body from the API, or empty string on success
96
+ #
97
+ def delete
98
+ request = Net::HTTP::Delete.new(uri)
99
+ result = submit(uri, authorize(request))
100
+ result.body
101
+ end
102
+
103
+ private
104
+
105
+ def token = api.token
106
+ def token_expiration = api.token_expiration
107
+
108
+ # Construct a business object API endpoint address
109
+ #
110
+ def uri
111
+ id_for_url = url_id ? CGI.escape(url_id) : api.imis_id
112
+ full_path = "#{API_PATH}/#{business_object_name}/#{id_for_url}"
113
+ URI(File.join(Imis.configuration.hostname, full_path))
114
+ end
115
+
116
+ # Manually assemble the matching data structure, with fields in the correct order
117
+ #
118
+ def filter_fields(fields)
119
+ existing = get
120
+
121
+ JSON.parse(JSON.dump(existing)).tap do |updated|
122
+ # The first property is always the iMIS ID again
123
+ updated['Properties']['$values'] = [existing['Properties']['$values'][0]]
124
+
125
+ # Iterate through all existing fields
126
+ existing['Properties']['$values'].each do |value|
127
+ next unless fields.keys.include?(value['Name'])
128
+
129
+ # Strings are not wrapped in the type definition structure
130
+ new_value = fields[value['Name']]
131
+ if new_value.is_a?(String)
132
+ value['Value'] = new_value
133
+ else
134
+ value['Value']['$value'] = new_value
135
+ end
136
+
137
+ # Add the completed field with the updated value
138
+ updated['Properties']['$values'] << value
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -8,25 +8,31 @@ module Usps
8
8
  IMIS_ROOT_URL_PROD = 'https://portal.americasboatingclub.org'
9
9
  IMIS_ROOT_URL_DEV = 'https://abcdev.imiscloud.com'
10
10
 
11
- attr_accessor :environment, :imis_id_query_name, :username, :password
11
+ attr_accessor :imis_id_query_name, :username, :password
12
+ attr_reader :environment
12
13
 
13
14
  def initialize
15
+ @environment = defined?(Rails) ? Rails.env : ActiveSupport::StringInquirer.new('development')
16
+ @imis_id_query_name = ENV.fetch('IMIS_ID_QUERY_NAME', nil)
17
+ @username = ENV.fetch('IMIS_USERNAME', nil)
18
+ @password = ENV.fetch('IMIS_PASSWORD', nil)
19
+
14
20
  yield self if block_given?
15
21
  end
16
22
 
23
+ def environment=(env)
24
+ @environment = ActiveSupport::StringInquirer.new(env.to_s)
25
+ end
26
+
17
27
  # Environment-specific API endpoint hostname
18
28
  #
19
29
  # @return The API hostname for the current environment
20
30
  #
21
31
  def hostname
22
- case environment.to_sym
23
- when :production
24
- IMIS_ROOT_URL_PROD
25
- when :development
26
- IMIS_ROOT_URL_DEV
27
- else
28
- raise Error::ApiError, "Unexpected API environment: #{environment}"
29
- end
32
+ return IMIS_ROOT_URL_PROD if environment.production?
33
+ return IMIS_ROOT_URL_DEV if environment.development?
34
+
35
+ raise Error::ApiError, "Unexpected API environment: #{environment}"
30
36
  end
31
37
 
32
38
  # Ruby 3.5 instance variable filter
@@ -47,7 +47,7 @@ module Usps
47
47
  end
48
48
 
49
49
  updates.map do |business_object_name, field_updates|
50
- api.put_fields(business_object_name, field_updates)
50
+ api.business_object(business_object_name).put_fields(field_updates)
51
51
  end
52
52
  end
53
53