friendly_shipping 0.6.4 → 0.7.0

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +18 -5
  3. data/.env.template +1 -1
  4. data/.rubocop.yml +4 -1
  5. data/CHANGELOG.md +14 -3
  6. data/README.md +1 -1
  7. data/friendly_shipping.gemspec +6 -7
  8. data/lib/friendly_shipping/api_error.rb +14 -0
  9. data/lib/friendly_shipping/api_error_handler.rb +30 -0
  10. data/lib/friendly_shipping/http_client.rb +9 -27
  11. data/lib/friendly_shipping/request.rb +5 -3
  12. data/lib/friendly_shipping/response.rb +27 -1
  13. data/lib/friendly_shipping/services/ship_engine/parse_label_response.rb +8 -2
  14. data/lib/friendly_shipping/services/ship_engine/parse_rate_estimate_response.rb +28 -6
  15. data/lib/friendly_shipping/services/ship_engine.rb +9 -5
  16. data/lib/friendly_shipping/services/ups/parse_shipment_accept_response.rb +1 -1
  17. data/lib/friendly_shipping/services/ups/parse_shipment_confirm_response.rb +1 -1
  18. data/lib/friendly_shipping/services/ups/parse_void_shipment_response.rb +1 -1
  19. data/lib/friendly_shipping/services/ups/rate_estimate_options.rb +1 -1
  20. data/lib/friendly_shipping/services/ups/rate_estimate_package_options.rb +1 -1
  21. data/lib/friendly_shipping/services/ups/shipping_methods.rb +4 -0
  22. data/lib/friendly_shipping/services/ups.rb +10 -2
  23. data/lib/friendly_shipping/services/ups_freight/api_error.rb +36 -0
  24. data/lib/friendly_shipping/services/ups_freight/generate_delivery_options_hash.rb +6 -4
  25. data/lib/friendly_shipping/services/ups_freight/generate_freight_ship_request_hash.rb +5 -2
  26. data/lib/friendly_shipping/services/ups_freight/generate_location_hash.rb +6 -9
  27. data/lib/friendly_shipping/services/ups_freight/generate_pickup_options_hash.rb +4 -3
  28. data/lib/friendly_shipping/services/ups_freight/generate_reference_hash.rb +27 -0
  29. data/lib/friendly_shipping/services/ups_freight/label_delivery_options.rb +7 -1
  30. data/lib/friendly_shipping/services/ups_freight/label_options.rb +28 -2
  31. data/lib/friendly_shipping/services/ups_freight/label_pickup_options.rb +3 -0
  32. data/lib/friendly_shipping/services/ups_freight/parse_freight_label_response.rb +27 -8
  33. data/lib/friendly_shipping/services/ups_freight/parse_shipment_document.rb +1 -1
  34. data/lib/friendly_shipping/services/ups_freight/shipment_information.rb +5 -2
  35. data/lib/friendly_shipping/services/ups_freight.rb +8 -5
  36. data/lib/friendly_shipping/services/usps/parse_package_rate.rb +16 -1
  37. data/lib/friendly_shipping/services/usps/rate_estimate_options.rb +1 -1
  38. data/lib/friendly_shipping/services/usps/rate_estimate_package_options.rb +23 -19
  39. data/lib/friendly_shipping/services/usps/serialize_rate_request.rb +2 -0
  40. data/lib/friendly_shipping/services/usps.rb +2 -1
  41. data/lib/friendly_shipping/version.rb +1 -1
  42. data/lib/friendly_shipping.rb +1 -0
  43. metadata +18 -35
  44. data/lib/friendly_shipping/services/ups_freight/restful_api_error_handler.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 107fa27ec95b49342549dbf24c53cc483cd05bdfbb64871940036aaac6567eb6
4
- data.tar.gz: 7dd9c281178e513482ae0cb93787eb551b09e5f697f10d4db50dc2aef21dd4a8
3
+ metadata.gz: 4e5444887ead0f957efaa619fc49bf737c34513f8a2480504beaabdbdab0eb2d
4
+ data.tar.gz: c52c103c53e9be10cef0d6288c1713de54441e536f8691cf0685c2bb0a4859ff
5
5
  SHA512:
