easypost 4.8.1 → 5.1.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.
- checksums.yaml +4 -4
- data/.gitattributes +5 -0
- data/.github/workflows/ci.yml +34 -5
- data/.gitignore +27 -20
- data/CHANGELOG.md +56 -0
- data/Makefile +30 -11
- data/README.md +111 -45
- data/UPGRADE_GUIDE.md +119 -0
- data/VERSION +1 -1
- data/easypost.gemspec +14 -10
- data/lib/easypost/client.rb +178 -0
- data/lib/easypost/connection.rb +2 -4
- data/lib/easypost/constants.rb +15 -0
- data/lib/easypost/errors/api/api_error.rb +108 -0
- data/lib/easypost/errors/api/bad_request_error.rb +6 -0
- data/lib/easypost/errors/api/connection_error.rb +6 -0
- data/lib/easypost/errors/api/external_api_error.rb +18 -0
- data/lib/easypost/errors/api/forbidden_error.rb +6 -0
- data/lib/easypost/errors/api/gateway_timeout_error.rb +6 -0
- data/lib/easypost/errors/api/internal_server_error.rb +6 -0
- data/lib/easypost/errors/api/invalid_request_error.rb +6 -0
- data/lib/easypost/errors/api/method_not_allowed_error.rb +6 -0
- data/lib/easypost/errors/api/not_found_error.rb +6 -0
- data/lib/easypost/errors/api/payment_error.rb +6 -0
- data/lib/easypost/errors/api/proxy_error.rb +6 -0
- data/lib/easypost/errors/api/rate_limit_error.rb +6 -0
- data/lib/easypost/errors/api/redirect_error.rb +6 -0
- data/lib/easypost/errors/api/retry_error.rb +6 -0
- data/lib/easypost/errors/api/service_unavailable_error.rb +6 -0
- data/lib/easypost/errors/api/ssl_error.rb +6 -0
- data/lib/easypost/errors/api/timeout_error.rb +6 -0
- data/lib/easypost/errors/api/unauthorized_error.rb +6 -0
- data/lib/easypost/errors/api/unknown_api_error.rb +6 -0
- data/lib/easypost/errors/easy_post_error.rb +7 -0
- data/lib/easypost/errors/end_of_pagination_error.rb +7 -0
- data/lib/easypost/errors/filtering_error.rb +4 -0
- data/lib/easypost/errors/invalid_object_error.rb +4 -0
- data/lib/easypost/errors/invalid_parameter_error.rb +11 -0
- data/lib/easypost/errors/missing_parameter_error.rb +9 -0
- data/lib/easypost/errors/signature_verification_error.rb +4 -0
- data/lib/easypost/errors.rb +32 -0
- data/lib/easypost/hooks/request_context.rb +16 -0
- data/lib/easypost/hooks/response_context.rb +23 -0
- data/lib/easypost/hooks.rb +34 -0
- data/lib/easypost/http_client.rb +117 -0
- data/lib/easypost/internal_utilities.rb +66 -0
- data/lib/easypost/models/address.rb +5 -0
- data/lib/easypost/models/api_key.rb +5 -0
- data/lib/easypost/models/base.rb +58 -0
- data/lib/easypost/models/batch.rb +5 -0
- data/lib/easypost/models/brand.rb +5 -0
- data/lib/easypost/{carbon_offset.rb → models/carbon_offset.rb} +1 -1
- data/lib/easypost/models/carrier_account.rb +5 -0
- data/lib/easypost/models/customs_info.rb +5 -0
- data/lib/easypost/models/customs_item.rb +5 -0
- data/lib/easypost/models/end_shipper.rb +5 -0
- data/lib/easypost/models/error.rb +21 -0
- data/lib/easypost/models/event.rb +5 -0
- data/lib/easypost/{insurance.rb → models/insurance.rb} +1 -1
- data/lib/easypost/models/order.rb +9 -0
- data/lib/easypost/models/parcel.rb +5 -0
- data/lib/easypost/models/payload.rb +5 -0
- data/lib/easypost/models/payment_method.rb +5 -0
- data/lib/easypost/models/pickup.rb +9 -0
- data/lib/easypost/{pickup_rate.rb → models/pickup_rate.rb} +1 -1
- data/lib/easypost/{postage_label.rb → models/postage_label.rb} +1 -1
- data/lib/easypost/models/rate.rb +5 -0
- data/lib/easypost/models/referral.rb +5 -0
- data/lib/easypost/{refund.rb → models/refund.rb} +1 -1
- data/lib/easypost/models/report.rb +5 -0
- data/lib/easypost/models/scan_form.rb +6 -0
- data/lib/easypost/models/shipment.rb +10 -0
- data/lib/easypost/{tax_identifier.rb → models/tax_identifier.rb} +1 -1
- data/lib/easypost/models/tracker.rb +5 -0
- data/lib/easypost/models/user.rb +5 -0
- data/lib/easypost/models/webhook.rb +6 -0
- data/lib/easypost/models.rb +35 -0
- data/lib/easypost/services/address.rb +50 -0
- data/lib/easypost/services/api_key.rb +8 -0
- data/lib/easypost/services/base.rb +27 -0
- data/lib/easypost/services/batch.rb +53 -0
- data/lib/easypost/services/beta_rate.rb +12 -0
- data/lib/easypost/services/beta_referral_customer.rb +40 -0
- data/lib/easypost/services/billing.rb +75 -0
- data/lib/easypost/services/carrier_account.rb +44 -0
- data/lib/easypost/services/carrier_metadata.rb +22 -0
- data/lib/easypost/services/customs_info.rb +17 -0
- data/lib/easypost/services/customs_item.rb +15 -0
- data/lib/easypost/services/end_shipper.rb +31 -0
- data/lib/easypost/services/event.rb +32 -0
- data/lib/easypost/services/insurance.rb +26 -0
- data/lib/easypost/services/order.rb +30 -0
- data/lib/easypost/services/parcel.rb +16 -0
- data/lib/easypost/services/pickup.rb +40 -0
- data/lib/easypost/services/rate.rb +8 -0
- data/lib/easypost/services/referral_customer.rb +103 -0
- data/lib/easypost/services/refund.rb +26 -0
- data/lib/easypost/services/report.rb +42 -0
- data/lib/easypost/services/scan_form.rb +25 -0
- data/lib/easypost/services/shipment.rb +106 -0
- data/lib/easypost/services/tracker.rb +38 -0
- data/lib/easypost/services/user.rb +66 -0
- data/lib/easypost/services/webhook.rb +34 -0
- data/lib/easypost/services.rb +32 -0
- data/lib/easypost/util.rb +116 -161
- data/lib/easypost/utilities/constants.rb +5 -0
- data/lib/easypost/utilities/json.rb +23 -0
- data/lib/easypost/utilities/static_mapper.rb +73 -0
- data/lib/easypost/utilities/system.rb +36 -0
- data/lib/easypost.rb +14 -136
- metadata +177 -65
- data/.rubocop.yml +0 -11
- data/CODE_OF_CONDUCT.md +0 -16
- data/CONTRIBUTING.md +0 -47
- data/SECURITY.md +0 -7
- data/SUPPORT.md +0 -3
- data/easycop.yml +0 -180
- data/lib/easypost/address.rb +0 -40
- data/lib/easypost/api_key.rb +0 -5
- data/lib/easypost/batch.rb +0 -50
- data/lib/easypost/beta/end_shipper.rb +0 -44
- data/lib/easypost/beta/referral.rb +0 -110
- data/lib/easypost/beta.rb +0 -7
- data/lib/easypost/billing.rb +0 -72
- data/lib/easypost/brand.rb +0 -13
- data/lib/easypost/carrier_account.rb +0 -9
- data/lib/easypost/carrier_type.rb +0 -5
- data/lib/easypost/customs_info.rb +0 -9
- data/lib/easypost/customs_item.rb +0 -9
- data/lib/easypost/end_shipper.rb +0 -24
- data/lib/easypost/error.rb +0 -32
- data/lib/easypost/event.rb +0 -11
- data/lib/easypost/object.rb +0 -171
- data/lib/easypost/order.rb +0 -37
- data/lib/easypost/parcel.rb +0 -9
- data/lib/easypost/payment_method.rb +0 -11
- data/lib/easypost/pickup.rb +0 -37
- data/lib/easypost/rate.rb +0 -9
- data/lib/easypost/referral.rb +0 -102
- data/lib/easypost/report.rb +0 -23
- data/lib/easypost/resource.rb +0 -106
- data/lib/easypost/scan_form.rb +0 -11
- data/lib/easypost/shipment.rb +0 -155
- data/lib/easypost/tracker.rb +0 -12
- data/lib/easypost/user.rb +0 -71
- data/lib/easypost/webhook.rb +0 -57
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
class EasyPost::Services::Shipment < EasyPost::Services::Service
|
|
6
|
+
MODEL_CLASS = EasyPost::Models::Shipment
|
|
7
|
+
|
|
8
|
+
# Create a Shipment.
|
|
9
|
+
def create(params = {}, with_carbon_offset = false)
|
|
10
|
+
wrapped_params = {
|
|
11
|
+
shipment: params,
|
|
12
|
+
carbon_offset: with_carbon_offset,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@client.make_request(:post, 'shipments', MODEL_CLASS, wrapped_params)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Retrieve a Shipment.
|
|
19
|
+
def retrieve(id)
|
|
20
|
+
@client.make_request(:get, "shipments/#{id}", MODEL_CLASS)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Retrieve a list of Shipments
|
|
24
|
+
def all(params = {})
|
|
25
|
+
response = @client.make_request(:get, 'shipments', MODEL_CLASS, params)
|
|
26
|
+
response.define_singleton_method(:purchased) { params[:purchased] }
|
|
27
|
+
response.define_singleton_method(:include_children) { params[:include_children] }
|
|
28
|
+
response
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get the next page of shipments.
|
|
32
|
+
def get_next_page(collection, page_size = nil)
|
|
33
|
+
get_next_page_helper(collection, collection.shipments, 'shipments', MODEL_CLASS, page_size)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Regenerate the rates of a Shipment.
|
|
37
|
+
def regenerate_rates(id, with_carbon_offset = false)
|
|
38
|
+
params = { carbon_offset: with_carbon_offset }
|
|
39
|
+
|
|
40
|
+
@client.make_request(:post, "shipments/#{id}/rerate", MODEL_CLASS, params)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Get the SmartRates of a Shipment.
|
|
44
|
+
def get_smart_rates(id)
|
|
45
|
+
@client.make_request(:get, "shipments/#{id}/smartrate", MODEL_CLASS).result || []
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Buy a Shipment.
|
|
49
|
+
def buy(id, params = {}, with_carbon_offset = false, end_shipper_id = nil)
|
|
50
|
+
if params.instance_of?(EasyPost::Models::Rate)
|
|
51
|
+
params = { rate: params.clone }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
params[:carbon_offset] = params[:with_carbon_offset] || with_carbon_offset
|
|
55
|
+
params.delete(:with_carbon_offset)
|
|
56
|
+
|
|
57
|
+
params[:end_shipper_id] = end_shipper_id if end_shipper_id
|
|
58
|
+
|
|
59
|
+
@client.make_request(:post, "shipments/#{id}/buy", MODEL_CLASS, params)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Insure a Shipment.
|
|
63
|
+
def insure(id, params = {})
|
|
64
|
+
params = { amount: params } if params.is_a?(Integer) || params.is_a?(Float)
|
|
65
|
+
|
|
66
|
+
@client.make_request(:post, "shipments/#{id}/insure", MODEL_CLASS, params)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Refund a Shipment.
|
|
70
|
+
def refund(id, params = {})
|
|
71
|
+
@client.make_request(:post, "shipments/#{id}/refund", MODEL_CLASS, params)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Convert the label format of a Shipment.
|
|
75
|
+
def label(id, params = {})
|
|
76
|
+
params = { file_format: params } if params.is_a?(String)
|
|
77
|
+
|
|
78
|
+
@client.make_request(:get, "shipments/#{id}/label", MODEL_CLASS, params)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Get the lowest SmartRate of a Shipment.
|
|
82
|
+
def lowest_smart_rate(id, delivery_days, delivery_accuracy)
|
|
83
|
+
smart_rates = get_smart_rates(id)
|
|
84
|
+
EasyPost::Util.get_lowest_smart_rate(smart_rates, delivery_days, delivery_accuracy)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Generate a form for a Shipment.
|
|
88
|
+
def generate_form(id, form_type, form_options = {})
|
|
89
|
+
params = {}
|
|
90
|
+
params[:type] = form_type
|
|
91
|
+
merged_params = params.merge(form_options)
|
|
92
|
+
wrapped_params = {
|
|
93
|
+
form: merged_params,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@client.make_request(:post, "shipments/#{id}/forms", MODEL_CLASS, wrapped_params)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Retrieves the estimated delivery date of each Rate via SmartRate.
|
|
100
|
+
def retrieve_estimated_delivery_date(id, planned_ship_date)
|
|
101
|
+
url = "shipments/#{id}/smartrate/delivery_date"
|
|
102
|
+
params = { planned_ship_date: planned_ship_date }
|
|
103
|
+
|
|
104
|
+
@client.make_request(:get, url, MODEL_CLASS, params).rates
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class EasyPost::Services::Tracker < EasyPost::Services::Service
|
|
4
|
+
MODEL_CLASS = EasyPost::Models::Tracker
|
|
5
|
+
|
|
6
|
+
# Create a Tracker
|
|
7
|
+
def create(params = {})
|
|
8
|
+
wrapped_params = { tracker: params }
|
|
9
|
+
|
|
10
|
+
@client.make_request(:post, 'trackers', MODEL_CLASS, wrapped_params)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Retrieve a Tracker
|
|
14
|
+
def retrieve(id)
|
|
15
|
+
@client.make_request(:get, "trackers/#{id}", MODEL_CLASS)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Retrieve a list of Trackers
|
|
19
|
+
def all(params)
|
|
20
|
+
response = @client.make_request(:get, 'trackers', MODEL_CLASS, params)
|
|
21
|
+
response.define_singleton_method(:tracking_code) { params[:tracking_code] }
|
|
22
|
+
response.define_singleton_method(:carrier) { params[:carrier] }
|
|
23
|
+
response
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Create multiple Tracker objects in bulk.
|
|
27
|
+
def create_list(params = {})
|
|
28
|
+
wrapped_params = { 'trackers' => params }
|
|
29
|
+
|
|
30
|
+
@client.make_request(:post, 'trackers/create_list', MODEL_CLASS, wrapped_params)
|
|
31
|
+
true # This endpoint does not return a response so we return true here instead
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Get the next page of trackers.
|
|
35
|
+
def get_next_page(collection, page_size = nil)
|
|
36
|
+
get_next_page_helper(collection, collection.trackers, 'trackers', MODEL_CLASS, page_size)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class EasyPost::Services::User < EasyPost::Services::Service
|
|
4
|
+
MODEL_CLASS = EasyPost::Models::User
|
|
5
|
+
|
|
6
|
+
# Create a child User.
|
|
7
|
+
def create(params = {})
|
|
8
|
+
@client.make_request(:post, 'users', MODEL_CLASS, params)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Retrieve a user
|
|
12
|
+
def retrieve(id)
|
|
13
|
+
@client.make_request(:get, "users/#{id}", MODEL_CLASS)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Retrieve the authenticated User.
|
|
17
|
+
def retrieve_me
|
|
18
|
+
@client.make_request(:get, 'users', MODEL_CLASS)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Update a User
|
|
22
|
+
def update(id, params = {})
|
|
23
|
+
@client.make_request(:put, "users/#{id}", MODEL_CLASS, params)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Delete a User
|
|
27
|
+
def delete(id)
|
|
28
|
+
@client.make_request(:delete, "users/#{id}")
|
|
29
|
+
|
|
30
|
+
# Return true if succeeds, an error will be thrown if it fails
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Retrieve a list of all ApiKey objects.
|
|
35
|
+
def all_api_keys
|
|
36
|
+
@client.make_request(:get, 'api_keys', EasyPost::Models::ApiKey)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Retrieve a list of ApiKey objects (works for the authenticated user or a child user).
|
|
40
|
+
def api_keys(id)
|
|
41
|
+
api_keys = all_api_keys
|
|
42
|
+
|
|
43
|
+
if api_keys.id == id
|
|
44
|
+
# This function was called on the authenticated user
|
|
45
|
+
my_api_keys = api_keys.keys
|
|
46
|
+
else
|
|
47
|
+
# This function was called on a child user (authenticated as parent, only return this child user's details).
|
|
48
|
+
my_api_keys = []
|
|
49
|
+
api_keys.children.each do |child|
|
|
50
|
+
if child.id == id
|
|
51
|
+
my_api_keys = child.keys
|
|
52
|
+
break
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
my_api_keys
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Update the Brand of a User.
|
|
61
|
+
def update_brand(id, params = {})
|
|
62
|
+
wrapped_params = { brand: params }
|
|
63
|
+
|
|
64
|
+
@client.make_request(:get, "users/#{id}/brand", EasyPost::Models::Brand, wrapped_params)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class EasyPost::Services::Webhook < EasyPost::Services::Service
|
|
4
|
+
MODEL_CLASS = EasyPost::Models::Webhook
|
|
5
|
+
|
|
6
|
+
# Create a Webhook.
|
|
7
|
+
def create(params = {})
|
|
8
|
+
wrapped_params = { webhook: params }
|
|
9
|
+
@client.make_request(:post, 'webhooks', MODEL_CLASS, wrapped_params)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Retrieve a Webhook
|
|
13
|
+
def retrieve(id)
|
|
14
|
+
@client.make_request(:get, "webhooks/#{id}", MODEL_CLASS)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Retrieve a list of Webhooks
|
|
18
|
+
def all(params = {})
|
|
19
|
+
@client.make_request(:get, 'webhooks', MODEL_CLASS, params)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Update a Webhook.
|
|
23
|
+
def update(id, params = {})
|
|
24
|
+
@client.make_request(:patch, "webhooks/#{id}", MODEL_CLASS, params)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Delete a Webhook.
|
|
28
|
+
def delete(id)
|
|
29
|
+
@client.make_request(:delete, "webhooks/#{id}")
|
|
30
|
+
|
|
31
|
+
# Return true if succeeds, an error will be thrown if it fails
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasyPost::Services
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
require_relative 'models'
|
|
7
|
+
require_relative 'services/base' # Must be imported first before the rest of child services
|
|
8
|
+
require_relative 'services/address'
|
|
9
|
+
require_relative 'services/api_key'
|
|
10
|
+
require_relative 'services/batch'
|
|
11
|
+
require_relative 'services/beta_rate'
|
|
12
|
+
require_relative 'services/beta_referral_customer'
|
|
13
|
+
require_relative 'services/billing'
|
|
14
|
+
require_relative 'services/carrier_account'
|
|
15
|
+
require_relative 'services/carrier_metadata'
|
|
16
|
+
require_relative 'services/customs_info'
|
|
17
|
+
require_relative 'services/customs_item'
|
|
18
|
+
require_relative 'services/end_shipper'
|
|
19
|
+
require_relative 'services/event'
|
|
20
|
+
require_relative 'services/insurance'
|
|
21
|
+
require_relative 'services/order'
|
|
22
|
+
require_relative 'services/parcel'
|
|
23
|
+
require_relative 'services/pickup'
|
|
24
|
+
require_relative 'services/rate'
|
|
25
|
+
require_relative 'services/referral_customer'
|
|
26
|
+
require_relative 'services/refund'
|
|
27
|
+
require_relative 'services/report'
|
|
28
|
+
require_relative 'services/scan_form'
|
|
29
|
+
require_relative 'services/shipment'
|
|
30
|
+
require_relative 'services/tracker'
|
|
31
|
+
require_relative 'services/user'
|
|
32
|
+
require_relative 'services/webhook'
|
data/lib/easypost/util.rb
CHANGED
|
@@ -1,198 +1,89 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
module EasyPost::Util
|
|
5
|
-
attr_accessor :os_name, :os_version, :os_arch
|
|
6
|
-
|
|
7
|
-
BY_PREFIX = {
|
|
8
|
-
'adr' => EasyPost::Address,
|
|
9
|
-
'batch' => EasyPost::Batch,
|
|
10
|
-
'brd' => EasyPost::Brand,
|
|
11
|
-
'ca' => EasyPost::CarrierAccount,
|
|
12
|
-
'cstinfo' => EasyPost::CustomsInfo,
|
|
13
|
-
'cstitem' => EasyPost::CustomsItem,
|
|
14
|
-
'es' => EasyPost::EndShipper,
|
|
15
|
-
'evt' => EasyPost::Event,
|
|
16
|
-
'hook' => EasyPost::Webhook,
|
|
17
|
-
'ins' => EasyPost::Insurance,
|
|
18
|
-
'order' => EasyPost::Order,
|
|
19
|
-
'pickup' => EasyPost::Pickup,
|
|
20
|
-
'pickuprate' => EasyPost::PickupRate,
|
|
21
|
-
'pl' => EasyPost::PostageLabel,
|
|
22
|
-
'plrep' => EasyPost::Report,
|
|
23
|
-
'prcl' => EasyPost::Parcel,
|
|
24
|
-
'rate' => EasyPost::Rate,
|
|
25
|
-
'refrep' => EasyPost::Report,
|
|
26
|
-
'rfnd' => EasyPost::Refund,
|
|
27
|
-
'sf' => EasyPost::ScanForm,
|
|
28
|
-
'shp' => EasyPost::Shipment,
|
|
29
|
-
'shpinvrep' => EasyPost::Report,
|
|
30
|
-
'shprep' => EasyPost::Report,
|
|
31
|
-
'trk' => EasyPost::Tracker,
|
|
32
|
-
'trkrep' => EasyPost::Report,
|
|
33
|
-
'user' => EasyPost::User,
|
|
34
|
-
}.freeze
|
|
35
|
-
|
|
36
|
-
BY_TYPE = {
|
|
37
|
-
'Address' => EasyPost::Address,
|
|
38
|
-
'Batch' => EasyPost::Batch,
|
|
39
|
-
'Brand' => EasyPost::Brand,
|
|
40
|
-
'CarbonOffset' => EasyPost::CarbonOffset,
|
|
41
|
-
'CarrierAccount' => EasyPost::CarrierAccount,
|
|
42
|
-
'CustomsInfo' => EasyPost::CustomsInfo,
|
|
43
|
-
'CustomsItem' => EasyPost::CustomsItem,
|
|
44
|
-
'EndShipper' => EasyPost::EndShipper,
|
|
45
|
-
'Event' => EasyPost::Event,
|
|
46
|
-
'Insurance' => EasyPost::Insurance,
|
|
47
|
-
'Order' => EasyPost::Order,
|
|
48
|
-
'Parcel' => EasyPost::Parcel,
|
|
49
|
-
'PaymentLogReport' => EasyPost::Report,
|
|
50
|
-
'Pickup' => EasyPost::Pickup,
|
|
51
|
-
'PickupRate' => EasyPost::PickupRate,
|
|
52
|
-
'PostageLabel' => EasyPost::PostageLabel,
|
|
53
|
-
'Rate' => EasyPost::Rate,
|
|
54
|
-
'Referral' => EasyPost::Beta::Referral,
|
|
55
|
-
'Refund' => EasyPost::Refund,
|
|
56
|
-
'RefundReport' => EasyPost::Report,
|
|
57
|
-
'Report' => EasyPost::Report,
|
|
58
|
-
'ScanForm' => EasyPost::ScanForm,
|
|
59
|
-
'Shipment' => EasyPost::Shipment,
|
|
60
|
-
'ShipmentInvoiceReport' => EasyPost::Report,
|
|
61
|
-
'ShipmentReport' => EasyPost::Report,
|
|
62
|
-
'TaxIdentifier' => EasyPost::TaxIdentifier,
|
|
63
|
-
'Tracker' => EasyPost::Tracker,
|
|
64
|
-
'TrackerReport' => EasyPost::Report,
|
|
65
|
-
'User' => EasyPost::User,
|
|
66
|
-
'Webhook' => EasyPost::Webhook,
|
|
67
|
-
}.freeze
|
|
68
|
-
|
|
69
|
-
def self.os_name
|
|
70
|
-
case RUBY_PLATFORM
|
|
71
|
-
when /linux/i
|
|
72
|
-
'Linux'
|
|
73
|
-
when /darwin/i
|
|
74
|
-
'Darwin'
|
|
75
|
-
when /cygwin|mswin|mingw|bccwin|wince|emx/i
|
|
76
|
-
'Windows'
|
|
77
|
-
else
|
|
78
|
-
'Unknown'
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def self.os_version
|
|
83
|
-
Gem::Platform.local.version
|
|
84
|
-
end
|
|
3
|
+
require 'easypost/constants'
|
|
85
4
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
5
|
+
# Client Library helper functions
|
|
6
|
+
module EasyPost::Util
|
|
7
|
+
# Gets the lowest rate of an EasyPost object such as a Shipment, Order, or Pickup.
|
|
8
|
+
# You can exclude by having `'!'` as the first element of your optional filter lists
|
|
9
|
+
def self.get_lowest_object_rate(easypost_object, carriers = [], services = [], rates_key = 'rates')
|
|
10
|
+
lowest_rate = nil
|
|
89
11
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
keys << key
|
|
98
|
-
result = form_encode_params(value, keys, result)
|
|
99
|
-
else
|
|
100
|
-
dict_key = build_dict_key(keys + [key])
|
|
101
|
-
result[dict_key] = value
|
|
12
|
+
carriers = EasyPost::InternalUtilities.normalize_string_list(carriers)
|
|
13
|
+
negative_carriers = []
|
|
14
|
+
carriers_copy = carriers.clone
|
|
15
|
+
carriers_copy.each do |carrier|
|
|
16
|
+
if carrier[0, 1] == '!'
|
|
17
|
+
negative_carriers << carrier[1..]
|
|
18
|
+
carriers.delete(carrier)
|
|
102
19
|
end
|
|
103
20
|
end
|
|
104
|
-
result
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Build a dict key from a list of keys.
|
|
108
|
-
# Example: [code, number] -> code[number]
|
|
109
|
-
def self.build_dict_key(keys)
|
|
110
|
-
result = keys[0].to_s
|
|
111
21
|
|
|
112
|
-
|
|
113
|
-
|
|
22
|
+
services = EasyPost::InternalUtilities.normalize_string_list(services)
|
|
23
|
+
negative_services = []
|
|
24
|
+
services_copy = services.clone
|
|
25
|
+
services_copy.each do |service|
|
|
26
|
+
if service[0, 1] == '!'
|
|
27
|
+
negative_services << service[1..]
|
|
28
|
+
services.delete(service)
|
|
29
|
+
end
|
|
114
30
|
end
|
|
115
31
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
when Hash
|
|
125
|
-
result = {}
|
|
126
|
-
obj.each { |k, v| result[k] = objects_to_ids(v) unless v.nil? }
|
|
127
|
-
result
|
|
128
|
-
when Array
|
|
129
|
-
obj.map { |v| objects_to_ids(v) }
|
|
130
|
-
else
|
|
131
|
-
obj
|
|
132
|
-
end
|
|
133
|
-
end
|
|
32
|
+
easypost_object.send(rates_key).each do |rate|
|
|
33
|
+
rate_carrier = rate.carrier.downcase
|
|
34
|
+
if carriers.size.positive? && !carriers.include?(rate_carrier)
|
|
35
|
+
next
|
|
36
|
+
end
|
|
37
|
+
if negative_carriers.size.positive? && negative_carriers.include?(rate_carrier)
|
|
38
|
+
next
|
|
39
|
+
end
|
|
134
40
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
41
|
+
rate_service = rate.service.downcase
|
|
42
|
+
if services.size.positive? && !services.include?(rate_service)
|
|
43
|
+
next
|
|
44
|
+
end
|
|
45
|
+
if negative_services.size.positive? && negative_services.include?(rate_service)
|
|
46
|
+
next
|
|
47
|
+
end
|
|
140
48
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
case response
|
|
144
|
-
when Array
|
|
145
|
-
response.map { |i| convert_to_easypost_object(i, api_key, parent) }
|
|
146
|
-
when Hash
|
|
147
|
-
if (cls_name = response[:object])
|
|
148
|
-
cls = BY_TYPE[cls_name]
|
|
149
|
-
elsif response[:id]
|
|
150
|
-
if response[:id].index('_').nil?
|
|
151
|
-
cls = EasyPost::EasyPostObject
|
|
152
|
-
elsif (cls_prefix = response[:id][0..response[:id].index('_')])
|
|
153
|
-
cls = BY_PREFIX[cls_prefix[0..-2]]
|
|
154
|
-
end
|
|
155
|
-
elsif response['id']
|
|
156
|
-
if response['id'].index('_').nil?
|
|
157
|
-
cls = EasyPost::EasyPostObject
|
|
158
|
-
elsif (cls_prefix = response['id'][0..response['id'].index('_')])
|
|
159
|
-
cls = BY_PREFIX[cls_prefix[0..-2]]
|
|
160
|
-
end
|
|
49
|
+
if lowest_rate.nil? || rate.rate.to_f < lowest_rate.rate.to_f
|
|
50
|
+
lowest_rate = rate
|
|
161
51
|
end
|
|
52
|
+
end
|
|
162
53
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
else
|
|
166
|
-
response
|
|
54
|
+
if lowest_rate.nil?
|
|
55
|
+
raise EasyPost::Errors::FilteringError.new(EasyPost::Constants::NO_MATCHING_RATES)
|
|
167
56
|
end
|
|
57
|
+
|
|
58
|
+
lowest_rate
|
|
168
59
|
end
|
|
169
60
|
|
|
170
|
-
# Gets the lowest rate
|
|
61
|
+
# Gets the lowest stateless rate.
|
|
171
62
|
# You can exclude by having `'!'` as the first element of your optional filter lists
|
|
172
|
-
def self.
|
|
63
|
+
def self.get_lowest_stateless_rate(stateless_rates, carriers = [], services = [])
|
|
173
64
|
lowest_rate = nil
|
|
174
65
|
|
|
175
|
-
carriers = EasyPost::
|
|
66
|
+
carriers = EasyPost::InternalUtilities.normalize_string_list(carriers)
|
|
176
67
|
negative_carriers = []
|
|
177
68
|
carriers_copy = carriers.clone
|
|
178
69
|
carriers_copy.each do |carrier|
|
|
179
70
|
if carrier[0, 1] == '!'
|
|
180
|
-
negative_carriers << carrier[1
|
|
71
|
+
negative_carriers << carrier[1..]
|
|
181
72
|
carriers.delete(carrier)
|
|
182
73
|
end
|
|
183
74
|
end
|
|
184
75
|
|
|
185
|
-
services = EasyPost::
|
|
76
|
+
services = EasyPost::InternalUtilities.normalize_string_list(services)
|
|
186
77
|
negative_services = []
|
|
187
78
|
services_copy = services.clone
|
|
188
79
|
services_copy.each do |service|
|
|
189
80
|
if service[0, 1] == '!'
|
|
190
|
-
negative_services << service[1
|
|
81
|
+
negative_services << service[1..]
|
|
191
82
|
services.delete(service)
|
|
192
83
|
end
|
|
193
84
|
end
|
|
194
85
|
|
|
195
|
-
|
|
86
|
+
stateless_rates.each do |rate|
|
|
196
87
|
rate_carrier = rate.carrier.downcase
|
|
197
88
|
if carriers.size.positive? && !carriers.include?(rate_carrier)
|
|
198
89
|
next
|
|
@@ -214,8 +105,72 @@ module EasyPost::Util
|
|
|
214
105
|
end
|
|
215
106
|
end
|
|
216
107
|
|
|
217
|
-
|
|
108
|
+
if lowest_rate.nil?
|
|
109
|
+
raise EasyPost::Errors::FilteringError.new(EasyPost::Constants::NO_MATCHING_RATES)
|
|
110
|
+
end
|
|
218
111
|
|
|
219
112
|
lowest_rate
|
|
220
113
|
end
|
|
114
|
+
|
|
115
|
+
# Converts a raw webhook event into an EasyPost object.
|
|
116
|
+
def self.receive_event(raw_input)
|
|
117
|
+
EasyPost::InternalUtilities::Json.convert_json_to_object(JSON.parse(raw_input), EasyPost::Models::EasyPostObject)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Get the lowest SmartRate from a list of SmartRate.
|
|
121
|
+
def self.get_lowest_smart_rate(smart_rates, delivery_days, delivery_accuracy)
|
|
122
|
+
valid_delivery_accuracy_values = Set[
|
|
123
|
+
'percentile_50',
|
|
124
|
+
'percentile_75',
|
|
125
|
+
'percentile_85',
|
|
126
|
+
'percentile_90',
|
|
127
|
+
'percentile_95',
|
|
128
|
+
'percentile_97',
|
|
129
|
+
'percentile_99',
|
|
130
|
+
]
|
|
131
|
+
lowest_smart_rate = nil
|
|
132
|
+
|
|
133
|
+
unless valid_delivery_accuracy_values.include?(delivery_accuracy.downcase)
|
|
134
|
+
raise EasyPost::Errors::InvalidParameterError.new(
|
|
135
|
+
'delivery_accuracy',
|
|
136
|
+
"Must be one of: #{valid_delivery_accuracy_values}",
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
smart_rates.each do |rate|
|
|
141
|
+
next if rate['time_in_transit'][delivery_accuracy] > delivery_days.to_i
|
|
142
|
+
|
|
143
|
+
if lowest_smart_rate.nil? || rate['rate'].to_f < lowest_smart_rate['rate'].to_f
|
|
144
|
+
lowest_smart_rate = rate
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
if lowest_smart_rate.nil?
|
|
149
|
+
raise EasyPost::Errors::FilteringError.new(EasyPost::Constants::NO_MATCHING_RATES)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
lowest_smart_rate
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Validate a webhook by comparing the HMAC signature header sent from EasyPost to your shared secret.
|
|
156
|
+
# If the signatures do not match, an error will be raised signifying the webhook either did not originate
|
|
157
|
+
# from EasyPost or the secrets do not match. If the signatures do match, the `event_body` will be returned
|
|
158
|
+
# as JSON.
|
|
159
|
+
def self.validate_webhook(event_body, headers, webhook_secret)
|
|
160
|
+
easypost_hmac_signature = headers['X-Hmac-Signature']
|
|
161
|
+
|
|
162
|
+
if easypost_hmac_signature.nil?
|
|
163
|
+
raise EasyPost::Errors::SignatureVerificationError.new(EasyPost::Constants::WEBHOOK_MISSING_SIGNATURE)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
encoded_webhook_secret = webhook_secret.unicode_normalize(:nfkd).encode('utf-8')
|
|
167
|
+
|
|
168
|
+
expected_signature = OpenSSL::HMAC.hexdigest('sha256', encoded_webhook_secret, event_body)
|
|
169
|
+
digest = "hmac-sha256-hex=#{expected_signature}"
|
|
170
|
+
unless digest == easypost_hmac_signature
|
|
171
|
+
raise EasyPost::Errors::SignatureVerificationError.new(EasyPost::Constants::WEBHOOK_SIGNATURE_MISMATCH)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
JSON.parse(event_body)
|
|
175
|
+
end
|
|
221
176
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasyPost::InternalUtilities::Json
|
|
4
|
+
def self.convert_json_to_object(data, cls = EasyPost::Models::EasyPostObject)
|
|
5
|
+
data = JSON.parse(data) if data.is_a?(String) # Parse JSON to a Hash or Array if it's a string
|
|
6
|
+
if data.is_a?(Array)
|
|
7
|
+
# Deserialize array data into an array of objects
|
|
8
|
+
data.map { |i| convert_json_to_object(i, cls) }
|
|
9
|
+
elsif data.is_a?(Hash)
|
|
10
|
+
# Deserialize hash data into a new object instance
|
|
11
|
+
cls.new(data)
|
|
12
|
+
else
|
|
13
|
+
# data is neither a Hash nor Array (but somehow was parsed as JSON? This should never happen)
|
|
14
|
+
data
|
|
15
|
+
end
|
|
16
|
+
rescue JSON::ParserError
|
|
17
|
+
data # Not JSON, return the original data (used mostly when dealing with final values like strings, booleans, etc.)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.http_response_is_json?(response)
|
|
21
|
+
response['Content-Type'] ? response['Content-Type'].start_with?('application/json') : false
|
|
22
|
+
end
|
|
23
|
+
end
|