friendly_shipping 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c51440567e32656dd9c373764b43ed8915c7bed5
4
- data.tar.gz: ac745c9dd9e1c087ec387c429f761633c15783bc
3
+ metadata.gz: 2ecad34500f6b87c3aa381a2fe93a9f96d5203f1
4
+ data.tar.gz: 5c6483ad49ba4ff5cd705dbeb9d44efa3ec865e6
5
5
  SHA512:
6
- metadata.gz: d8b986a76ea28d2ba06f7d6ad3bfea61748e0d291d95f59b9d1ea826cd6bc0d94b16b5cefe7fd2d9d03f0cbf32762b6a7fc9145d4d06054b3d0bceff2125b723
7
- data.tar.gz: faee889795cd2d7588771e9938e1660eec64e3c72e65994dac6e348b4a5aff1eef514d0f1d5ae44a39f0bf2ff47057d1c6a7f68efde8a20ce431b9d50739540f
6
+ metadata.gz: aeb1401d8bdbda932c92e5b79a9fb7d231e67eb3635210c64c53e90805fffd7a7e98056968289b44197874350543a658327b62d4e69a1d69974d385a6352cf6b
7
+ data.tar.gz: 52d31ae21c4878b3e95770f59bad92a7f51619b6fb10ebe03d3295a666b9497f79c2ceb1450d366ce3e2e2e45d03cb7c43e122928e7c34f79d702d7d7003e073
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_runtime_dependency "rest-client", "~> 2.0"
26
26
  spec.add_runtime_dependency "dry-monads", "~> 1.0"
27
27
  spec.add_runtime_dependency "data_uri", "~> 0.0.3"
28
+ spec.add_runtime_dependency "money", ">= 6.0.0"
28
29
  spec.required_ruby_version = '>= 2.4'
29
30
 
30
31
  spec.add_development_dependency "bundler"
@@ -8,7 +8,9 @@ module FriendlyShipping
8
8
  :data,
9
9
  :label_format,
10
10
  :shipment_cost,
11
- :label_data
11
+ :label_data,
12
+ :original_request,
13
+ :original_response
12
14
 
13
15
  def initialize(
14
16
  id: nil,
@@ -19,7 +21,9 @@ module FriendlyShipping
19
21
  label_format: nil,
20
22
  label_data: nil,
21
23
  shipment_cost: nil,
22
- data: {}
24
+ data: {},
25
+ original_request: nil,
26
+ original_response: nil
23
27
  )
24
28
  @id = id
25
29
  @shipment_id = shipment_id
@@ -30,6 +34,8 @@ module FriendlyShipping
30
34
  @shipment_cost = shipment_cost
31
35
  @label_data = label_data
32
36
  @data = data
37
+ @original_request = original_request
38
+ @original_response = original_response
33
39
  end
34
40
  end
35
41
  end
