@amazon-sp-api-release/amazon-sp-api-sdk-js 1.0.0-rc6 → 1.0.0-rc8
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/README.md +22 -25
- 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
CHANGED
|
@@ -49,42 +49,39 @@ Add the following line to the `dependencies` in your `package.json` file:
|
|
|
49
49
|
|
|
50
50
|
In order to call one of the APIs included in the Selling Partner API, you need to:
|
|
51
51
|
1. Configure credentials (Note: Use your individual credentials for `clientId`, `clientSecret` and `refreshToken`)
|
|
52
|
-
2.
|
|
52
|
+
2. Enable auto `accessToken` retrievel using our built in ApiClient function OR retrieve `accessToken` using our `LwaAuthClient` helper (Be aware for some APIs, you will need extra step to get `RDT Token`)
|
|
53
53
|
2. Create an instance for a specific API and API client, then apply `accessToken` to it.
|
|
54
|
-
3. Call an operation
|
|
54
|
+
3. Call an API operation
|
|
55
55
|
|
|
56
56
|
For an example, refer to the following sample code for connecting to Sellers API:
|
|
57
57
|
|
|
58
58
|
```javascript
|
|
59
|
-
import { LwaAuthClient } from '@amazon-sp-api-release/amazon-sp-api-sdk-js';
|
|
60
|
-
|
|
61
59
|
import {
|
|
62
60
|
SellersSpApi
|
|
63
61
|
} from '@amazon-sp-api-release/amazon-sp-api-sdk-js';
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
})();
|
|
63
|
+
async function getMarketplaceParticipations() {
|
|
64
|
+
try {
|
|
65
|
+
//Configure Sellers ApiClient
|
|
66
|
+
const sellersApiClient = new SellersSpApi.ApiClient(AppConfig.spApiNAEndpoint);
|
|
67
|
+
sellersApiClient.enableAutoRetrievalAccessToken('<YOUR_CLIENT_ID>','<YOUR_CLIENT_SECRET>', '<YOUR_REFRESH_TOKEN>' null);
|
|
68
|
+
const sellersApi = new SellersSpApi.SellersApi(sellersApiClient);
|
|
69
|
+
|
|
70
|
+
//Call GetMarkerplaceParticipations API
|
|
71
|
+
const participations = await sellersApi.getMarketplaceParticipations();
|
|
72
|
+
console.log(
|
|
73
|
+
JSON.stringify(participations, null, ' ') +
|
|
74
|
+
'\n**********************************'
|
|
75
|
+
)
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Exception when calling getMarketplaceParticipations API', error.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getMarketplaceParticipations();
|
|
85
82
|
```
|
|
86
83
|
|
|
87
|
-
Alternatively, you can go to `@amazon-sp-api-release/amazon-sp-api-sdk-js/sample-node-app` and copy over and modify `index.js` and `app.config.mjs` files and give them a try.
|
|
84
|
+
Alternatively, you can go to `@amazon-sp-api-release/amazon-sp-api-sdk-js/sample-node-app` and copy over and modify `index.js` and `app.config.mjs` files and give them a try. You can see multiple API operation call sampleswith various way of retrieving token, as well how to set up rate limiter and retry logic when making API calls.
|
|
88
85
|
|
|
89
86
|
##### Additional Note:
|
|
90
87
|
This Amazon Selling Partner API JavaScript SDK is fully compatible with ECMAScript modules (ESM). You can use modern ES6+ import/export syntax as demonstrated in the example code:
|
|
@@ -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
|
+
}
|