easypost 4.8.1 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
data/easypost.gemspec CHANGED
@@ -7,7 +7,7 @@ require 'easypost/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'easypost'
9
9
  spec.version = EasyPost::VERSION
10
- spec.licenses = ['MIT']
10
+ spec.license = 'MIT'
11
11
  spec.summary = 'EasyPost Ruby Client Library'
12
12
  spec.description = 'Client library for accessing the EasyPost shipping API via Ruby.'
13
13
  spec.authors = 'EasyPost Developers'
@@ -15,20 +15,24 @@ Gem::Specification.new do |spec|
15
15
  spec.homepage = 'https://www.easypost.com/docs'
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
- f.match(%r{^(test|spec|features)/})
18
+ f.match(%r{^(docs|examples|spec)/})
19
19
  end
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ['lib']
22
- spec.required_ruby_version = '>= 2.5'
22
+ spec.required_ruby_version = '>= 2.6'
23
23
 
24
- spec.add_development_dependency 'brakeman', '~> 5.2'
24
+ spec.add_development_dependency 'brakeman', '~> 5.4'
25
+ spec.add_development_dependency 'faraday', '~> 2.7.5' # used for integration tests
25
26
  spec.add_development_dependency 'pry', '~> 0.14'
27
+ spec.add_development_dependency 'psych', '~> 5.1'
26
28
  spec.add_development_dependency 'rake', '~> 13.0'
27
- spec.add_development_dependency 'rspec', '~> 3.10'
28
- spec.add_development_dependency 'rubocop', '= 1.27' # rubocop 1.28 requires Ruby 2.6+
29
- spec.add_development_dependency 'rubocop-rspec', '~> 2.7'
30
- spec.add_development_dependency 'simplecov', '~> 0.21'
29
+ spec.add_development_dependency 'rdoc', '~> 6.5'
30
+ spec.add_development_dependency 'rspec', '~> 3.12'
31
+ spec.add_development_dependency 'rubocop', '~> 1.49'
32
+ spec.add_development_dependency 'rubocop-rspec', '2.19' # pin to 2.19 because latest version doesn't support Ruby 2.6
33
+ spec.add_development_dependency 'simplecov', '~> 0.22'
31
34
  spec.add_development_dependency 'simplecov-lcov', '~> 0.8'
