friendly_shipping 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-relaxed.yml +3 -4
  3. data/.rubocop.yml +9 -0
  4. data/CHANGELOG.md +5 -1
  5. data/friendly_shipping.gemspec +10 -10
  6. data/lib/friendly_shipping/api_failure.rb +2 -15
  7. data/lib/friendly_shipping/services/ups/parse_address_classification_response.rb +5 -2
  8. data/lib/friendly_shipping/services/ups/parse_address_validation_response.rb +5 -2
  9. data/lib/friendly_shipping/services/ups/parse_city_state_lookup_response.rb +5 -2
  10. data/lib/friendly_shipping/services/ups/parse_rate_response.rb +5 -1
  11. data/lib/friendly_shipping/services/ups/parse_shipment_accept_response.rb +5 -1
  12. data/lib/friendly_shipping/services/ups/parse_shipment_confirm_response.rb +5 -1
  13. data/lib/friendly_shipping/services/ups/parse_time_in_transit_response.rb +5 -1
  14. data/lib/friendly_shipping/services/ups/parse_void_shipment_response.rb +5 -1
  15. data/lib/friendly_shipping/services/ups/parse_xml_response.rb +16 -7
  16. data/lib/friendly_shipping/services/ups_freight/generate_delivery_options_hash.rb +21 -0
  17. data/lib/friendly_shipping/services/ups_freight/generate_document_options_hash.rb +28 -0
  18. data/lib/friendly_shipping/services/ups_freight/generate_email_options_hash.rb +25 -0
  19. data/lib/friendly_shipping/services/ups_freight/generate_freight_rate_request_hash.rb +2 -10
  20. data/lib/friendly_shipping/services/ups_freight/generate_freight_ship_request_hash.rb +81 -0
  21. data/lib/friendly_shipping/services/ups_freight/generate_location_hash.rb +5 -2
  22. data/lib/friendly_shipping/services/ups_freight/generate_pickup_options_hash.rb +21 -0
  23. data/lib/friendly_shipping/services/ups_freight/generate_pickup_request_hash.rb +31 -0
  24. data/lib/friendly_shipping/services/ups_freight/label_delivery_options.rb +29 -0
  25. data/lib/friendly_shipping/services/ups_freight/label_document_options.rb +56 -0
  26. data/lib/friendly_shipping/services/ups_freight/label_email_options.rb +40 -0
  27. data/lib/friendly_shipping/services/ups_freight/label_item_options.rb +10 -0
  28. data/lib/friendly_shipping/services/ups_freight/label_options.rb +37 -0
  29. data/lib/friendly_shipping/services/ups_freight/label_package_options.rb +10 -0
  30. data/lib/friendly_shipping/services/ups_freight/label_pickup_options.rb +29 -0
  31. data/lib/friendly_shipping/services/ups_freight/parse_freight_label_response.rb +57 -0
  32. data/lib/friendly_shipping/services/ups_freight/parse_freight_rate_response.rb +29 -32
  33. data/lib/friendly_shipping/services/ups_freight/parse_shipment_document.rb +24 -0
  34. data/lib/friendly_shipping/services/ups_freight/pickup_request_options.rb +29 -0
  35. data/lib/friendly_shipping/services/ups_freight/rates_options.rb +3 -6
  36. data/lib/friendly_shipping/services/ups_freight/restful_api_error_handler.rb +24 -0
  37. data/lib/friendly_shipping/services/ups_freight/shipment_document.rb +21 -0
  38. data/lib/friendly_shipping/services/ups_freight/shipment_information.rb +35 -0
  39. data/lib/friendly_shipping/services/ups_freight.rb +42 -12
  40. data/lib/friendly_shipping/services/usps/parse_address_validation_response.rb +5 -1
  41. data/lib/friendly_shipping/services/usps/parse_city_state_lookup_response.rb +5 -1
  42. data/lib/friendly_shipping/services/usps/parse_rate_response.rb +5 -2
  43. data/lib/friendly_shipping/services/usps/parse_time_in_transit_response.rb +5 -1
  44. data/lib/friendly_shipping/services/usps/parse_xml_response.rb +15 -5
  45. data/lib/friendly_shipping/version.rb +1 -1
  46. metadata +70 -35
  47. data/lib/friendly_shipping/services/ups_freight/generate_ups_security_hash.rb +0 -23
  48. data/lib/friendly_shipping/services/ups_freight/parse_json_response.rb +0 -38
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelEmailOptions
7
+ EMAIL_TYPES = {
8
+ ship_notification: '001',
9
+ delivery_notification: '002',
10
+ exception_notification: '003',
11
+ bol_labels: '004'
12
+ }.freeze
13
+
14
+ attr_reader :email_type,
15
+ :email,
16
+ :undeliverable_email,
17
+ :subject,
18
+ :body
19
+
20
+ def initialize(
21
+ email:,
22
+ email_type:,
23
+ undeliverable_email:,
24
+ subject: nil,
25
+ body: nil
26
+ )
27
+ @email = email
28
+ @email_type = email_type
29
+ @undeliverable_email = undeliverable_email
30
+ @subject = subject
31
+ @body = body
32
+ end
33
+
34
+ def email_type_code
35
+ EMAIL_TYPES.fetch(email_type)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelItemOptions < RatesItemOptions
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelOptions < RatesOptions
7
+ attr_reader :document_options,
8
+ :email_options,
9
+ :pickup_options,
10
+ :delivery_options,
11
+ :pickup_instructions,
12
+ :delivery_instructions,
13
+ :handling_instructions
14
+
15
+ def initialize(
16
+ document_options: [],
17
+ email_options: [],
18
+ pickup_options: nil,
19
+ delivery_options: nil,
20
+ pickup_instructions: nil,
21
+ delivery_instructions: nil,
22
+ handling_instructions: nil,
23
+ **kwargs
24
+ )
25
+ @pickup_options = pickup_options
26
+ @delivery_options = delivery_options
27
+ @document_options = document_options
28
+ @email_options = email_options
29
+ @pickup_instructions = pickup_instructions
30
+ @delivery_instructions = delivery_instructions
31
+ @handling_instructions = handling_instructions
32
+ super kwargs
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelPackageOptions < RatesPackageOptions
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelPickupOptions
7
+ attr_reader :holiday_pickup,
8
+ :inside_pickup,
9
+ :weekend_pickup,
10
+ :lift_gate_required,
11
+ :limited_access_pickup
12
+
13
+ def initialize(
14
+ holiday_pickup: nil,
15
+ inside_pickup: nil,
16
+ weekend_pickup: nil,
17
+ lift_gate_required: nil,
18
+ limited_access_pickup: nil
19
+ )
20
+ @holiday_pickup = holiday_pickup
21
+ @inside_pickup = inside_pickup
22
+ @weekend_pickup = weekend_pickup
23
+ @lift_gate_required = lift_gate_required
24
+ @limited_access_pickup = limited_access_pickup
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'friendly_shipping/services/ups_freight/parse_shipment_document'
4
+ require 'friendly_shipping/services/ups_freight/shipment_information'
5
+
6
+ module FriendlyShipping
7
+ module Services
8
+ class UpsFreight
9
+ class ParseFreightLabelResponse
10
+ class << self
11
+ def call(request:, response:)
12
+ json = JSON.parse(response.body)
13
+
14
+ warnings_json = Array.wrap(json.dig("FreightShipResponse", "Response", "Alert"))
15
+ warnings = warnings_json.map do |detailed_warning|
16
+ status = detailed_warning['Code']
17
+ desc = detailed_warning['Description']
18
+ [status, desc].compact.join(": ")
19
+ end.join("\n")
20
+
21
+ shipment_results = json.dig("FreightShipResponse", "ShipmentResults")
22
+
23
+ service_code = shipment_results.dig("Service", "Code")
24
+ shipping_method = SHIPPING_METHODS.detect { |sm| sm.service_code == service_code }
25
+
26
+ total_shipment_charge = shipment_results.dig("TotalShipmentCharge")
27
+ currency = Money::Currency.new(total_shipment_charge['CurrencyCode'])
28
+ amount = total_shipment_charge['MonetaryValue'].to_f
29
+ total_money = Money.new(amount * currency.subunit_to_unit, currency)
30
+
31
+ images_data = Array.wrap(shipment_results.dig("Documents", "Image"))
32
+
33
+ bol_id = shipment_results.dig("BOLID")
34
+ shipment_number = shipment_results.dig("ShipmentNumber")
35
+ pickup_request_number = shipment_results.dig("PickupRequestConfirmationNumber")
36
+
37
+ documents = images_data.map { |image_data| ParseShipmentDocument.call(image_data: image_data) }
38
+
39
+ FriendlyShipping::ApiResult.new(
40
+ ShipmentInformation.new(
41
+ total: total_money,
42
+ bol_id: bol_id,
43
+ number: shipment_number,
44
+ pickup_request_number: pickup_request_number,
45
+ shipping_method: shipping_method,
46
+ warnings: warnings,
47
+ documents: documents
48
+ ),
49
+ original_request: request,
50
+ original_response: response
51
+ )
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'friendly_shipping/rate'
4
4
  require 'friendly_shipping/api_result'
