easypost 3.5.1 → 5.2.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 +9 -0
- data/.github/CODEOWNERS +2 -0
- data/.github/ISSUE_TEMPLATE/bug_report.yml +81 -0
- data/.github/ISSUE_TEMPLATE/feature_request.yml +37 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
- data/.github/workflows/ci.yml +54 -5
- data/.gitignore +27 -17
- data/.gitmodules +3 -0
- data/CHANGELOG.md +295 -119
- data/Gemfile +2 -0
- data/Makefile +70 -0
- data/README.md +184 -72
- data/Rakefile +2 -1
- data/UPGRADE_GUIDE.md +181 -0
- data/VERSION +1 -1
- data/bin/easypost-irb +5 -3
- data/easypost.gemspec +27 -20
- data/lib/easypost/client.rb +179 -0
- data/lib/easypost/connection.rb +64 -0
- 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/models/carbon_offset.rb +5 -0
- data/lib/easypost/models/carrier_account.rb +5 -0
- data/lib/easypost/models/carrier_type.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/models/insurance.rb +6 -0
- 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/models/pickup_rate.rb +5 -0
- data/lib/easypost/models/postage_label.rb +5 -0
- data/lib/easypost/models/rate.rb +5 -0
- data/lib/easypost/models/referral.rb +5 -0
- data/lib/easypost/models/refund.rb +5 -0
- 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/models/tax_identifier.rb +6 -0
- 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 +36 -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/carrier_type.rb +10 -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 +33 -0
- data/lib/easypost/util.rb +160 -116
- 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/version.rb +3 -1
- data/lib/easypost.rb +20 -143
- metadata +249 -46
- data/lib/easypost/address.rb +0 -58
- data/lib/easypost/api_key.rb +0 -2
- data/lib/easypost/batch.rb +0 -49
- data/lib/easypost/brand.rb +0 -9
- data/lib/easypost/carrier_account.rb +0 -5
- data/lib/easypost/carrier_type.rb +0 -2
- data/lib/easypost/customs_info.rb +0 -5
- data/lib/easypost/customs_item.rb +0 -5
- data/lib/easypost/error.rb +0 -31
- data/lib/easypost/event.rb +0 -7
- data/lib/easypost/insurance.rb +0 -2
- data/lib/easypost/object.rb +0 -151
- data/lib/easypost/order.rb +0 -28
- data/lib/easypost/parcel.rb +0 -2
- data/lib/easypost/pickup.rb +0 -26
- data/lib/easypost/pickup_rate.rb +0 -3
- data/lib/easypost/postage_label.rb +0 -2
- data/lib/easypost/print_job.rb +0 -2
- data/lib/easypost/printer.rb +0 -24
- data/lib/easypost/rate.rb +0 -2
- data/lib/easypost/refund.rb +0 -2
- data/lib/easypost/report.rb +0 -29
- data/lib/easypost/resource.rb +0 -75
- data/lib/easypost/scan_form.rb +0 -6
- data/lib/easypost/shipment.rb +0 -129
- data/lib/easypost/tax_identifier.rb +0 -2
- data/lib/easypost/tracker.rb +0 -7
- data/lib/easypost/user.rb +0 -56
- data/lib/easypost/webhook.rb +0 -29
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class EasyPost::Services::ScanForm < EasyPost::Services::Service
|
|
4
|
+
MODEL_CLASS = EasyPost::Models::ScanForm
|
|
5
|
+
|
|
6
|
+
# Create a ScanForm.
|
|
7
|
+
def create(params = {})
|
|
8
|
+
@client.make_request(:post, 'scan_forms', MODEL_CLASS, params)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Retrieve a ScanForm.
|
|
12
|
+
def retrieve(id)
|
|
13
|
+
@client.make_request(:get, "scan_forms/#{id}", MODEL_CLASS)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Retrieve a list of ScanForms
|
|
17
|
+
def all(params = {})
|
|
18
|
+
@client.make_request(:get, 'scan_forms', MODEL_CLASS, params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Get the next page of ScanForms.
|
|
22
|
+
def get_next_page(collection, page_size = nil)
|
|
23
|
+
get_next_page_helper(collection, collection.scan_forms, 'scan_forms', MODEL_CLASS, page_size)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -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,33 @@
|
|
|
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/carrier_type'
|
|
17
|
+
require_relative 'services/customs_info'
|
|
18
|
+
require_relative 'services/customs_item'
|
|
19
|
+
require_relative 'services/end_shipper'
|
|
20
|
+
require_relative 'services/event'
|
|
21
|
+
require_relative 'services/insurance'
|
|
22
|
+
require_relative 'services/order'
|
|
23
|
+
require_relative 'services/parcel'
|
|
24
|
+
require_relative 'services/pickup'
|
|
25
|
+
require_relative 'services/rate'
|
|
26
|
+
require_relative 'services/referral_customer'
|
|
27
|
+
require_relative 'services/refund'
|
|
28
|
+
require_relative 'services/report'
|
|
29
|
+
require_relative 'services/scan_form'
|
|
30
|
+
require_relative 'services/shipment'
|
|
31
|
+
require_relative 'services/tracker'
|
|
32
|
+
require_relative 'services/user'
|
|
33
|
+
require_relative 'services/webhook'
|
data/lib/easypost/util.rb
CHANGED
|
@@ -1,132 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'easypost/constants'
|
|
4
|
+
|
|
5
|
+
# Client Library helper functions
|
|
1
6
|
module EasyPost::Util
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def self.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
11
|
+
|
|
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)
|
|
19
|
+
end
|
|
14
20
|
end
|
|
15
|
-
end
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
30
|
+
end
|
|
31
|
+
|
|
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
|
|
20
40
|
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
48
|
+
|
|
49
|
+
if lowest_rate.nil? || rate.rate.to_f < lowest_rate.rate.to_f
|
|
50
|
+
lowest_rate = rate
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if lowest_rate.nil?
|
|
55
|
+
raise EasyPost::Errors::FilteringError.new(EasyPost::Constants::NO_MATCHING_RATES)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
lowest_rate
|
|
23
59
|
end
|
|
24
60
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
61
|
+
# Gets the lowest stateless rate.
|
|
62
|
+
# You can exclude by having `'!'` as the first element of your optional filter lists
|
|
63
|
+
def self.get_lowest_stateless_rate(stateless_rates, carriers = [], services = [])
|
|
64
|
+
lowest_rate = nil
|
|
65
|
+
|
|
66
|
+
carriers = EasyPost::InternalUtilities.normalize_string_list(carriers)
|
|
67
|
+
negative_carriers = []
|
|
68
|
+
carriers_copy = carriers.clone
|
|
69
|
+
carriers_copy.each do |carrier|
|
|
70
|
+
if carrier[0, 1] == '!'
|
|
71
|
+
negative_carriers << carrier[1..]
|
|
72
|
+
carriers.delete(carrier)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
services = EasyPost::InternalUtilities.normalize_string_list(services)
|
|
77
|
+
negative_services = []
|
|
78
|
+
services_copy = services.clone
|
|
79
|
+
services_copy.each do |service|
|
|
80
|
+
if service[0, 1] == '!'
|
|
81
|
+
negative_services << service[1..]
|
|
82
|
+
services.delete(service)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
stateless_rates.each do |rate|
|
|
87
|
+
rate_carrier = rate.carrier.downcase
|
|
88
|
+
if carriers.size.positive? && !carriers.include?(rate_carrier)
|
|
89
|
+
next
|
|
90
|
+
end
|
|
91
|
+
if negative_carriers.size.positive? && negative_carriers.include?(rate_carrier)
|
|
92
|
+
next
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
rate_service = rate.service.downcase
|
|
96
|
+
if services.size.positive? && !services.include?(rate_service)
|
|
97
|
+
next
|
|
98
|
+
end
|
|
99
|
+
if negative_services.size.positive? && negative_services.include?(rate_service)
|
|
100
|
+
next
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if lowest_rate.nil? || rate.rate.to_f < lowest_rate.rate.to_f
|
|
104
|
+
lowest_rate = rate
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
if lowest_rate.nil?
|
|
109
|
+
raise EasyPost::Errors::FilteringError.new(EasyPost::Constants::NO_MATCHING_RATES)
|
|
37
110
|
end
|
|
111
|
+
|
|
112
|
+
lowest_rate
|
|
38
113
|
end
|
|
39
114
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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)
|
|
43
118
|
end
|
|
44
119
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
'
|
|
67
|
-
|
|
68
|
-
'
|
|
69
|
-
|
|
70
|
-
'ShipmentReport' => EasyPost::Report,
|
|
71
|
-
'Tracker' => EasyPost::Tracker,
|
|
72
|
-
'TrackerReport' => EasyPost::Report,
|
|
73
|
-
'User' => EasyPost::User,
|
|
74
|
-
'Webhook' => EasyPost::Webhook
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
prefixes = {
|
|
78
|
-
'adr' => EasyPost::Address,
|
|
79
|
-
'batch' => EasyPost::Batch,
|
|
80
|
-
'ca' => EasyPost::CarrierAccount,
|
|
81
|
-
'cstinfo' => EasyPost::CustomsInfo,
|
|
82
|
-
'cstitem' => EasyPost::CustomsItem,
|
|
83
|
-
'evt' => EasyPost::Event,
|
|
84
|
-
'hook' => EasyPost::Webhook,
|
|
85
|
-
'ins' => EasyPost::Insurance,
|
|
86
|
-
'order' => EasyPost::Order,
|
|
87
|
-
'pickup' => EasyPost::Pickup,
|
|
88
|
-
'pickuprate' => EasyPost::PickupRate,
|
|
89
|
-
'pl' => EasyPost::PostageLabel,
|
|
90
|
-
'plrep' => EasyPost::Report,
|
|
91
|
-
'prcl' => EasyPost::Parcel,
|
|
92
|
-
'printer' => EasyPost::Printer,
|
|
93
|
-
'printjob' => EasyPost::PrintJob,
|
|
94
|
-
'rate' => EasyPost::Rate,
|
|
95
|
-
'refrep' => EasyPost::Report,
|
|
96
|
-
'rfnd' => EasyPost::Refund,
|
|
97
|
-
'sf' => EasyPost::ScanForm,
|
|
98
|
-
'shp' => EasyPost::Shipment,
|
|
99
|
-
'shpinvrep' => EasyPost::Report,
|
|
100
|
-
'shprep' => EasyPost::Report,
|
|
101
|
-
'trk' => EasyPost::Tracker,
|
|
102
|
-
'trkrep' => EasyPost::Report,
|
|
103
|
-
'user' => EasyPost::User
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
case response
|
|
107
|
-
when Array
|
|
108
|
-
return response.map { |i| convert_to_easypost_object(i, api_key, parent) }
|
|
109
|
-
when Hash
|
|
110
|
-
if cls_name = response[:object]
|
|
111
|
-
cls = types[cls_name]
|
|
112
|
-
elsif response[:id]
|
|
113
|
-
if response[:id].index('_').nil?
|
|
114
|
-
cls = EasyPost::EasyPostObject
|
|
115
|
-
elsif cls_prefix = response[:id][0..response[:id].index('_')]
|
|
116
|
-
cls = prefixes[cls_prefix[0..-2]]
|
|
117
|
-
end
|
|
118
|
-
elsif response['id']
|
|
119
|
-
if response['id'].index('_').nil?
|
|
120
|
-
cls = EasyPost::EasyPostObject
|
|
121
|
-
elsif cls_prefix = response['id'][0..response['id'].index('_')]
|
|
122
|
-
cls = prefixes[cls_prefix[0..-2]]
|
|
123
|
-
end
|
|
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
|
|
124
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')
|
|
125
167
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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)
|
|
130
172
|
end
|
|
173
|
+
|
|
174
|
+
JSON.parse(event_body)
|
|
131
175
|
end
|
|
132
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
|