6
- metadata.gz: 30c37297f77dc6be490cf9719211780b81b0d91cdc49311b349fc3844a6cbcb5755265e663b80e24a67cda9847e19d328808cec786cf7172d6986ccb75bbf6eb
7
- data.tar.gz: 022c69680c07d9fed0b2b9afa8093e40b20bb7b441b332acd743078f74db70f3de03227953952de8b08048aa40eb902895529c781b6332036cf4e38ba653804e
6
+ metadata.gz: eff139906b09356f48bbb34e39587f41f3802cba25abb87921b7e2883865d399d4b043b793c5e0ca40fddf9fe9ca00b326be39722466a9c59c83020a67d6674e
7
+ data.tar.gz: 396fe842544a93357d601d383a27d116b5b6f4e2947fec39703e7bd70a57b8fe30a63f68eb7312761db56e3533d59fbf70a3c87bb673ac3d497912c84b5a703b
data/.circleci/config.yml CHANGED
@@ -2,12 +2,15 @@
2
2
  #
3
3
  # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4
4
  #
5
- version: 2
5
+ version: 2.1
6
6
  jobs:
7
- build:
7
+ test:
8
+ parameters:
9
+ ruby-version:
10
+ type: string
8
11
  docker:
9
12
  # specify the version you desire here
10
- - image: circleci/ruby:2.5
13
+ - image: cimg/ruby:<< parameters.ruby-version >>
11
14
 
12
15
  # Specify service dependencies here if necessary
13
16
  # CircleCI maintains a library of pre-built images
@@ -22,7 +25,7 @@ jobs:
22
25
  # Download and cache dependencies
23
26
  - restore_cache:
24
27
  keys:
25
- - v1-dependencies-{{ checksum "friendly_shipping.gemspec" }}
28
+ - v1-dependencies-{{ checksum "friendly_shipping.gemspec" }}-<< parameters.ruby-version >>
26
29
  # fallback to using the latest cache if no exact match is found
27
30
  - v1-dependencies-
28
31
 
@@ -35,7 +38,7 @@ jobs:
35
38
  - save_cache:
36
39
  paths:
37
40
  - ./vendor/bundle
38
- key: v1-dependencies-{{ checksum "Gemfile.lock" }}
41
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}-<< parameters.ruby-version >>
39
42
 
40
43
  - run:
41
44
  name: run Rubocop
@@ -60,3 +63,13 @@ jobs:
60
63
  - store_artifacts:
61
64
  path: /tmp/test-results
62
65
  destination: test-results
66
+
67
+ workflows:
68
+ build_and_test:
69
+ jobs:
70
+ - test:
71
+ matrix:
72
+ parameters:
73
+ # https://github.com/CircleCI-Public/cimg-ruby
74
+ # only supports the last three ruby versions
75
+ ruby-version: ["3.1", "3.0", "2.7"]
data/.env.template CHANGED
@@ -2,7 +2,7 @@
2
2
  # NO PASSWORDS OR SENSITIVE INFORMATION SHOULD BE STORED HERE
3
3
  # Only pass along how those passwords/info are reference via ENV VARS
4
4
 
5
- SHIPENGINE_API_KEY=Ship Engine API key
5
+ SHIPENGINE_API_KEY=ShipEngine API key
6
6
  SHIPENGINE_CARRIER_ID=Carrier ID from your ShipEngine account to run test labels with
7
7
 
8
8
  UPS_KEY=UPS API access key
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.5
2
+ TargetRubyVersion: 2.7
3
3
 
4
4
  inherit_from:
5
5
  - .rubocop-relaxed.yml
@@ -15,3 +15,6 @@ Style/HashTransformKeys:
15
15
 
16
16
  Style/HashTransformValues:
17
17
  Enabled: true
18
+
19
+ Naming/VariableNumber:
20
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -4,22 +4,33 @@ 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.4] - 2021-01-27
7
+ ## [0.7.0] - 2022-12-14
8
+ - Removes dependency on unmaintained data_uri gem
9
+ - Bumps required Ruby to 2.7
10
+
11
+ ## [0.6.5] - 2022-04-25
8
12
 
9
13
  ### Added