5
- require 'friendly_shipping/services/ups_freight/parse_json_response'
6
5
  require 'friendly_shipping/services/ups_freight/shipping_methods'
7
6
 
8
7
  module FriendlyShipping
@@ -11,40 +10,38 @@ module FriendlyShipping
11
10
  class ParseFreightRateResponse
12
11
  class << self
13
12
  def call(request:, response:)
14
- parsed_json = ParseJSONResponse.call(response.body, 'FreightRateResponse')
13
+ json = JSON.parse(response.body)
15
14
 
16
- parsed_json.fmap do |json|
17
- service_code = json.dig("FreightRateResponse", "Service", "Code")
18
- shipping_method = SHIPPING_METHODS.detect { |sm| sm.service_code == service_code }
19
- total_shipment_charge = json.dig("FreightRateResponse", "TotalShipmentCharge")
20
- currency = Money::Currency.new(total_shipment_charge['CurrencyCode'])
21
- amount = total_shipment_charge['MonetaryValue'].to_f
22
- total_money = Money.new(amount * currency.subunit_to_unit, currency)
23
- data = {
24
- customer_context: json.dig("FreightRateResponse", "TransactionReference", "TransactionIdentifier"),
25
- commodities: Array.wrap(json.dig("FreightRateResponse", "Commodity")),
26
- response_body: json
27
- }
15
+ service_code = json.dig("FreightRateResponse", "Service", "Code")
16
+ shipping_method = SHIPPING_METHODS.detect { |sm| sm.service_code == service_code }
17
+ total_shipment_charge = json.dig("FreightRateResponse", "TotalShipmentCharge")
18
+ currency = Money::Currency.new(total_shipment_charge['CurrencyCode'])
19
+ amount = total_shipment_charge['MonetaryValue'].to_f
20
+ total_money = Money.new(amount * currency.subunit_to_unit, currency)
21
+ data = {
22
+ customer_context: json.dig("FreightRateResponse", "TransactionReference", "TransactionIdentifier"),
23
+ commodities: Array.wrap(json.dig("FreightRateResponse", "Commodity")),
24
+ response_body: json
25
+ }
28
26
 
