easypost 4.13.1 → 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 +19 -2
  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 -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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c71d319c7e56e6787ce3e63fa8afaa634d8eeeb57ba77fd754f86e284a66dff9
4
- data.tar.gz: 90e4523dc2a2dd2ff8af06ad0cdd95a24ff3fa93c098d81458deeb3d9cd7f60a
3
+ metadata.gz: b93e828aba22bcf673243120c7d51ab0d5bc813828204c5045ee7cbf4526bad2
4
+ data.tar.gz: 904d0537ad8f78924dee006329b59a1b07ce3496c33d07149684ec4219547978
5
5
  SHA512:
6
- metadata.gz: 99646c67e4f7264adb2a3bbf48dcd2224623a88c69ce4650a9066a79ee7e7a1203816f5b39b9d2086840ea47a512de0184f7547cb92a741e6669fcb68f7618f9
7
- data.tar.gz: bbd168c93b623642b947955088d0baf089027c1d19335bca4ff41ae0ca39e770019953934c23b5a7d30194cdfecc6d5e29717ed37b1525bb90525825b3a8b937
6
+ metadata.gz: 64bbae9d00138cfc8634bb12a2d07b96dbfc951a93c2c983fa4a7da77e49bfc3d5ae2471ebca3ec84a5cb7c74fa88a1b851998c799119c071200bc37cd0645eb
7
+ data.tar.gz: 1d6910376670da796ee8daf8ed7c54e54591cd7f0770b7d363a38bd9d406a229e1df364fa7c16aa0f688830e171be82cb0ddb2f2331dd125a980158fad8d23c1
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  strategy:
13
13
  matrix:
14
- rubyversion: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2']
14
+ rubyversion: ['2.6', '2.7', '3.0', '3.1', '3.2']
15
15
  steps:
16
16
  - name: Checkout Repository
17
17
  uses: actions/checkout@v3
@@ -48,3 +48,24 @@ jobs:
48
48
  run: make lint
49
49
  - name: Run security analysis
50
50
  run: make scan
51
+ docs:
52
+ if: github.ref == 'refs/heads/master'
53
+ runs-on: ubuntu-latest
54
+ steps:
55
+ - name: Checkout Repository
56
+ uses: actions/checkout@v3
57
+ - name: Set up Ruby
58
+ uses: ruby/setup-ruby@v1
59
+ with:
60
+ ruby-version: '3.2'
61
+ rubygems: '3.0.0'
62
+ bundler-cache: true
63
+ - name: Install Dependencies
64
+ run: make install
65
+ - name: Generate Docs
66
+ run: make docs
67
+ - name: Deploy docs
68
+ uses: peaceiris/actions-gh-pages@v3
69
+ with:
70
+ github_token: ${{ secrets.GITHUB_TOKEN }}
71
+ publish_dir: docs
data/.gitignore CHANGED
@@ -1,11 +1,20 @@
1
+ ._*
2
+ .AppleDouble
1
3
  .bundle
2
4
  .config
5
+ .DS_Store
6
+ .env
3
7
  .idea/
8
+ .LSOverride
9
+ .Spotlight-V100
10
+ .Trashes
4
11
  *.gem
5
12
  *.rbc
6
13
  coverage
7
14
  dist
15
+ docs
8
16
  Gemfile.lock
17
+ Icon
9
18
  InstalledFiles
10
19
  lib/bundler/man
11
20
  pkg
@@ -14,15 +23,4 @@ spec/reports
14
23
  test/tmp
15
24
  test/version_tmp
16
25
  tmp
17
-
18
-
19
- # macOS
20
- ._*
21
- .AppleDouble
22
- .DS_Store
23
- .env
24
- .LSOverride
25
- .Spotlight-V100
26
- .Trashes
27
- Icon
28
26
  vendor/
data/.rubocop.yml CHANGED
@@ -2,10 +2,15 @@ inherit_from: easycop.yml
2
2
 
3
3
  AllCops:
4
4
  SuggestExtensions: false
5
+ Exclude:
6
+ - bin/**/*
7
+ - docs/**/*
8
+ - examples/**/*
9
+ - vendor/bundle/**/*
5
10
  # We are ignoring RSpec/FilePath because Simplecov doesn't play nice with nested spec files
6
11
  RSpec/FilePath:
7
12
  Enabled: false
8
13
  # TODO: Remove this once we start using keyword arguments
