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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +5 -0
  3. data/.github/workflows/ci.yml +34 -5
  4. data/.gitignore +27 -20
  5. data/CHANGELOG.md +56 -0
  6. data/Makefile +30 -11
  7. data/README.md +111 -45
  8. data/UPGRADE_GUIDE.md +119 -0
  9. data/VERSION +1 -1
  10. data/easypost.gemspec +14 -10
  11. data/lib/easypost/client.rb +178 -0
  12. data/lib/easypost/connection.rb +2 -4
  13. data/lib/easypost/constants.rb +15 -0
  14. data/lib/easypost/errors/api/api_error.rb +108 -0
  15. data/lib/easypost/errors/api/bad_request_error.rb +6 -0
  16. data/lib/easypost/errors/api/connection_error.rb +6 -0
  17. data/lib/easypost/errors/api/external_api_error.rb +18 -0
  18. data/lib/easypost/errors/api/forbidden_error.rb +6 -0
  19. data/lib/easypost/errors/api/gateway_timeout_error.rb +6 -0
  20. data/lib/easypost/errors/api/internal_server_error.rb +6 -0
  21. data/lib/easypost/errors/api/invalid_request_error.rb +6 -0
  22. data/lib/easypost/errors/api/method_not_allowed_error.rb +6 -0
  23. data/lib/easypost/errors/api/not_found_error.rb +6 -0
  24. data/lib/easypost/errors/api/payment_error.rb +6 -0
  25. data/lib/easypost/errors/api/proxy_error.rb +6 -0
  26. data/lib/easypost/errors/api/rate_limit_error.rb +6 -0
  27. data/lib/easypost/errors/api/redirect_error.rb +6 -0
  28. data/lib/easypost/errors/api/retry_error.rb +6 -0
  29. data/lib/easypost/errors/api/service_unavailable_error.rb +6 -0
  30. data/lib/easypost/errors/api/ssl_error.rb +6 -0
  31. data/lib/easypost/errors/api/timeout_error.rb +6 -0
  32. data/lib/easypost/errors/api/unauthorized_error.rb +6 -0
  33. data/lib/easypost/errors/api/unknown_api_error.rb +6 -0
  34. data/lib/easypost/errors/easy_post_error.rb +7 -0
  35. data/lib/easypost/errors/end_of_pagination_error.rb +7 -0
  36. data/lib/easypost/errors/filtering_error.rb +4 -0
  37. data/lib/easypost/errors/invalid_object_error.rb +4 -0
  38. data/lib/easypost/errors/invalid_parameter_error.rb +11 -0
  39. data/lib/easypost/errors/missing_parameter_error.rb +9 -0
  40. data/lib/easypost/errors/signature_verification_error.rb +4 -0
  41. data/lib/easypost/errors.rb +32 -0
  42. data/lib/easypost/hooks/request_context.rb +16 -0
  43. data/lib/easypost/hooks/response_context.rb +23 -0
  44. data/lib/easypost/hooks.rb +34 -0
  45. data/lib/easypost/http_client.rb +117 -0
  46. data/lib/easypost/internal_utilities.rb +66 -0
  47. data/lib/easypost/models/address.rb +5 -0
  48. data/lib/easypost/models/api_key.rb +5 -0
  49. data/lib/easypost/models/base.rb +58 -0
  50. data/lib/easypost/models/batch.rb +5 -0
  51. data/lib/easypost/models/brand.rb +5 -0
  52. data/lib/easypost/{carbon_offset.rb → models/carbon_offset.rb} +1 -1
  53. data/lib/easypost/models/carrier_account.rb +5 -0
  54. data/lib/easypost/models/customs_info.rb +5 -0
  55. data/lib/easypost/models/customs_item.rb +5 -0
  56. data/lib/easypost/models/end_shipper.rb +5 -0
  57. data/lib/easypost/models/error.rb +21 -0
  58. data/lib/easypost/models/event.rb +5 -0
  59. data/lib/easypost/{insurance.rb → models/insurance.rb} +1 -1
  60. data/lib/easypost/models/order.rb +9 -0
  61. data/lib/easypost/models/parcel.rb +5 -0
  62. data/lib/easypost/models/payload.rb +5 -0
  63. data/lib/easypost/models/payment_method.rb +5 -0
  64. data/lib/easypost/models/pickup.rb +9 -0
  65. data/lib/easypost/{pickup_rate.rb → models/pickup_rate.rb} +1 -1
  66. data/lib/easypost/{postage_label.rb → models/postage_label.rb} +1 -1
  67. data/lib/easypost/models/rate.rb +5 -0
  68. data/lib/easypost/models/referral.rb +5 -0
  69. data/lib/easypost/{refund.rb → models/refund.rb} +1 -1
  70. data/lib/easypost/models/report.rb +5 -0
  71. data/lib/easypost/models/scan_form.rb +6 -0
  72. data/lib/easypost/models/shipment.rb +10 -0
  73. data/lib/easypost/{tax_identifier.rb → models/tax_identifier.rb} +1 -1
  74. data/lib/easypost/models/tracker.rb +5 -0
  75. data/lib/easypost/models/user.rb +5 -0
  76. data/lib/easypost/models/webhook.rb +6 -0
  77. data/lib/easypost/models.rb +35 -0
  78. data/lib/easypost/services/address.rb +50 -0
  79. data/lib/easypost/services/api_key.rb +8 -0
  80. data/lib/easypost/services/base.rb +27 -0
  81. data/lib/easypost/services/batch.rb +53 -0
  82. data/lib/easypost/services/beta_rate.rb +12 -0
  83. data/lib/easypost/services/beta_referral_customer.rb +40 -0
  84. data/lib/easypost/services/billing.rb +75 -0
  85. data/lib/easypost/services/carrier_account.rb +44 -0
  86. data/lib/easypost/services/carrier_metadata.rb +22 -0
  87. data/lib/easypost/services/customs_info.rb +17 -0
  88. data/lib/easypost/services/customs_item.rb +15 -0
  89. data/lib/easypost/services/end_shipper.rb +31 -0
  90. data/lib/easypost/services/event.rb +32 -0
  91. data/lib/easypost/services/insurance.rb +26 -0
  92. data/lib/easypost/services/order.rb +30 -0
  93. data/lib/easypost/services/parcel.rb +16 -0
  94. data/lib/easypost/services/pickup.rb +40 -0
  95. data/lib/easypost/services/rate.rb +8 -0
  96. data/lib/easypost/services/referral_customer.rb +103 -0
  97. data/lib/easypost/services/refund.rb +26 -0
  98. data/lib/easypost/services/report.rb +42 -0
  99. data/lib/easypost/services/scan_form.rb +25 -0
  100. data/lib/easypost/services/shipment.rb +106 -0
  101. data/lib/easypost/services/tracker.rb +38 -0
  102. data/lib/easypost/services/user.rb +66 -0
  103. data/lib/easypost/services/webhook.rb +34 -0
  104. data/lib/easypost/services.rb +32 -0
  105. data/lib/easypost/util.rb +116 -161
  106. data/lib/easypost/utilities/constants.rb +5 -0
  107. data/lib/easypost/utilities/json.rb +23 -0
  108. data/lib/easypost/utilities/static_mapper.rb +73 -0
  109. data/lib/easypost/utilities/system.rb +36 -0
  110. data/lib/easypost.rb +14 -136
  111. metadata +177 -65
  112. data/.rubocop.yml +0 -11
  113. data/CODE_OF_CONDUCT.md +0 -16
  114. data/CONTRIBUTING.md +0 -47
  115. data/SECURITY.md +0 -7
  116. data/SUPPORT.md +0 -3
  117. data/easycop.yml +0 -180
  118. data/lib/easypost/address.rb +0 -40
  119. data/lib/easypost/api_key.rb +0 -5
  120. data/lib/easypost/batch.rb +0 -50
  121. data/lib/easypost/beta/end_shipper.rb +0 -44
  122. data/lib/easypost/beta/referral.rb +0 -110
  123. data/lib/easypost/beta.rb +0 -7
  124. data/lib/easypost/billing.rb +0 -72
  125. data/lib/easypost/brand.rb +0 -13
  126. data/lib/easypost/carrier_account.rb +0 -9
  127. data/lib/easypost/carrier_type.rb +0 -5
  128. data/lib/easypost/customs_info.rb +0 -9
  129. data/lib/easypost/customs_item.rb +0 -9
  130. data/lib/easypost/end_shipper.rb +0 -24
  131. data/lib/easypost/error.rb +0 -32
  132. data/lib/easypost/event.rb +0 -11
  133. data/lib/easypost/object.rb +0 -171
  134. data/lib/easypost/order.rb +0 -37
  135. data/lib/easypost/parcel.rb +0 -9
  136. data/lib/easypost/payment_method.rb +0 -11
  137. data/lib/easypost/pickup.rb +0 -37
  138. data/lib/easypost/rate.rb +0 -9
  139. data/lib/easypost/referral.rb +0 -102
  140. data/lib/easypost/report.rb +0 -23
  141. data/lib/easypost/resource.rb +0 -106
  142. data/lib/easypost/scan_form.rb +0 -11
  143. data/lib/easypost/shipment.rb +0 -155
  144. data/lib/easypost/tracker.rb +0 -12
  145. data/lib/easypost/user.rb +0 -71
  146. data/lib/easypost/webhook.rb +0 -57