29
- days_in_transit = json.dig("FreightRateResponse", "TimeInTransit", "DaysInTransit")
30
- if days_in_transit
31
- data[:days_in_transit] = days_in_transit.to_i
32
- end
33
-
34
- FriendlyShipping::ApiResult.new(
35
- [
36
- FriendlyShipping::Rate.new(
37
- amounts: {
38
- total: total_money
39
- },
40
- shipping_method: shipping_method,
41
- data: data
42
- )
43
- ],
44
- original_request: request,
45
- original_response: response
46
- )
27
+ days_in_transit = json.dig("FreightRateResponse", "TimeInTransit", "DaysInTransit")
28
+ if days_in_transit
29
+ data[:days_in_transit] = days_in_transit.to_i
47
30
  end
31
+
32
+ FriendlyShipping::ApiResult.new(
33
+ [
34
+ FriendlyShipping::Rate.new(
35
+ amounts: {
36
+ total: total_money
37
+ },
38
+ shipping_method: shipping_method,
39
+ data: data
40
+ )
41
+ ],
42
+ original_request: request,
43
+ original_response: response
44
+ )
48
45
  end
49
46
  end
50
47
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'friendly_shipping/services/ups_freight/shipment_document'
4
+
5
+ module FriendlyShipping
6
+ module Services
7
+ class UpsFreight
8
+ class ParseShipmentDocument
9
+ REVERSE_DOCUMENT_TYPES = LabelDocumentOptions::DOCUMENT_TYPES.map(&:reverse_each).map(&:to_a).to_h
10
+
11
+ def self.call(image_data:)
12
+ format_code = image_data.dig("Type", "Code")
13
+ graphic_image_b64 = image_data.dig("GraphicImage")
14
+
15
+ ShipmentDocument.new(
16
+ format: image_data.dig("Format", "Code").downcase.to_sym,
17
+ binary: Base64.decode64(graphic_image_b64),
18
+ document_type: REVERSE_DOCUMENT_TYPES.fetch(format_code)
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class PickupRequestOptions
7
+ attr_reader :pickup_time_window,
8
+ :requester,
9
+ :third_party_requester,
10
+ :requester_email,
11
+ :comments
12
+
13
+ def initialize(
14
+ pickup_time_window:,
15
+ requester:,
16
+ requester_email:,
17
+ comments: nil,
18
+ third_party_requester: false
19
+ )
20
+ @pickup_time_window = pickup_time_window
21
+ @requester = requester
22
+ @third_party_requester = third_party_requester
23
+ @requester_email = requester_email
24
+ @comments = comments
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -31,8 +31,7 @@ module FriendlyShipping
31
31
  :billing_code,
32
32
  :customer_context,
33
33
  :shipping_method,
34
- :pickup_date,
35
- :pickup_comments,
34
+ :pickup_request_options,
36
35
  :commodity_information_generator
37
36
 
38
37
  def initialize(
@@ -41,8 +40,7 @@ module FriendlyShipping
41
40
  shipping_method:,
42
41
  billing: :prepaid,
43
42
  customer_context: nil,
44
- pickup_date: nil,
45
- pickup_comments: nil,
43
+ pickup_request_options: nil,
46
44
  commodity_information_generator: GenerateCommodityInformation,
47
45
  **kwargs
48
46
  )
@@ -51,8 +49,7 @@ module FriendlyShipping
51
49
  @shipping_method = shipping_method
52
50
  @billing_code = BILLING_CODES.fetch(billing)
53
51
  @customer_context = customer_context
54
- @pickup_date = pickup_date
55
- @pickup_comments = pickup_comments
52
+ @pickup_request_options = pickup_request_options
56
53
  @commodity_information_generator = commodity_information_generator
57
54
  super(**kwargs.merge(package_options_class: RatesPackageOptions))
58
55
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class RestfulApiErrorHandler
7
+ extend Dry::Monads::Result::Mixin
8
+
9
+ def self.call(error)
10
+ parsed_json = JSON.parse(error.response.body)
11
+ errors = parsed_json.dig('response', 'errors')
12
+
13
+ failure_string = errors.map do |err|
14
+ status = err['code']
15
+ desc = err['message']
16
+ [status, desc].compact.join(": ").presence || 'UPS could not process the request.'
17
+ end.join("\n")
18
+
19
+ Failure(failure_string)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class ShipmentDocument
7
+ attr_reader :format, :document_type, :binary
8
+
9
+ def initialize(
10
+ format:,
11
+ document_type:,
12
+ binary:
13
+ )
14
+ @format = format
15
+ @document_type = document_type
16
+ @binary = binary
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class ShipmentInformation
7
+ attr_reader :documents,
8
+ :number,
9
+ :pickup_request_number,
10
+ :total,
11
+ :bol_id,
12
+ :shipping_method,
13
+ :warnings
14
+
15
+ def initialize(
16
+ total:,
17
+ bol_id:,
18
+ number:,
19
+ pickup_request_number: nil,
20
+ documents: [],
21
+ shipping_method: nil,
22
+ warnings: nil
23
+ )
24
+ @total = total
25
+ @bol_id = bol_id
26
+ @number = number
27
+ @pickup_request_number = pickup_request_number
28
+ @documents = documents
29
+ @shipping_method = shipping_method
30
+ @warnings = warnings
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -4,11 +4,21 @@ require 'dry/monads/result'
4
4
  require 'friendly_shipping/http_client'
5
5
  require 'friendly_shipping/services/ups_freight/shipping_methods'
6
6
  require 'friendly_shipping/services/ups_freight/rates_options'
7
+ require 'friendly_shipping/services/ups_freight/label_options'
7
8
  require 'friendly_shipping/services/ups_freight/rates_package_options'
8
9
  require 'friendly_shipping/services/ups_freight/rates_item_options'
10
+ require 'friendly_shipping/services/ups_freight/label_package_options'
11
+ require 'friendly_shipping/services/ups_freight/label_item_options'
12
+ require 'friendly_shipping/services/ups_freight/label_document_options'
13
+ require 'friendly_shipping/services/ups_freight/label_email_options'
14
+ require 'friendly_shipping/services/ups_freight/label_pickup_options'
15
+ require 'friendly_shipping/services/ups_freight/label_delivery_options'
16
+ require 'friendly_shipping/services/ups_freight/pickup_request_options'
17
+ require 'friendly_shipping/services/ups_freight/parse_freight_label_response'
9
18
  require 'friendly_shipping/services/ups_freight/parse_freight_rate_response'
10
19
  require 'friendly_shipping/services/ups_freight/generate_freight_rate_request_hash'
11
- require 'friendly_shipping/services/ups_freight/generate_ups_security_hash'
20
+ require 'friendly_shipping/services/ups_freight/generate_freight_ship_request_hash'
21
+ require 'friendly_shipping/services/ups_freight/restful_api_error_handler'
12
22
 
13
23
  module FriendlyShipping
14
24
  module Services
@@ -28,10 +38,11 @@ module FriendlyShipping
28
38
  LIVE_URL = 'https://onlinetools.ups.com'
29
39
 
30
40
  RESOURCES = {
31
- rates: '/rest/FreightRate'
41
+ rates: '/ship/v1801/freight/rating/ground',
42
+ labels: '/ship/v1607/freight/shipments/Ground'
32
43
  }.freeze
33
44
 
34
- def initialize(key:, login:, password:, test: true, client: HttpClient.new)
45
+ def initialize(key:, login:, password:, test: true, client: HttpClient.new(error_handler: RestfulApiErrorHandler))
35
46
  @key = key
36
47
  @login = login
37
48
  @password = password
@@ -50,22 +61,41 @@ module FriendlyShipping
50
61
  # `FriendlyShipping::ApiResult` object.
51
62
  def rate_estimates(shipment, options:, debug: false)
52
63
  freight_rate_request_hash = GenerateFreightRateRequestHash.call(shipment: shipment, options: options)
53
- url = base_url + RESOURCES[:rates]
54
- request = FriendlyShipping::Request.new(
55
- url: url,
56
- body: authentication_hash.merge(freight_rate_request_hash).to_json,
57
- debug: debug
58
- )
64
+ request = build_request(:rates, freight_rate_request_hash, debug)
59
65
 
60
- client.post(request).bind do |response|
66
+ client.post(request).fmap do |response|
61
67
  ParseFreightRateResponse.call(response: response, request: request)
62
68
  end
63
69
  end
64
70
 
71
+ # Get labels for a shipment
72
+ # @param [Physical::Shipment] location The shipment we want to get rates for
73
+ # @param [FriendlyShipping::Services::UpsFreight::LabelOptions] options Options for shipping this shipment.
74
+ # @return [Result<ApiResult<ShipmentInformation>] The information that you need for shipping this shipment.
75
+ def labels(shipment, options:, debug: false)
76
+ freight_ship_request_hash = GenerateFreightShipRequestHash.call(shipment: shipment, options: options)
77
+ request = build_request(:labels, freight_ship_request_hash, debug)
78
+
79
+ client.post(request).fmap do |response|
80
+ ParseFreightLabelResponse.call(response: response, request: request)
81
+ end
82
+ end
83
+
65
84
  private
66
85
 
67
- def authentication_hash
68
- GenerateUpsSecurityHash.call(key: key, login: login, password: password)
86
+ def build_request(action, payload, debug)
87
+ url = base_url + RESOURCES[action]
88
+ FriendlyShipping::Request.new(
89
+ url: url,
90
+ body: payload.to_json,
91
+ headers: {
92
+ Accept: 'application/json',
93
+ Username: login,
94
+ Password: password,
95
+ AccessLicenseNumber: key
96
+ },
97
+ debug: debug
98
+ )
69
99
  end
70
100
 
71
101
  def base_url
@@ -12,7 +12,11 @@ module FriendlyShipping
12
12
  # @return [Result<FriendlyShipping::AddressValidationResult>]
13
13
  def call(request:, response:)
14
14
  # Filter out error responses and directly return a failure
15
- parsing_result = ParseXMLResponse.call(response.body, 'AddressValidateResponse')
15
+ parsing_result = ParseXMLResponse.call(
16
+ request: request,
17
+ response: response,
18
+ expected_root_tag: 'AddressValidateResponse'
19
+ )
16
20
  parsing_result.fmap do |xml|
17
21
  address = xml.root.at('Address')
18
22
  suggestions = [
@@ -12,7 +12,11 @@ module FriendlyShipping
12
12
  # @return [Result<FriendlyShipping::AddressValidationResult>]
13
13
  def call(request:, response:)
14
14
  # Filter out error responses and directly return a failure
15
- parsing_result = ParseXMLResponse.call(response.body, 'CityStateLookupResponse')
15
+ parsing_result = ParseXMLResponse.call(
16
+ request: request,
17
+ response: response,
18
+ expected_root_tag: 'CityStateLookupResponse'
19
+ )
16
20
  parsing_result.fmap do |xml|
17
21
  address = xml.root.at('ZipCode')
18
22
  suggestions = [
@@ -20,8 +20,11 @@ module FriendlyShipping
20
20
  # @return [Result<ApiResult<Array<FriendlyShipping::Rate>>>] When successfully parsing, an array of rates in a Success Monad.
21
21
  def call(request:, response:, shipment:, options:)
22
22
  # Filter out error responses and directly return a failure
23
- parsing_result = ParseXMLResponse.call(response.body, 'RateV4Response')
24
- rates = []
23
+ parsing_result = ParseXMLResponse.call(
24
+ request: request,
25
+ response: response,
26
+ expected_root_tag: 'RateV4Response'
27
+ )
25
28
  parsing_result.fmap do |xml|
26
29
  # Get all the possible rates for each package
27
30
  rates_by_package = rates_from_response_node(xml, shipment, options)
@@ -15,7 +15,11 @@ module FriendlyShipping
15
15
  # @return [Result<ApiResult<Array<FriendlyShipping::Timing>>>] When successfully parsing, an array of timings in a Success Monad.
16
16
  def call(request:, response:)
17
17
  # Filter out error responses and directly return a failure
18
- parsing_result = ParseXMLResponse.call(response.body, 'SDCGetLocationsResponse')
18
+ parsing_result = ParseXMLResponse.call(
19
+ request: request,
20
+ response: response,
21
+ expected_root_tag: 'SDCGetLocationsResponse'
22
+ )
19
23
  parsing_result.fmap do |xml|
20
24
  expedited_commitments = xml.xpath('//Expedited')
21
25
  expedited_timings = parse_expedited_commitment_nodes(expedited_commitments)
@@ -8,18 +8,18 @@ module FriendlyShipping
8
8
  ERROR_TAG = 'Error'
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? || ![expected_root_tag, 'Error'].include?(xml.root.name)
15
- Failure('Invalid document')
15
+ wrap_failure('Invalid document', request, response)
16
16
  elsif request_successful?(xml)
17
17
  Success(xml)
18
18
  else
19
- Failure(error_message(xml))
19
+ wrap_failure(error_message(xml), request, response)
20
20
  end
21
21
  rescue Nokogiri::XML::SyntaxError => e
22
- Failure(e)
22
+ wrap_failure(e, request, response)
23
23
  end
24
24
 
25
25
  private
@@ -33,6 +33,16 @@ module FriendlyShipping
33
33
  desc = xml.xpath('//Error/Description')&.text
34
34
  [number, desc].select(&:present?).join(': ').presence&.strip || 'USPS could not process the request.'
35
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
36
46
  end
37
47
  end
38
48
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FriendlyShipping
4
- VERSION = "0.5.2"
4
+ VERSION = "0.5.3"
5
5
  end