@amazon-sp-api-release/amazon-sp-api-sdk-js 1.0.0-rc6 → 1.0.0-rc7
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.
- package/helper/DefaultRateLimitFetcher.mjs +59 -0
- package/helper/LwaAuthClient.mjs +56 -22
- package/helper/RateLimitConfiguration.mjs +53 -0
- package/helper/ScopeConstants.mjs +8 -0
- package/helper/SuperagentRateLimiter.mjs +51 -0
- package/helper/rate-limits.yml +874 -0
- package/index.js +51 -49
- package/package.json +6 -6
- package/sample-node-app/app.config.mjs +4 -3
- package/sample-node-app/index.js +155 -24
- package/src/apluscontent_v2020_11_01/ApiClient.js +152 -70
- package/src/apluscontent_v2020_11_01/api/AplusContentApi.js +10 -10
- package/src/appintegrations_v2024_04_01/ApiClient.js +152 -70
- package/src/appintegrations_v2024_04_01/api/AppIntegrationsApi.js +3 -3
- package/src/applications_v2023_11_30/ApiClient.js +152 -70
- package/src/applications_v2023_11_30/api/ApplicationsApi.js +1 -1
- package/src/awd_v2024_05_09/ApiClient.js +152 -70
- package/src/awd_v2024_05_09/api/AwdApi.js +11 -11
- package/src/catalogitems_v2022_04_01/ApiClient.js +152 -70
- package/src/catalogitems_v2022_04_01/api/CatalogApi.js +2 -2
- package/src/datakiosk_v2023_11_15/ApiClient.js +152 -70
- package/src/datakiosk_v2023_11_15/api/QueriesApi.js +5 -5
- package/src/easyship_v2022_03_23/ApiClient.js +152 -70
- package/src/easyship_v2022_03_23/api/EasyShipApi.js +5 -5
- package/src/fbaeligibility_v1/ApiClient.js +152 -70
- package/src/fbaeligibility_v1/api/FbaInboundApi.js +1 -1
- package/src/fbainventory_v1/ApiClient.js +152 -70
- package/src/fbainventory_v1/api/FbaInventoryApi.js +4 -4
- package/src/feeds_v2021_06_30/ApiClient.js +152 -70
- package/src/feeds_v2021_06_30/api/FeedsApi.js +6 -6
- package/src/finances_2024_06_19/ApiClient.js +152 -70
- package/src/finances_2024_06_19/api/DefaultApi.js +1 -1
- package/src/finances_v0/ApiClient.js +152 -70
- package/src/finances_v0/api/DefaultApi.js +4 -4
- package/src/fulfillmentinbound_v0/ApiClient.js +152 -70
- package/src/fulfillmentinbound_v0/api/FbaInboundApi.js +6 -6
- package/src/fulfillmentinbound_v2024_03_20/ApiClient.js +152 -70
- package/src/fulfillmentinbound_v2024_03_20/api/FbaInboundApi.js +45 -45
- package/src/fulfillmentoutbound_v2020_07_01/ApiClient.js +152 -70
- package/src/fulfillmentoutbound_v2020_07_01/api/FbaOutboundApi.js +14 -14
- package/src/invoices_v2024_06_19/ApiClient.js +152 -70
- package/src/invoices_v2024_06_19/api/InvoicesApi.js +7 -7
- package/src/invoicing_v0/ApiClient.js +152 -70
- package/src/invoicing_v0/api/ShipmentInvoiceApi.js +3 -3
- package/src/listingsitems_v2021_08_01/ApiClient.js +152 -70
- package/src/listingsitems_v2021_08_01/api/ListingsApi.js +5 -5
- package/src/listingsrestrictions_v2021_08_01/ApiClient.js +152 -70
- package/src/listingsrestrictions_v2021_08_01/api/ListingsApi.js +1 -1
- package/src/merchantfulfillment_v0/ApiClient.js +152 -70
- package/src/merchantfulfillment_v0/api/MerchantFulfillmentApi.js +5 -5
- package/src/messaging_v1/ApiClient.js +152 -70
- package/src/messaging_v1/api/MessagingApi.js +13 -13
- package/src/notifications_v1/ApiClient.js +152 -70
- package/src/notifications_v1/api/NotificationsApi.js +8 -8
- package/src/orders_v0/ApiClient.js +152 -70
- package/src/orders_v0/api/OrdersV0Api.js +9 -9
- package/src/orders_v0/api/ShipmentApi.js +1 -1
- package/src/pricing_v0/ApiClient.js +152 -70
- package/src/pricing_v0/api/ProductPricingApi.js +6 -6
- package/src/pricing_v2022_05_01/ApiClient.js +152 -70
- package/src/pricing_v2022_05_01/api/ProductPricingApi.js +2 -2
- package/src/productfees_v0/ApiClient.js +152 -70
- package/src/productfees_v0/api/FeesApi.js +3 -3
- package/src/producttypedefinitions_v2020_09_01/ApiClient.js +152 -70
- package/src/producttypedefinitions_v2020_09_01/api/DefinitionsApi.js +2 -2
- package/src/replenishment_v2022_11_07/ApiClient.js +152 -70
- package/src/replenishment_v2022_11_07/api/OffersApi.js +2 -2
- package/src/replenishment_v2022_11_07/api/SellingpartnersApi.js +1 -1
- package/src/reports_v2021_06_30/ApiClient.js +152 -70
- package/src/reports_v2021_06_30/api/ReportsApi.js +9 -9
- package/src/sales_v1/ApiClient.js +152 -70
- package/src/sales_v1/api/SalesApi.js +1 -1
- package/src/sellers_v1/ApiClient.js +152 -70
- package/src/sellers_v1/api/SellersApi.js +2 -2
- package/src/services_v1/ApiClient.js +152 -70
- package/src/services_v1/api/ServiceApi.js +17 -17
- package/src/shipping_v2/ApiClient.js +152 -70
- package/src/shipping_v2/api/ShippingApi.js +20 -20
- package/src/solicitations_v1/ApiClient.js +152 -70
- package/src/solicitations_v1/api/SolicitationsApi.js +2 -2
- package/src/supplysources_v2020_07_01/ApiClient.js +152 -70
- package/src/supplysources_v2020_07_01/api/SupplySourcesApi.js +6 -6
- package/src/tokens_v2021_03_01/ApiClient.js +152 -70
- package/src/tokens_v2021_03_01/api/TokensApi.js +1 -1
- package/src/transfers_v2024_06_01/ApiClient.js +152 -70
- package/src/transfers_v2024_06_01/api/DefaultApi.js +2 -2
- package/src/uploads_v2020_11_01/ApiClient.js +152 -70
- package/src/uploads_v2020_11_01/api/UploadsApi.js +1 -1
- package/src/vehicles_v2024_11_01/ApiClient.js +152 -70
- package/src/vehicles_v2024_11_01/api/AutomotiveApi.js +1 -1
- package/src/vehicles_v2024_11_01/api/VehiclesApi.js +1 -1
- package/src/vendordfinventory_v1/ApiClient.js +152 -70
- package/src/vendordfinventory_v1/api/UpdateInventoryApi.js +1 -1
- package/src/vendordforders_v2021_12_28/ApiClient.js +152 -70
- package/src/vendordforders_v2021_12_28/api/VendorOrdersApi.js +3 -3
- package/src/vendordfpayments_v1/ApiClient.js +152 -70
- package/src/vendordfpayments_v1/api/VendorInvoiceApi.js +1 -1
- package/src/vendordfshipping_v2021_12_28/ApiClient.js +152 -70
- package/src/vendordfshipping_v2021_12_28/api/CreateContainerLabelApi.js +1 -1
- package/src/vendordfshipping_v2021_12_28/api/CustomerInvoicesApi.js +2 -2
- package/src/vendordfshipping_v2021_12_28/api/VendorShippingApi.js +4 -4
- package/src/vendordfshipping_v2021_12_28/api/VendorShippingLabelsApi.js +4 -4
- package/src/vendordftransactions_v2021_12_28/ApiClient.js +152 -70
- package/src/vendordftransactions_v2021_12_28/api/VendorTransactionApi.js +1 -1
- package/src/vendorinvoices_v1/ApiClient.js +152 -70
- package/src/vendorinvoices_v1/api/VendorPaymentsApi.js +1 -1
- package/src/vendororders_v1/ApiClient.js +152 -70
- package/src/vendororders_v1/api/VendorOrdersApi.js +4 -4
- package/src/vendorshipments_v1/ApiClient.js +152 -70
- package/src/vendorshipments_v1/api/VendorShippingApi.js +4 -4
- package/src/vendortransactionstatus_v1/ApiClient.js +152 -70
- package/src/vendortransactionstatus_v1/api/VendorTransactionApi.js +1 -1
- package/README.md +0 -107
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { parse } from 'yaml';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { RateLimitConfiguration } from './RateLimitConfiguration.mjs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname } from 'path';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
|
|
8
|
+
export class DefaultRateLimitFetcher {
|
|
9
|
+
|
|
10
|
+
#defaultRateLimitMap;
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
this.#defaultRateLimitMap = null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Initialize the configuration by loading the YAML file
|
|
17
|
+
async init() {
|
|
18
|
+
const fileName = fileURLToPath(import.meta.url);
|
|
19
|
+
const dirName = dirname(fileName);
|
|
20
|
+
try {
|
|
21
|
+
const fileContents = await readFile(join(dirName, 'rate-limits.yml'), 'utf8');
|
|
22
|
+
this.#defaultRateLimitMap = parse(fileContents);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
this.#defaultRateLimitMap = {};
|
|
25
|
+
throw new error('Error loading rate-limits.yml:', error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getLimit(operation) {
|
|
30
|
+
const burst = this.getValue(operation, 1);
|
|
31
|
+
const rateLimitPermit = this.getValue(operation, 0) / this.getValue(operation, 2);
|
|
32
|
+
return new RateLimitConfiguration(rateLimitPermit, burst);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getValue(operation, position) {
|
|
36
|
+
if (this.#defaultRateLimitMap &&
|
|
37
|
+
this.#defaultRateLimitMap[operation] &&
|
|
38
|
+
position < this.#defaultRateLimitMap[operation].length) {
|
|
39
|
+
return this.#defaultRateLimitMap[operation][position];
|
|
40
|
+
}
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Singleton implementation
|
|
45
|
+
static #instance = null;
|
|
46
|
+
|
|
47
|
+
static async create() {
|
|
48
|
+
const instance = new DefaultRateLimitFetcher();
|
|
49
|
+
await instance.init();
|
|
50
|
+
return instance;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static async getInstance() {
|
|
54
|
+
if (!DefaultRateLimitFetcher.#instance) {
|
|
55
|
+
DefaultRateLimitFetcher.#instance = await DefaultRateLimitFetcher.create();
|
|
56
|
+
}
|
|
57
|
+
return DefaultRateLimitFetcher.#instance;
|
|
58
|
+
}
|
|
59
|
+
}
|
package/helper/LwaAuthClient.mjs
CHANGED
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
import superagent from 'superagent';
|
|
2
2
|
|
|
3
3
|
export class LwaAuthClient {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
#
|
|
4
|
+
/**
|
|
5
|
+
* Private variable to store LWA credential and refresh token.
|
|
6
|
+
* @type {Object<String, String>}
|
|
7
|
+
*/
|
|
8
|
+
#lwaClientInfo = {};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Private variable to cache access token that is retrieved by auto-retrieval.
|
|
12
|
+
* @type {Map<String, {String, Number}> | null}
|
|
13
|
+
*/
|
|
14
|
+
#cachedTokenMap = null;
|
|
15
|
+
|
|
9
16
|
/**
|
|
10
17
|
* Constructs a new LwaAuthClient.
|
|
11
18
|
* @class
|
|
12
19
|
* @param {String} clientId LWA client ID. Get this value from SP-API Developer Portal.
|
|
13
20
|
* @param {String} clientSecret LWA client secret. Get this value from SP-API Developer Portal.
|
|
14
|
-
* @param {String} refreshToken LWA refresh token. Get this value from SP-API Developer Portal.
|
|
21
|
+
* @param {String|null} refreshToken LWA refresh token. Get this value from SP-API Developer Portal.
|
|
22
|
+
* @param {String|null} scope LWA scope(s) for grantless operations
|
|
15
23
|
*/
|
|
16
|
-
constructor(clientId, clientSecret, refreshToken) {
|
|
24
|
+
constructor(clientId, clientSecret, refreshToken = null, scope = null) {
|
|
17
25
|
if (!clientId || typeof clientId !== 'string') {
|
|
18
26
|
throw new Error(`invalid clientId.`);
|
|
19
27
|
}
|
|
20
28
|
if (!clientSecret || typeof clientSecret !== 'string') {
|
|
21
29
|
throw new Error(`invalid clientSecret`);
|
|
22
30
|
}
|
|
23
|
-
if (!refreshToken ||
|
|
24
|
-
throw new Error(`
|
|
31
|
+
if ((!refreshToken && !scope) || (refreshToken && scope)) {
|
|
32
|
+
throw new Error(`Either refreshToken or scope must be defined`);
|
|
25
33
|
}
|
|
26
|
-
this.#
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
this.#lwaClientInfo = {
|
|
35
|
+
clientId, clientSecret, refreshToken, scope
|
|
36
|
+
};
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
/**
|
|
@@ -33,17 +41,30 @@ export class LwaAuthClient {
|
|
|
33
41
|
* @returns {Promise<String>} LWA access token.
|
|
34
42
|
*/
|
|
35
43
|
async getAccessToken() {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
const key = JSON.stringify(this.#lwaClientInfo);
|
|
45
|
+
|
|
46
|
+
if (this.#cachedTokenMap) {
|
|
47
|
+
const cachedTokenItem = this.#cachedTokenMap.get(key);
|
|
48
|
+
|
|
49
|
+
if (cachedTokenItem) {
|
|
50
|
+
const cachedToken = cachedTokenItem.cachedToken;
|
|
51
|
+
const cachedTokenExpiration = cachedTokenItem.cachedTokenExpiration;
|
|
52
|
+
//Adjustment in milliseconds (60s) to avoid using nearly expired tokens
|
|
53
|
+
const adjustedExpiryTime = cachedTokenExpiration - 60000;
|
|
54
|
+
if (adjustedExpiryTime > Date.now()) {
|
|
55
|
+
return Promise.resolve(cachedToken);
|
|
56
|
+
} else {
|
|
57
|
+
this.#cachedTokenMap.delete(key);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
39
60
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
61
|
+
|
|
62
|
+
const res = await this.#doRefresh();
|
|
63
|
+
if (!this.#cachedTokenMap) {
|
|
64
|
+
this.#cachedTokenMap = new Map();
|
|
43
65
|
}
|
|
44
|
-
this.#
|
|
45
|
-
|
|
46
|
-
return this.#accessToken;
|
|
66
|
+
this.#cachedTokenMap.set(key, {cachedToken: res.access_token, cachedTokenExpiration: Date.now() + res.expires_in * 1000});
|
|
67
|
+
return res.access_token;
|
|
47
68
|
}
|
|
48
69
|
|
|
49
70
|
/**
|
|
@@ -51,9 +72,22 @@ export class LwaAuthClient {
|
|
|
51
72
|
* @returns {Promise<Object>} LWA token response.
|
|
52
73
|
*/
|
|
53
74
|
#doRefresh = async () => {
|
|
75
|
+
let requestBody = null;
|
|
76
|
+
if (this.#lwaClientInfo.scope) {
|
|
77
|
+
//grantless operations
|
|
78
|
+
requestBody = `grant_type=client_credentials&client_id=${this.#lwaClientInfo.clientId}&client_secret=${this.#lwaClientInfo.clientSecret}&scope=${this.#lwaClientInfo.scope}`;
|
|
79
|
+
} else {
|
|
80
|
+
requestBody = `grant_type=refresh_token&refresh_token=${this.#lwaClientInfo.refreshToken}&client_id=${this.#lwaClientInfo.clientId}&client_secret=${this.#lwaClientInfo.clientSecret}`;
|
|
81
|
+
}
|
|
54
82
|
const res = await superagent.post('https://api.amazon.com/auth/o2/token')
|
|
55
|
-
.send(
|
|
83
|
+
.send(requestBody)
|
|
56
84
|
.set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
|
|
57
85
|
return res.body;
|
|
58
86
|
}
|
|
87
|
+
|
|
88
|
+
clearCachedTokenMap() {
|
|
89
|
+
if (this.#cachedTokenMap) {
|
|
90
|
+
this.#cachedTokenMap.clear();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
59
93
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export class RateLimitConfiguration {
|
|
2
|
+
/**
|
|
3
|
+
* RateLimiter Permit: requests per second
|
|
4
|
+
* @type {number}
|
|
5
|
+
* @private
|
|
6
|
+
*/
|
|
7
|
+
#rateLimitPermit;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Burst value
|
|
11
|
+
* @type {number}
|
|
12
|
+
* @private
|
|
13
|
+
*/
|
|
14
|
+
#burstRequests;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {number} rateLimitPermit rate limit in requests/second
|
|
19
|
+
* @param {number} burstRequests number of busrt requests allowed
|
|
20
|
+
*/
|
|
21
|
+
constructor(rateLimitPermit, burstRequests) {
|
|
22
|
+
this.#rateLimitPermit = rateLimitPermit;
|
|
23
|
+
this.#burstRequests = burstRequests;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @returns {number}
|
|
28
|
+
*/
|
|
29
|
+
getRateLimitPermit() {
|
|
30
|
+
return this.#rateLimitPermit;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {number} rateLimitPermit
|
|
35
|
+
*/
|
|
36
|
+
setRateLimitPermit(rateLimitPermit) {
|
|
37
|
+
this.#rateLimitPermit = rateLimitPermit;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @returns {number}
|
|
42
|
+
*/
|
|
43
|
+
getBurstRequests() {
|
|
44
|
+
return this.#burstRequests;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {number} burstRequests
|
|
49
|
+
*/
|
|
50
|
+
setBurstRequests(burstRequests) {
|
|
51
|
+
this.#burstRequests = burstRequests;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LWA Scope Constants
|
|
3
|
+
*/
|
|
4
|
+
export class ScopeConstants {
|
|
5
|
+
static SCOPE_NOTIFICATION_API = 'sellingpartnerapi::notifications';
|
|
6
|
+
static SCOPE_MIGRATION_API = 'sellingpartnerapi::migration';
|
|
7
|
+
static SCOPE_APPLICATION_MANAGEMENT_API = 'sellingpartnerapi::client_credential:rotation';
|
|
8
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Bottleneck from 'bottleneck';
|
|
2
|
+
import { RateLimitConfiguration } from "./RateLimitConfiguration.mjs";
|
|
3
|
+
|
|
4
|
+
export class SuperagentRateLimiter {
|
|
5
|
+
/** @type {Bottleneck} */
|
|
6
|
+
#limiter;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {RateLimitConfiguration} config
|
|
10
|
+
*/
|
|
11
|
+
constructor(config) {
|
|
12
|
+
if (!config.getRateLimitPermit() || !config.getBurstRequests()) {
|
|
13
|
+
throw new Error("Invalid parameter for RateLimitConfiguration");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const rateLimit = config.getRateLimitPermit();
|
|
17
|
+
const burstRequests = config.getBurstRequests();
|
|
18
|
+
|
|
19
|
+
//Math.floor to handle special case for ShipmentInvoiceApi API
|
|
20
|
+
const refreshAmount = rateLimit < 1 ? 1 : Math.floor(rateLimit);
|
|
21
|
+
const refreshInterval = rateLimit < 1 ? Math.ceil(1/rateLimit) * 1000 : 1000;
|
|
22
|
+
|
|
23
|
+
this.#limiter = new Bottleneck({
|
|
24
|
+
reservoir: burstRequests, // Initial capacity
|
|
25
|
+
reservoirRefreshAmount: refreshAmount, // How many tokens to add during each refresh
|
|
26
|
+
reservoirRefreshInterval: refreshInterval, // Refresh interval in milliseconds
|
|
27
|
+
maxConcurrent: burstRequests // Maximum number of requests running at the same time
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates a Superagent plugin that implements rate limiting
|
|
34
|
+
* @returns {function} Superagent plugin
|
|
35
|
+
*/
|
|
36
|
+
getPlugin() {
|
|
37
|
+
return (request) => {
|
|
38
|
+
// Add rate limiting before the request is sent
|
|
39
|
+
request.on('request', async () => {
|
|
40
|
+
try {
|
|
41
|
+
// Schedule the request
|
|
42
|
+
await this.#limiter.schedule(async () => {
|
|
43
|
+
return Promise.resolve();
|
|
44
|
+
});
|
|
45
|
+
} catch (error) {
|
|
46
|
+
throw new Error(`Rate limit exceed error: ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|