easypost 4.13.1 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +24 -1
  3. data/.gitignore +11 -11
  4. data/CHANGELOG.md +23 -2
  5. data/Makefile +15 -10
  6. data/README.md +49 -39
  7. data/UPGRADE_GUIDE.md +119 -0
  8. data/VERSION +1 -1
  9. data/easypost.gemspec +12 -10
  10. data/lib/easypost/client.rb +129 -0
  11. data/lib/easypost/connection.rb +2 -4
  12. data/lib/easypost/constants.rb +15 -0
  13. data/lib/easypost/errors/api/api_error.rb +106 -0
  14. data/lib/easypost/errors/api/connection_error.rb +6 -0
  15. data/lib/easypost/errors/api/external_api_error.rb +18 -0
  16. data/lib/easypost/errors/api/forbidden_error.rb +6 -0
  17. data/lib/easypost/errors/api/gateway_timeout_error.rb +6 -0
  18. data/lib/easypost/errors/api/internal_server_error.rb +6 -0
  19. data/lib/easypost/errors/api/invalid_request_error.rb +6 -0
  20. data/lib/easypost/errors/api/method_not_allowed_error.rb +6 -0
  21. data/lib/easypost/errors/api/not_found_error.rb +6 -0
  22. data/lib/easypost/errors/api/payment_error.rb +6 -0
  23. data/lib/easypost/errors/api/proxy_error.rb +6 -0
  24. data/lib/easypost/errors/api/rate_limit_error.rb +6 -0
  25. data/lib/easypost/errors/api/redirect_error.rb +6 -0
  26. data/lib/easypost/errors/api/retry_error.rb +6 -0
  27. data/lib/easypost/errors/api/service_unavailable_error.rb +6 -0
  28. data/lib/easypost/errors/api/ssl_error.rb +6 -0
  29. data/lib/easypost/errors/api/timeout_error.rb +6 -0
  30. data/lib/easypost/errors/api/unauthorized_error.rb +6 -0
  31. data/lib/easypost/errors/api/unknown_api_error.rb +6 -0
  32. data/lib/easypost/errors/easy_post_error.rb +7 -0
  33. data/lib/easypost/errors/end_of_pagination_error.rb +7 -0
  34. data/lib/easypost/errors/filtering_error.rb +4 -0
  35. data/lib/easypost/errors/invalid_object_error.rb +4 -0
  36. data/lib/easypost/errors/invalid_parameter_error.rb +11 -0
  37. data/lib/easypost/errors/missing_parameter_error.rb +9 -0
  38. data/lib/easypost/errors/signature_verification_error.rb +4 -0
  39. data/lib/easypost/errors.rb +31 -0
  40. data/lib/easypost/http_client.rb +65 -0
  41. data/lib/easypost/internal_utilities.rb +66 -0
  42. data/lib/easypost/models/address.rb +5 -0
  43. data/lib/easypost/models/api_key.rb +5 -0
  44. data/lib/easypost/models/base.rb +58 -0
  45. data/lib/easypost/models/batch.rb +5 -0
  46. data/lib/easypost/models/brand.rb +5 -0
  47. data/lib/easypost/{carbon_offset.rb → models/carbon_offset.rb} +1 -1
  48. data/lib/easypost/models/carrier_account.rb +5 -0
  49. data/lib/easypost/models/customs_info.rb +5 -0
  50. data/lib/easypost/models/customs_item.rb +5 -0
  51. data/lib/easypost/models/end_shipper.rb +5 -0
  52. data/lib/easypost/models/error.rb +21 -0
  53. data/lib/easypost/models/event.rb +5 -0
  54. data/lib/easypost/models/insurance.rb +6 -0
  55. data/lib/easypost/models/order.rb +9 -0
  56. data/lib/easypost/models/parcel.rb +5 -0
  57. data/lib/easypost/{payload.rb → models/payload.rb} +1 -1
  58. data/lib/easypost/models/payment_method.rb +5 -0
  59. data/lib/easypost/models/pickup.rb +9 -0
  60. data/lib/easypost/{pickup_rate.rb → models/pickup_rate.rb} +1 -1
  61. data/lib/easypost/{postage_label.rb → models/postage_label.rb} +1 -1
  62. data/lib/easypost/models/rate.rb +5 -0
  63. data/lib/easypost/models/referral.rb +5 -0
  64. data/lib/easypost/models/refund.rb +5 -0
  65. data/lib/easypost/models/report.rb +5 -0
  66. data/lib/easypost/models/scan_form.rb +6 -0
  67. data/lib/easypost/models/shipment.rb +10 -0
  68. data/lib/easypost/{tax_identifier.rb → models/tax_identifier.rb} +1 -1
  69. data/lib/easypost/models/tracker.rb +5 -0
  70. data/lib/easypost/models/user.rb +5 -0
  71. data/lib/easypost/models/webhook.rb +6 -0
  72. data/lib/easypost/models.rb +35 -0
  73. data/lib/easypost/services/address.rb +50 -0
  74. data/lib/easypost/services/api_key.rb +8 -0
  75. data/lib/easypost/services/base.rb +27 -0
  76. data/lib/easypost/services/batch.rb +53 -0
  77. data/lib/easypost/services/beta_rate.rb +12 -0
  78. data/lib/easypost/services/beta_referral_customer.rb +40 -0
  79. data/lib/easypost/services/billing.rb +75 -0
  80. data/lib/easypost/services/carrier_account.rb +44 -0
  81. data/lib/easypost/services/carrier_metadata.rb +22 -0
  82. data/lib/easypost/services/customs_info.rb +17 -0
  83. data/lib/easypost/services/customs_item.rb +15 -0
  84. data/lib/easypost/services/end_shipper.rb +31 -0
  85. data/lib/easypost/services/event.rb +32 -0
  86. data/lib/easypost/services/insurance.rb +26 -0
  87. data/lib/easypost/services/order.rb +30 -0
  88. data/lib/easypost/services/parcel.rb +16 -0
  89. data/lib/easypost/services/pickup.rb +40 -0
  90. data/lib/easypost/services/rate.rb +8 -0
  91. data/lib/easypost/services/referral_customer.rb +103 -0
  92. data/lib/easypost/services/refund.rb +26 -0
  93. data/lib/easypost/services/report.rb +42 -0
  94. data/lib/easypost/services/scan_form.rb +25 -0
  95. data/lib/easypost/services/shipment.rb +106 -0
  96. data/lib/easypost/services/tracker.rb +38 -0
  97. data/lib/easypost/services/user.rb +66 -0
  98. data/lib/easypost/services/webhook.rb +34 -0
  99. data/lib/easypost/services.rb +32 -0
  100. data/lib/easypost/util.rb +80 -187
  101. data/lib/easypost/utilities/constants.rb +5 -0
  102. data/lib/easypost/utilities/json.rb +23 -0
  103. data/lib/easypost/utilities/static_mapper.rb +73 -0
  104. data/lib/easypost/utilities/system.rb +36 -0
  105. data/lib/easypost.rb +12 -138
  106. metadata +147 -66
  107. data/.rubocop.yml +0 -11
  108. data/easycop.yml +0 -180
  109. data/lib/easypost/address.rb +0 -55
  110. data/lib/easypost/api_key.rb +0 -5
  111. data/lib/easypost/batch.rb +0 -52
  112. data/lib/easypost/beta/end_shipper.rb +0 -44
  113. data/lib/easypost/beta/payment_refund.rb +0 -5
  114. data/lib/easypost/beta/rate.rb +0 -14
  115. data/lib/easypost/beta/referral.rb +0 -158
  116. data/lib/easypost/beta.rb +0 -8
  117. data/lib/easypost/billing.rb +0 -72
  118. data/lib/easypost/brand.rb +0 -13
  119. data/lib/easypost/carrier_account.rb +0 -26
  120. data/lib/easypost/carrier_type.rb +0 -5
  121. data/lib/easypost/customs_info.rb +0 -9
  122. data/lib/easypost/customs_item.rb +0 -9
  123. data/lib/easypost/end_shipper.rb +0 -26
  124. data/lib/easypost/error.rb +0 -46
  125. data/lib/easypost/event.rb +0 -38
  126. data/lib/easypost/insurance.rb +0 -20
  127. data/lib/easypost/object.rb +0 -171
  128. data/lib/easypost/order.rb +0 -37
  129. data/lib/easypost/parcel.rb +0 -9
  130. data/lib/easypost/payment_method.rb +0 -12
  131. data/lib/easypost/pickup.rb +0 -47
  132. data/lib/easypost/rate.rb +0 -9
  133. data/lib/easypost/referral.rb +0 -117
  134. data/lib/easypost/refund.rb +0 -19
  135. data/lib/easypost/report.rb +0 -44
  136. data/lib/easypost/resource.rb +0 -124
  137. data/lib/easypost/scan_form.rb +0 -26
  138. data/lib/easypost/shipment.rb +0 -186
  139. data/lib/easypost/tracker.rb +0 -43
  140. data/lib/easypost/user.rb +0 -74
  141. data/lib/easypost/webhook.rb +0 -57
