friendly_shipping 0.5.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-relaxed.yml +3 -4
  3. data/.rubocop.yml +9 -0
  4. data/CHANGELOG.md +28 -1
  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 +8 -0
  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 +6 -2
  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 +5 -1
  49. data/lib/friendly_shipping/services/usps/parse_xml_response.rb +15 -5
  50. data/lib/friendly_shipping/services/usps/rate_estimate_package_options.rb +4 -1
  51. data/lib/friendly_shipping/services/usps/serialize_rate_request.rb +9 -3
  52. data/lib/friendly_shipping/services/usps/shipping_methods.rb +1 -2
  53. data/lib/friendly_shipping/version.rb +1 -1
  54. metadata +75 -40
  55. data/lib/friendly_shipping/services/ups_freight/generate_ups_security_hash.rb +0 -23
  56. 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: 8bb403d6836f79ac9aa2640acde019549c1bad34f811b83a89200fc42d2988d0
4
- data.tar.gz: cafc834f0f658fcbb642b05a63b0ae7a5199c4cb931a763d0fbe818e6120b507
3
+ metadata.gz: c625d1ee6c38d9d51bbcadd7280f6b0b415cecb5256d5d884c1da53b6a2afd38
4
+ data.tar.gz: 4ce888754298c7a7b5bddb724bb518fb2a0e8993f497188756a70cf72965f5cb
5
5
  SHA512:
6
- metadata.gz: d57c7d63efe4b830468831b40dd0f983fd4c500da0a0a7bb27d75b0f789041fdf39f22ce737d4bbf9c5c8853fc0207133df1188b64a73e5389d6898dfc15add0
7
- data.tar.gz: d582a29e16a2d99bd397bb955ade4b0d220a93e749b0f0cbb96d645aa71c7ece47197b74fd5a65c2d2cd95988612b6f71c3599220aea4a97b3ae73606be4cb72
6
+ metadata.gz: c20df19636bdc7ace8658ada396f7d094da5f7ea776e6b555af0aa875e893a0d5d6bb5143e1ee9ac89922c8924478caf08afd6527859d539318d7971c054112a
7
+ data.tar.gz: b5ba5a4fc4b5c66861c4d04743f30ce0a3d5ad3452dad0646bc9a8683a666d76ef41b4eec9bb583713d9a500ae5632341cc92e331acdd71c89d42c21c221b190
@@ -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,11 +4,38 @@ 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.2] - 2020-08-12
8
+
9
+ - UPS Service: Be more resilient when UPS does not send a PickupTime element
10
+
11
+ ## [0.6.1] - 2020-03-11
12
+
13
+ - Add Content-Type header to UPS Freight API requests, fixing "Name too long" 500 error responses
14
+
15
+ ## [0.6.0] - 2020-03-11
16
+
17
+ - Changelog additions missed in previous release
18
+
19
+ ## [0.5.3] - 2020-03-11
20
+
21
+ - UPS Service: Add support for shipping labels / bills of lading (#92)
22
+ - UPS/USPS Services: Return ApiFailure instead of a string for failed API responses (#95)
23
+ - UPS/USPS Services: Refactor ApiFailure to subclass ApiResponse (#96)
24
+
25
+ ## [0.5.2] - 2020-01-31
26
+
27
+ ### Added
28
+ - USPS Service: Added rectangular boolean to rate options class (#89)
29
+ - USPS Service: Added readable body to request class (#88)
30
+
31
+ ### Removed
32
+ - USPS Service: Drop deprecated rectangular container (#89)
33
+
7
34
  ## [0.5.1] - 2020-01-28
8
35
 
9
36
  ### Changed
10
37
  - USPS Service: Rename "Package Services" shipping method (#85)
11
- - Documentation updates (#86)
38
+ - Documentation updates (#86)
12
39
 
13
40
  ## [0.5] - 2020-01-24
14
41
 
@@ -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
 
@@ -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|
@@ -21,7 +25,7 @@ module FriendlyShipping
21
25
  delivery_time = service_summary.at('EstimatedArrival/Time').text
22
26
  delivery = Time.parse("#{delivery_date} #{delivery_time}")
23
27
  pickup_date = service_summary.at('EstimatedArrival/PickupDate').text
24
- pickup_time = service_summary.at('EstimatedArrival/PickupTime').text
28
+ pickup_time = service_summary.at('EstimatedArrival/PickupTime')&.text
25
29
  pickup = Time.parse("#{pickup_date} #{pickup_time}")
26
30
 
27
31
  # Some additional data
@@ -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,