easypost 4.13.0 → 5.0.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/.github/workflows/ci.yml +22 -1
- data/.gitignore +9 -11
- data/.rubocop.yml +6 -1
- data/CHANGELOG.md +21 -0
- data/Makefile +1 -1
- data/README.md +45 -38
- data/UPGRADE_GUIDE.md +119 -0
- data/VERSION +1 -1
- data/easycop.yml +3 -3
- data/easypost.gemspec +12 -10
- data/lib/easypost/client.rb +129 -0
- data/lib/easypost/connection.rb +2 -4
- data/lib/easypost/constants.rb +15 -0
- data/lib/easypost/errors/api/api_error.rb +106 -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 +31 -0
- data/lib/easypost/http_client.rb +62 -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/models/insurance.rb +6 -0
- data/lib/easypost/models/order.rb +9 -0
- data/lib/easypost/models/parcel.rb +5 -0
- data/lib/easypost/{payload.rb → models/payload.rb} +1 -1
- 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/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/{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 +15 -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 +80 -187
- 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 +12 -138
- metadata +147 -64
- data/lib/easypost/address.rb +0 -55
- data/lib/easypost/api_key.rb +0 -5
- data/lib/easypost/batch.rb +0 -52
- data/lib/easypost/beta/end_shipper.rb +0 -44
- data/lib/easypost/beta/payment_refund.rb +0 -5
- data/lib/easypost/beta/rate.rb +0 -14
- data/lib/easypost/beta/referral.rb +0 -158
- data/lib/easypost/beta.rb +0 -8
- data/lib/easypost/billing.rb +0 -72
- data/lib/easypost/brand.rb +0 -13
- data/lib/easypost/carrier_account.rb +0 -26
- 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 -26
- data/lib/easypost/error.rb +0 -32
- data/lib/easypost/event.rb +0 -38
- data/lib/easypost/insurance.rb +0 -20
- 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 -12
- data/lib/easypost/pickup.rb +0 -47
- data/lib/easypost/rate.rb +0 -9
- data/lib/easypost/referral.rb +0 -117
- data/lib/easypost/refund.rb +0 -19
- data/lib/easypost/report.rb +0 -44
- data/lib/easypost/resource.rb +0 -124
- data/lib/easypost/scan_form.rb +0 -26
- data/lib/easypost/shipment.rb +0 -186
- data/lib/easypost/tracker.rb +0 -43
- data/lib/easypost/user.rb +0 -74
- data/lib/easypost/webhook.rb +0 -57
data/lib/easypost/util.rb
CHANGED
|
@@ -1,208 +1,35 @@
|
|
|
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
|
-
'ak' => EasyPost::ApiKey,
|
|
9
|
-
'adr' => EasyPost::Address,
|
|
10
|
-
'bank' => EasyPost::PaymentMethod,
|
|
11
|
-
'batch' => EasyPost::Batch,
|
|
12
|
-
'brd' => EasyPost::Brand,
|
|
13
|
-
'ca' => EasyPost::CarrierAccount,
|
|
14
|
-
'card' => EasyPost::PaymentMethod,
|
|
15
|
-
'cstinfo' => EasyPost::CustomsInfo,
|
|
16
|
-
'cstitem' => EasyPost::CustomsItem,
|
|
17
|
-
'es' => EasyPost::EndShipper,
|
|
18
|
-
'evt' => EasyPost::Event,
|
|
19
|
-
'hook' => EasyPost::Webhook,
|
|
20
|
-
'ins' => EasyPost::Insurance,
|
|
21
|
-
'order' => EasyPost::Order,
|
|
22
|
-
'payload' => EasyPost::Payload,
|
|
23
|
-
'pickup' => EasyPost::Pickup,
|
|
24
|
-
'pickuprate' => EasyPost::PickupRate,
|
|
25
|
-
'pl' => EasyPost::PostageLabel,
|
|
26
|
-
'plrep' => EasyPost::Report,
|
|
27
|
-
'prcl' => EasyPost::Parcel,
|
|
28
|
-
'rate' => EasyPost::Rate,
|
|
29
|
-
'refrep' => EasyPost::Report,
|
|
30
|
-
'rfnd' => EasyPost::Refund,
|
|
31
|
-
'sf' => EasyPost::ScanForm,
|
|
32
|
-
'shp' => EasyPost::Shipment,
|
|
33
|
-
'shpinvrep' => EasyPost::Report,
|
|
34
|
-
'shprep' => EasyPost::Report,
|
|
35
|
-
'trk' => EasyPost::Tracker,
|
|
36
|
-
'trkrep' => EasyPost::Report,
|
|
37
|
-
'user' => EasyPost::User,
|
|
38
|
-
}.freeze
|
|
39
|
-
|
|
40
|
-
BY_TYPE = {
|
|
41
|
-
'Address' => EasyPost::Address,
|
|
42
|
-
'ApiKey' => EasyPost::ApiKey,
|
|
43
|
-
'BankAccount' => EasyPost::PaymentMethod,
|
|
44
|
-
'Batch' => EasyPost::Batch,
|
|
45
|
-
'Brand' => EasyPost::Brand,
|
|
46
|
-
'CarbonOffset' => EasyPost::CarbonOffset,
|
|
47
|
-
'CarrierAccount' => EasyPost::CarrierAccount,
|
|
48
|
-
'CreditCard' => EasyPost::PaymentMethod,
|
|
49
|
-
'CustomsInfo' => EasyPost::CustomsInfo,
|
|
50
|
-
'CustomsItem' => EasyPost::CustomsItem,
|
|
51
|
-
'EndShipper' => EasyPost::EndShipper,
|
|
52
|
-
'Event' => EasyPost::Event,
|
|
53
|
-
'Insurance' => EasyPost::Insurance,
|
|
54
|
-
'Order' => EasyPost::Order,
|
|
55
|
-
'Parcel' => EasyPost::Parcel,
|
|
56
|
-
'PaymentLogReport' => EasyPost::Report,
|
|
57
|
-
'Pickup' => EasyPost::Pickup,
|
|
58
|
-
'PickupRate' => EasyPost::PickupRate,
|
|
59
|
-
'PostageLabel' => EasyPost::PostageLabel,
|
|
60
|
-
'Rate' => EasyPost::Rate,
|
|
61
|
-
'Referral' => EasyPost::Beta::Referral,
|
|
62
|
-
'Refund' => EasyPost::Refund,
|
|
63
|
-
'RefundReport' => EasyPost::Report,
|
|
64
|
-
'Report' => EasyPost::Report,
|
|
65
|
-
'ScanForm' => EasyPost::ScanForm,
|
|
66
|
-
'Shipment' => EasyPost::Shipment,
|
|
67
|
-
'ShipmentInvoiceReport' => EasyPost::Report,
|
|
68
|
-
'ShipmentReport' => EasyPost::Report,
|
|
69
|
-
'TaxIdentifier' => EasyPost::TaxIdentifier,
|
|
70
|
-
'Tracker' => EasyPost::Tracker,
|
|
71
|
-
'TrackerReport' => EasyPost::Report,
|
|
72
|
-
'User' => EasyPost::User,
|
|
73
|
-
'Webhook' => EasyPost::Webhook,
|
|
74
|
-
}.freeze
|
|
75
|
-
|
|
76
|
-
def self.os_name
|
|
77
|
-
case RUBY_PLATFORM
|
|
78
|
-
when /linux/i
|
|
79
|
-
'Linux'
|
|
80
|
-
when /darwin/i
|
|
81
|
-
'Darwin'
|
|
82
|
-
when /cygwin|mswin|mingw|bccwin|wince|emx/i
|
|
83
|
-
'Windows'
|
|
84
|
-
else
|
|
85
|
-
'Unknown'
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def self.os_version
|
|
90
|
-
Gem::Platform.local.version
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def self.os_arch
|
|
94
|
-
Gem::Platform.local.cpu
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Form-encode a multi-layer dictionary to a one-layer dictionary.
|
|
98
|
-
def self.form_encode_params(hash, parent_keys = [], parent_dict = {})
|
|
99
|
-
result = parent_dict or {}
|
|
100
|
-
keys = parent_keys or []
|
|
101
|
-
|
|
102
|
-
hash.each do |key, value|
|
|
103
|
-
if value.instance_of?(Hash)
|
|
104
|
-
keys << key
|
|
105
|
-
result = form_encode_params(value, keys, result)
|
|
106
|
-
else
|
|
107
|
-
dict_key = build_dict_key(keys + [key])
|
|
108
|
-
result[dict_key] = value
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
result
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# Build a dict key from a list of keys.
|
|
115
|
-
# Example: [code, number] -> code[number]
|
|
116
|
-
def self.build_dict_key(keys)
|
|
117
|
-
result = keys[0].to_s
|
|
118
|
-
|
|
119
|
-
keys[1..-1].each do |key|
|
|
120
|
-
result += "[#{key}]"
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
result
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Converts an object to an object ID.
|
|
127
|
-
def self.objects_to_ids(obj)
|
|
128
|
-
case obj
|
|
129
|
-
when EasyPost::Resource
|
|
130
|
-
{ id: obj.id }
|
|
131
|
-
when Hash
|
|
132
|
-
result = {}
|
|
133
|
-
obj.each { |k, v| result[k] = objects_to_ids(v) unless v.nil? }
|
|
134
|
-
result
|
|
135
|
-
when Array
|
|
136
|
-
obj.map { |v| objects_to_ids(v) }
|
|
137
|
-
else
|
|
138
|
-
obj
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Normalizes a list of strings.
|
|
143
|
-
def self.normalize_string_list(lst)
|
|
144
|
-
lst = lst.is_a?(String) ? lst.split(',') : Array(lst)
|
|
145
|
-
lst.map(&:to_s).map(&:downcase).map(&:strip)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Convert data to an EasyPost Object.
|
|
149
|
-
def self.convert_to_easypost_object(response, api_key, parent = nil, name = nil)
|
|
150
|
-
case response
|
|
151
|
-
when Array
|
|
152
|
-
response.map { |i| convert_to_easypost_object(i, api_key, parent) }
|
|
153
|
-
when Hash
|
|
154
|
-
# Determine class based on the "object" key in the JSON response
|
|
155
|
-
cls_name = response[:object] || response['object']
|
|
156
|
-
if cls_name
|
|
157
|
-
# Use the "object" key value to look up the class
|
|
158
|
-
cls = BY_TYPE[cls_name]
|
|
159
|
-
else
|
|
160
|
-
# Fallback to determining class based on the "id" prefix in the JSON response
|
|
161
|
-
id = response[:id] || response['id']
|
|
162
|
-
if id.nil? || id.index('_').nil?
|
|
163
|
-
# ID not present or prefix not present (ID malformed)
|
|
164
|
-
cls = EasyPost::EasyPostObject
|
|
165
|
-
else
|
|
166
|
-
# Parse the prefix from the ID and use it to look up the class
|
|
167
|
-
cls_prefix = id[0..id.index('_')][0..-2]
|
|
168
|
-
cls = BY_PREFIX[cls_prefix]
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
# Fallback to using the generic class if other determination methods fail (or class lookup produced no results)
|
|
172
|
-
cls ||= EasyPost::EasyPostObject
|
|
173
|
-
cls.construct_from(response, api_key, parent, name)
|
|
174
|
-
else
|
|
175
|
-
# response is neither a Hash nor Array (used mostly when dealing with final values like strings, booleans, etc.)
|
|
176
|
-
response
|
|
177
|
-
end
|
|
178
|
-
end
|
|
3
|
+
require 'easypost/constants'
|
|
179
4
|
|
|
5
|
+
# Client Library helper functions
|
|
6
|
+
module EasyPost::Util
|
|
180
7
|
# Gets the lowest rate of an EasyPost object such as a Shipment, Order, or Pickup.
|
|
181
8
|
# You can exclude by having `'!'` as the first element of your optional filter lists
|
|
182
9
|
def self.get_lowest_object_rate(easypost_object, carriers = [], services = [], rates_key = 'rates')
|
|
183
10
|
lowest_rate = nil
|
|
184
11
|
|
|
185
|
-
carriers = EasyPost::
|
|
12
|
+
carriers = EasyPost::InternalUtilities.normalize_string_list(carriers)
|
|
186
13
|
negative_carriers = []
|
|
187
14
|
carriers_copy = carriers.clone
|
|
188
15
|
carriers_copy.each do |carrier|
|
|
189
16
|
if carrier[0, 1] == '!'
|
|
190
|
-
negative_carriers << carrier[1
|
|
17
|
+
negative_carriers << carrier[1..]
|
|
191
18
|
carriers.delete(carrier)
|
|
192
19
|
end
|
|
193
20
|
end
|
|
194
21
|
|
|
195
|
-
services = EasyPost::
|
|
22
|
+
services = EasyPost::InternalUtilities.normalize_string_list(services)
|
|
196
23
|
negative_services = []
|
|
197
24
|
services_copy = services.clone
|
|
198
25
|
services_copy.each do |service|
|
|
199
26
|
if service[0, 1] == '!'
|
|
200
|
-
negative_services << service[1
|
|
27
|
+
negative_services << service[1..]
|
|
201
28
|
services.delete(service)
|
|
202
29
|
end
|
|
203
30
|
end
|
|
204
31
|
|
|
205
|
-
easypost_object
|
|
32
|
+
easypost_object.send(rates_key).each do |rate|
|
|
206
33
|
rate_carrier = rate.carrier.downcase
|
|
207
34
|
if carriers.size.positive? && !carriers.include?(rate_carrier)
|
|
208
35
|
next
|
|
@@ -224,7 +51,9 @@ module EasyPost::Util
|
|
|
224
51
|
end
|
|
225
52
|
end
|
|
226
53
|
|
|
227
|
-
|
|
54
|
+
if lowest_rate.nil?
|
|
55
|
+
raise EasyPost::Errors::FilteringError.new(EasyPost::Constants::NO_MATCHING_RATES)
|
|
56
|
+
end
|
|
228
57
|
|
|
229
58
|
lowest_rate
|
|
230
59
|
end
|
|
@@ -234,22 +63,22 @@ module EasyPost::Util
|
|
|
234
63
|
def self.get_lowest_stateless_rate(stateless_rates, carriers = [], services = [])
|
|
235
64
|
lowest_rate = nil
|
|
236
65
|
|
|
237
|
-
carriers = EasyPost::
|
|
66
|
+
carriers = EasyPost::InternalUtilities.normalize_string_list(carriers)
|
|
238
67
|
negative_carriers = []
|
|
239
68
|
carriers_copy = carriers.clone
|
|
240
69
|
carriers_copy.each do |carrier|
|
|
241
70
|
if carrier[0, 1] == '!'
|
|
242
|
-
negative_carriers << carrier[1
|
|
71
|
+
negative_carriers << carrier[1..]
|
|
243
72
|
carriers.delete(carrier)
|
|
244
73
|
end
|
|
245
74
|
end
|
|
246
75
|
|
|
247
|
-
services = EasyPost::
|
|
76
|
+
services = EasyPost::InternalUtilities.normalize_string_list(services)
|
|
248
77
|
negative_services = []
|
|
249
78
|
services_copy = services.clone
|
|
250
79
|
services_copy.each do |service|
|
|
251
80
|
if service[0, 1] == '!'
|
|
252
|
-
negative_services << service[1
|
|
81
|
+
negative_services << service[1..]
|
|
253
82
|
services.delete(service)
|
|
254
83
|
end
|
|
255
84
|
end
|
|
@@ -276,8 +105,72 @@ module EasyPost::Util
|
|
|
276
105
|
end
|
|
277
106
|
end
|
|
278
107
|
|
|
279
|
-
|
|
108
|
+
if lowest_rate.nil?
|
|
109
|
+
raise EasyPost::Errors::FilteringError.new(EasyPost::Constants::NO_MATCHING_RATES)
|
|
110
|
+
end
|
|
280
111
|
|
|
281
112
|
lowest_rate
|
|
282
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
|
|
283
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
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../models'
|
|
4
|
+
|
|
5
|
+
module EasyPost::InternalUtilities::StaticMapper
|
|
6
|
+
BY_PREFIX = {
|
|
7
|
+
'adr' => EasyPost::Models::Address,
|
|
8
|
+
'ak' => EasyPost::Models::ApiKey,
|
|
9
|
+
'bank' => EasyPost::Models::PaymentMethod,
|
|
10
|
+
'batch' => EasyPost::Models::Batch,
|
|
11
|
+
'brd' => EasyPost::Models::Brand,
|
|
12
|
+
'ca' => EasyPost::Models::CarrierAccount,
|
|
13
|
+
'card' => EasyPost::Models::PaymentMethod,
|
|
14
|
+
'cstinfo' => EasyPost::Models::CustomsInfo,
|
|
15
|
+
'cstitem' => EasyPost::Models::CustomsItem,
|
|
16
|
+
'es' => EasyPost::Models::EndShipper,
|
|
17
|
+
'evt' => EasyPost::Models::Event,
|
|
18
|
+
'hook' => EasyPost::Models::Webhook,
|
|
19
|
+
'ins' => EasyPost::Models::Insurance,
|
|
20
|
+
'order' => EasyPost::Models::Order,
|
|
21
|
+
'payload' => EasyPost::Models::Payload,
|
|
22
|
+
'pickup' => EasyPost::Models::Pickup,
|
|
23
|
+
'pickuprate' => EasyPost::Models::PickupRate,
|
|
24
|
+
'pl' => EasyPost::Models::PostageLabel,
|
|
25
|
+
'plrep' => EasyPost::Models::Report,
|
|
26
|
+
'prcl' => EasyPost::Models::Parcel,
|
|
27
|
+
'rate' => EasyPost::Models::Rate,
|
|
28
|
+
'refrep' => EasyPost::Models::Report,
|
|
29
|
+
'rfnd' => EasyPost::Models::Refund,
|
|
30
|
+
'sf' => EasyPost::Models::ScanForm,
|
|
31
|
+
'shp' => EasyPost::Models::Shipment,
|
|
32
|
+
'shpinvrep' => EasyPost::Models::Report,
|
|
33
|
+
'shprep' => EasyPost::Models::Report,
|
|
34
|
+
'trk' => EasyPost::Models::Tracker,
|
|
35
|
+
'trkrep' => EasyPost::Models::Report,
|
|
36
|
+
'user' => EasyPost::Models::User,
|
|
37
|
+
}.freeze
|
|
38
|
+
|
|
39
|
+
BY_TYPE = {
|
|
40
|
+
'Address' => EasyPost::Models::Address,
|
|
41
|
+
'ApiKey' => EasyPost::Models::ApiKey,
|
|
42
|
+
'BankAccount' => EasyPost::Models::PaymentMethod,
|
|
43
|
+
'Batch' => EasyPost::Models::Batch,
|
|
44
|
+
'Brand' => EasyPost::Models::Brand,
|
|
45
|
+
'CarbonOffset' => EasyPost::Models::CarbonOffset,
|
|
46
|
+
'CarrierAccount' => EasyPost::Models::CarrierAccount,
|
|
47
|
+
'CreditCard' => EasyPost::Models::PaymentMethod,
|
|
48
|
+
'CustomsInfo' => EasyPost::Models::CustomsInfo,
|
|
49
|
+
'CustomsItem' => EasyPost::Models::CustomsItem,
|
|
50
|
+
'EndShipper' => EasyPost::Models::EndShipper,
|
|
51
|
+
'Event' => EasyPost::Models::Event,
|
|
52
|
+
'Insurance' => EasyPost::Models::Insurance,
|
|
53
|
+
'Order' => EasyPost::Models::Order,
|
|
54
|
+
'Parcel' => EasyPost::Models::Parcel,
|
|
55
|
+
'PaymentLogReport' => EasyPost::Models::Report,
|
|
56
|
+
'Pickup' => EasyPost::Models::Pickup,
|
|
57
|
+
'PickupRate' => EasyPost::Models::PickupRate,
|
|
58
|
+
'PostageLabel' => EasyPost::Models::PostageLabel,
|
|
59
|
+
'Rate' => EasyPost::Models::Rate,
|
|
60
|
+
'Refund' => EasyPost::Models::Refund,
|
|
61
|
+
'RefundReport' => EasyPost::Models::Report,
|
|
62
|
+
'Report' => EasyPost::Models::Report,
|
|
63
|
+
'ScanForm' => EasyPost::Models::ScanForm,
|
|
64
|
+
'Shipment' => EasyPost::Models::Shipment,
|
|
65
|
+
'ShipmentInvoiceReport' => EasyPost::Models::Report,
|
|
66
|
+
'ShipmentReport' => EasyPost::Models::Report,
|
|
67
|
+
'TaxIdentifier' => EasyPost::Models::TaxIdentifier,
|
|
68
|
+
'Tracker' => EasyPost::Models::Tracker,
|
|
69
|
+
'TrackerReport' => EasyPost::Models::Report,
|
|
70
|
+
'User' => EasyPost::Models::User,
|
|
71
|
+
'Webhook' => EasyPost::Models::Webhook,
|
|
72
|
+
}.freeze
|
|
73
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasyPost::InternalUtilities::System
|
|
4
|
+
def self.os_name
|
|
5
|
+
case RUBY_PLATFORM
|
|
6
|
+
when /linux/i
|
|
7
|
+
'Linux'
|
|
8
|
+
when /darwin/i
|
|
9
|
+
'Darwin'
|
|
10
|
+
when /cygwin|mswin|mingw|bccwin|wince|emx/i
|
|
11
|
+
'Windows'
|
|
12
|
+
else
|
|
13
|
+
'Unknown'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.os_version
|
|
18
|
+
Gem::Platform.local.version
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.os_arch
|
|
22
|
+
Gem::Platform.local.cpu
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.ruby_version
|
|
26
|
+
RUBY_VERSION
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.ruby_patchlevel
|
|
30
|
+
RUBY_PATCHLEVEL
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.lib_version
|
|
34
|
+
File.open(File.expand_path('../../VERSION', __dir__)).read.strip
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/easypost.rb
CHANGED
|
@@ -5,150 +5,24 @@ require 'cgi'
|
|
|
5
5
|
require 'net/http'
|
|
6
6
|
|
|
7
7
|
require 'easypost/version'
|
|
8
|
-
require 'easypost/object'
|
|
9
|
-
require 'easypost/resource'
|
|
10
|
-
require 'easypost/error'
|
|
11
8
|
require 'easypost/connection'
|
|
12
|
-
|
|
13
|
-
# Resources
|
|
14
|
-
require 'easypost/address'
|
|
15
|
-
require 'easypost/api_key'
|
|
16
|
-
require 'easypost/batch'
|
|
17
|
-
require 'easypost/billing'
|
|
18
|
-
require 'easypost/brand'
|
|
19
|
-
require 'easypost/carbon_offset'
|
|
20
|
-
require 'easypost/carrier_account'
|
|
21
|
-
require 'easypost/carrier_type'
|
|
22
|
-
require 'easypost/customs_info'
|
|
23
|
-
require 'easypost/customs_item'
|
|
24
|
-
require 'easypost/end_shipper'
|
|
25
|
-
require 'easypost/event'
|
|
26
|
-
require 'easypost/insurance'
|
|
27
|
-
require 'easypost/order'
|
|
28
|
-
require 'easypost/parcel'
|
|
29
|
-
require 'easypost/payload'
|
|
30
|
-
require 'easypost/payment_method' # deprecated
|
|
31
|
-
require 'easypost/pickup_rate'
|
|
32
|
-
require 'easypost/pickup'
|
|
33
|
-
require 'easypost/postage_label'
|
|
34
|
-
require 'easypost/rate'
|
|
35
|
-
require 'easypost/referral'
|
|
36
|
-
require 'easypost/refund'
|
|
37
|
-
require 'easypost/report'
|
|
38
|
-
require 'easypost/scan_form'
|
|
39
|
-
require 'easypost/shipment'
|
|
40
|
-
require 'easypost/tax_identifier'
|
|
41
|
-
require 'easypost/tracker'
|
|
42
|
-
require 'easypost/user'
|
|
43
|
-
require 'easypost/webhook'
|
|
44
|
-
require 'easypost/beta'
|
|
45
|
-
|
|
46
9
|
require 'easypost/util'
|
|
47
10
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class << self
|
|
52
|
-
attr_accessor :api_key, :api_base
|
|
53
|
-
attr_writer :default_connection
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
self.api_base = DEFAULT_API_BASE
|
|
57
|
-
|
|
58
|
-
def self.user_agent
|
|
59
|
-
@user_agent ||=
|
|
60
|
-
"EasyPost/v2 RubyClient/#{EasyPost::VERSION} Ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} " \
|
|
61
|
-
"OS/#{EasyPost::Util.os_name} OSVersion/#{EasyPost::Util.os_version} " \
|
|
62
|
-
"OSArch/#{EasyPost::Util.os_arch}"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def self.default_headers
|
|
66
|
-
@default_headers ||= {
|
|
67
|
-
'Content-Type' => 'application/json',
|
|
68
|
-
'User-Agent' => user_agent,
|
|
69
|
-
}
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def self.default_connection
|
|
73
|
-
@default_connection ||= EasyPost::Connection.new(
|
|
74
|
-
uri: URI(api_base),
|
|
75
|
-
config: http_config,
|
|
76
|
-
)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def self.authorization(key)
|
|
80
|
-
"Basic #{Base64.strict_encode64("#{key}:")}"
|
|
81
|
-
end
|
|
11
|
+
# Client
|
|
12
|
+
require 'easypost/client'
|
|
13
|
+
require 'easypost/http_client'
|
|
82
14
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
http_config.clear
|
|
86
|
-
self.default_connection = nil
|
|
87
|
-
end
|
|
15
|
+
# Services
|
|
16
|
+
require 'easypost/services'
|
|
88
17
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
timeout: 60,
|
|
92
|
-
open_timeout: 30,
|
|
93
|
-
verify_ssl: OpenSSL::SSL::VERIFY_PEER,
|
|
94
|
-
}
|
|
18
|
+
# Models
|
|
19
|
+
require 'easypost/models'
|
|
95
20
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
http_config[:min_version] = OpenSSL::SSL::TLS1_2_VERSION
|
|
99
|
-
else
|
|
100
|
-
http_config[:ssl_version] = :TLSv1_2 # rubocop:disable Naming/VariableNumber
|
|
101
|
-
end
|
|
21
|
+
# Exceptions
|
|
22
|
+
require 'easypost/errors'
|
|
102
23
|
|
|
103
|
-
|
|
104
|
-
|
|
24
|
+
# Internal Utilities
|
|
25
|
+
require 'easypost/internal_utilities'
|
|
105
26
|
|
|
106
|
-
|
|
107
|
-
def self.http_config
|
|
108
|
-
@http_config ||= default_http_config
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Set the HTTP config.
|
|
112
|
-
def self.http_config=(http_config_params)
|
|
113
|
-
http_config.merge!(http_config_params)
|
|
114
|
-
|
|
115
|
-
self.default_connection = nil
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Create an EasyPost Client.
|
|
119
|
-
#
|
|
120
|
-
# @deprecated
|
|
121
|
-
def self.make_client(url)
|
|
122
|
-
EasyPost::Connection.new(uri: URI(url), config: http_config).create
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Make an HTTP request against the {default_connection}
|
|
126
|
-
#
|
|
127
|
-
# @param method [Symbol] the HTTP Verb (get, method, put, post, etc.)
|
|
128
|
-
# @param path [String] URI path of the resource
|
|
129
|
-
# @param requested_api_key [String] ({EasyPost.api_key}) key set Authorization header.
|
|
130
|
-
# @param body [Object] (nil) object to be dumped to JSON
|
|
131
|
-
# @raise [EasyPost::Error] if the response has a non-2xx status code
|
|
132
|
-
# @return [Hash] JSON object parsed from the response body
|
|
133
|
-
def self.make_request(method, path, api_key = nil, body = nil)
|
|
134
|
-
default_connection.call(method, path, api_key || EasyPost.api_key, body)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def self.parse_response(status:, body:, json:)
|
|
138
|
-
if status < 200 || status >= 300
|
|
139
|
-
error = JSON.parse(body)['error']
|
|
140
|
-
|
|
141
|
-
raise EasyPost::Error.new(
|
|
142
|
-
error['message'],
|
|
143
|
-
status,
|
|
144
|
-
error['code'],
|
|
145
|
-
error['errors'],
|
|
146
|
-
body,
|
|
147
|
-
)
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
json || !body.nil? && !body.match(/\A\s+\z/) ? JSON.parse(body) : body
|
|
151
|
-
rescue JSON::ParserError
|
|
152
|
-
raise "Invalid response object from API, unable to decode.\n#{body}"
|
|
153
|
-
end
|
|
27
|
+
module EasyPost
|
|
154
28
|
end
|