9
14
  Style/OptionalBooleanParameter:
10
15
  Exclude:
11
- - 'lib/easypost/shipment.rb'
16
+ - 'lib/easypost/services/*.rb'
data/CHANGELOG.md CHANGED
@@ -1,8 +1,25 @@
1
1
  # CHANGELOG
2
2
 
3
- ## v4.13.1 (2023-04-18)
4
-
3
+ ## v5.0.0 (2023-06-06)
4
+
5
+ See our [Upgrade Guide](UPGRADE_GUIDE.md#upgrading-from-4x-to-50) for more details.
6
+
7
+ - Library is now thread-safe. Closes GitHub Issue ([#183](https://github.com/EasyPost/easypost-ruby/issues/183))
8
+ - Initialize a `Client` object with an API key. Optionally set open and read timeout params
9
+ - Previous classes have been diverted into `Services` and `Models`
10
+ - All methods (i.e. `create`, `retrieve`, `all`) exist in services, accessed via property of the client (eg: `client.shipment.create()`)
11
+ - E.g. `bought_shipment = client.shipment.buy(shipment_id, rate)`
12
+ - Beta namespace changed from `easypost.beta.x` to `client.beta_x`
13
+ - References to `Referral` are now `ReferralCustomer` and `referral_customer` to match the API and docs
14
+ - References to `smartrate` are now `SmartRate` and `smart_rate` to match the API and docs
15
+ - Rename function `get_smartrates` to `get_smart_rates`
16
+ - Rename function `get_lowest_smartrate` to `get_lowest_smart_rate`
17
+ - Empty API response functions for `delete` return `true` instead of empty object
18
+ - Drops support for Ruby 2.5
19
+ - Bumps all dev dependencies
5
20
  - Improves Error Deserialization to dynamically handle edge cases that have a bad format
21
+ - Adds `retrieve_estimated_delivery_date` function in Shipment
22
+ - Removes deprecated `endshipper` beta class, please use the GA one `EasyPost::Services::EndShipper`
6
23
 
7
24
  ## v4.13.0 (2023-04-04)
8
25
 
data/Makefile CHANGED
@@ -22,7 +22,7 @@ docs:
22
22
 
23
23
  ## fix - Fix Rubocop errors
24
24
  fix:
25
- bundle exec rubocop -A
25
+ bundle exec rubocop -a
26
26
 
27
27
  ## install - Install globally from source
28
28
  install:
data/README.md CHANGED
@@ -24,9 +24,9 @@ A simple create & buy shipment example:
24
24
  ```ruby
25
25
  require 'easypost'
26
26
 
27
- EasyPost.api_key = ENV['EASYPOST_API_KEY']
27
+ client = EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY'])
28
28
 
29
- shipment = EasyPost::Shipment.create(
29
+ shipment = client.shipment.create(
30
30
  from_address: {
31
31
  company: 'EasyPost',
32
32
  street1: '118 2nd Street',
@@ -53,39 +53,45 @@ shipment = EasyPost::Shipment.create(
53
53
  },
54
54
  )
55
55
 
56
- shipment.buy(rate: shipment.lowest_rate)
56
+ bought_shipment = client.shipment.buy(shipment.id, rate: shipment.lowest_rate)
57
57
 
58
- puts shipment
58
+ puts bought_shipment
59
59
  ```
60
60
 
61
61
  ### Custom Connections
62
62
 
63
- Set `EasyPost.default_connection` to an object that responds to `call(method, path, api_key = nil, body = nil)`
63
+ Pass in a lambda function as `custom_client_exec` when initializing a client that responds to `call(method, uri, headers, open_timeout, read_timeout, body = nil)` where:
64
+ - `uri` is the fully-qualified URL of the EasyPost endpoint, including query parameters (`Uri` object)
65
+ - `method` is the lowercase name of the HTTP method being used for the request (e.g. `:get`, `:post`, `:put`, `:delete`)
66
+ - `headers` is a hash with all headers needed for the request pre-populated, including authorization (`Hash` object)
67
+ - `open_timeout` is the number of seconds to wait for the connection to open (integer)
68
+ - `read_timeout` is the number of seconds to wait for one block to be read (integer)
69
+ - `body` is a string of the body data to be included in the request, or nil (e.g. GET or DELETE request) (string or `nil`)
70
+
71
+ The lambda function should return an object with `code` and `body` attributes, where:
72
+ - `code` is the HTTP response status code (integer)
73
+ - `body` is the response body (string)
64
74
 
65
75
  #### Faraday
66
76
 
67
77
  ```ruby
68
78
  require 'faraday'
69
- require 'faraday/response/logger'
70
- require 'logger'
71
-
72
- EasyPost.default_connection = lambda do |method, path, api_key = nil, body = nil|
73
- Faraday
74
- .new(url: EasyPost.api_base, headers: EasyPost.default_headers) { |builder|
75
- builder.use Faraday::Response::Logger, Logger.new(STDOUT), {bodies: true, headers: true}
76
- builder.adapter :net_http
77
- }
78
- .public_send(method, path) { |request|
79
- request.headers['Authorization'] = EasyPost.authorization(api_key)
80
- request.body = JSON.dump(EasyPost::Util.objects_to_ids(body)) if body
81
- }.yield_self { |response|
82
- EasyPost.parse_response(
83
- status: response.status,
84
- body: response.body,
85
- json: response.headers['Content-Type'].start_with?('application/json'),
86
- )
87
- }
88
- end
79
+
80
+ custom_connection = lambda { |method, uri, headers, _open_timeout, _read_timeout, body = nil|
81
+ conn = Faraday.new(url: uri.to_s, headers: headers) do |faraday|
82
+ faraday.adapter Faraday.default_adapter
83
+ end
84
+ conn.public_send(method, uri.path) { |request|
85
+ request.body = body if body
86
+ }.yield_self do |response|
87
+ OpenStruct.new(code: response.status, body: response.body)
88
+ end
89
+ }
90
+
91
+ my_client = described_class.new(
92
+ api_key: ENV['EASYPOST_API_KEY'],
93
+ custom_client_exec: custom_connection,
94
+ )
89
95
  ```
90
96
 
91
97
  #### Typhoeus
@@ -93,27 +99,28 @@ end
93
99
  ```ruby
94
100
  require 'typhoeus'
95
101
 
96
- EasyPost.default_connection = lambda do |method, path, api_key = nil, body = nil|
102
+ custom_connection = lambda { |method, uri, headers, _open_timeout, _read_timeout, body = nil|
97
103
  Typhoeus.public_send(
98
104
  method,
99
- File.join(EasyPost.api_base, path),
100
- headers: EasyPost.default_headers.merge('Authorization' => EasyPost.authorization(api_key)),
101
- body: body.nil? ? nil : JSON.dump(EasyPost::Util.objects_to_ids(body)),
102
- ).yield_self { |response|
103
- EasyPost.parse_response(
104
- status: response.code,
105
- body: response.body,
106
- json: response.headers['Content-Type'].start_with?('application/json'),
107
- )
108
- }
109
- end
105
+ uri.to_s,
106
+ headers: headers,
107
+ body: body,
108
+ ).yield_self do |response|
109
+ OpenStruct.new(code: response.code, body: response.body)
110
+ end
111
+ }
112
+
113
+ my_client = described_class.new(
114
+ api_key: ENV['EASYPOST_API_KEY'],
115
+ custom_client_exec: custom_connection,
116
+ )
110
117
  ```
111
118
 
112
119
  ## Documentation
113
120
 
114
121
  API documentation can be found at: <https://easypost.com/docs/api>.
115
122
 
116
- Library documentation can be found on the web at: <https://easypost.github.io/easypost-ruby/> or locally in the `docs` directory.
123
+ Library documentation can be found on the web at: <https://easypost.github.io/easypost-ruby/> or by building them locally via the `make docs` command.
117
124
 
118
125
  Upgrading major versions of this project? Refer to the [Upgrade Guide](UPGRADE_GUIDE.md).
119
126
 
data/UPGRADE_GUIDE.md CHANGED
@@ -2,8 +2,127 @@
2
2
 
3
3
  Use the following guide to assist in the upgrade process of the `easypost-ruby` library between major versions.
4
4
 
5
+ - [Upgrading from 4.x to 5.0](#upgrading-from-4x-to-50)
6
+ - [Upgrading from 3.x to 4.0](#upgrading-from-3x-to-40)
7
+
8
+ ## Upgrading from 4.x to 5.0
9
+
10
+ ### 5.0 High Impact Changes
11
+
12
+ - [New Client object](#50-thread-safe-with-client-object)
13
+ - [Updated Dependencies](#50-updated-dependencies)
14
+ - [Improved Error Handling](#50-improved-error-handling)
15
+
16
+ ### 5.0 Medium Impact Changes
17
+
18
+ - [Corrected Naming Conventions](#50-corrected-naming-conventions)
19
+
20
+ ### 5.0 Low Impact Changes
21
+
22
+ - [Beta Namespace Changed](#50-beta-namespace-changed)
23
+
24
+ ## 5.0 Thread-Safe with Client Object
25
+
26
+ Likelihood of Impact: High
27
+
28
+ This library is now thread-safe with the introduction of a new `Client` object. Instead of defining a global API key that all requests use, you now create an `Client` object and pass your API key to it with optional open and read timeout params. You then call your desired functions against a `service` which are called against a `Client` object:
29
+
30
+ ```ruby
31
+ # Old way
32
+ EasyPost.api_key = ENV['EASYPOST_API_KEY']
33
+ shipment = EasyPost::Shipment.retrieve('shp_...')
34
+
35
+ # New way
36
+ client = EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY'])
37
+ shipment = client.shipment.retrieve('shp_...')
38
+ ```
39
+
40
+ All instance methods are now static with the exception of `lowest_rate` as these make API calls and require the Client object(EasyPost objects do not contain an API key to make API calls with).
41
+
42
+ Previously used `.save()` instance methods are now static `.update()` functions where you specify first the ID of the object you are updating and second, the parameters that need updating.
43
+
44
+ ## 5.0 Updated Dependencies
45
+
46
+ Likelihood of Impact: High
47
+
48
+ **Ruby 2.6 Required**
49
+
50
+ easypost-ruby now requires Ruby 2.6 or greater.
51
+
52
+ **Dependencies**
53
+
54
+ All dependencies had minor version bumps.
55
+
56
+ ## 5.0 Improved Error Handling
57
+
58
+ Likelihood of Impact: High
59
+
60
+ There are ~2 dozen new error types that extend either `ApiError` or `EasyPostError`.
61
+
62
+ New ApiErrors (extends EasyPostError):
63
+
64
+ - `ApiError`
65
+ - `ConnectionError`
66
+ - `ExternalApiError`
67
+ - `ForbiddenError`
68
+ - `GatewayTimeoutError`
69
+ - `InternalServerError`
70
+ - `InvalidRequestError`
71
+ - `MethodNotAllowedError`
72
+ - `NotFoundError`
73
+ - `PaymentError`
74
+ - `ProxyError`
75
+ - `RateLimitError`
76
+ - `RedirectError`
77
+ - `RetryError`
78
+ - `ServiceUnavailableError`
79
+ - `SSLError`
80
+ - `TimeoutError`
81
+ - `UnauthorizedError`
82
+ - `UnknownApiError`
83
+
84
+ New EasyPostErrors (extends builtin Exception):
85
+
86
+ - `EasyPostError`
87
+ - `EndOfPaginationError`
88
+ - `FilteringError`
89
+ - `InvalidObjectError`
90
+ - `InvalidParameterError`
91
+ - `MissingParameterError`
92
+ - `SignatureVerificationError`
93
+
94
+ ApiErrors will behave like the previous Error class did. They will include a `message`, `http_status`, and `http_body`. Additionally, a new `code` and `errors` keys are present and populate when available. This class extends the more generic `EasyPostError` which only contains a message, used for errors thrown directly from this library.
95
+
96
+ ## 5.0 Corrected Naming Conventions
97
+
98
+ Likelihood of Impact: Medium
99
+
100
+ Occurances of `referral` are now `referral_customer` and `Referral` are now `ReferralCustomer` to match the documentation and API.
101
+
102
+ Occurances of `smartrate` are now `smart_rate` and `Smartrate` are now `SmartRate` to match the documentation and API.
103
+
104
+ Occurances of `scanform` are now `scan_form` and `Scanform` are now `ScanForm` to match the documentation and API.
105
+
106
+ Rename function `get_smartrates` to `get_smart_rates`
107
+
108
+ Rename function `get_lowest_smartrate` to `get_lowest_smart_rate`
109
+
110
+ ## 5.0 Beta Namespace Changed
111
+
112
+ Likelihood of Impact: Low
113
+
114
+ Previously, the beta namespace was found at `easypost.beta.x` where `x` is the name of your model. Now, the beta namespace is simply prepended to the name of your service: `client.beta_x`. for instance, you can call `client.beta_referral_customer.add_payment_method()`.
115
+
116
+ ## 5.0 Return True For Empty API Response
117
+
118
+ Likelihood of Impact: Low
119
+
120
+ Empty API response functions for `delete` return `true` instead of empty object
121
+
5
122
  ## Upgrading from 3.x to 4.0
6
123
 
124
+ **NOTICE:** v4 is deprecated.
125
+
7
126
  ### 4.0 High Impact Changes
8
127
 
9
128
  * [Updating Dependencies](#40-updating-dependencies)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.13.1
1
+ 5.0.0
data/easycop.yml CHANGED
@@ -5,7 +5,7 @@
5
5
  require:
6
6
  - rubocop-rspec
7
7
  AllCops:
8
- TargetRubyVersion: 2.5
8
+ TargetRubyVersion: 2.6
9
9
  NewCops: disable
10
10
  Layout/BlockAlignment:
11
11
  Enabled: true
@@ -41,7 +41,7 @@ Layout/FirstMethodArgumentLineBreak:
41
41
  Enabled: true
42
42
  Layout/LineLength:
43
43
  Max: 120
44
- IgnoredPatterns: # deprecated in 1.28
44
+ AllowedPatterns:
45
45
  - "(\\A|\\s)#"
46
46
  Layout/LineEndStringConcatenationIndentation: # new in 1.18
47
47
  Enabled: true
@@ -104,7 +104,7 @@ Metrics/ParameterLists:
104
104
  Enabled: false
105
105
  Metrics/AbcSize:
106
106
  Enabled: false
107
- Gemspec/DateAssignment: # new in 1.10
107
+ Gemspec/DeprecatedAttributeAssignment:
108
108
  Enabled: true
109
109
  Lint/AmbiguousAssignment: # new in 1.7
110
110
  Enabled: true
data/easypost.gemspec CHANGED
@@ -19,18 +19,20 @@ Gem::Specification.new do |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'
26
- spec.add_development_dependency 'psych', '~> 4.0' # TODO: pinned because rdoc has an optimistic pin of this dep and 5.0 breaks on CI
27
+ spec.add_development_dependency 'psych', '~> 5.1'
27
28
  spec.add_development_dependency 'rake', '~> 13.0'
28
- spec.add_development_dependency 'rdoc', '~> 6.4'
29
- spec.add_development_dependency 'rspec', '~> 3.10'
30
- spec.add_development_dependency 'rubocop', '= 1.27' # rubocop 1.28 requires Ruby 2.6+
31
- spec.add_development_dependency 'rubocop-rspec', '= 2.10' # rubocop-rspec 2.11 requires Ruby 2.6+
32
- 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'
33
34
  spec.add_development_dependency 'simplecov-lcov', '~> 0.8'
34
- spec.add_development_dependency 'vcr', '= 6.0' # VCR 6.1 requires Ruby 2.6+
35
- 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'
36
38
  end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'services'
4
+ require_relative 'http_client'
5
+ require_relative 'internal_utilities'
6
+ require 'json'
7
+
8
+ class EasyPost::Client
9
+ attr_reader :open_timeout, :read_timeout, :api_base
10
+
11
+ # Initialize a new Client object
12
+ # @param api_key [String] the API key to be used for requests
13
+ # @param read_timeout [Integer] (60) the number of seconds to wait for a response before timing out
14
+ # @param open_timeout [Integer] (30) the number of seconds to wait for the connection to open before timing out
15
+ # @param api_base [String] ('https://api.easypost.com') the base URL for the API
16
+ # @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)
17
+ # @return [EasyPost::Client] the client object
18
+ def initialize(api_key:, read_timeout: 60, open_timeout: 30, api_base: 'https://api.easypost.com',
19
+ custom_client_exec: nil)
20
+ raise EasyPost::Errors::MissingParameterError.new('api_key') if api_key.nil?
21
+
22
+ @api_key = api_key
23
+ @api_base = api_base
24
+ @api_version = 'v2'
25
+ @read_timeout = read_timeout
26
+ @open_timeout = open_timeout
27
+ @lib_version = File.open(File.expand_path('../../VERSION', __dir__)).read.strip
28
+
29
+ # Make an HTTP client once, reuse it for all requests made by this client
30
+ # Configuration is immutable, so this is safe
31
+ @http_client = EasyPost::HttpClient.new(api_base, http_config, custom_client_exec)
32
+ end
33
+
34
+ SERVICE_CLASSES = [
35
+ EasyPost::Services::Address,
36
+ EasyPost::Services::ApiKey,
37
+ EasyPost::Services::Batch,
38
+ EasyPost::Services::BetaRate,
39
+ EasyPost::Services::BetaReferralCustomer,
40
+ EasyPost::Services::Billing,
41
+ EasyPost::Services::CarrierAccount,
42
+ EasyPost::Services::CarrierMetadata,
43
+ EasyPost::Services::CustomsInfo,
44
+ EasyPost::Services::CustomsItem,
45
+ EasyPost::Services::EndShipper,
46
+ EasyPost::Services::Event,
47
+ EasyPost::Services::Insurance,
48
+ EasyPost::Services::Order,
49
+ EasyPost::Services::Parcel,
50
+ EasyPost::Services::Pickup,
51
+ EasyPost::Services::Rate,
52
+ EasyPost::Services::ReferralCustomer,
53
+ EasyPost::Services::Refund,
54
+ EasyPost::Services::Report,
55
+ EasyPost::Services::ScanForm,
56
+ EasyPost::Services::Shipment,
57
+ EasyPost::Services::Tracker,
58
+ EasyPost::Services::User,
59
+ EasyPost::Services::Webhook,
60
+ ].freeze
61
+
62
+ # Loop over the SERVICE_CLASSES to automatically define the method and instance variable instead of manually define it
63
+ SERVICE_CLASSES.each do |cls|
64
+ define_method(EasyPost::InternalUtilities.to_snake_case(cls.name.split('::').last)) do
65
+ instance_variable_set("@#{EasyPost::InternalUtilities.to_snake_case(cls.name.split('::').last)}", cls.new(self))
66
+ end
67
+ end
68
+
69
+ # Make an HTTP request
70
+ #
71
+ # @param method [Symbol] the HTTP Verb (get, method, put, post, etc.)
72
+ # @param endpoint [String] URI path of the resource
73
+ # @param cls [Class] the class to deserialize to
74
+ # @param body [Object] (nil) object to be dumped to JSON
75
+ # @param api_version [String] the version of API to hit
76
+ # @raise [EasyPost::Error] if the response has a non-2xx status code
77
+ # @return [Hash] JSON object parsed from the response body
78
+ def make_request(
79
+ method,
80
+ endpoint,
81
+ cls = EasyPost::Models::EasyPostObject,
82
+ body = nil,
83
+ api_version = EasyPost::InternalUtilities::Constants::API_VERSION
84
+ )
85
+ response = @http_client.request(method, endpoint, nil, body, api_version)
86
+
87
+ potential_error = EasyPost::Errors::ApiError.handle_api_error(response)
88
+ raise potential_error unless potential_error.nil?
89
+
90
+ EasyPost::InternalUtilities::Json.convert_json_to_object(response.body, cls)
91
+ end
92
+
93
+ private
94
+
95
+ def http_config
96
+ http_config = {
97
+ read_timeout: @read_timeout,
98
+ open_timeout: @open_timeout,
99
+ headers: default_headers,
100
+ }
101
+
102
+ http_config[:min_version] = OpenSSL::SSL::TLS1_2_VERSION
103
+ http_config
104
+ end
105
+
106
+ def default_headers
107
+ {
108
+ 'Content-Type' => 'application/json',
109
+ 'User-Agent' => user_agent,
110
+ 'Authorization' => authorization,
111
+ }
112
+ end
113
+
114
+ def user_agent
115
+ ruby_version = EasyPost::InternalUtilities::System.ruby_version
116
+ ruby_patchlevel = EasyPost::InternalUtilities::System.ruby_patchlevel
117
+
118
+ "EasyPost/#{@api_version} " \
119
+ "RubyClient/#{@lib_version} " \
120
+ "Ruby/#{ruby_version}-p#{ruby_patchlevel} " \
121
+ "OS/#{EasyPost::InternalUtilities::System.os_name} " \
122
+ "OSVersion/#{EasyPost::InternalUtilities::System.os_version} " \
123
+ "OSArch/#{EasyPost::InternalUtilities::System.os_arch}"
124
+ end
125
+
126
+ def authorization
127
+ "Bearer #{@api_key}"
128
+ end
129
+ 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