@@ -0,0 +1,41 @@
1
+ module FriendlyShipping
2
+ class Rate
3
+ class NoAmountsGiven < StandardError; end
4
+ attr_reader :shipping_method,
5
+ :amounts,
6
+ :remote_service_id,
7
+ :delivery_date,
8
+ :warnings,
9
+ :errors,
10
+ :data,
11
+ :original_request,
12
+ :original_response
13
+
14
+ def initialize(
15
+ shipping_method:,
16
+ amounts:,
17
+ remote_service_id: nil,
18
+ delivery_date: nil,
19
+ warnings: [],
20
+ errors: [],
21
+ data: {},
22
+ original_request: nil,
23
+ original_response: nil
24
+ )
25
+ @remote_service_id = remote_service_id
26
+ @shipping_method = shipping_method
27
+ @amounts = amounts
28
+ @delivery_date = delivery_date
29
+ @warnings = warnings
30
+ @errors = errors
31
+ @data = data
32
+ @original_request = original_request
33
+ @original_response = original_response
34
+ end
35
+
36
+ def total_amount
37
+ raise NoAmountsGiven if amounts.empty?
38
+ amounts.map { |_name, amount| amount }.sum
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ module FriendlyShipping
2
+ class Request
3
+ attr_reader :url, :body, :headers
4
+
5
+ def initialize(url:, body: nil, headers: {})
6
+ @url = url
7
+ @body = body
8
+ @headers = headers
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module FriendlyShipping
2
+ class Response
3
+ attr_reader :status, :body, :headers
4
+
5
+ def initialize(status:, body:, headers:)
6
+ @status = status
7
+ @body = body
8
+ @headers = headers
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,66 @@
1
+ require 'dry/monads/result'
2
+ require 'friendly_shipping/bad_request'
3
+ require 'rest-client'
4
+
5
+ module FriendlyShipping
6
+ module Services
7
+ class ShipEngine
8
+ class Client
9
+ extend Dry::Monads::Result::Mixin
10
+ class <<self
11
+ def get(request)
12
+ http_response = ::RestClient.get(
13
+ request.url, request.headers
14
+ )
15
+
16
+ Success(convert_to_friendly_response(http_response))
17
+ rescue ::RestClient::Exception => error
18
+ Failure(error)
19
+ end
20
+
21
+ def post(friendly_shipping_request)
22
+ http_response = ::RestClient.post(
23
+ friendly_shipping_request.url,
24
+ friendly_shipping_request.body,
25
+ friendly_shipping_request.headers
26
+ )
27
+
28
+ Success(convert_to_friendly_response(http_response))
29
+ rescue ::RestClient::Exception => error
30
+ if error.http_code == 400
31
+ Failure(BadRequest.new(error))
32
+ else
33
+ Failure(error)
34
+ end
35
+ end
36
+
37
+ def put(request)
38
+ http_response = ::RestClient.put(
39
+ request.url,
40
+ request.body,
41
+ request.headers
42
+ )
43
+
44
+ Success(convert_to_friendly_response(http_response))
45
+ rescue ::RestClient::Exception => error
46
+ if error.http_code == 400
47
+ Failure(BadRequest.new(error))
48
+ else
49
+ Failure(error)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def convert_to_friendly_response(http_response)
56
+ FriendlyShipping::Response.new(
57
+ status: http_response.code,
58
+ body: http_response.body,
59
+ headers: http_response.headers
60
+ )
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -11,29 +11,34 @@ module FriendlyShipping
11
11
  def call
12
12
  parsed_json = JSON.parse(@response.body)
13
13
  parsed_json['carriers'].map do |carrier_data|
14
- FriendlyShipping::Carrier.new(
14
+ carrier = FriendlyShipping::Carrier.new(
15
15
  id: carrier_data['carrier_id'],
16
16
  name: carrier_data['friendly_name'],
17
17
  code: carrier_data['carrier_code'],
18
- shipping_methods: parse_shipping_methods(carrier_data['services']),
19
18
  balance: carrier_data['balance'],
20
19
  data: carrier_data
21
20
  )
21
+
22
+ carrier_data['services'].each do |method_hash|
23
+ shipping_method = parse_shipping_method(carrier, method_hash)
24
+ carrier.shipping_methods << shipping_method
25
+ end
26
+
27
+ carrier
22
28
  end
23
29
  end
24
30
 
25
31
  private
26
32
 
27
- def parse_shipping_methods(shipping_methods_data)
28
- shipping_methods_data.map do |shipping_method_data|
33
+ def parse_shipping_method(carrier, shipping_method_data)
29
34
  FriendlyShipping::ShippingMethod.new(
35
+ carrier: carrier,
30
36
  name: shipping_method_data["name"],
31
37
  service_code: shipping_method_data["service_code"],
32
38
  domestic: shipping_method_data["domestic"],
33
39
  international: shipping_method_data["international"],
34
40
  multi_package: shipping_method_data["is_multi_package_supported"]
35
41
  )
36
- end
37
42
  end
38
43
  end
39
44
  end
@@ -5,12 +5,8 @@ module FriendlyShipping
5
5
  module Services
6
6
  class ShipEngine
7
7
  class ParseLabelResponse
8
- def initialize(response:)
9
- @response = response
10
- end
11
-
12
- def call
13
- parsed_json = JSON.parse(@response.body)
8
+ def self.call(request:, response:)
9
+ parsed_json = JSON.parse(response.body)
14
10
  label_uri_string = parsed_json['label_download']['href']
15
11
  label_data = nil
16
12
  label_url = nil
@@ -29,7 +25,9 @@ module FriendlyShipping
29
25
  label_data: label_data,
30
26
  label_format: parsed_json['label_format'].to_sym,
31
27
  shipment_cost: parsed_json['shipment_cost']['amount'],
32
- data: parsed_json
28
+ data: parsed_json,
29
+ original_request: request,
30
+ original_response: response
33
31
  )