32
- spec.add_development_dependency 'vcr', '= 6.0' # VCR 6.1 requires Ruby 2.6+
33
- spec.add_development_dependency 'webmock', '~> 3.14'
35
+ spec.add_development_dependency 'typhoeus', '~> 1.4.0' # used for integration tests
36
+ spec.add_development_dependency 'vcr', '~> 6.1'
37
+ spec.add_development_dependency 'webmock', '~> 3.18'
34
38
  end
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'services'
4
+ require_relative 'http_client'
5
+ require_relative 'internal_utilities'
6
+ require 'json'
7
+ require 'securerandom'
8
+
9
+ class EasyPost::Client
10
+ attr_reader :open_timeout, :read_timeout, :api_base
11
+
12
+ # Initialize a new Client object
13
+ # @param api_key [String] the API key to be used for requests
14
+ # @param read_timeout [Integer] (60) the number of seconds to wait for a response before timing out
15
+ # @param open_timeout [Integer] (30) the number of seconds to wait for the connection to open before timing out
16
+ # @param api_base [String] ('https://api.easypost.com') the base URL for the API
17
+ # @param custom_client_exec [Proc] (nil) a custom client execution block to be used for requests instead of the default HTTP client (function signature: method, uri, headers, open_timeout, read_timeout, body = nil)
18
+ # @return [EasyPost::Client] the client object
19
+ def initialize(api_key:, read_timeout: 60, open_timeout: 30, api_base: 'https://api.easypost.com',
20
+ custom_client_exec: nil)
21
+ raise EasyPost::Errors::MissingParameterError.new('api_key') if api_key.nil?
22
+
23
+ @api_key = api_key
24
+ @api_base = api_base
25
+ @api_version = 'v2'
26
+ @read_timeout = read_timeout
27
+ @open_timeout = open_timeout
28
+ @lib_version = File.open(File.expand_path('../../VERSION', __dir__)).read.strip
29
+
30
+ # Make an HTTP client once, reuse it for all requests made by this client
31
+ # Configuration is immutable, so this is safe
32
+ @http_client = EasyPost::HttpClient.new(api_base, http_config, custom_client_exec)
33
+ end
34
+
35
+ SERVICE_CLASSES = [
36
+ EasyPost::Services::Address,
37
+ EasyPost::Services::ApiKey,
38
+ EasyPost::Services::Batch,
39
+ EasyPost::Services::BetaRate,
40
+ EasyPost::Services::BetaReferralCustomer,
41
+ EasyPost::Services::Billing,
42
+ EasyPost::Services::CarrierAccount,
43
+ EasyPost::Services::CarrierMetadata,
44
+ EasyPost::Services::CustomsInfo,
45
+ EasyPost::Services::CustomsItem,
46
+ EasyPost::Services::EndShipper,
47
+ EasyPost::Services::Event,
48
+ EasyPost::Services::Insurance,
49
+ EasyPost::Services::Order,
50
+ EasyPost::Services::Parcel,
51
+ EasyPost::Services::Pickup,
52
+ EasyPost::Services::Rate,
53
+ EasyPost::Services::ReferralCustomer,
54
+ EasyPost::Services::Refund,
55
+ EasyPost::Services::Report,
56
+ EasyPost::Services::ScanForm,
57
+ EasyPost::Services::Shipment,
58
+ EasyPost::Services::Tracker,
59
+ EasyPost::Services::User,
60
+ EasyPost::Services::Webhook,
61
+ ].freeze
62
+
63
+ # Loop over the SERVICE_CLASSES to automatically define the method and instance variable instead of manually define it
64
+ SERVICE_CLASSES.each do |cls|
65
+ define_method(EasyPost::InternalUtilities.to_snake_case(cls.name.split('::').last)) do
66
+ instance_variable_set("@#{EasyPost::InternalUtilities.to_snake_case(cls.name.split('::').last)}", cls.new(self))
67
+ end
68
+ end
69
+
70
+ # Make an HTTP request
71
+ #
72
+ # @param method [Symbol] the HTTP Verb (get, method, put, post, etc.)
73
+ # @param endpoint [String] URI path of the resource
74
+ # @param cls [Class] the class to deserialize to
75
+ # @param body [Object] (nil) object to be dumped to JSON
76
+ # @param api_version [String] the version of API to hit
77
+ # @raise [EasyPost::Error] if the response has a non-2xx status code
78
+ # @return [Hash] JSON object parsed from the response body
79
+ def make_request(
80
+ method,
81
+ endpoint,
82
+ cls = EasyPost::Models::EasyPostObject,
83
+ body = nil,
84
+ api_version = EasyPost::InternalUtilities::Constants::API_VERSION
85
+ )
86
+ response = @http_client.request(method, endpoint, nil, body, api_version)
87
+
88
+ potential_error = EasyPost::Errors::ApiError.handle_api_error(response)
89
+ raise potential_error unless potential_error.nil?
90
+
91
+ EasyPost::InternalUtilities::Json.convert_json_to_object(response.body, cls)
92
+ end
93
+
94
+ # Subscribe a request hook
95
+ #
96
+ # @param name [Symbol] the name of the hook. Defaults ot a ranom hexadecimal-based symbol
97
+ # @param block [Block] a code block that will be executed before a request is made
98
+ # @return [Symbol] the name of the request hook
99
+ def subscribe_request_hook(name = SecureRandom.hex.to_sym, &block)
100
+ EasyPost::Hooks.subscribe(:request, name, block)
101
+ end
102
+
103
+ # Unsubscribe a request hook
104
+ #
105
+ # @param name [Symbol] the name of the hook
106
+ # @return [Block] the hook code block
107
+ def unsubscribe_request_hook(name)
108
+ EasyPost::Hooks.unsubscribe(:request, name)
109
+ end
110
+
111
+ # Unsubscribe all request hooks
112
+ #
113
+ # @return [Hash] a hash containing all request hook subscriptions
114
+ def unsubscribe_all_request_hooks
115
+ EasyPost::Hooks.unsubscribe_all(:request)
116
+ end
117
+
118
+ # Subscribe a response hook
119
+ #
120
+ # @param name [Symbol] the name of the hook. Defaults ot a ranom hexadecimal-based symbol
121
+ # @param block [Block] a code block that will be executed upon receiving the response from a request
122
+ # @return [Symbol] the name of the response hook
123
+ def subscribe_response_hook(name = SecureRandom.hex.to_sym, &block)
124
+ EasyPost::Hooks.subscribe(:response, name, block)
125
+ end
126
+
127
+ # Unsubscribe a response hook
128
+ #
129
+ # @param name [Symbol] the name of the hook
130
+ # @return [Block] the hook code block
131
+ def unsubscribe_response_hook(name)
132
+ EasyPost::Hooks.unsubscribe(:response, name)
133
+ end
134
+
135
+ # Unsubscribe all response hooks
136
+ #
137
+ # @return [Hash] a hash containing all response hook subscriptions
138
+ def unsubscribe_all_response_hooks
139
+ EasyPost::Hooks.unsubscribe_all(:response)
140
+ end
141
+
142
+ private
143
+
144
+ def http_config
145
+ http_config = {
146
+ read_timeout: @read_timeout,
147
+ open_timeout: @open_timeout,
148
+ headers: default_headers,
149
+ }
150
+
151
+ http_config[:min_version] = OpenSSL::SSL::TLS1_2_VERSION
152
+ http_config
153
+ end
154
+
155
+ def default_headers
156
+ {
157
+ 'Content-Type' => 'application/json',
158
+ 'User-Agent' => user_agent,
159
+ 'Authorization' => authorization,
160
+ }
161
+ end
162
+
163
+ def user_agent
164
+ ruby_version = EasyPost::InternalUtilities::System.ruby_version
165
+ ruby_patchlevel = EasyPost::InternalUtilities::System.ruby_patchlevel
166
+
167
+ "EasyPost/#{@api_version} " \
168
+ "RubyClient/#{@lib_version} " \
169
+ "Ruby/#{ruby_version}-p#{ruby_patchlevel} " \
170
+ "OS/#{EasyPost::InternalUtilities::System.os_name} " \
171
+ "OSVersion/#{EasyPost::InternalUtilities::System.os_version} " \
172
+ "OSArch/#{EasyPost::InternalUtilities::System.os_arch}"
173
+ end
174
+
175
+ def authorization
176
+ "Bearer #{@api_key}"
177
+ end
178
+ end
@@ -10,9 +10,7 @@ EasyPost::Connection = Struct.new(:uri, :config, keyword_init: true) do
10
10
  # @raise [EasyPost::Error] if the response has a non-2xx status code
