friendly_shipping 0.5 → 0.6.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-relaxed.yml +3 -4
  3. data/.rubocop.yml +9 -0
  4. data/CHANGELOG.md +29 -0
  5. data/friendly_shipping.gemspec +12 -12
  6. data/lib/friendly_shipping/api_failure.rb +2 -15
  7. data/lib/friendly_shipping/http_client.rb +16 -9
  8. data/lib/friendly_shipping/request.rb +7 -1
  9. data/lib/friendly_shipping/services/ship_engine/bad_request_handler.rb +15 -3
  10. data/lib/friendly_shipping/services/ups.rb +9 -1
  11. data/lib/friendly_shipping/services/ups/parse_address_classification_response.rb +5 -2
  12. data/lib/friendly_shipping/services/ups/parse_address_validation_response.rb +5 -2
  13. data/lib/friendly_shipping/services/ups/parse_city_state_lookup_response.rb +5 -2
  14. data/lib/friendly_shipping/services/ups/parse_rate_response.rb +5 -1
  15. data/lib/friendly_shipping/services/ups/parse_shipment_accept_response.rb +5 -1
  16. data/lib/friendly_shipping/services/ups/parse_shipment_confirm_response.rb +5 -1
  17. data/lib/friendly_shipping/services/ups/parse_time_in_transit_response.rb +5 -1
  18. data/lib/friendly_shipping/services/ups/parse_void_shipment_response.rb +5 -1
  19. data/lib/friendly_shipping/services/ups/parse_xml_response.rb +16 -7
  20. data/lib/friendly_shipping/services/ups_freight.rb +44 -13
  21. data/lib/friendly_shipping/services/ups_freight/generate_delivery_options_hash.rb +21 -0
  22. data/lib/friendly_shipping/services/ups_freight/generate_document_options_hash.rb +28 -0
  23. data/lib/friendly_shipping/services/ups_freight/generate_email_options_hash.rb +25 -0
  24. data/lib/friendly_shipping/services/ups_freight/generate_freight_rate_request_hash.rb +2 -10
  25. data/lib/friendly_shipping/services/ups_freight/generate_freight_ship_request_hash.rb +81 -0
  26. data/lib/friendly_shipping/services/ups_freight/generate_location_hash.rb +5 -2
  27. data/lib/friendly_shipping/services/ups_freight/generate_pickup_options_hash.rb +21 -0
  28. data/lib/friendly_shipping/services/ups_freight/generate_pickup_request_hash.rb +31 -0
  29. data/lib/friendly_shipping/services/ups_freight/label_delivery_options.rb +29 -0
  30. data/lib/friendly_shipping/services/ups_freight/label_document_options.rb +56 -0
  31. data/lib/friendly_shipping/services/ups_freight/label_email_options.rb +40 -0
  32. data/lib/friendly_shipping/services/ups_freight/label_item_options.rb +10 -0
  33. data/lib/friendly_shipping/services/ups_freight/label_options.rb +37 -0
  34. data/lib/friendly_shipping/services/ups_freight/label_package_options.rb +10 -0
  35. data/lib/friendly_shipping/services/ups_freight/label_pickup_options.rb +29 -0
  36. data/lib/friendly_shipping/services/ups_freight/parse_freight_label_response.rb +57 -0
  37. data/lib/friendly_shipping/services/ups_freight/parse_freight_rate_response.rb +29 -32
  38. data/lib/friendly_shipping/services/ups_freight/parse_shipment_document.rb +24 -0
  39. data/lib/friendly_shipping/services/ups_freight/pickup_request_options.rb +29 -0
  40. data/lib/friendly_shipping/services/ups_freight/rates_options.rb +3 -6
  41. data/lib/friendly_shipping/services/ups_freight/restful_api_error_handler.rb +30 -0
  42. data/lib/friendly_shipping/services/ups_freight/shipment_document.rb +21 -0
  43. data/lib/friendly_shipping/services/ups_freight/shipment_information.rb +35 -0
  44. data/lib/friendly_shipping/services/usps.rb +1 -0
  45. data/lib/friendly_shipping/services/usps/parse_address_validation_response.rb +5 -1
  46. data/lib/friendly_shipping/services/usps/parse_city_state_lookup_response.rb +5 -1
  47. data/lib/friendly_shipping/services/usps/parse_rate_response.rb +5 -2
  48. data/lib/friendly_shipping/services/usps/parse_time_in_transit_response.rb +6 -2
  49. data/lib/friendly_shipping/services/usps/parse_xml_response.rb +15 -5
  50. data/lib/friendly_shipping/services/usps/rate_estimate_options.rb +1 -1
  51. data/lib/friendly_shipping/services/usps/rate_estimate_package_options.rb +4 -1
  52. data/lib/friendly_shipping/services/usps/serialize_rate_request.rb +11 -5
  53. data/lib/friendly_shipping/services/usps/shipping_methods.rb +4 -4
  54. data/lib/friendly_shipping/version.rb +1 -1
  55. metadata +74 -39
  56. data/lib/friendly_shipping/services/ups_freight/generate_ups_security_hash.rb +0 -23
  57. data/lib/friendly_shipping/services/ups_freight/parse_json_response.rb +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7033f8bf845a41a0cefa6899a465fd78a85077ff565c7c30f67d04f20708ff6
