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.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +22 -1
  3. data/.gitignore +9 -11
  4. data/.rubocop.yml +6 -1
  5. data/CHANGELOG.md +21 -0
  6. data/Makefile +1 -1
  7. data/README.md +45 -38
  8. data/UPGRADE_GUIDE.md +119 -0
  9. data/VERSION +1 -1
  10. data/easycop.yml +3 -3
  11. data/easypost.gemspec +12 -10
  12. data/lib/easypost/client.rb +129 -0
  13. data/lib/easypost/connection.rb +2 -4
  14. data/lib/easypost/constants.rb +15 -0
  15. data/lib/easypost/errors/api/api_error.rb +106 -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 +31 -0
  42. data/lib/easypost/http_client.rb +62 -0
  43. data/lib/easypost/internal_utilities.rb +66 -0
  44. data/lib/easypost/models/address.rb +5 -0
  45. data/lib/easypost/models/api_key.rb +5 -0
  46. data/lib/easypost/models/base.rb +58 -0
  47. data/lib/easypost/models/batch.rb +5 -0
  48. data/lib/easypost/models/brand.rb +5 -0
  49. data/lib/easypost/{carbon_offset.rb → models/carbon_offset.rb} +1 -1
  50. data/lib/easypost/models/carrier_account.rb +5 -0
  51. data/lib/easypost/models/customs_info.rb +5 -0
  52. data/lib/easypost/models/customs_item.rb +5 -0
  53. data/lib/easypost/models/end_shipper.rb +5 -0
  54. data/lib/easypost/models/error.rb +21 -0
  55. data/lib/easypost/models/event.rb +5 -0
  56. data/lib/easypost/models/insurance.rb +6 -0
  57. data/lib/easypost/models/order.rb +9 -0
  58. data/lib/easypost/models/parcel.rb +5 -0
  59. data/lib/easypost/{payload.rb → models/payload.rb} +1 -1
  60. data/lib/easypost/models/payment_method.rb +5 -0
  61. data/lib/easypost/models/pickup.rb +9 -0
  62. data/lib/easypost/{pickup_rate.rb → models/pickup_rate.rb} +1 -1
  63. data/lib/easypost/{postage_label.rb → models/postage_label.rb} +1 -1
  64. data/lib/easypost/models/rate.rb +5 -0
  65. data/lib/easypost/models/referral.rb +5 -0
  66. data/lib/easypost/models/refund.rb +5 -0
  67. data/lib/easypost/models/report.rb +5 -0
  68. data/lib/easypost/models/scan_form.rb +6 -0
  69. data/lib/easypost/models/shipment.rb +10 -0
  70. data/lib/easypost/{tax_identifier.rb → models/tax_identifier.rb} +1 -1
  71. data/lib/easypost/models/tracker.rb +5 -0
  72. data/lib/easypost/models/user.rb +5 -0
  73. data/lib/easypost/models/webhook.rb +6 -0
  74. data/lib/easypost/models.rb +35 -0
  75. data/lib/easypost/services/address.rb +50 -0
  76. data/lib/easypost/services/api_key.rb +8 -0
  77. data/lib/easypost/services/base.rb +27 -0
  78. data/lib/easypost/services/batch.rb +53 -0
  79. data/lib/easypost/services/beta_rate.rb +12 -0
  80. data/lib/easypost/services/beta_referral_customer.rb +40 -0
  81. data/lib/easypost/services/billing.rb +75 -0
  82. data/lib/easypost/services/carrier_account.rb +44 -0
  83. data/lib/easypost/services/carrier_metadata.rb +22 -0
  84. data/lib/easypost/services/customs_info.rb +15 -0
  85. data/lib/easypost/services/customs_item.rb +15 -0
  86. data/lib/easypost/services/end_shipper.rb +31 -0
  87. data/lib/easypost/services/event.rb +32 -0
  88. data/lib/easypost/services/insurance.rb +26 -0
  89. data/lib/easypost/services/order.rb +30 -0
  90. data/lib/easypost/services/parcel.rb +16 -0
  91. data/lib/easypost/services/pickup.rb +40 -0
  92. data/lib/easypost/services/rate.rb +8 -0
  93. data/lib/easypost/services/referral_customer.rb +103 -0
  94. data/lib/easypost/services/refund.rb +26 -0
  95. data/lib/easypost/services/report.rb +42 -0
  96. data/lib/easypost/services/scan_form.rb +25 -0
  97. data/lib/easypost/services/shipment.rb +106 -0
  98. data/lib/easypost/services/tracker.rb +38 -0
  99. data/lib/easypost/services/user.rb +66 -0
  100. data/lib/easypost/services/webhook.rb +34 -0
  101. data/lib/easypost/services.rb +32 -0
  102. data/lib/easypost/util.rb +80 -187
  103. data/lib/easypost/utilities/constants.rb +5 -0
  104. data/lib/easypost/utilities/json.rb +23 -0
  105. data/lib/easypost/utilities/static_mapper.rb +73 -0
  106. data/lib/easypost/utilities/system.rb +36 -0
  107. data/lib/easypost.rb +12 -138
  108. metadata +147 -64
  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 -32
  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,62 @@
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, use_ssl: true, read_timeout: read_timeout, open_timeout: open_timeout,
47
+ ) do |http|
48
+ http.request(request)
49
+ end
50
+ rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EHOSTUNREACH => e
51
+ # Raise a timeout error if the request times out.
52
+ raise EasyPost::Errors::TimeoutError.new(e.message)
53
+ rescue OpenSSL::SSL::SSLError => e
54
+ # Raise an SSL error if the request fails due to an SSL error.
55
+ raise EasyPost::Errors::SslError.new(e.message)
56
+ rescue StandardError => e
57
+ # Raise an unknown HTTP error if anything else causes the request to fail to complete
58
+ # (this is different from processing 4xx/5xx errors from the API)
59
+ raise EasyPost::Errors::UnknownApiError.new(e.message)
60
+ end
61
+ end
62
+ 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