easypost 4.13.1 → 5.0.0

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 +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