@@ -1,155 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
-
5
- # The workhorse of the EasyPost API, a Shipment is made up of a "to" and "from" Address, the Parcel
6
- # being shipped, and any customs forms required for international deliveries.
7
- class EasyPost::Shipment < EasyPost::Resource
8
- # Create a Shipment.
9
- def self.create(params = {}, api_key = nil, with_carbon_offset = false)
10
- wrapped_params = {
11
- shipment: params,
12
- carbon_offset: with_carbon_offset,
13
- }
14
-
15
- response = EasyPost.make_request(:post, url, api_key, wrapped_params)
16
- EasyPost::Util.convert_to_easypost_object(response, api_key)
17
- end
18
-
19
- # Regenerate the rates of a Shipment.
20
- def regenerate_rates(with_carbon_offset = false)
21
- params = {}
22
- params[:carbon_offset] = with_carbon_offset
23
-
24
- response = EasyPost.make_request(:post, "#{url}/rerate", @api_key, params)
25
- refresh_from(response, @api_key)
26
-
27
- self
28
- end
29
-
30
- # Get the SmartRates of a Shipment.
31
- def get_smartrates # rubocop:disable Naming/AccessorMethodName
32
- response = EasyPost.make_request(:get, "#{url}/smartrate", @api_key)
33
-
34
- response.fetch('result', [])
35
- end
36
-
37
- # Buy a Shipment.
38
- def buy(params = {}, with_carbon_offset = false, end_shipper_id = nil)
39
- if params.instance_of?(EasyPost::Rate)
40
- temp = params.clone
41
- params = {}
42
- params[:rate] = temp
43
- end
44
-
45
- if params[:with_carbon_offset]
46
- params[:carbon_offset] = params[:with_carbon_offset]
47
- params.delete(:with_carbon_offset)
48
- else
49
- params[:carbon_offset] = with_carbon_offset
50
- end
51
-
52
- if end_shipper_id
53
- params[:end_shipper_id] = end_shipper_id
54
- end
55
-
56
- response = EasyPost.make_request(:post, "#{url}/buy", @api_key, params)
57
- refresh_from(response, @api_key)
58
-
59
- self
60
- end
61
-
62
- # Insure a Shipment.
63
- def insure(params = {})
64
- if params.is_a?(Integer) || params.is_a?(Float)
65
- temp = params.clone
66
- params = {}
67
- params[:amount] = temp
68
- end
69
-
70
- response = EasyPost.make_request(:post, "#{url}/insure", @api_key, params)
71
- refresh_from(response, @api_key)
72
-
73
- self
74
- end
75
-
76
- # Refund a Shipment.
77
- def refund(params = {})
78
- response = EasyPost.make_request(:get, "#{url}/refund", @api_key, params)
79
- refresh_from(response, @api_key)
80
-
81
- self
82
- end
83
-
84
- # Convert the label format of a Shipment.
85
- def label(params = {})
86
- if params.is_a?(String)
87
- temp = params.clone
88
- params = {}
89
- params[:file_format] = temp
90
- end
91
-
92
- response = EasyPost.make_request(:get, "#{url}/label", @api_key, params)
93
- refresh_from(response, @api_key)
94
-
95
- self
96
- end
97
-
98
- # Get the lowest rate of a Shipment (can exclude by having `'!'` as the first element of your optional filter lists).
99
- def lowest_rate(carriers = [], services = [])
100
- EasyPost::Util.get_lowest_object_rate(self, carriers, services)
101
- end
102
-
103
- # Get the lowest smartrate of a Shipment.
104
- def lowest_smartrate(delivery_days, delivery_accuracy)
105
- smartrates = get_smartrates
106
- EasyPost::Shipment.get_lowest_smartrate(smartrates, delivery_days, delivery_accuracy)
107
- end
108
-
109
- # Get the lowest smartrate from a list of smartrates.
110
- def self.get_lowest_smartrate(smartrates, delivery_days, delivery_accuracy)
111
- valid_delivery_accuracy_values = Set[
112
- 'percentile_50',
113
- 'percentile_75',
114
- 'percentile_85',
115
- 'percentile_90',
116
- 'percentile_95',
117
- 'percentile_97',
118
- 'percentile_99',
119
- ]
120
- lowest_smartrate = nil
121
-
122
- unless valid_delivery_accuracy_values.include?(delivery_accuracy.downcase)
123
- raise EasyPost::Error.new("Invalid delivery accuracy value, must be one of: #{valid_delivery_accuracy_values}")
124
- end
125
-
126
- smartrates.each do |rate|
127
- next if rate['time_in_transit'][delivery_accuracy] > delivery_days.to_i
128
-
129
- if lowest_smartrate.nil? || rate['rate'].to_f < lowest_smartrate['rate'].to_f
130
- lowest_smartrate = rate
131
- end
132
- end
133
-
134
- if lowest_smartrate.nil?
135
- raise EasyPost::Error.new('No rates found.')
136
- end
137
-
138
- lowest_smartrate
139
- end
140
-
141
- # Generate a form for a Shipment.
142
- def generate_form(form_type, form_options = {})
143
- params = {}
144
- params[:type] = form_type
145
- merged_params = params.merge(form_options)
146
- wrapped_params = {
147
- form: merged_params,
148
- }
149
-
150
- response = EasyPost.make_request(:post, "#{url}/forms", @api_key, wrapped_params)
151
- refresh_from(response, @api_key)
152
-
153
- self
154
- end
155
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # A Tracker object contains all of the tracking information for a package.
4
- class EasyPost::Tracker < EasyPost::Resource
5
- # Create multiple Tracker objects in bulk.
6
- def self.create_list(params = {}, api_key = nil)
7
- url = "#{self.url}/create_list"
8
- new_params = { 'trackers' => params }
9
- EasyPost.make_request(:post, url, api_key, new_params)
10
- true # This endpoint does not return a response so we return true here instead
11
- end
12
- end
data/lib/easypost/user.rb DELETED
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The User object can be used to manage your own account and to create child accounts.
4
- class EasyPost::User < EasyPost::Resource
5
- # Create a child User.
6
- def self.create(params = {}, api_key = nil)
7
- response = EasyPost.make_request(:post, url, api_key, { class_name.to_sym => params })
8
- EasyPost::Util.convert_to_easypost_object(response, api_key)
9
- end
10
-
11
- # Save (update) a User.
12
- def save
13
- if @unsaved_values.length.positive?
14
- values = {}
15
- @unsaved_values.each { |k| values[k] = @values[k] }
16
-
17
- wrapped_params = { user: values }
18
-
19
- response = EasyPost.make_request(:patch, url, @api_key, wrapped_params)
20
- refresh_from(response, api_key)
21
- end
22
- self
23
- end
24
-
25
- # Delete a User.
26
- def delete
27
- EasyPost.make_request(:delete, url, @api_key)
28
- self
29
- end
30
-
31
- # Retrieve the authenticated User.
32
- def self.retrieve_me
33
- all
34
- end
35
-
36
- # Retrieve a list of ApiKey objects.
37
- def self.all_api_keys
38
- EasyPost::ApiKey.all
39
- end
40
-
41
- # Retrieve a list of ApiKey objects of a child User.
42
- def api_keys
43
- api_keys = EasyPost::User.all_api_keys
44
-
45
- if api_keys.id == id
46
- my_api_keys = api_keys.keys
47
- else
48
- api_keys.children.each do |child|
49
- if child.id == id
50
- my_api_keys = child.keys
51
- break
52
- end
53
- end
54
- end
55
-
56
- my_api_keys
57
- end
58
-
59
- # Update the Brand of a User.
60
- def update_brand(**attrs)
61
- brand = EasyPost::Brand.new
62
- data = { object: 'Brand', user_id: id, **attrs }
63
- # Add accessors manually because there's no API to retrieve a brand
64
- brand.add_accessors(data.keys)
65
- # Assigning values with accessors defined above
66
- data.each do |key, val|
67
- brand.send("#{key}=", val)
68
- end
69
- brand.save
70
- end
71
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Each Webhook contains the url which EasyPost will notify whenever an object in our system updates. Several types of objects are processed
4
- # asynchronously in the EasyPost system, so whenever an object updates, an Event is sent via HTTP POST to each configured webhook URL.
5
- class EasyPost::Webhook < EasyPost::Resource
6
- # Update a Webhook.
7
- def update(params = {})
8
- # NOTE: This method is redefined here since the "url" method conflicts with the objects field
9
- unless id
10
- raise EasyPost::Error.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}")
11
- end
12
-
13
- instance_url = "#{self.class.url}/#{CGI.escape(id)}"
14
-
15
- response = EasyPost.make_request(:patch, instance_url, @api_key, params)
16
- refresh_from(response, api_key)
17
-
18
- self
19
- end
20
-
21
- # Delete a Webhook.
22
- def delete
23
- # NOTE: This method is redefined here since the "url" method conflicts with the objects field
24
- unless id
25
- raise EasyPost::Error.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}")
26
- end
27
-
28
- instance_url = "#{self.class.url}/#{CGI.escape(id)}"
29
-
30
- response = EasyPost.make_request(:delete, instance_url, @api_key)
31
- refresh_from(response, api_key)
32
-
33
- self
34
- end
35
-
36
- # Validate a webhook by comparing the HMAC signature header sent from EasyPost to your shared secret.
37
- # If the signatures do not match, an error will be raised signifying the webhook either did not originate
38
- # from EasyPost or the secrets do not match. If the signatures do match, the `event_body` will be returned
39
- # as JSON.
40
- def self.validate_webhook(event_body, headers, webhook_secret)
41
- easypost_hmac_signature = headers['X-Hmac-Signature']
42
-
43
- if easypost_hmac_signature.nil?
44
- raise EasyPost::Error.new('Webhook received does not contain an HMAC signature.')
45
- end
46
-
47
- encoded_webhook_secret = webhook_secret.unicode_normalize(:nfkd).encode('utf-8')
48
-
49
- expected_signature = OpenSSL::HMAC.hexdigest('sha256', encoded_webhook_secret, event_body)
50
- digest = "hmac-sha256-hex=#{expected_signature}"
51
- unless digest == easypost_hmac_signature
52
- raise EasyPost::Error.new('Webhook received did not originate from EasyPost or had a webhook secret mismatch.')
53
- end
54
-
55
- JSON.parse(event_body)
56
- end
57
- end