11
11
  # @return [Hash] JSON object parsed from the response body
12
12
  def call(method, path, api_key = nil, body = nil)
13
- if api_key.nil?
14
- raise EasyPost::Error, 'No API key provided.'
15
- end
13
+ raise EasyPost::Errors::MissingParameterError.new('api_key') if api_key.nil?
16
14
 
17
15
  connection =
18
16
  if config[:proxy]
@@ -49,7 +47,7 @@ EasyPost::Connection = Struct.new(:uri, :config, keyword_init: true) do
49
47
  end
50
48
 
51
49
  request = Net::HTTP.const_get(method.capitalize).new(path)
52
- request.body = JSON.dump(EasyPost::Util.objects_to_ids(body)) if body
50
+ request.body = JSON.dump(EasyPost::InternalUtilities.objects_to_ids(body)) if body
53
51
 
54
52
  EasyPost.default_headers.each_pair { |h, v| request[h] = v }
55
53
  request['Authorization'] = EasyPost.authorization(api_key)
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::Constants
4
+ API_ERROR_DETAILS_PARSING_ERROR = 'API error details could not be parsed.'
5
+ INVALID_PARAMETER = '%s is not a valid parameter.'
6
+ INVALID_PAYMENT_METHOD = 'The chosen payment method is not valid. Please try again.'
7
+ MISSING_REQUIRED_PARAMETER = 'Required parameter %s is missing.'
8
+ NO_MATCHING_RATES = 'No matching rates found.'
9
+ NO_MORE_PAGES = 'There are no more pages to retrieve.'
10
+ NO_PAYMENT_METHODS = 'Billing has not been setup for this user. Please add a payment method.'
11
+ STRIPE_CARD_CREATE_FAILED = 'Could not send card details to Stripe, please try again later.'
12
+ UNEXPECTED_HTTP_STATUS_CODE = 'Unexpected HTTP status code received: %s'
13
+ WEBHOOK_MISSING_SIGNATURE = 'Webhook received does not contain an HMAC signature.'
14
+ WEBHOOK_SIGNATURE_MISMATCH = 'Webhook received did not originate from EasyPost or had a webhook secret mismatch.'
15
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../easy_post_error'
4
+ require 'easypost/constants'
5
+
6
+ class EasyPost::Errors::ApiError < EasyPost::Errors::EasyPostError
7
+ attr_reader :status_code, :code, :errors
8
+
9
+ def initialize(message, status_code = nil, error_code = nil, sub_errors = nil)
10
+ super message
11
+ @status_code = status_code
12
+ @code = error_code
13
+ @errors = sub_errors
14
+ end
15
+
16
+ def pretty_print
17
+ error_string = "#{code} (#{status_code}): #{message}"
18
+ errors&.each do |error|
19
+ error_string += "\nField: #{error.field}\nMessage: #{error.message}"
20
+ end
21
+ error_string
22
+ end
23
+
24
+ # Recursively traverses a JSON element to extract error messages and returns them as a comma-separated string.
25
+ def self.collect_error_messages(error_message, messages_list)
26
+ case error_message
27
+ when Hash
28
+ error_message.each_value { |value| collect_error_messages(value, messages_list) }
29
+ when Array
30
+ error_message.each { |value| collect_error_messages(value, messages_list) }
31
+ else
32
+ messages_list.push(error_message.to_s)
33
+ end
34
+
35
+ messages_list.join(', ')
36
+ end
37
+
38
+ def self.handle_api_error(response)
39
+ status_code = response.code
40
+ status_code = status_code.to_i if status_code.is_a?(String)
41
+
42
+ if status_code >= 200 && status_code <= 299
43
+ return nil
44
+ end
45
+
46
+ # Try to parse the response body as JSON
47
+ begin
48
+ error_data = JSON.parse(response.body)['error']
49
+
50
+ error_message = error_data['message']
51
+ error_type = error_data['code']
52
+ errors = error_data['errors']&.map do |error|
53
+ EasyPost::Models::Error.from_api_error_response(error)
54
+ end
55
+ rescue StandardError
56
+ error_message = response.code.to_s
57
+ error_type = EasyPost::Constants::API_ERROR_DETAILS_PARSING_ERROR
58
+ errors = nil
59
+ end
60
+
61
+ cls = exception_cls_from_status_code(status_code)
62
+
63
+ if cls == EasyPost::Errors::UnknownApiError
64
+ return EasyPost::Errors::UnknownApiError.new(
65
+ EasyPost::Constants::UNEXPECTED_HTTP_STATUS_CODE % status_code,
66
+ status_code,
67
+ )
68
+ end
69
+
70
+ # Return (don't throw here) an instance of the appropriate error class
71
+ cls.new(error_message, status_code, error_type, errors)
72
+ end
73
+
74
+ def self.exception_cls_from_status_code(status_code)
75
+ case status_code
76
+ when 0
77
+ EasyPost::Errors::ConnectionError
78
+ when 300, 301, 302, 303, 304, 305, 306, 307, 308
79
+ EasyPost::Errors::RedirectError
80
+ when 400
81
+ EasyPost::Errors::BadRequestError
82
+ when 401
83
+ EasyPost::Errors::UnauthorizedError
84
+ when 402
85
+ EasyPost::Errors::PaymentError
86
+ when 403
87
+ EasyPost::Errors::ForbiddenError
88
+ when 404
89
+ EasyPost::Errors::NotFoundError
90
+ when 405
91
+ EasyPost::Errors::MethodNotAllowedError
92
+ when 408
93
+ EasyPost::Errors::TimeoutError
94
+ when 422
95
+ EasyPost::Errors::InvalidRequestError
96
+ when 429
97
+ EasyPost::Errors::RateLimitError
98
+ when 500
99
+ EasyPost::Errors::InternalServerError
100
+ when 502, 504
101
+ EasyPost::Errors::GatewayTimeoutError
102
+ when 503
103
+ EasyPost::Errors::ServiceUnavailableError
104
+ else
105
+ EasyPost::Errors::UnknownApiError
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::BadRequestError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::ConnectionError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::Errors::ExternalApiError < EasyPost::Errors::EasyPostError
4
+ attr_reader :status_code
5
+
6
+ def initialize(message, status_code = nil)
7
+ super message
8
+ @status_code = status_code
9
+ end
10
+
11
+ def pretty_print
12
+ if status_code.nil?
13
+ return message
14
+ end
15
+
16
+ "(#{status_code}): #{message}"
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::ForbiddenError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::GatewayTimeoutError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::InternalServerError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::InvalidRequestError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::MethodNotAllowedError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::NotFoundError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::PaymentError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::ProxyError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::RateLimitError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::RedirectError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::RetryError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::ServiceUnavailableError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::SslError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::TimeoutError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::UnauthorizedError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_error'
4
+
5
+ class EasyPost::Errors::UnknownApiError < EasyPost::Errors::ApiError
6
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::Errors::EasyPostError < StandardError
4
+ def pretty_print
5
+ message.to_s
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::Errors::EndOfPaginationError < EasyPost::Errors::EasyPostError
4
+ def initialize
5
+ super EasyPost::Constants::NO_MORE_PAGES
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::Errors::FilteringError < EasyPost::Errors::EasyPostError
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::Errors::InvalidObjectError < EasyPost::Errors::EasyPostError
4
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easypost/constants'
4
+
5
+ class EasyPost::Errors::InvalidParameterError < EasyPost::Errors::EasyPostError
6
+ # @param [String] parameter The name of the parameter that was invalid.
7
+ # @param [String] suggestion Optional suggestion message for a valid parameter.
8
+ def initialize(parameter, suggestion = nil)
9
+ super EasyPost::Constants::INVALID_PARAMETER % parameter + (suggestion.nil? ? '' : " #{suggestion}")
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easypost/constants'
4
+
5
+ class EasyPost::Errors::MissingParameterError < EasyPost::Errors::EasyPostError
6
+ def initialize(parameter)
7
+ super EasyPost::Constants::MISSING_REQUIRED_PARAMETER % parameter
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::Errors::SignatureVerificationError < EasyPost::Errors::EasyPostError
4
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EasyPost::Errors
4
+ end
5
+
6
+ require_relative 'errors/easy_post_error'
7
+ require_relative 'errors/end_of_pagination_error'
8
+ require_relative 'errors/api/external_api_error'
9
+ require_relative 'errors/filtering_error'
10
+ require_relative 'errors/invalid_object_error'
11
+ require_relative 'errors/invalid_parameter_error'
12
+ require_relative 'errors/missing_parameter_error'
13
+ require_relative 'errors/signature_verification_error'
14
+ require_relative 'errors/api/api_error'
15
+ require_relative 'errors/api/bad_request_error'
16
+ require_relative 'errors/api/connection_error'
17
+ require_relative 'errors/api/forbidden_error'
18
+ require_relative 'errors/api/gateway_timeout_error'
19
+ require_relative 'errors/api/internal_server_error'
20
+ require_relative 'errors/api/invalid_request_error'
21
+ require_relative 'errors/api/method_not_allowed_error'
22
+ require_relative 'errors/api/not_found_error'
23
+ require_relative 'errors/api/payment_error'
24
+ require_relative 'errors/api/proxy_error'
25
+ require_relative 'errors/api/rate_limit_error'
26
+ require_relative 'errors/api/redirect_error'
27
+ require_relative 'errors/api/retry_error'
28
+ require_relative 'errors/api/service_unavailable_error'
29
+ require_relative 'errors/api/ssl_error'
30
+ require_relative 'errors/api/timeout_error'
31
+ require_relative 'errors/api/unauthorized_error'
32
+ require_relative 'errors/api/unknown_api_error'
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::Hooks::RequestContext
4
+ attr_reader :method, :path, :headers, :request_body, :request_timestamp, :request_uuid
5
+
6
+ def initialize(method:, path:, headers:, request_body:, request_timestamp:, request_uuid:)
7
+ @method = method
8
+ @path = path
9
+ @headers = headers
10
+ @request_body = request_body
11
+ @request_timestamp = request_timestamp
12
+ @request_uuid = request_uuid
13
+
14
+ freeze
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::Hooks::ResponseContext
4
+ attr_reader :http_status, :method, :path, :headers, :response_body,
5
+ :request_timestamp, :response_timestamp, :request_uuid,
6
+ :client_response_object
7
+
8
+ def initialize(http_status:, method:, path:, headers:, response_body:,
9
+ request_timestamp:, response_timestamp:, request_uuid:,
10
+ client_response_object:)
11
+ @http_status = http_status
12
+ @method = method
13
+ @path = path
14
+ @headers = headers
15
+ @response_body = response_body
16
+ @request_timestamp = request_timestamp
17
+ @response_timestamp = response_timestamp
18
+ @request_uuid = request_uuid
19
+ @client_response_object = client_response_object
20
+
21
+ freeze
22
+ end
23
+ end