34
32
  ]
35
33
  end
@@ -0,0 +1,46 @@
1
+ require 'json'
2
+ require 'money'
3
+
4
+ module FriendlyShipping
5
+ module Services
6
+ class ShipEngine
7
+ class ParseRateEstimateResponse
8
+ class << self
9
+ def call(response:, carriers:, request:)
10
+ parsed_json = JSON.parse(response.body)
11
+ parsed_json.map do |rate|
12
+ carrier = carriers.detect { |c| c.id == rate['carrier_id'] }
13
+ next unless carrier
14
+ shipping_method = carrier.shipping_methods.detect { |sm| sm.service_code == rate['service_code'] }
15
+ next unless shipping_method
16
+ amounts = get_amounts(rate)
17
+ FriendlyShipping::Rate.new(
18
+ shipping_method: shipping_method,
19
+ amounts: amounts,
20
+ remote_service_id: rate['rate_id'],
21
+ delivery_date: Time.parse(rate['estimated_delivery_date']),
22
+ warnings: rate['warning_messages'],
23
+ errors: rate['error_messages'],
24
+ original_request: request,
25
+ original_response: response
26
+ )
27
+ end.compact
28
+ end
29
+
30
+ private
31
+
32
+ def get_amounts(rate_hash)
33
+ [:shipping, :other, :insurance, :confirmation].map do |name|
34
+ currency = Money::Currency.new(rate_hash["#{name}_amount"]["currency"])
35
+ amount = rate_hash["#{name}_amount"]["amount"] * currency.subunit_to_unit
36
+ [
37
+ name,
38
+ Money.new(amount, currency)
39
+ ]
40
+ end.to_h
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -44,12 +44,7 @@ module FriendlyShipping
44
44
 
45
45
  def serialize_packages(packages)
46
46
  packages.map do |package|
47
- package_hash = {
48
- weight: {
49
- value: package.weight.convert_to(:ounce).value.to_f,
50
- unit: "ounce"
51
- }
52
- }
47
+ package_hash = serialize_weight(package.weight)
53
48
  if package.container.properties[:usps_label_messages]
54
49
  package_hash.merge!(label_messages: package.container.properties[:usps_label_messages])
55
50
  end
@@ -69,6 +64,17 @@ module FriendlyShipping
69
64
  package_hash
70
65
  end
71
66
  end
67
+
68
+ def serialize_weight(weight)
69
+ ounces = weight.convert_to(:ounce).value.to_f
70
+ {
71
+ weight: {
72
+ # Max weight for USPS First Class is 15.9 oz, not 16 oz
73
+ value: ounces.between?(15.9, 16) ? 15.9 : ounces,
74
+ unit: "ounce"
75
+ }
76
+ }
77
+ end
72
78
  end
73
79
  end
74
80
  end
