sp_api_clients 2.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 (90) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +161 -0
  3. data/LICENSE +21 -0
  4. data/README.md +284 -0
  5. data/lib/amazon_sp_clients/adapter_loader.rb +28 -0
  6. data/lib/amazon_sp_clients/api_client.rb +309 -0
  7. data/lib/amazon_sp_clients/api_error.rb +37 -0
  8. data/lib/amazon_sp_clients/api_response.rb +54 -0
  9. data/lib/amazon_sp_clients/configuration.rb +144 -0
  10. data/lib/amazon_sp_clients/marketplaces.rb +63 -0
  11. data/lib/amazon_sp_clients/middlewares/raise_error.rb +166 -0
  12. data/lib/amazon_sp_clients/session.rb +124 -0
  13. data/lib/amazon_sp_clients/sp_fba_inventory.rb +8 -0
  14. data/lib/amazon_sp_clients/sp_feeds_2021.rb +8 -0
  15. data/lib/amazon_sp_clients/sp_fulfillment_outbound_2020.rb +8 -0
  16. data/lib/amazon_sp_clients/sp_listings_items_2021.rb +8 -0
  17. data/lib/amazon_sp_clients/sp_orders_v0.rb +8 -0
  18. data/lib/amazon_sp_clients/sp_reports_2021.rb +8 -0
  19. data/lib/amazon_sp_clients/sp_tokens_2021.rb +8 -0
  20. data/lib/amazon_sp_clients/sp_vdf_inventory_v1.rb +8 -0
  21. data/lib/amazon_sp_clients/sp_vdf_orders_v1.rb +8 -0
  22. data/lib/amazon_sp_clients/sp_vdf_shipping_v1.rb +8 -0
  23. data/lib/amazon_sp_clients/sp_vendor_invoices.rb +8 -0
  24. data/lib/amazon_sp_clients/sp_vendor_orders.rb +8 -0
  25. data/lib/amazon_sp_clients/sp_vendor_transaction_status.rb +8 -0
  26. data/lib/amazon_sp_clients/sp_vendors_shipments.rb +8 -0
  27. data/lib/amazon_sp_clients/token_exchange_auth.rb +82 -0
  28. data/lib/amazon_sp_clients/uploader.rb +77 -0
  29. data/lib/amazon_sp_clients/v2/api.rb +43 -0
  30. data/lib/amazon_sp_clients/v2/apis/fba_inventory.rb +80 -0
  31. data/lib/amazon_sp_clients/v2/apis/feeds_2021.rb +86 -0
  32. data/lib/amazon_sp_clients/v2/apis/fulfillment_outbound_2020.rb +184 -0
  33. data/lib/amazon_sp_clients/v2/apis/listings_items_2021.rb +144 -0
  34. data/lib/amazon_sp_clients/v2/apis/orders_v0.rb +165 -0
  35. data/lib/amazon_sp_clients/v2/apis/reports_2021.rb +117 -0
  36. data/lib/amazon_sp_clients/v2/apis/tokens_2021.rb +25 -0
  37. data/lib/amazon_sp_clients/v2/apis/vdf_inventory_v1.rb +26 -0
  38. data/lib/amazon_sp_clients/v2/apis/vdf_orders_v1.rb +61 -0
  39. data/lib/amazon_sp_clients/v2/apis/vdf_shipping_v1.rb +139 -0
  40. data/lib/amazon_sp_clients/v2/apis/vendor_invoices.rb +25 -0
  41. data/lib/amazon_sp_clients/v2/apis/vendor_orders.rb +106 -0
  42. data/lib/amazon_sp_clients/v2/apis/vendor_transaction_status.rb +25 -0
  43. data/lib/amazon_sp_clients/v2/apis/vendors_shipments.rb +120 -0
  44. data/lib/amazon_sp_clients/v2/apis.rb +82 -0
  45. data/lib/amazon_sp_clients/v2/client.rb +198 -0
  46. data/lib/amazon_sp_clients/v2/config.rb +85 -0
  47. data/lib/amazon_sp_clients/v2/credentials.rb +91 -0
  48. data/lib/amazon_sp_clients/v2/documents.rb +115 -0
  49. data/lib/amazon_sp_clients/v2/error_mapper.rb +235 -0
  50. data/lib/amazon_sp_clients/v2/errors.rb +117 -0
  51. data/lib/amazon_sp_clients/v2/lwa.rb +100 -0
  52. data/lib/amazon_sp_clients/v2/rdt.rb +104 -0
  53. data/lib/amazon_sp_clients/v2/token.rb +39 -0
  54. data/lib/amazon_sp_clients/v2.rb +33 -0
  55. data/lib/amazon_sp_clients/version.rb +3 -0
  56. data/lib/amazon_sp_clients.rb +69 -0
  57. data/lib/sp_api_clients.rb +6 -0
  58. data/sp_api_clients.gemspec +53 -0
  59. data/vendor/fba_inventory/lib/sp_fba_inventory/api/fba_inventory_api.rb +289 -0
  60. data/vendor/fba_inventory/lib/sp_fba_inventory.rb +12 -0
  61. data/vendor/feeds_2021/lib/sp_feeds_2021/api/feeds_api.rb +367 -0
  62. data/vendor/feeds_2021/lib/sp_feeds_2021.rb +12 -0
  63. data/vendor/fulfillment_outbound_2020/lib/sp_fulfillment_outbound_2020/api/fba_outbound_api.rb +845 -0
  64. data/vendor/fulfillment_outbound_2020/lib/sp_fulfillment_outbound_2020.rb +12 -0
  65. data/vendor/listings_items_2021/lib/sp_listings_items_2021/api/listings_api.rb +480 -0
  66. data/vendor/listings_items_2021/lib/sp_listings_items_2021.rb +12 -0
  67. data/vendor/orders_v0/lib/sp_orders_v0/api/orders_v0_api.rb +593 -0
  68. data/vendor/orders_v0/lib/sp_orders_v0/api/shipment_api.rb +83 -0
  69. data/vendor/orders_v0/lib/sp_orders_v0.rb +13 -0
  70. data/vendor/reports_2021/lib/sp_reports_2021/api/reports_api.rb +530 -0
  71. data/vendor/reports_2021/lib/sp_reports_2021.rb +12 -0
  72. data/vendor/tokens_2021/lib/sp_tokens_2021/api/tokens_api.rb +77 -0
  73. data/vendor/tokens_2021/lib/sp_tokens_2021.rb +12 -0
  74. data/vendor/vdf_inventory_v1/lib/sp_vdf_inventory_v1/api/update_inventory_api.rb +83 -0
  75. data/vendor/vdf_inventory_v1/lib/sp_vdf_inventory_v1.rb +12 -0
  76. data/vendor/vdf_orders_v1/lib/sp_vdf_orders_v1/api/vendor_orders_api.rb +217 -0
  77. data/vendor/vdf_orders_v1/lib/sp_vdf_orders_v1.rb +12 -0
  78. data/vendor/vdf_shipping_v1/lib/sp_vdf_shipping_v1/api/customer_invoices_api.rb +152 -0
  79. data/vendor/vdf_shipping_v1/lib/sp_vdf_shipping_v1/api/vendor_shipping_api.rb +264 -0
  80. data/vendor/vdf_shipping_v1/lib/sp_vdf_shipping_v1/api/vendor_shipping_labels_api.rb +208 -0
  81. data/vendor/vdf_shipping_v1/lib/sp_vdf_shipping_v1.rb +14 -0
  82. data/vendor/vendor_invoices/lib/sp_vendor_invoices/api/vendor_payments_api.rb +77 -0
  83. data/vendor/vendor_invoices/lib/sp_vendor_invoices.rb +12 -0
  84. data/vendor/vendor_orders/lib/sp_vendor_orders/api/vendor_orders_api.rb +323 -0
  85. data/vendor/vendor_orders/lib/sp_vendor_orders.rb +12 -0
  86. data/vendor/vendor_transaction_status/lib/sp_vendor_transaction_status/api/vendor_transaction_api.rb +75 -0
  87. data/vendor/vendor_transaction_status/lib/sp_vendor_transaction_status.rb +12 -0
  88. data/vendor/vendors_shipments/lib/sp_vendors_shipments/api/vendor_shipping_api.rb +337 -0
  89. data/vendor/vendors_shipments/lib/sp_vendors_shipments.rb +12 -0
  90. metadata +321 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0627464f44e6cbe35d99ea9405211a6aebf7d2276eea6532f886d9c96a33d1d8