14
+ - USPS Service: Add support for returned dimensional weight (#128)
15
+ - USPS Service: Add support for returned fees (#127)
10
16
 
17
+ ### Changed
18
+ - ShipEngine Service: Prevent exceptions when no rates are returned (#125)
19
+ - Misc dependency updates (#116, #120, #121, #124)
20
+
21
+ ## [0.6.4] - 2021-01-27
22
+
23
+ ### Added
11
24
  - UPS Service: Include negotiated charges for UPS (#119)
12
25
  - UPS Service: Include shipment-level itemized charges (#117)
13
26
 
14
27
  ## [0.6.3] - 2020-10-30
15
28
 
16
29
  ### Added
17
-
18
30
  - USPS Service: Append HFP (Hold For Pickup) to service code when necessary (#110)
19
31
  - USPS Service: Add Priority Cubic shipping method (#113)
20
32
 
21
33
  ### Changed
22
-
23
34
  - USPS Service: Refactor to use explicit service codes (#111)
24
35
  - USPS Service: Match Priority Express by CLASSID instead of service name (#112)
25
36
  - UPS Service: Rename peak surcharge keys to match UPS docs (#114)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # FriendlyShipping - the friendly shipping provider API wrapper
2
2
 
3
- This gem provides wrappers for popular shipping provider APIs. Currently, there are implementations for rate quoting and address validation, as well as shipping label generation via the `ShipStation` API.
3
+ This gem provides wrappers for popular shipping provider APIs. Currently, there are implementations for rate quoting and address validation, as well as shipping label generation via the `ShipEngine` API.
4
4
 
5
5
  ## Installation
6
6
 
@@ -22,23 +22,22 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
24
 
25
- spec.add_runtime_dependency "data_uri", "~> 0.0.3"
26
25
  spec.add_runtime_dependency "dry-monads", "~> 1.0"
27
26
  spec.add_runtime_dependency "money", "~> 6.0"
28
27
  spec.add_runtime_dependency "nokogiri", "~> 1.6"
29
- spec.add_runtime_dependency "physical", "~> 0.4", ">= 0.4.4"
28
+ spec.add_runtime_dependency "physical", "~> 0.4", ">= 0.4.5"
30
29
  spec.add_runtime_dependency "rest-client", "~> 2.0"
31
- spec.required_ruby_version = '>= 2.5'
30
+ spec.required_ruby_version = '>= 2.7'
32
31
 
33
- spec.add_development_dependency "bundler", ">= 1.17", "< 3"
32
+ spec.add_development_dependency "bundler", ">= 2.1.4", "< 3"
34
33
  spec.add_development_dependency "dotenv", "~> 2.7"
35
- spec.add_development_dependency "factory_bot", "~> 5.0"
34
+ spec.add_development_dependency "factory_bot", "~> 6.2"
36
35
  spec.add_development_dependency "pry", "~> 0.12"
37
36
  spec.add_development_dependency "rake", ">= 12.3.3"
38
37
  spec.add_development_dependency "rspec", "~> 3.0"
39
38
  spec.add_development_dependency "rspec_junit_formatter", "~> 0.4"
40
- spec.add_development_dependency "rubocop", ">= 0.80", "< 1"
39
+ spec.add_development_dependency "rubocop"
41
40
  spec.add_development_dependency "simplecov", "~> 0.17"
42
- spec.add_development_dependency "vcr", "~> 5.0"
41
+ spec.add_development_dependency "vcr", "~> 6.0"
43
42
  spec.add_development_dependency "webmock", "~> 3.6"
44
43
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ class ApiError < StandardError
5
+ attr_reader :cause
6
+
7
+ # @param [RestClient::Exception] cause
8
+ # @param [String] msg
9
+ def initialize(cause, msg = nil)
10
+ @cause = cause
11
+ super msg || cause.message
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'friendly_shipping/api_error'
4
+
5
+ module FriendlyShipping
6
+ class ApiErrorHandler
7
+ include Dry::Monads[:result]
8
+
9
+ attr_reader :api_error_class
10
+
11
+ # @param [Class] api_error_class
12
+ def initialize(api_error_class: FriendlyShipping::ApiError)
13
+ @api_error_class = api_error_class
14
+ end
15
+
16
+ # @param [StandardError] error
17
+ # @param [FriendlyShipping::Request] original_request
18
+ # @param [RestClient::Response] original_response
19
+ # @return [Dry::Monads::Failure<FriendlyShipping::ApiFailure>]
20
+ def call(error, original_request: nil, original_response: nil)
21
+ Failure(
22
+ ApiFailure.new(
23
+ api_error_class.new(error),
24
+ original_request: original_request,
25
+ original_response: Response.new_from_rest_client_response(original_response)
26
+ )
27
+ )
28
+ end
29
+ end
30
+ end
@@ -1,17 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/result'
4
- require 'friendly_shipping/api_failure'
3
+ require 'dry/monads'
5
4
  require 'rest-client'
6
5
 
6
+ require 'friendly_shipping/api_failure'
7
+ require 'friendly_shipping/api_error_handler'
8
+
7
9
  module FriendlyShipping
8
10
  class HttpClient
9
- include Dry::Monads::Result::Mixin
11
+ include Dry::Monads[:result]
10
12
 
11
13
  attr_reader :error_handler
12
14
 
13
15
  # @param [Proc] error_handler Called to handle an error if one occurs
14
- def initialize(error_handler: method(:wrap_in_failure))
16
+ def initialize(error_handler: FriendlyShipping::ApiErrorHandler.new)
15
17
  @error_handler = error_handler
16
18
  end
17
19
 
@@ -20,7 +22,7 @@ module FriendlyShipping
20
22
  request.url, request.headers
21
23
  )
22
24
 
23
- Success(convert_to_friendly_response(http_response))
25
+ Success(Response.new_from_rest_client_response(http_response))
24
26
  rescue ::RestClient::Exception => e
25
27
  error_handler.call(e, original_request: request, original_response: e.response)
26
28
  end
@@ -32,7 +34,7 @@ module FriendlyShipping
32
34
  request.headers
33
35
  )
34
36
 
35
- Success(convert_to_friendly_response(http_response))
37
+ Success(Response.new_from_rest_client_response(http_response))
36
38
  rescue ::RestClient::Exception => e
37
39
  error_handler.call(e, original_request: request, original_response: e.response)
38
40
  end
@@ -44,29 +46,9 @@ module FriendlyShipping
44
46
  request.headers
45
47
  )
46
48
 
47
- Success(convert_to_friendly_response(http_response))
49
+ Success(Response.new_from_rest_client_response(http_response))
48
50
  rescue ::RestClient::Exception => e
49
51
  error_handler.call(e, original_request: request, original_response: e.response)
50
52
  end
51
-
52
- private
53
-
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
- )
62
- end
63
-
64
- def convert_to_friendly_response(http_response)
65
- FriendlyShipping::Response.new(
66
- status: http_response.code,
67
- body: http_response.body,
68
- headers: http_response.headers
69
- )
70
- end
71
53
  end
72
54
  end
@@ -2,15 +2,17 @@
2
2
 
3
3
  module FriendlyShipping
4
4
  class Request
5
- attr_reader :url, :body, :headers, :debug
5
+ attr_reader :url, :http_method, :body, :headers, :debug
6
6
 
7
7
  # @param [String] url The HTTP request URL
8
+ # @param [String] http_method The HTTP request method
8
9
  # @param [String] body The HTTP request body
9
- # # @param [String] readable_body Human-readable HTTP request body
10
+ # @param [String] readable_body Human-readable HTTP request body
10
11
  # @param [Hash] headers The HTTP request headers
11
12
  # @param [Boolean] debug Whether to debug the request
12
- def initialize(url:, body: nil, readable_body: nil, headers: {}, debug: false)
13
+ def initialize(url:, http_method: nil, body: nil, readable_body: nil, headers: {}, debug: false)
13
14
  @url = url
15
+ @http_method = http_method
14
16
  @body = body
15
17
  @readable_body = readable_body
16
18
  @headers = headers
@@ -10,7 +10,33 @@ module FriendlyShipping
10
10
  def initialize(status:, body:, headers:)
11
11
  @status = status
12
12
  @body = body
13
- @headers = headers
13
+ @headers = headers || {}
14
+ end
15
+
16
+ alias_method :code, :status
17
+
18
+ # @param [RestClient::Response] response
19
+ # @return [FriendlyShipping::Response]
20
+ def self.new_from_rest_client_response(response)
21
+ new(status: response&.code, body: response&.body, headers: response&.headers)
22
+ end
23
+
24
+ # @param [Object] other
25
+ def ==(other)
26
+ other.class == self.class &&
27
+ other.attributes == attributes
28
+ end
29
+
30
+ alias_method :eql?, :==
31
+
32
+ def hash
33
+ attributes.hash
34
+ end
35
+
36
+ protected
37
+
38
+ def attributes
39
+ [status, body, headers]
14
40
  end
15
41
  end
16
42
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
- require 'data_uri'
5
4
 
6
5
  module FriendlyShipping
7
6
  module Services
@@ -14,7 +13,14 @@ module FriendlyShipping
14
13
  label_data = nil
15
14
  label_url = nil
16
15
  if label_uri_string.starts_with?('data')
17
- label_data = URI::Data.new(label_uri_string).data
16
+ # This URI has the following form:
17
+ # data:application/zpl;base64,XlhBDQpeTEwxMjE4....
18
+ # We don't know the content type here, but we can assume Base64
19
+ # encoding.
20
+ # This next line splits the URI at the first occurrence of ";base64,",
21
+ # giving us the desired base64 encoded string.
22
+ _, base64_encoded = label_uri_string.split(";base64,", 2)
23
+ label_data = Base64.decode64(base64_encoded)
18
24
  else
19
25
  label_url = label_uri_string
20
26
  end
@@ -10,8 +10,14 @@ module FriendlyShipping
10
10
 
11
11
  class << self
12
12
  def call(response:, request:, options:)
13
+ error_messages = []
13
14
  parsed_json = JSON.parse(response.body)
14
15
  rates = parsed_json.map do |rate|
16
+ if rate['validation_status'] == 'invalid'
17
+ error_messages.concat rate['error_messages']
18
+ next
19
+ end
20
+
15
21
  carrier = options.carriers.detect { |c| c.id == rate['carrier_id'] }
16
22
  next unless carrier
17
23
 
@@ -31,17 +37,33 @@ module FriendlyShipping
31
37
  )
32
38
  end.compact
33
39
 
34
- Success(
35
- ApiResult.new(
36
- rates,
37
- original_request: request,
38
- original_response: response
40
+ if valid_rates(parsed_json)
41
+ Success(
42
+ ApiResult.new(
43
+ rates,
44
+ original_request: request,
45
+ original_response: response
46
+ )
47
+ )
48
+ else
49
+ Failure(
50
+ ApiFailure.new(
51
+ error_messages,
52
+ original_request: request,
53
+ original_response: response
54
+ )
39
55
  )
40
- )
56
+ end
41
57
  end
42
58
 
43
59
  private
44
60
 
61
+ def valid_rates(parsed_json)
62
+ parsed_json.map do |rate|
63
+ ["valid", "has_warnings", "unknown"].include? rate['validation_status']
64
+ end.any?
65
+ end
66
+
45
67
  def get_amounts(rate_hash)
46
68
  [:shipping, :other, :insurance, :confirmation].map do |name|
47
69
  currency = Money::Currency.new(rate_hash["#{name}_amount"]["currency"])
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/result'
3
+ require 'dry/monads'
4
4
  require 'friendly_shipping/http_client'
5
5
  require 'friendly_shipping/services/ship_engine/bad_request_handler'
6
6
  require 'friendly_shipping/services/ship_engine/parse_carrier_response'
@@ -28,12 +28,13 @@ module FriendlyShipping
28
28
  @client = client
29
29
  end
30
30
 
31
- # Get configured carriers from USPS
31
+ # Get configured carriers from ShipEngine
32
32
  #
33
- # @return [Result<ApiResult<Array<Carrier>>>] Carriers configured in your shipstation account
33
+ # @return [Result<ApiResult<Array<Carrier>>>] Carriers configured in your account
34
34
  def carriers(debug: false)
35
35
  request = FriendlyShipping::Request.new(
36
36
  url: API_BASE + API_PATHS[:carriers],
37
+ http_method: "GET",
37
38
  headers: request_headers,
38
39
  debug: debug
39
40
  )
@@ -53,7 +54,8 @@ module FriendlyShipping
53
54
  # can be serialized into an error message using `to_s`.
54
55
  def rate_estimates(shipment, options: FriendlyShipping::Services::ShipEngine::RateEstimatesOptions.new, debug: false)
55
56
  request = FriendlyShipping::Request.new(
56
- url: API_BASE + 'rates/estimate',
57
+ url: "#{API_BASE}rates/estimate",
58
+ http_method: "POST",
57
59
  body: SerializeRateEstimateRequest.call(shipment: shipment, options: options).to_json,
58
60
  headers: request_headers,
59
61
  debug: debug
@@ -68,13 +70,14 @@ module FriendlyShipping
68
70
  # @param [Physical::Shipment] shipment The shipment object we're trying to get labels for
69
71
  # Note: Some ShipEngine carriers, notably USPS, only support one package per shipment, and that's
70
72
  # all that the integration supports at this point.
71
- # @param [FriendlyShipping::Services::ShipEngine::LabelOptions] The options relevant to estimating rates. See object description.
73
+ # @param [FriendlyShipping::Services::ShipEngine::LabelOptions] options The options relevant to estimating rates. See object description.
72
74
  #
73
75
  # @return [Result<ApiResult<Array<FriendlyShipping::Label>>>] The label returned.
74
76
  #
75
77
  def labels(shipment, options:)
76
78
  request = FriendlyShipping::Request.new(
77
79
  url: API_BASE + API_PATHS[:labels],
80
+ http_method: "POST",
78
81
  body: SerializeLabelShipment.call(shipment: shipment, options: options, test: test).to_json,
79
82
  headers: request_headers
80
83
  )
@@ -86,6 +89,7 @@ module FriendlyShipping
86
89
  def void(label, debug: false)
87
90
  request = FriendlyShipping::Request.new(
88
91
  url: "#{API_BASE}labels/#{label.id}/void",
92
+ http_method: "PUT",
89
93
  body: '',
90
94
  headers: request_headers,
91
95
  debug: debug
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/result'
3
+ require 'dry/monads'
4
4
  require 'friendly_shipping/services/ups/parse_money_element'
5
5
 
6
6
  module FriendlyShipping
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/result'
3
+ require 'dry/monads'
4
4
 
5
5
  module FriendlyShipping
6
6
  module Services
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/result'
3
+ require 'dry/monads'
4
4
 
5
5
  module FriendlyShipping
6
6
  module Services
@@ -84,7 +84,7 @@ module FriendlyShipping
84
84
  @shipper = shipper
85
85
  @shipping_method = shipping_method
86
86
  @with_time_in_transit = with_time_in_transit
87
- super kwargs.merge(package_options_class: package_options_class)
87
+ super(**kwargs.merge(package_options_class: package_options_class))
88
88
  end
89
89
 
90
90
  def pickup_type_code
@@ -16,7 +16,7 @@ module FriendlyShipping
16
16
  **kwargs
17
17
  )
18
18
  @transmit_dimensions = transmit_dimensions
19
- super kwargs
19
+ super(**kwargs)
20
20
  end
21
21
  end
22
22
  end
@@ -39,6 +39,10 @@ module FriendlyShipping
39
39
  ['US', 'domestic', 'UPS Next Day Air', '01'],
40
40
  ['US', 'domestic', 'UPS Next Day Air Early', '14'],
41
41
  ['US', 'domestic', 'UPS Next Day Air Saver', '13'],
42
+ ['US', 'domestic', 'UPS SurePost Less than 1LB', '92'],
43
+ ['US', 'domestic', 'UPS SurePost 1LB or greater', '93'],
44
+ ['US', 'domestic', 'UPS SurePost BPM', '94'],
45
+ ['US', 'domestic', 'UPS SurePost Media Mail', '95'],
42
46
  ['CA', 'domestic', 'UPS Expedited Canadian domestic shipments', '02'],
43
47
  ['CA', 'domestic', 'UPS Express Saver Canadian domestic shipments', '13'],
44
48
  ['CA', 'domestic', 'UPS 3 Day Select Shipments originating in Canada to CA and US 48', '12'],
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/result'
3
+ require 'dry/monads'
4
4
  require 'friendly_shipping/http_client'
5
5
  require 'friendly_shipping/services/ups/serialize_access_request'
6
6
  require 'friendly_shipping/services/ups/serialize_city_state_lookup_request'
@@ -26,7 +26,7 @@ require 'friendly_shipping/services/ups/timing_options'
26
26
  module FriendlyShipping
27
27
  module Services
28
28
  class Ups
29
- include Dry::Monads::Result::Mixin
29
+ include Dry::Monads[:result]
30
30
 
31
31
  attr_reader :test, :key, :login, :password, :client
32
32
 
@@ -73,6 +73,7 @@ module FriendlyShipping
73
73
  url = base_url + RESOURCES[:rates]
74
74
  request = FriendlyShipping::Request.new(
75
75
  url: url,
76
+ http_method: "POST",
76
77
  body: access_request_xml + rate_request_xml,
77
78
  readable_body: rate_request_xml,
78
79
  debug: debug
@@ -95,6 +96,7 @@ module FriendlyShipping
95
96
 
96
97
  request = FriendlyShipping::Request.new(
97
98
  url: time_in_transit_url,
99
+ http_method: "POST",
98
100
  body: access_request_xml + time_in_transit_request_xml,
99
101
  readable_body: time_in_transit_request_xml,
100
102
  debug: debug
@@ -115,6 +117,7 @@ module FriendlyShipping
115
117
 
116
118
  ship_confirm_request = FriendlyShipping::Request.new(
117
119
  url: ship_confirm_url,
120
+ http_method: "POST",
118
121
  body: access_request_xml + ship_confirm_request_xml,
119
122
  readable_body: ship_confirm_request_xml,
120
123
  debug: debug
@@ -134,6 +137,7 @@ module FriendlyShipping
134
137
 
135
138
  ship_accept_request = FriendlyShipping::Request.new(
136
139
  url: ship_accept_url,
140
+ http_method: "POST",
137
141
  body: access_request_xml + ship_accept_request_xml,
138
142
  readable_body: ship_accept_request_xml,
139
143
  debug: debug
@@ -156,6 +160,7 @@ module FriendlyShipping
156
160
  url = base_url + RESOURCES[:address_validation]
157
161
  request = FriendlyShipping::Request.new(
158
162
  url: url,
163
+ http_method: "POST",
159
164
  body: access_request_xml + address_validation_request_xml,
160
165
  readable_body: address_validation_request_xml,
161
166
  debug: debug
@@ -174,6 +179,7 @@ module FriendlyShipping
174
179
  url = base_url + RESOURCES[:address_validation]
175
180
  request = FriendlyShipping::Request.new(
176
181
  url: url,
182
+ http_method: "POST",
177
183
  body: access_request_xml + address_validation_request_xml,
178
184
  readable_body: address_validation_request_xml,
179
185
  debug: debug
@@ -193,6 +199,7 @@ module FriendlyShipping
193
199
  url = base_url + RESOURCES[:city_state_lookup]
194
200
  request = FriendlyShipping::Request.new(
195
201
  url: url,
202
+ http_method: "POST",
196
203
  body: access_request_xml + city_state_lookup_request_xml,
197
204
  readable_body: city_state_lookup_request_xml,
198
205
  debug: debug
@@ -208,6 +215,7 @@ module FriendlyShipping
208
215
  void_request_xml = SerializeVoidShipmentRequest.call(label: label)
209
216
  request = FriendlyShipping::Request.new(
210
217
  url: url,
218
+ http_method: "POST",
211
219
  body: access_request_xml + void_request_xml,
212
220
  readable_body: void_request_xml,
213
221
  debug: debug
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'friendly_shipping/api_error'
4
+
5
+ module FriendlyShipping
6
+ module Services
7
+ class UpsFreight
8
+ class ApiError < FriendlyShipping::ApiError
9
+ # @param [RestClient::Exception] cause
10
+ def initialize(cause)
11
+ super cause, parse_message(cause)
12
+ end
13
+
14
+ private
15
+
16
+ # @param [RestClient::Exception] cause
17
+ def parse_message(cause)
18
+ parsed_json = JSON.parse(cause.response.body)
19
+
20
+ if parsed_json['httpCode'].present?
21
+ status = [parsed_json['httpCode'], parsed_json['httpMessage']].compact.join(" ")
22
+ desc = parsed_json['moreInformation']
23
+ [status, desc].compact.join(": ")
24
+ else
25
+ errors = parsed_json.dig('response', 'errors') || []
26
+ errors.map do |err|
27
+ status = err['code']
28
+ desc = err['message']
29
+ [status, desc].compact.join(": ").presence || "UPS Freight could not process the request."
30
+ end.join("\n")
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end