@@ -0,0 +1,25 @@
1
+ module FriendlyShipping
2
+ module Services
3
+ class ShipEngine
4
+ class SerializeRateEstimateRequest
5
+ def self.call(shipment:, carriers:)
6
+ {
7
+ carrier_ids: carriers.map(&:id),
8
+ from_country_code: shipment.origin.country.alpha_2_code,
9
+ from_postal_code: shipment.origin.zip,
10
+ to_country_code: shipment.destination.country.alpha_2_code,
11
+ to_postal_code: shipment.destination.zip,
12
+ to_city_locality: shipment.destination.city,
13
+ to_state_province: shipment.destination.region.code,
14
+ weight: {
15
+ value: shipment.packages.map { |p| p.weight.convert_to(:pound).value.to_f }.sum,
16
+ unit: 'pound'
17
+ },
18
+ confirmation: 'none',
19
+ address_residential_indicator: shipment.destination.residential? ? "yes" : "no"
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,10 +1,12 @@
1
1
 
2
2
  require 'dry/monads/result'
3
- require 'friendly_shipping/rest_client'
3
+ require 'friendly_shipping/services/ship_engine/client'
4
4
  require 'friendly_shipping/services/ship_engine/parse_carrier_response'
5
5
  require 'friendly_shipping/services/ship_engine/serialize_label_shipment'
6
+ require 'friendly_shipping/services/ship_engine/serialize_rate_estimate_request'
6
7
  require 'friendly_shipping/services/ship_engine/parse_label_response'
7
8
  require 'friendly_shipping/services/ship_engine/parse_void_response'
9
+ require 'friendly_shipping/services/ship_engine/parse_rate_estimate_response'
8
10
 
9
11
  module FriendlyShipping
10
12
  module Services
@@ -15,36 +17,58 @@ module FriendlyShipping
15
17
  labels: "labels"
16
18
  }
17
19
 
18
- def initialize(token:, test: true)
20
+ def initialize(token:, test: true, client: self.class::Client)
19
21
  @token = token
20
22
  @test = test
23
+ @client = client
21
24
  end
22
25
 
23
26
  def carriers
24
- path = API_BASE + API_PATHS[:carriers]
25
- FriendlyShipping::RestClient.get(path, request_headers).fmap do |response|
27
+ request = FriendlyShipping::Request.new(
28
+ url: API_BASE + API_PATHS[:carriers],
29
+ headers: request_headers
30
+ )
31
+ client.get(request).fmap do |response|
26
32
  ParseCarrierResponse.new(response: response).call
27
33
  end
28
34
  end
29
35
 
36
+ def rate_estimates(shipment, carriers)
37
+ request = FriendlyShipping::Request.new(
38
+ url: API_BASE + 'rates/estimate',
39
+ body: SerializeRateEstimateRequest.(shipment: shipment, carriers: carriers).to_json,
40
+ headers: request_headers
41
+ )
42
+ client.post(request).fmap do |response|
43
+ ParseRateEstimateResponse.(response: response, request: request, carriers: carriers)
44
+ end
45
+ end
46
+
30
47
  def labels(shipment)
31
- payload = SerializeLabelShipment.new(shipment: shipment).call.merge(test_label: test).to_json
32
- path = API_BASE + API_PATHS[:labels]
33
- FriendlyShipping::RestClient.post(path, payload, request_headers).fmap do |response|
34
- ParseLabelResponse.new(response: response).call
48
+ request = FriendlyShipping::Request.new(
49
+ url: API_BASE + API_PATHS[:labels],
50
+ body: SerializeLabelShipment.new(shipment: shipment).call.merge(test_label: test).to_json,
51
+ headers: request_headers
52
+ )
53
+ client.post(request).fmap do |response|
54
+ ParseLabelResponse.(request: request, response: response)
35
55
  end
36
56
  end
37
57
 
38
58
  def void(label)
39
- path = "#{API_BASE}labels/#{label.id}/void"
40
- FriendlyShipping::RestClient.put(path, '', request_headers).bind do |response|
59
+ request = FriendlyShipping::Request.new(
60
+ url: "#{API_BASE}labels/#{label.id}/void",
61
+ body: '',
62
+ headers: request_headers
63
+ )
64
+ client.put(request).bind do |response|
41
65
  ParseVoidResponse.new(response: response).call
42
66
  end
43
67
  end
44
68
 
45
69
  private
46
70
 
47
- attr_reader :token, :test
71
+ attr_reader :token, :test, :client
48
72
 
49
73
  def request_headers