4
+ data.tar.gz: 6dd7221be23aada8e99d15d247d33b6c22d3e96e0b21e974253e3dd9df89e29d
5
+ SHA512:
6
+ metadata.gz: defb167f4de77e60be1b1c21dfeab17017717e70117a3a3f41539296cc0cdbee17c5b3d449630b4df7b9c8cfec2433f6f1624de6840fc4dcad7f32092e82197e
7
+ data.tar.gz: c917ac15645bed83f5e5362eec43807b23e81acd4c4555c9ef3a12c1660d48c0d161aa89511035ccb5a1dcf8a8fa8f7280731500b064c9824829178789d1a333
data/CHANGELOG.md ADDED
@@ -0,0 +1,161 @@
1
+ # Changelog
2
+
3
+ Notable changes to this gem. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ Changes that only track updates to Amazon's API models are not
7
+ considered breaking and do not trigger a major version bump.
8
+
9
+ ## [2.0.0] - 2026-09-03
10
+
11
+ ### Added
12
+
13
+ - The `AmazonSpClients::V2` namespace (`require 'amazon_sp_clients/v2'`):
14
+ a per-merchant `Client` with an explicit, frozen `Config` instead of
15
+ the thread-local global; a thread-safe token source (a callback, or
16
+ LWA refresh with one exchange per expiry); restricted data tokens as
17
+ an `rdt:` argument with per-resource caching; typed errors under
18
+ `V2::Error`; and one generated class per API module, for the same 14
19
+ modules. Responses are the same `ApiResponse`. The README has the
20
+ usage and the v1 to V2 migration table.
21
+ - Feed upload, feed result download and report download on the V2
22
+ client. Report documents come back gunzipped and UTF-8 tagged when
23
+ Amazon gzipped them.
24
+ - `RDT.report_document(id)`, the resource list for a restricted report
25
+ document, next to the `ORDERS` and `ORDERS_AND_ITEMS` presets.
26
+ - `rake yard:verify` and a CI job that fail when a public V2 object
27
+ has no doc comment.
28
+ - A second template set in the generator. Each `codegen-config.yml`
29
+ entry lists its template sets. Generation now refuses to run while
30
+ tracked generated files belong to no configured module, and fails on
31
+ names that Ruby or the V2 method signature already use.
32
+ - A release workflow. Pushing a `v*` tag publishes the gem to
33
+ rubygems.org through Trusted Publishing and opens a GitHub Release
34
+ with the CHANGELOG entry. `rake release` is disabled.
35
+
36
+ ### Changed
37
+
38
+ - The gem is named `sp_api_clients` on rubygems.org; the old name
39
+ started with a company name that is not ours. The `AmazonSpClients`
40
+ namespace and the `amazon_sp_clients/...` require paths are
41
+ unchanged, and `require 'sp_api_clients'` loads the v1 entry point.
42
+ A host that still installs `amazon_sp_clients` from git must drop
43
+ that line when it takes this gem: Bundler treats them as two gems,
44
+ and both ship the same files. The V2 default User-Agent names the
45
+ gem, so it changes too.
46
+ - The built gem ships only `lib/`, `vendor/`, the gemspec and the
47
+ top-level docs. Tests, the generator, CI config and dev tooling stay
48
+ out of it.
49
+ - The deprecated AWS IAM and logger settings stay as no-ops. They were
50
+ announced for removal in 2.0, but every consumer still sets them, so
51
+ they leave together with the v1 API in a later major.
52
+ - The Faraday requirement is unchanged (`>= 1.10, < 3`). The major
53
+ version marks the new namespace, not a dependency change. v1 is
54
+ unchanged.
55
+
56
+ ## [1.9.0] - 2026-09-01
57
+
58
+ ### Added
59
+
60
+ - Faraday 2 support. The gem now runs on Faraday 1.10 and Faraday 2;
61
+ CI tests the suite against both majors on Ruby 3.3 and 3.4.
62
+ - A clear `LoadError` at boot when the bundle pairs Faraday 2 with
63
+ faraday-httpclient 1.x. A stale lock can produce this pair, since
64
+ that adapter gem has no runtime dependency on faraday. The message
65
+ names the `bundle update` command that fixes the lock.
66
+
67
+ ### Changed
68
+
69
+ - The `faraday` dependency is `>= 1.10, < 3` (was `~> 1.4`).
70
+ - New dependencies `faraday-httpclient` and `faraday-retry`. Faraday 1
71
+ already ships both, so nothing changes there. Faraday 2 stopped
72
+ bundling them: the first provides the HTTPClient adapter the gem
73
+ uses, the second defines `Faraday::RetriableResponse`, which the
74
+ error middleware raises on HTTP 429 and consumers rescue for
75
+ throttling.
76
+
77
+ ### Removed
78
+
79
+ - The unused `faraday_middleware` dependency. It pinned
80
+ `faraday ~> 1.0`, which blocked Faraday 2 in any bundle with this
81
+ gem.
82
+ - The `params` field in error payloads (`error.response[:request]`).
83
+ Real adapters never fill it, so it was always `nil`. Error classes
84
+ and messages are unchanged.
85
+
86
+ ## [1.8.0] - 2026-09-01
87
+
88
+ ### Added
89
+
90
+ - Pure-Ruby code generator (`lib/generator`, `rake generate`). It
91
+ replaces swagger-codegen, so regeneration no longer needs Java. The
92
+ Amazon spec revision is pinned in `selling-partner-api-models.sha`;
93
+ CI verifies committed output matches regeneration at that pin, and a
94
+ nightly workflow opens a PR when Amazon updates the specs.
95
+ - CI on GitHub Actions: RSpec on Ruby 3.3 and 3.4, plus RuboCop.
96
+ - Characterization specs that pin the current public behavior of
97
+ `Configuration`, `Session`, `ApiClient`, `ApiResponse`,
98
+ `TokenExchangeAuth`, the `RaiseError` middleware, and the module
99
+ helpers (feed upload/download, report download).
100
+ - LICENSE (MIT) and this changelog.
101
+
102
+ ### Changed
103
+
104
+ - Minimum Ruby version is 3.3 (was 2.3).
105
+ - Generated files were regenerated with the new generator. Same code,
106
+ new `Generated by:` header, and the unused commented-out model
107
+ requires are gone from the vendor entry files.
108
+ - `codegen-config.yml` now lists only the 14 API modules that have
109
+ consumers.
110
+
111
+ ### Deprecated
112
+
113
+ - The AWS IAM configuration settings `access_key`, `secret_key`,
114
+ `role_arn`, and `credentials_provider`. Amazon dropped the SigV4
115
+ signing requirement in October 2023. The setters are accepted and
116
+ ignored; they go away together with the v1 API in a later major.
117
+
118
+ ### Fixed
119
+
120
+ - Grantless sessions re-authenticate after their token expires
121
+ (a typo'd variable made `refresh` a no-op for them).
122
+ - POST, PUT, and PATCH requests keep their query params. They were
123
+ silently dropped before, which broke 21 operations, including
124
+ `put_listings_item` and `patch_listings_item`.
125
+ - Per-operation header params (such as `Accept`) are sent.
126
+ - Raised errors no longer carry secrets: auth headers are filtered
127
+ from the exception payload, and the token service's request body
128
+ (client secret, refresh token) is filtered too.
129
+ - `config.endpoint = 'zz'` raises a clear `ArgumentError` instead of
130
+ `KeyError: key not found: nil`.
131
+ - A string-keyed response hash no longer turns `ApiResponse#payload`
132
+ into nil.
133
+ - `MARKETPLACE_IDS` includes Saudi Arabia (`sa`), matching
134
+ `MARKETPLACE_ENDPOINT_MAP`.
135
+ - `config.timeout` now applies to every connection (token exchange,
136
+ feed upload, report download). Only the API client honored it
137
+ before; the rest could hang forever.
138
+
139
+ ### Removed
140
+
141
+ - AWS SigV4 request signing and the STS AssumeRole call, with the
142
+ `aws-sdk-core` and `aws-sigv4` dependencies. `new_session` no
143
+ longer makes a synchronous AWS network call, and
144
+ `Session#role_credentials` / `Session#credentials_provider` are
145
+ gone.
146
+ - The 17 vendored API modules with no consumers (catalog_items_2020,
147
+ catalog_items_v0, fba_inbound, fulfillment_inbound_v0,
148
+ merchant_fulfillment_v0, messaging, notifications, product_fees_v0,
149
+ product_pricing_v0, sales, sellers, services, shipping,
150
+ solicitations, uploads_2020, vdf_payments_v1, vdf_transactions_v1)
151
+ and their require shims.
152
+ - The generated per-module scaffolding under `vendor/`: models
153
+ (1,037 files nothing required), per-module api_client /
154
+ configuration / api_error / version copies, and gem boilerplate.
155
+ Each vendor module now holds only its entry file and API classes.
156
+ - Dead code: `ServiceError`, `Configuration#basic_auth_token`, the
157
+ inert `host=` writer, and typhoeus-era configuration attrs
158
+ (`verify_ssl`, `cert_file`, `params_encoding`, and friends).
159
+ - Travis CI config, `.ruby-gemset`, and the broken `.gitmodules`.
160
+ - The swagger-codegen mustache templates (`codegen-templates/`) and
161
+ the old `codegen:generate` / `codegen:clean` rake tasks.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-2026 Dropstream
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,284 @@
1
+ # sp_api_clients
2
+
3
+ > **Status: experimental.** This gem is in the middle of large changes:
4
+ > a new code generator, a second client (`V2`), and consumers still
5
+ > moving over. Interfaces can change between releases. For a maintained,
6
+ > complete SP-API client we recommend
7
+ > [peddler](https://github.com/lineofflight/peddler).
8
+
9
+ The gem is `sp_api_clients`. The code keeps the `AmazonSpClients`
10
+ namespace and the `amazon_sp_clients/...` require paths. This project
11
+ is not affiliated with or endorsed by Amazon.
12
+
13
+ ## Installation
14
+
15
+ ```ruby
16
+ gem 'sp_api_clients', '~> 2.0'
17
+ ```
18
+
19
+ Releases are on [rubygems.org](https://rubygems.org/gems/sp_api_clients).
20
+ A git source (`gem 'sp_api_clients', git: ...`) still works, but it
21
+ follows a branch instead of a version. If you keep one, pin a `tag:`.
22
+
23
+ Either way this requires only the main (root) gem, but won't load any of
24
+ the generated SP APIs. The idea is to generate code for all APIs we may
25
+ need across our system, but allow requiring per project/repo basis.
26
+ Because of that, each API must be required explicitly:
27
+
28
+ ```ruby
29
+ require 'amazon_sp_clients' # or 'sp_api_clients'; Bundler.require does this for you
30
+
31
+ # in active_cart
32
+ require 'amazon_sp_clients/sp_orders_v0' # Orders API
33
+ # ...and others
34
+
35
+ # in active_fulfillment
36
+ require 'amazon_sp_clients/sp_fba_inventory' # FBA Inventory API
37
+ # ...and others
38
+ ```
39
+
40
+ Generally the you should look for files with `sp_` prefix inside `lib` dir.
41
+
42
+ ### Faraday
43
+
44
+ The gem works with Faraday 1.10 and Faraday 2; CI runs the suite
45
+ against both.
46
+
47
+ When you move an app from Faraday 1 to Faraday 2, run:
48
+
49
+ ```sh
50
+ bundle update faraday faraday-httpclient faraday-retry
51
+ ```
52
+
53
+ A plain `bundle install` after changing the `faraday` pin is not
54
+ enough. The lock keeps `faraday-httpclient` 1.x, which cannot load
55
+ under Faraday 2 (it has no runtime dependency on faraday, so nothing
56
+ forces the 2.x adapter). The gem detects this pair at boot and raises
57
+ a `LoadError` with the command above.
58
+
59
+ ## Usage Example
60
+
61
+ ```ruby
62
+ require 'amazon_sp_clients/sp_orders_v0'
63
+ require 'dotenv/load'
64
+
65
+ AmazonSpClients.configure do |c|
66
+ c.client_id = ENV['AMZ_CLIENT_ID']
67
+ c.client_secret = ENV['AMZ_CLIENT_SECRET']
68
+
69
+ c.sandbox_env!
70
+ c.logger = Logger.new($stdout)
71
+ c.logger.level = Logger::DEBUG
72
+ end
73
+
74
+ session = AmazonSpClients.new_session(refresh_token)
75
+
76
+ orders_api = AmazonSpClients::SpOrdersV0::OrdersV0Api.new(session)
77
+ get_orders_response =
78
+ orders_api.get_orders(['ATVPDKIKX0DER'], created_after: 'TEST_CASE_200')
79
+
80
+ puts get_orders_response.payload # Hash with symbolized keys
81
+ ```
82
+
83
+ The AWS IAM settings (`access_key`, `secret_key`, `role_arn`,
84
+ `credentials_provider`) are deprecated. Amazon dropped the SigV4
85
+ signing requirement in October 2023, so the gem no longer signs
86
+ requests or calls STS. The setters still exist but do nothing. They
87
+ stay until the v1 API itself is removed in a later major.
88
+
89
+ ### Restricted operations (requesting PII data)
90
+
91
+ ```ruby
92
+ orders_api = AmazonSpClients::SpOrdersV0::OrdersV0Api.new(session)
93
+ addr_resp =
94
+ orders_api.get_order_address('113-1435144-7135426', auth_names: [:pii])
95
+ ```
96
+
97
+ ## V2 client
98
+
99
+ `AmazonSpClients::V2` is the second-generation client. It fixes the v1
100
+ design flaws: no global config, a thread-safe token source, restricted
101
+ data tokens as a plain argument, and typed errors. v1 stays in the gem,
102
+ so you can migrate one class at a time.
103
+
104
+ ```ruby
105
+ require 'amazon_sp_clients/v2'
106
+
107
+ config = AmazonSpClients::V2::Config.new(
108
+ endpoint: 'na', # 'na', 'eu', 'fe' or a country code such as 'de'
109
+ sandbox: false,
110
+ timeout: 60, # read/write seconds; open_timeout: 10
111
+ client_id: nil, # LWA app credentials; only refresh-token clients and LWA need them
112
+ client_secret: nil
113
+ )
114
+
115
+ # The block runs before every request and returns the current access token.
116
+ client = AmazonSpClients::V2::Client.new(config) { store.access_token }
117
+
118
+ # Or let the client exchange a refresh token itself (needs client_id and client_secret).
119
+ client = AmazonSpClients::V2::Client.with_refresh_token(config, refresh_token)
120
+
121
+ orders = client.orders_v0.get_orders(['ATVPDKIKX0DER'], created_after: '2026-09-01T00:00:00Z')
122
+ orders.payload[:Orders] # symbol keys, as in v1
123
+ orders.payload[:NextToken]
124
+ orders.reported_rate_limit # Float from x-amzn-RateLimit-Limit, or nil
125
+ ```
126
+
127
+ To read a rotated refresh token back, build the credentials yourself
128
+ and keep the reference:
129
+
130
+ ```ruby
131
+ lwa = AmazonSpClients::V2::LWA.new(config)
132
+ creds = AmazonSpClients::V2::Credentials::RefreshToken.new(lwa, refresh_token)
133
+ client = AmazonSpClients::V2::Client.new(config, credentials: creds)
134
+ creds.refresh_token # the latest one LWA returned; persist it when it changes
135
+ ```
136
+
137
+ One client per merchant. It owns one connection and is safe to share
138
+ across threads. Required parameters are positional, in the same order
139
+ as v1; optional ones are keywords. Unknown keywords raise
140
+ `ArgumentError` (v1 ignored them), so pass only the parameters the
141
+ operation has.
142
+
143
+ ### Restricted data tokens
144
+
145
+ Operations that return PII take `rdt:`, a list of restricted resources.
146
+ The client fetches the token, caches it until it expires, and sends it
147
+ instead of the access token.
148
+
149
+ ```ruby
150
+ rdt = AmazonSpClients::V2::RDT
151
+ client.orders_v0.get_orders(ids, created_after: since, rdt: rdt::ORDERS_AND_ITEMS)
152
+ client.reports_2021.get_report_document(doc_id, rdt: rdt.report_document(doc_id))
153
+
154
+ # Any other restricted resource: method, path template, PII fields wanted.
155
+ rdt.resource('GET', '/orders/v0/orders/{orderId}', %w[buyerInfo shippingAddress])
156
+ ```
157
+
158
+ ### Errors
159
+
160
+ Every failure is an `AmazonSpClients::V2::Error`. Match on the class
161
+ and on `code`, never on the message.
162
+
163
+ | Class | When |
164
+ |---|---|
165
+ | `ThrottledError` | 429. Not a `ClientError`, so rescuing `ClientError` does not swallow it. |
166
+ | `UnauthorizedError`, `ForbiddenError` | 401, 403 |
167
+ | `NotFoundError` | 404 |
168
+ | `BadRequestError`, `ClientError` | 400, other 4xx. `code` holds the SP-API error code, e.g. `InvalidInput`. |
169
+ | `ServerError` | 5xx |
170
+ | `InvalidGrantError`, `InvalidClientError`, `AuthError` | LWA rejected the token request; `code` is `invalid_grant`, `invalid_client`, ... |
171
+ | `TimeoutError`, `ConnectionError` | no usable response; the original exception is `cause` |
172
+ | `ParseError` | a 2xx body that is not JSON |
173
+ | `DocumentError` | a presigned S3 upload or download failed |
174
+
175
+ Rescue subclasses before parents. Every error carries `status`,
176
+ `request_id`, `request` and `response`, with secrets redacted.
177
+
178
+ ### Feed and report documents
179
+
180
+ ```ruby
181
+ client.upload_feed_document(feed_document_payload, 'text/xml; charset=UTF-8', body)
182
+ client.download_feed_result(feed_document_payload) # Hash with string keys
183
+ client.download_report_document(report_document_payload) # String, gunzipped when needed
184
+ ```
185
+
186
+ ### Migrating from v1
187
+
188
+ | v1 | V2 |
189
+ |---|---|
190
+ | `AmazonSpClients.configure` block | `Config.new(endpoint: ...)`, one per merchant |
191
+ | `AmazonSpClients.new_callback_session { token }` | `Client.new(config) { token }` |
192
+ | `AmazonSpClients.new_session(refresh_token)` | `Client.with_refresh_token(config, refresh_token)` |
193
+ | `TokenExchangeAuth.new(refresh_token).exchange` | `LWA.new(config).exchange(refresh_token: refresh_token)`. The config must carry `client_id` and `client_secret`; `LWA.new` raises `ArgumentError` when they are missing, where v1 sent the request and got an LWA error back. |
194
+ | `response[:access_token]` | `token.access_token`, `token.expires_in`, `token.expires_at` |
195
+ | `SpOrdersV0::OrdersV0Api.new(session)` | `client.orders_v0` |
196
+ | `get_orders(ids, opts)` with an options Hash | `get_orders(ids, **opts)` with real parameter names only |
197
+ | `auth_names: [:orders_and_items]` | `rdt: RDT::ORDERS_AND_ITEMS` |
198
+ | `auth_names: [{ method: 'GET', path: path }]` | `rdt: RDT.report_document(doc_id)` for a report document, `rdt: [RDT.resource('GET', path)]` for anything else |
199
+ | `AmazonSpClients.configure.region` | `config.region` |
200
+ | `AmazonSpClients.configure.sandbox_env!` / `disable_sandbox!` | `Config.new(sandbox: true)`. The config is frozen, so a client cannot be switched after it is built. |
201
+ | `c.logger`, `c.role_arn`, `c.access_key`, `c.secret_key`, `c.credentials_provider` | nothing. v1 has ignored them since 1.8.0; delete the lines, and the AWS credential code that fed them. |
202
+ | the `Dropstream/1.0` user agent | `Config.new(user_agent: 'YourApp/1.0 ...')`. The default names this gem; Amazon asks for the application name. |
203
+ | `rescue Faraday::RetriableResponse` | `rescue V2::ThrottledError` |
204
+ | `rescue Faraday::ForbiddenError, Faraday::UnauthorizedError` | `rescue V2::ForbiddenError, V2::UnauthorizedError`. In v1 these also covered the token endpoint; in V2 those are `AuthError` (see the next row). |
205
+ | message starts with `Service 'token'` | `rescue V2::AuthError`, then `e.code`. A token-endpoint 4xx other than 429 is an `AuthError`, 401 and 403 included, so branch on `e.status` if you mapped those to an auth failure. The other three cases are not `AuthError`: 429 is a `ThrottledError`, 5xx is a `ServerError`, and a 200 with no `access_token` is a `ParseError`. |
206
+ | `rescue Faraday::ResourceNotFound` | `rescue V2::NotFoundError` |
207
+ | `rescue Faraday::BadRequestError, Faraday::ClientError` | `rescue V2::BadRequestError, V2::ClientError` |
208
+ | `rescue Faraday::ServerError` | `rescue V2::ServerError, V2::TimeoutError`. Faraday makes a timeout a `ServerError`; V2 makes it a `ConnectionError`, so add it where you treated 5xx as worth retrying. |
209
+ | `Faraday::ConnectionFailed`, `Faraday::SSLError` reaching your last `rescue` | `rescue V2::ConnectionError`. v1 let them fall through untyped; V2 wraps every transport failure, `TimeoutError` included, with the original exception as `cause`. |
210
+ | `e.message =~ /Please try again/` and other text matches | Match on the class and on `code`. The text changed: `"<status> <code>: <message> (<details>)"` for SP-API, `"<status> <code>: <description>"` for LWA, `"<status> (no body)"` and friends when there is nothing to show. The wording is not a contract. |
211
+ | `order_statuses: []` in the options Hash | The same on the wire: `[]` is sent as an empty value (`OrderStatuses=`), a nil keyword is left off. |
212
+ | message matches `InvalidInput` | `e.code == 'InvalidInput'` |
213
+ | `upload_feed_data`, `download_feed_report`, `download_report_document` | `client.upload_feed_document`, `client.download_feed_result`, `client.download_report_document`. The last one gunzips for you. The first returns nil; v1 returned the S3 response, which nothing read. |
214
+
215
+ ## Code generation
216
+
217
+ The v1 API classes under `vendor/` and the V2 classes under
218
+ `lib/amazon_sp_clients/v2/apis/` are generated by `lib/generator` (plain
219
+ Ruby + ERB, no external tools) from Amazon's official
220
+ [selling-partner-api-models](https://github.com/amzn/selling-partner-api-models)
221
+ specs. The generator clones that repo into `amzn-models/` (gitignored)
222
+ and checks out the exact revision pinned in
223
+ `selling-partner-api-models.sha`, so generation is reproducible.
224
+
225
+ ```sh
226
+ bundle exec rake generate # regenerate at the pinned revision
227
+ bundle exec rake generate:setup # only clone/sync the spec repo
228
+ bundle exec rake generate:verify # regenerate and fail on any drift (runs in CI)
229
+ bundle exec rake generate:update # pull latest specs, regenerate, advance the pin
230
+ ```
231
+
232
+ Which APIs get generated, and with which template sets (`v1`, `v2`), is
233
+ controlled by `codegen-config.yml`. To adopt newer Amazon specs, run
234
+ `rake generate:update` and review the diff — the pin file change plus
235
+ the regenerated files — in its own PR.
236
+
237
+ `bundle exec rake yard:verify` fails when a public V2 object has no doc
238
+ comment; CI runs it.
239
+
240
+ Generated files carry a `Generated by: lib/generator` header. Never edit
241
+ them by hand; change the generator (or the templates in
242
+ `lib/generator/templates/`) and regenerate.
243
+
244
+ ## Releasing
245
+
246
+ A release is a version tag. Nothing else publishes. The `Release`
247
+ workflow (`.github/workflows/release.yml`) checks the tag against the
248
+ version, runs the suite, builds the gem, pushes it to rubygems.org and
249
+ opens a GitHub Release with the CHANGELOG entry.
250
+
251
+ The push uses rubygems.org's Trusted Publishing: the job's GitHub OIDC
252
+ token is exchanged for a short-lived key, so no API key is stored
253
+ anywhere. rubygems.org has to trust this repository's workflow first.
254
+ Before the first release, sign in to rubygems.org, open the Trusted
255
+ publishers page of your profile and add a pending publisher: gem
256
+ `sp_api_clients`, repository owner `dropstream`, repository
257
+ `amazon_sp_clients` (the repository keeps its old name), workflow
258
+ `release.yml`, environment empty. A
259
+ pending publisher expires when the first push does not follow soon
260
+ (12 hours at the time of writing). After the first release the
261
+ publisher belongs to the gem and stays.
262
+
263
+ 1. Set `AmazonSpClients::VERSION` in `lib/amazon_sp_clients/version.rb`.
264
+ 2. Re-lock all three lockfiles, or CI's frozen install fails:
265
+
266
+ ```sh
267
+ bundle lock --local
268
+ BUNDLE_GEMFILE=gemfiles/faraday_1.gemfile bundle lock --local
269
+ BUNDLE_GEMFILE=gemfiles/faraday_2.gemfile bundle lock --local
270
+ ```
271
+
272
+ 3. Give the CHANGELOG entry its release date: `## [X.Y.Z] - YYYY-MM-DD`.
273
+ The workflow stops when the version has no entry.
274
+ 4. Commit, then push an annotated tag on that commit:
275
+
276
+ ```sh
277
+ git tag -a vX.Y.Z -m 'vX.Y.Z'
278
+ git push origin vX.Y.Z
279
+ ```
280
+
281
+ The tag must be `v` plus the version, or the workflow stops before it
282
+ builds. rubygems.org rejects a version it already has, so a failed
283
+ release needs a new version and a new tag, not a re-run. `rake release`
284
+ is disabled; it would tag and push from a laptop.
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+
5
+ module AmazonSpClients
6
+ # Loads the HTTPClient Faraday adapter (Faraday 2 no longer auto-loads
7
+ # adapter gems; a no-op under Faraday 1).
8
+ #
9
+ # Bundler can pair Faraday 2 with faraday-httpclient 1.x — that adapter
10
+ # gem has no runtime dependency on faraday, so a stale lock entry
11
+ # survives a `bundle update faraday`. The pair fails at require time
12
+ # with a cryptic NoMethodError; turn it into a message that names the
13
+ # fix.
14
+ module AdapterLoader
15
+ FIX_CMD = 'bundle update faraday faraday-httpclient faraday-retry'
16
+
17
+ def self.require_adapter!
18
+ require 'faraday/httpclient'
19
+ rescue NoMethodError
20
+ version = Gem.loaded_specs['faraday-httpclient']&.version
21
+ raise LoadError,
22
+ "faraday-httpclient #{version} cannot load under " \
23
+ "Faraday #{Faraday::VERSION}. Run: #{FIX_CMD}"
24
+ end
25
+ end
26
+ end
27
+
28
+ AmazonSpClients::AdapterLoader.require_adapter!