4
- data.tar.gz: 2c5207e0139608d78b7c65b6f273b9dc222bc750bc47e587147e8f486c908b43
3
+ metadata.gz: 774450d05e80ad658e9ab8d29ea372b142dca9b712bdf37f3baf4b5faa68d4c0
4
+ data.tar.gz: 1fc8c87b5812e0b31d2f74ff57e24887e0b4ec7b916b3f34371631bac8b897c9
5
5
  SHA512:
6
- metadata.gz: 00d7ce8fe1d737b482d71a6ff96195a82dc70b94c7784291c9cc18eb2f71ca529dadf33900a7f5aef223a4f89a3b1297403d7bc352dd9e8255c139b11b7351c1
7
- data.tar.gz: 7a7746f0c59dfcd03d2a330142f106cfe3c0fc7b82628859691d28c0dbd27e3d7584670c44f3509780271aaaf44fbd83551d0e1415ac14807d9ec2578891ab95
6
+ metadata.gz: 1d9f8099888cebd49a1c7fcfec69ddeb4170e04bea8d0a3ed0b0c5dcce9d413606b0386ae2034784563dfa628dd5d3b927f9250ef40a6431e28462238476a9e7
7
+ data.tar.gz: 6cac63b02a0f637f68fca0948bb8fa59361b8d13a3752d9de0a9a2cb55ec6aaf9654d89ee9530c0b11e2b81b44b928097defe17acfabe1a92c4635c9f9ab0ad2
@@ -133,6 +133,9 @@ Style/WordArray:
133
133
  Enabled: false
134
134
  StyleGuide: https://relaxed.ruby.style/#stylewordarray
135
135
 
136
+ Layout/LineLength:
137
+ Enabled: false
138
+
136
139
  Lint/AmbiguousRegexpLiteral:
137
140
  Enabled: false
138
141
  StyleGuide: https://relaxed.ruby.style/#lintambiguousregexpliteral
@@ -156,9 +159,6 @@ Metrics/ModuleLength:
156
159
  Metrics/CyclomaticComplexity:
157
160
  Enabled: false
158
161
 
159
- Metrics/LineLength:
160
- Enabled: false
161
-
162
162
  Metrics/MethodLength:
163
163
  Enabled: false
164
164
 
@@ -167,4 +167,3 @@ Metrics/ParameterLists:
167
167
 
168
168
  Metrics/PerceivedComplexity:
169
169
  Enabled: false
170
-
@@ -6,3 +6,12 @@ inherit_from:
6
6
 
7
7
  Metrics/BlockLength:
8
8
  Enabled: false