50
74
  {
@@ -1,13 +1,14 @@
1
1
  module FriendlyShipping
2
2
  class ShippingMethod
3
- attr_reader :name, :service_code
3
+ attr_reader :name, :service_code, :carrier
4
4
 
5
- def initialize(name: nil, service_code: nil, domestic: nil, international: nil, multi_package: nil)
5
+ def initialize(name: nil, service_code: nil, domestic: nil, international: nil, multi_package: nil, carrier: nil)
6
6
  @name = name
7
7
  @service_code = service_code
8
8
  @domestic = domestic
9
9
  @international = international
10
10
  @multi_package = multi_package
11
+ @carrier = carrier
11
12
  end
12
13
 
13
14
  def domestic?
@@ -1,3 +1,3 @@
1
1
  module FriendlyShipping
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
@@ -1,10 +1,13 @@
1
1
  require "physical"
2
2
 
3
3
  require "friendly_shipping/version"
4
+ require "friendly_shipping/request"
5
+ require "friendly_shipping/response"
4
6
  require "friendly_shipping/carrier"
5
7
  require "friendly_shipping/services/ship_engine"
6
8
  require "friendly_shipping/shipping_method"
7
9
  require "friendly_shipping/label"
10
+ require "friendly_shipping/rate"
8
11
 
9
12
  module FriendlyShipping
10
13
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friendly_shipping
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-24 00:00:00.000000000 Z
11
+ date: 2019-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: physical
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.0.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: money
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 6.0.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 6.0.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -131,12 +145,17 @@ files:
131
145
  - lib/friendly_shipping/bad_request.rb
132
146
  - lib/friendly_shipping/carrier.rb
133
147
  - lib/friendly_shipping/label.rb
134
- - lib/friendly_shipping/rest_client.rb
148
+ - lib/friendly_shipping/rate.rb
149
+ - lib/friendly_shipping/request.rb
150
+ - lib/friendly_shipping/response.rb
135
151
  - lib/friendly_shipping/services/ship_engine.rb
152
+ - lib/friendly_shipping/services/ship_engine/client.rb
136
153
  - lib/friendly_shipping/services/ship_engine/parse_carrier_response.rb
137
154
  - lib/friendly_shipping/services/ship_engine/parse_label_response.rb
155
+ - lib/friendly_shipping/services/ship_engine/parse_rate_estimate_response.rb
138
156
  - lib/friendly_shipping/services/ship_engine/parse_void_response.rb
139
157
  - lib/friendly_shipping/services/ship_engine/serialize_label_shipment.rb
158
+ - lib/friendly_shipping/services/ship_engine/serialize_rate_estimate_request.rb
140
159
  - lib/friendly_shipping/shipping_method.rb
141
160
  - lib/friendly_shipping/version.rb
142
161
  homepage: https://github.com/friendly_cart/friendly_shipping
@@ -1,53 +0,0 @@
1
- require 'dry/monads/result'
2
- require 'friendly_shipping/bad_request'
3
- require 'rest-client'
4
-
5
- module FriendlyShipping
6
- class RestClient
7
- extend Dry::Monads::Result::Mixin
8
- class <<self
9
- def get(path, headers)
10
- Success(
11
- ::RestClient.get(
12
- path,
13
- headers
14
- )
15
- )
16
- rescue ::RestClient::Exception => error
17
- Failure(error)
18
- end
19
-
20
- def post(path, payload, headers)
21
- Success(
22
- ::RestClient.post(
23
- path,
24
- payload,
25
- headers
26
- )
27
- )
28
- rescue ::RestClient::Exception => error
29
- if error.http_code == 400
30
- Failure(BadRequest.new(error))
31
- else
32
- Failure(error)
33
- end
34
- end
35
-
36
- def put(path, payload, headers)
37
- Success(
38
- ::RestClient.put(
39
- path,
40
- payload,
41
- headers
42
- )
43
- )
44
- rescue ::RestClient::Exception => error
45
- if error.http_code == 400
46
- Failure(BadRequest.new(error))
47
- else
48
- Failure(error)
49
- end
50
- end
51
- end
52
- end
53
- end