@@ -0,0 +1,106 @@
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 401
81
+ EasyPost::Errors::UnauthorizedError
82
+ when 402
83
+ EasyPost::Errors::PaymentError
84
+ when 403
85
+ EasyPost::Errors::ForbiddenError
86
+ when 404
87
+ EasyPost::Errors::NotFoundError
88
+ when 405
89
+ EasyPost::Errors::MethodNotAllowedError
90
+ when 408
91
+ EasyPost::Errors::TimeoutError
92
+ when 422
93
+ EasyPost::Errors::InvalidRequestError
94
+ when 429
95
+ EasyPost::Errors::RateLimitError
96
+ when 500
97
+ EasyPost::Errors::InternalServerError
98
+ when 502, 504
99
+ EasyPost::Errors::GatewayTimeoutError
100
+ when 503
101
+ EasyPost::Errors::ServiceUnavailableError
102
+ else
103
+ EasyPost::Errors::UnknownApiError
104
+ end
105
+ end
106
+ 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,31 @@
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/connection_error'
16
+ require_relative 'errors/api/forbidden_error'
17
+ require_relative 'errors/api/gateway_timeout_error'
18
+ require_relative 'errors/api/internal_server_error'
19
+ require_relative 'errors/api/invalid_request_error'
20
+ require_relative 'errors/api/method_not_allowed_error'
21
+ require_relative 'errors/api/not_found_error'
22
+ require_relative 'errors/api/payment_error'
23
+ require_relative 'errors/api/proxy_error'
24
+ require_relative 'errors/api/rate_limit_error'
25
+ require_relative 'errors/api/redirect_error'
26
+ require_relative 'errors/api/retry_error'
27
+ require_relative 'errors/api/service_unavailable_error'
28
+ require_relative 'errors/api/ssl_error'
29
+ require_relative 'errors/api/timeout_error'
30
+ require_relative 'errors/api/unauthorized_error'
31
+ require_relative 'errors/api/unknown_api_error'
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EasyPost::HttpClient
4
+ def initialize(base_url, config, custom_client_exec = nil)
5
+ @base_url = base_url
6
+ @config = config
7
+ @custom_client_exec = custom_client_exec
8
+ end
9
+
10
+ # Execute an HTTP request to the API.
11
+ def request(
12
+ method,
13
+ path,
14
+ headers = nil,
15
+ body = nil,
16
+ api_version = EasyPost::InternalUtilities::Constants::API_VERSION
17
+ )
18
+ # Remove leading slash from path.
19
+ path = path[1..] if path[0] == '/'
20
+
21
+ uri = URI.parse("#{@base_url}/#{api_version}/#{path}")
22
+ headers = @config[:headers].merge(headers || {})
23
+ body = JSON.dump(EasyPost::InternalUtilities.objects_to_ids(body)) if body
24
+ open_timeout = @config[:open_timeout]
25
+ read_timeout = @config[:read_timeout]
26
+
27
+ # Execute the request, return the response.
28
+
29
+ if @custom_client_exec
30
+ @custom_client_exec.call(method, uri, headers, open_timeout, read_timeout, body)
31
+ else
32
+ default_request_execute(method, uri, headers, open_timeout, read_timeout, body)
33
+ end
34
+ end
35
+
36
+ def default_request_execute(method, uri, headers, open_timeout, read_timeout, body = nil)
37
+ # Create the request, set the headers and body if necessary.
38
+ request = Net::HTTP.const_get(method.capitalize).new(uri)
39
+ headers.each { |k, v| request[k] = v }
40
+ request.body = body if body
41
+
42
+ begin
43
+ # Attempt to make the request and return the response.
44
+ Net::HTTP.start(
45
+ uri.host,
46
+ uri.port,
47
+ use_ssl: true,
48
+ read_timeout: read_timeout,
49
+ open_timeout: open_timeout,
50
+ ) do |http|
51
+ http.request(request)
52
+ end
53
+ rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EHOSTUNREACH => e
54
+ # Raise a timeout error if the request times out.
55
+ raise EasyPost::Errors::TimeoutError.new(e.message)
56
+ rescue OpenSSL::SSL::SSLError => e
57
+ # Raise an SSL error if the request fails due to an SSL error.
58
+ raise EasyPost::Errors::SslError.new(e.message)
59
+ rescue StandardError => e
60
+ # Raise an unknown HTTP error if anything else causes the request to fail to complete
61
+ # (this is different from processing 4xx/5xx errors from the API)
62
+ raise EasyPost::Errors::UnknownApiError.new(e.message)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EasyPost::InternalUtilities
4
+ # Convert a string to snake case
5
+ def self.to_snake_case(str)
6
+ str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
7
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
8
+ .downcase
9
+ end
10
+
11
+ # Form-encode a multi-layer dictionary to a one-layer dictionary.
12
+ def self.form_encode_params(hash, parent_keys = [], parent_dict = {})
13
+ result = parent_dict or {}
14
+ keys = parent_keys or []
15
+
16
+ hash.each do |key, value|
17
+ if value.instance_of?(Hash)
18
+ keys << key
19
+ result = form_encode_params(value, keys, result)
20
+ else
21
+ dict_key = build_dict_key(keys + [key])
22
+ result[dict_key] = value
23
+ end
24
+ end
25
+ result
26
+ end
27
+
28
+ # Build a dict key from a list of keys.
29
+ # Example: [code, number] -> code[number]
30
+ def self.build_dict_key(keys)
31
+ result = keys[0].to_s
32
+
33
+ keys[1..].each do |key|
34
+ result += "[#{key}]"
35
+ end
36
+
37
+ result
38
+ end
39
+
40
+ # Converts an object to an object ID.
41
+ def self.objects_to_ids(obj)
42
+ case obj
43
+ when EasyPost::Models::EasyPostObject
44
+ { id: obj.id }
45
+ when Hash
46
+ result = {}
47
+ obj.each { |k, v| result[k] = objects_to_ids(v) unless v.nil? }
48
+ result
49
+ when Array
50
+ obj.map { |v| objects_to_ids(v) }
51
+ else
52
+ obj
53
+ end
54
+ end
55
+
56
+ # Normalizes a list of strings.
57
+ def self.normalize_string_list(lst)
58
+ lst = lst.is_a?(String) ? lst.split(',') : Array(lst)
59
+ lst.map(&:to_s).map(&:downcase).map(&:strip)
60
+ end
61
+ end
62
+
63
+ require_relative 'utilities/json'
64
+ require_relative 'utilities/system'
65
+ require_relative 'utilities/static_mapper'
66
+ require_relative 'utilities/constants'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Address objects are used to represent people, places, and organizations in a number of contexts.
4
+ class EasyPost::Models::Address < EasyPost::Models::EasyPostObject
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # An ApiKey object that has your EasyPost API.
4
+ class EasyPost::Models::ApiKey < EasyPost::Models::EasyPostObject
5
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ # The base class for all JSON objects in the library.
6
+ class EasyPost::Models::Object
7
+ def initialize(data)
8
+ @values = data
9
+ add_properties(data)
10
+ end
11
+
12
+ # Convert to a string.
13
+ def to_s(*_args)
14
+ JSON.dump(@values)
15
+ end
16
+
17
+ # Convert object to hash
18
+ def to_hash
19
+ JSON.parse(JSON.dump(@values))
20
+ end
21
+
22
+ # Get element of an array.
23
+ def [](key)
24
+ @values[key.to_s]
25
+ end
26
+
27
+ # Set the element of an array.
28
+ def []=(key, value)
29
+ send(:"#{key}=", value)
30
+ end
31
+
32
+ private
33
+
34
+ def add_properties(values)
35
+ values.each do |key, _|
36
+ define_singleton_method(key) { handle_value(@values[key]) } # getter
37
+ define_singleton_method("#{key}=") { |v| @values[key] = handle_value(v) } # setter
38
+ end
39
+ end
40
+
41
+ def handle_value(val)
42
+ case val
43
+ when Hash
44
+ type = EasyPost::InternalUtilities::StaticMapper::BY_TYPE[val['object']] if val['object']
45
+ prefix = EasyPost::InternalUtilities::StaticMapper::BY_PREFIX[val['id'].split('_').first] if val['id']
46
+ cls = type || prefix || EasyPost::Models::EasyPostObject
47
+ cls.new(val)
48
+ when Array
49
+ val.map { |item| handle_value(item) }
50
+ else
51
+ val
52
+ end
53
+ end
54
+ end
55
+
56
+ # The base class for all API objects in the library that have an ID (plus optional timestamps).
57
+ class EasyPost::Models::EasyPostObject < EasyPost::Models::Object
58
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The Batch object allows you to perform operations on multiple Shipments at once.
4
+ class EasyPost::Models::Batch < EasyPost::Models::EasyPostObject
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The Brand object allows you to customize the publicly-accessible html page that shows tracking details for every EasyPost tracker.
4
+ class EasyPost::Models::Brand < EasyPost::Models::EasyPostObject
5
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # The CarbonOffset object is a summary of carbon offset data for a given rate, including grams, price and currency
4
- class EasyPost::CarbonOffset < EasyPost::Resource
4
+ class EasyPost::Models::CarbonOffset < EasyPost::Models::EasyPostObject
5
5
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A CarrierAccount encapsulates your credentials with the carrier.
4
+ class EasyPost::Models::CarrierAccount < EasyPost::Models::EasyPostObject
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # CustomsInfo objects contain CustomsItem objects and all necessary information for the generation of customs forms required for international shipping.
4
+ class EasyPost::Models::CustomsInfo < EasyPost::Models::EasyPostObject
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A CustomsItem object describes goods for international shipment and should be created then included in a CustomsInfo object.
4
+ class EasyPost::Models::CustomsItem < EasyPost::Models::EasyPostObject
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # EndShipper objects are fully-qualified Address objects that require all parameters and get verified upon creation.
4
+ class EasyPost::Models::EndShipper < EasyPost::Models::EasyPostObject
5
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # EasyPost Error object.
4
+ class EasyPost::Models::Error
5
+ attr_reader :code, :field, :message
6
+
7
+ # Initialize a new EasyPost Error
8
+ def initialize(code, field = nil, message = nil)
9
+ @code = code
10
+ @field = field
11
+ @message = message
12
+ end
13
+
14
+ # Create an EasyPost Error from an API error response.
15
+ def self.from_api_error_response(data)
16
+ code = data['code']
17
+ field = data['field'] || nil
18
+ message = data['message'] || nil
19
+ EasyPost::Models::Error.new(code, field, message)
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Webhook Events are triggered by changes in objects you've created via the API.
4
+ class EasyPost::Models::Event < EasyPost::Models::EasyPostObject
5
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # An Insurance object represents insurance for packages purchased both via the EasyPost API as well
4
+ # as shipments purchased through third parties and later registered with EasyPost.
5
+ class EasyPost::Models::Insurance < EasyPost::Models::EasyPostObject
6
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The Order object represents a collection of packages and can be used for Multi-Piece Shipments.
4
+ class EasyPost::Models::Order < EasyPost::Models::EasyPostObject
5
+ # Get the lowest rate of an Order (can exclude by having `'!'` as the first element of your optional filter lists).
6
+ def lowest_rate(carriers = [], services = [])
7
+ EasyPost::Util.get_lowest_object_rate(self, carriers, services)
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Parcel objects represent the physical container being shipped.
4
+ class EasyPost::Models::Parcel < EasyPost::Models::EasyPostObject
5
+ end