9
+
10
+ Style/HashEachMethods:
11
+ Enabled: true
12
+
13
+ Style/HashTransformKeys:
14
+ Enabled: true
15
+
16
+ Style/HashTransformValues:
17
+ Enabled: true
@@ -4,6 +4,35 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.6.1] - 2020-03-11
8
+
9
+ - Add Content-Type header to UPS Freight API requests, fixing "Name too long" 500 error responses
10
+
11
+ ## [0.6.0] - 2020-03-11
12
+
13
+ - Changelog additions missed in previous release
14
+
15
+ ## [0.5.3] - 2020-03-11
16
+
17
+ - UPS Service: Add support for shipping labels / bills of lading (#92)
18
+ - UPS/USPS Services: Return ApiFailure instead of a string for failed API responses (#95)
19
+ - UPS/USPS Services: Refactor ApiFailure to subclass ApiResponse (#96)
20
+
21
+ ## [0.5.2] - 2020-01-31
22
+
23
+ ### Added
24
+ - USPS Service: Added rectangular boolean to rate options class (#89)
25
+ - USPS Service: Added readable body to request class (#88)
26
+
27
+ ### Removed
28
+ - USPS Service: Drop deprecated rectangular container (#89)
29
+
30
+ ## [0.5.1] - 2020-01-28
31
+
32
+ ### Changed
33
+ - USPS Service: Rename "Package Services" shipping method (#85)
34
+ - Documentation updates (#86)
35
+
7
36
  ## [0.5] - 2020-01-24
8
37
 
9
38
  ### Removed
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
 
13
13
  spec.summary = "An integration layer for shipping services"
14
14
  spec.description = "Allows you to quote or ship a Physical::Shipment object"
15
- spec.homepage = "https://github.com/friendly_cart/friendly_shipping"
15
+ spec.homepage = "https://github.com/friendlycart/friendly_shipping"
16
16
  spec.license = "MIT"
17
17
 
18
18
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
@@ -24,21 +24,21 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_runtime_dependency "data_uri", "~> 0.0.3"
26
26
  spec.add_runtime_dependency "dry-monads", "~> 1.0"
27
- spec.add_runtime_dependency "money", ">= 6.0.0"
28
- spec.add_runtime_dependency "nokogiri", ">= 1.6"
29
- spec.add_runtime_dependency "physical", ">= 0.4.4"
27
+ spec.add_runtime_dependency "money", "~> 6.0"
28
+ spec.add_runtime_dependency "nokogiri", "~> 1.6"
29
+ spec.add_runtime_dependency "physical", "~> 0.4", ">= 0.4.4"
30
30
  spec.add_runtime_dependency "rest-client", "~> 2.0"
31
31
  spec.required_ruby_version = '>= 2.5'
32
32
 
33
- spec.add_development_dependency "bundler"
34
- spec.add_development_dependency "dotenv"
33
+ spec.add_development_dependency "bundler", ">= 1.17", "< 3"
34
+ spec.add_development_dependency "dotenv", "~> 2.7"
35
35
  spec.add_development_dependency "factory_bot", "~> 5.0"
36
- spec.add_development_dependency "pry"
37
- spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "pry", "~> 0.12"
37
+ spec.add_development_dependency "rake", ">= 12.3.3"
38
38
  spec.add_development_dependency "rspec", "~> 3.0"
39
39
  spec.add_development_dependency "rspec_junit_formatter", "~> 0.4"
40
- spec.add_development_dependency "rubocop"
41
- spec.add_development_dependency "simplecov"
42
- spec.add_development_dependency "vcr"
43
- spec.add_development_dependency "webmock"
40
+ spec.add_development_dependency "rubocop", ">= 0.80", "< 1"
41
+ spec.add_development_dependency "simplecov", "~> 0.17"
42
+ spec.add_development_dependency "vcr", "~> 5.0"
43
+ spec.add_development_dependency "webmock", "~> 3.6"
44
44
  end
@@ -1,21 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FriendlyShipping
4
- class ApiFailure
5
- attr_reader :failure, :original_request, :original_response
6
-
7
- # @param [Object] failure The API failure
8
- # @param [FriendlyShipping::Request] original_request The HTTP request (when debugging is enabled)
9
- # @param [FriendlyShipping::Response] original_response The HTTP response (when debugging is enabled)
10
- def initialize(failure, original_request:, original_response:)
11
- @failure = failure
12
-
13
- # We do not want to attach debugging information in every single response to save memory in production
14
- return unless original_request&.debug
15
-
16
- @original_request = original_request
17
- @original_response = original_response
18
- end
4
+ class ApiFailure < ApiResult
5
+ alias_method :failure, :data
19
6
 
20
7
  def to_s
21
8
  failure.to_s
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'dry/monads/result'
4
+ require 'friendly_shipping/api_failure'
4
5
  require 'rest-client'
5
6
 
6
7
  module FriendlyShipping
@@ -21,19 +22,19 @@ module FriendlyShipping
21
22
 
22
23
  Success(convert_to_friendly_response(http_response))
23
24
  rescue ::RestClient::Exception => e
24
- error_handler.call(e)
25
+ error_handler.call(e, original_request: request, original_response: e.response)
25
26
  end
26
27
 
27
- def post(friendly_shipping_request)
28
+ def post(request)
28
29
  http_response = ::RestClient.post(
29
- friendly_shipping_request.url,
30
- friendly_shipping_request.body,
31
- friendly_shipping_request.headers
30
+ request.url,
31
+ request.body,
32
+ request.headers
32
33
  )
33
34
 
34
35
  Success(convert_to_friendly_response(http_response))
35
36
  rescue ::RestClient::Exception => e
36
- error_handler.call(e)
37
+ error_handler.call(e, original_request: request, original_response: e.response)
37
38
  end
38
39
 
39
40
  def put(request)
@@ -45,13 +46,19 @@ module FriendlyShipping
45
46
 
46
47
  Success(convert_to_friendly_response(http_response))
47
48
  rescue ::RestClient::Exception => e
48
- error_handler.call(e)
49
+ error_handler.call(e, original_request: request, original_response: e.response)
49
50
  end
50
51
 
51
52
  private
52
53
 
53
- def wrap_in_failure(error)
54
- Failure(error)
54
+ def wrap_in_failure(error, original_request: nil, original_response: nil)
55
+ Failure(
56
+ ApiFailure.new(
57
+ error,
58
+ original_request: original_request,
59
+ original_response: original_response
60
+ )
61
+ )
55
62
  end
56
63
 
57
64
  def convert_to_friendly_response(http_response)
@@ -6,13 +6,19 @@ module FriendlyShipping
6
6
 
7
7
  # @param [String] url The HTTP request URL
8
8
  # @param [String] body The HTTP request body
9
+ # # @param [String] readable_body Human-readable HTTP request body
9
10
  # @param [Hash] headers The HTTP request headers
10
11
  # @param [Boolean] debug Whether to debug the request
11
- def initialize(url:, body: nil, headers: {}, debug: false)
12
+ def initialize(url:, body: nil, readable_body: nil, headers: {}, debug: false)
12
13
  @url = url
13
14
  @body = body
15
+ @readable_body = readable_body
14
16
  @headers = headers
15
17
  @debug = debug
16
18
  end
19
+
20
+ def readable_body
21
+ @readable_body.presence || @body
22
+ end
17
23
  end
18
24
  end
@@ -8,11 +8,23 @@ module FriendlyShipping
8
8
  class BadRequestHandler
9
9
  extend Dry::Monads::Result::Mixin
10
10
 
11
- def self.call(error)
11
+ def self.call(error, original_request: nil, original_response: nil)
12
12
  if error.http_code == 400
13
- Failure(BadRequest.new(error))
13
+ Failure(
14
+ ApiFailure.new(
15
+ BadRequest.new(error),
16
+ original_request: original_request,
17
+ original_response: original_response
18
+ )
19
+ )
14
20
  else
15
- Failure(error)
21
+ Failure(
22
+ ApiFailure.new(
23
+ error,
24
+ original_request: original_request,
25
+ original_response: original_response
26
+ )
27
+ )
16
28
  end
17
29
  end
18
30
  end
@@ -74,6 +74,7 @@ module FriendlyShipping
74
74
  request = FriendlyShipping::Request.new(
75
75
  url: url,
76
76
  body: access_request_xml + rate_request_xml,
77
+ readable_body: rate_request_xml,
77
78
  debug: debug
78
79
  )
79
80
 
@@ -84,7 +85,7 @@ module FriendlyShipping
84
85
 
85
86
  # Get timing information for a shipment
86
87
  # @param [Physical::Shipment] shipment The shipment we want to estimate timings for
87
- # @param [FriendlyShipping::Services::Ups::TimingOptions] Options for this call
88
+ # @param [FriendlyShipping::Services::Ups::TimingOptions] options Options for this call
88
89
  def timings(shipment, options:, debug: false)
89
90
  time_in_transit_request_xml = SerializeTimeInTransitRequest.call(
90
91
  shipment: shipment,
@@ -95,6 +96,7 @@ module FriendlyShipping
95
96
  request = FriendlyShipping::Request.new(
96
97
  url: time_in_transit_url,
97
98
  body: access_request_xml + time_in_transit_request_xml,
99
+ readable_body: time_in_transit_request_xml,
98
100
  debug: debug
99
101
  )
100
102
 
@@ -114,6 +116,7 @@ module FriendlyShipping
114
116
  ship_confirm_request = FriendlyShipping::Request.new(
115
117
  url: ship_confirm_url,
116
118
  body: access_request_xml + ship_confirm_request_xml,
119
+ readable_body: ship_confirm_request_xml,
117
120
  debug: debug
118
121
  )
119
122
 
@@ -132,6 +135,7 @@ module FriendlyShipping
132
135
  ship_accept_request = FriendlyShipping::Request.new(
133
136
  url: ship_accept_url,
134
137
  body: access_request_xml + ship_accept_request_xml,
138
+ readable_body: ship_accept_request_xml,
135
139
  debug: debug
136
140
  )
137
141
 
@@ -153,6 +157,7 @@ module FriendlyShipping
153
157
  request = FriendlyShipping::Request.new(
154
158
  url: url,
155
159
  body: access_request_xml + address_validation_request_xml,
160
+ readable_body: address_validation_request_xml,
156
161
  debug: debug
157
162
  )
158
163
 
@@ -170,6 +175,7 @@ module FriendlyShipping
170
175
  request = FriendlyShipping::Request.new(
171
176
  url: url,
172
177
  body: access_request_xml + address_validation_request_xml,
178
+ readable_body: address_validation_request_xml,
173
179
  debug: debug
174
180
  )
175
181
 
@@ -188,6 +194,7 @@ module FriendlyShipping
188
194
  request = FriendlyShipping::Request.new(
189
195
  url: url,
190
196
  body: access_request_xml + city_state_lookup_request_xml,
197
+ readable_body: city_state_lookup_request_xml,
191
198
  debug: debug
192
199
  )
193
200
 
@@ -202,6 +209,7 @@ module FriendlyShipping
202
209
  request = FriendlyShipping::Request.new(
203
210
  url: url,
204
211
  body: access_request_xml + void_request_xml,
212
+ readable_body: void_request_xml,
205
213
  debug: debug
206
214
  )
207
215
  client.post(request).bind do |response|
@@ -7,8 +7,11 @@ module FriendlyShipping
7
7
  extend Dry::Monads::Result::Mixin
8
8
 
9
9
  def self.call(request:, response:)
10
- parsing_result = ParseXMLResponse.call(response.body, 'AddressValidationResponse')
11
-
10
+ parsing_result = ParseXMLResponse.call(
11
+ request: request,
12
+ response: response,
13
+ expected_root_tag: 'AddressValidationResponse'
14
+ )
12
15
  parsing_result.bind do |xml|
13
16
  address_type = xml.at('AddressClassification/Description')&.text&.downcase
14
17
  Success(
@@ -7,8 +7,11 @@ module FriendlyShipping
7
7
  extend Dry::Monads::Result::Mixin
8
8
 
9
9
  def self.call(request:, response:)
10
- parsing_result = ParseXMLResponse.call(response.body, 'AddressValidationResponse')
11
-
10
+ parsing_result = ParseXMLResponse.call(
11
+ request: request,
12
+ response: response,
13
+ expected_root_tag: 'AddressValidationResponse'
14
+ )
12
15
  parsing_result.bind do |xml|
13
16
  if xml.at('NoCandidatesIndicator')
14
17
  Failure(
@@ -7,8 +7,11 @@ module FriendlyShipping
7
7
  extend Dry::Monads::Result::Mixin
8
8
 
9
9
  def self.call(request:, response:, location:)
10
- parsing_result = ParseXMLResponse.call(response.body, 'AddressValidationResponse')
11
-
10
+ parsing_result = ParseXMLResponse.call(
11
+ request: request,
12
+ response: response,
13
+ expected_root_tag: 'AddressValidationResponse'
14
+ )
12
15
  parsing_result.fmap do |xml|
13
16
  FriendlyShipping::ApiResult.new(
14
17
  [
@@ -9,7 +9,11 @@ module FriendlyShipping
9
9
  class ParseRateResponse
10
10
  class << self
11
11
  def call(request:, response:, shipment:)
12
- parsing_result = ParseXMLResponse.call(response.body, 'RatingServiceSelectionResponse')
12
+ parsing_result = ParseXMLResponse.call(
13
+ request: request,
14
+ response: response,
15
+ expected_root_tag: 'RatingServiceSelectionResponse'
16
+ )
13
17
  parsing_result.fmap do |xml|
14
18
  FriendlyShipping::ApiResult.new(
15
19
  build_rates(xml, shipment),
@@ -11,7 +11,11 @@ module FriendlyShipping
11
11
 
12
12
  class << self
13
13
  def call(request:, response:)
14
- parsing_result = ParseXMLResponse.call(response.body, 'ShipmentAcceptResponse')
14
+ parsing_result = ParseXMLResponse.call(
15
+ request: request,
16
+ response: response,
17
+ expected_root_tag: 'ShipmentAcceptResponse'
18
+ )
15
19
  parsing_result.fmap do |xml|
16
20
  FriendlyShipping::ApiResult.new(
17
21
  build_labels(xml),
@@ -7,7 +7,11 @@ module FriendlyShipping
7
7
  class Ups
8
8
  class ParseShipmentConfirmResponse
9
9
  def self.call(request:, response:)
10
- parsing_result = ParseXMLResponse.call(response.body, 'ShipmentConfirmResponse')
10
+ parsing_result = ParseXMLResponse.call(
11
+ request: request,
12
+ response: response,
13
+ expected_root_tag: 'ShipmentConfirmResponse'
14
+ )
11
15
  parsing_result.fmap do |xml|
12
16
  FriendlyShipping::ApiResult.new(
13
17
  xml.root.at('ShipmentDigest').text,
@@ -5,7 +5,11 @@ module FriendlyShipping
5
5
  class Ups
6
6
  class ParseTimeInTransitResponse
7
7
  def self.call(request:, response:)
8
- parsing_result = ParseXMLResponse.call(response.body, 'TimeInTransitResponse')
8
+ parsing_result = ParseXMLResponse.call(
9
+ request: request,
10
+ response: response,
11
+ expected_root_tag: 'TimeInTransitResponse'
12
+ )
9
13
  parsing_result.fmap do |xml|
10
14
  origin_country_code = xml.at('TransitResponse/TransitFrom/AddressArtifactFormat/CountryCode').text
11
15
  timings = xml.root.xpath('//TransitResponse/ServiceSummary').map do |service_summary|
@@ -7,7 +7,11 @@ module FriendlyShipping
7
7
  class Ups
8
8
  class ParseVoidShipmentResponse
9
9
  def self.call(request:, response:)
10
- parsing_result = ParseXMLResponse.call(response.body, 'VoidShipmentResponse')
10
+ parsing_result = ParseXMLResponse.call(
11
+ request: request,
12
+ response: response,
13
+ expected_root_tag: 'VoidShipmentResponse'
14
+ )
11
15
  parsing_result.fmap do |xml|
12
16
  FriendlyShipping::ApiResult.new(
13
17
  xml.root.at('ResponseStatusDescription').text,
@@ -8,19 +8,18 @@ module FriendlyShipping
8
8
  SUCCESSFUL_RESPONSE_STATUS_CODE = '1'
9
9
 
10
10
  class << self
11
- def call(response_body, expected_root_tag)
12
- xml = Nokogiri.XML(response_body, &:strict)
11
+ def call(request:, response:, expected_root_tag:)
12
+ xml = Nokogiri.XML(response.body, &:strict)
13
13
 
14
14
  if xml.root.nil? || xml.root.name != expected_root_tag
15
- Failure('Invalid document')
16
- end
17
- if request_successful?(xml)
15
+ wrap_failure('Invalid document', request, response)
16
+ elsif request_successful?(xml)
18
17
  Success(xml)
19
18
  else
20
- Failure(error_message(xml))
19
+ wrap_failure(error_message(xml), request, response)
21
20
  end
22
21
  rescue Nokogiri::XML::SyntaxError => e
23
- Failure(e)
22
+ wrap_failure(e, request, response)
24
23
  end
25
24
 
26
25
  private
@@ -34,6 +33,16 @@ module FriendlyShipping
34
33
  desc = xml.root.at_xpath('Response/Error/ErrorDescription')&.text
35
34
  [status, desc].compact.join(": ").presence || 'UPS could not process the request.'
36
35
  end
36
+
37
+ def wrap_failure(failure, request, response)
38
+ Failure(
39
+ FriendlyShipping::ApiFailure.new(
40
+ failure,
41
+ original_request: request,
42
+ original_response: response
43
+ )
44
+ )
45
+ end
37
46
  end
38
47
  end
39
48
  end