friendly_shipping 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
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