@amazon-sp-api-release/amazon-sp-api-sdk-js 1.0.0-rc5 → 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.
Files changed (113) hide show
  1. package/helper/DefaultRateLimitFetcher.mjs +59 -0
  2. package/helper/LwaAuthClient.mjs +56 -22
  3. package/helper/RateLimitConfiguration.mjs +53 -0
  4. package/helper/ScopeConstants.mjs +8 -0
  5. package/helper/SuperagentRateLimiter.mjs +51 -0
  6. package/helper/rate-limits.yml +874 -0
  7. package/index.js +51 -49
  8. package/package.json +6 -6
  9. package/sample-node-app/app.config.mjs +4 -3
  10. package/sample-node-app/index.js +155 -24
  11. package/src/apluscontent_v2020_11_01/ApiClient.js +152 -70
  12. package/src/apluscontent_v2020_11_01/api/AplusContentApi.js +10 -10
  13. package/src/appintegrations_v2024_04_01/ApiClient.js +152 -70
  14. package/src/appintegrations_v2024_04_01/api/AppIntegrationsApi.js +3 -3
  15. package/src/applications_v2023_11_30/ApiClient.js +152 -70
  16. package/src/applications_v2023_11_30/api/ApplicationsApi.js +1 -1
  17. package/src/awd_v2024_05_09/ApiClient.js +152 -70
  18. package/src/awd_v2024_05_09/api/AwdApi.js +11 -11
  19. package/src/catalogitems_v2022_04_01/ApiClient.js +152 -70
  20. package/src/catalogitems_v2022_04_01/api/CatalogApi.js +2 -2
  21. package/src/datakiosk_v2023_11_15/ApiClient.js +152 -70
  22. package/src/datakiosk_v2023_11_15/api/QueriesApi.js +5 -5
  23. package/src/easyship_v2022_03_23/ApiClient.js +152 -70
  24. package/src/easyship_v2022_03_23/api/EasyShipApi.js +5 -5
  25. package/src/fbaeligibility_v1/ApiClient.js +152 -70
  26. package/src/fbaeligibility_v1/api/FbaInboundApi.js +1 -1
  27. package/src/fbainventory_v1/ApiClient.js +152 -70
  28. package/src/fbainventory_v1/api/FbaInventoryApi.js +4 -4
  29. package/src/feeds_v2021_06_30/ApiClient.js +152 -70
  30. package/src/feeds_v2021_06_30/api/FeedsApi.js +6 -6
  31. package/src/finances_2024_06_19/ApiClient.js +152 -70
  32. package/src/finances_2024_06_19/api/DefaultApi.js +1 -1
  33. package/src/finances_v0/ApiClient.js +152 -70
  34. package/src/finances_v0/api/DefaultApi.js +4 -4
  35. package/src/fulfillmentinbound_v0/ApiClient.js +152 -70
  36. package/src/fulfillmentinbound_v0/api/FbaInboundApi.js +6 -6
  37. package/src/fulfillmentinbound_v2024_03_20/ApiClient.js +152 -70
  38. package/src/fulfillmentinbound_v2024_03_20/api/FbaInboundApi.js +45 -45
  39. package/src/fulfillmentoutbound_v2020_07_01/ApiClient.js +152 -70
  40. package/src/fulfillmentoutbound_v2020_07_01/api/FbaOutboundApi.js +14 -14
  41. package/src/invoices_v2024_06_19/ApiClient.js +152 -70
  42. package/src/invoices_v2024_06_19/api/InvoicesApi.js +7 -7
  43. package/src/invoicing_v0/ApiClient.js +152 -70
  44. package/src/invoicing_v0/api/ShipmentInvoiceApi.js +3 -3
  45. package/src/listingsitems_v2021_08_01/ApiClient.js +152 -70
  46. package/src/listingsitems_v2021_08_01/api/ListingsApi.js +5 -5
  47. package/src/listingsrestrictions_v2021_08_01/ApiClient.js +152 -70
  48. package/src/listingsrestrictions_v2021_08_01/api/ListingsApi.js +1 -1
  49. package/src/merchantfulfillment_v0/ApiClient.js +152 -70
  50. package/src/merchantfulfillment_v0/api/MerchantFulfillmentApi.js +5 -5
  51. package/src/messaging_v1/ApiClient.js +152 -70
  52. package/src/messaging_v1/api/MessagingApi.js +13 -13
  53. package/src/notifications_v1/ApiClient.js +152 -70
  54. package/src/notifications_v1/api/NotificationsApi.js +8 -8
  55. package/src/orders_v0/ApiClient.js +152 -70
  56. package/src/orders_v0/api/OrdersV0Api.js +9 -9
  57. package/src/orders_v0/api/ShipmentApi.js +1 -1
  58. package/src/pricing_v0/ApiClient.js +152 -70
  59. package/src/pricing_v0/api/ProductPricingApi.js +6 -6
  60. package/src/pricing_v2022_05_01/ApiClient.js +152 -70
  61. package/src/pricing_v2022_05_01/api/ProductPricingApi.js +2 -2
  62. package/src/productfees_v0/ApiClient.js +152 -70
  63. package/src/productfees_v0/api/FeesApi.js +3 -3
  64. package/src/producttypedefinitions_v2020_09_01/ApiClient.js +152 -70
  65. package/src/producttypedefinitions_v2020_09_01/api/DefinitionsApi.js +2 -2
  66. package/src/replenishment_v2022_11_07/ApiClient.js +152 -70
  67. package/src/replenishment_v2022_11_07/api/OffersApi.js +2 -2
  68. package/src/replenishment_v2022_11_07/api/SellingpartnersApi.js +1 -1
  69. package/src/reports_v2021_06_30/ApiClient.js +152 -70
  70. package/src/reports_v2021_06_30/api/ReportsApi.js +9 -9
  71. package/src/sales_v1/ApiClient.js +152 -70
  72. package/src/sales_v1/api/SalesApi.js +1 -1
  73. package/src/sellers_v1/ApiClient.js +152 -70
  74. package/src/sellers_v1/api/SellersApi.js +2 -2
  75. package/src/services_v1/ApiClient.js +152 -70
  76. package/src/services_v1/api/ServiceApi.js +17 -17
  77. package/src/shipping_v2/ApiClient.js +152 -70
  78. package/src/shipping_v2/api/ShippingApi.js +20 -20
  79. package/src/solicitations_v1/ApiClient.js +152 -70
  80. package/src/solicitations_v1/api/SolicitationsApi.js +2 -2
  81. package/src/supplysources_v2020_07_01/ApiClient.js +152 -70
  82. package/src/supplysources_v2020_07_01/api/SupplySourcesApi.js +6 -6
  83. package/src/tokens_v2021_03_01/ApiClient.js +152 -70
  84. package/src/tokens_v2021_03_01/api/TokensApi.js +1 -1
  85. package/src/transfers_v2024_06_01/ApiClient.js +152 -70
  86. package/src/transfers_v2024_06_01/api/DefaultApi.js +2 -2
  87. package/src/uploads_v2020_11_01/ApiClient.js +152 -70
  88. package/src/uploads_v2020_11_01/api/UploadsApi.js +1 -1
  89. package/src/vehicles_v2024_11_01/ApiClient.js +152 -70
  90. package/src/vehicles_v2024_11_01/api/AutomotiveApi.js +1 -1
  91. package/src/vehicles_v2024_11_01/api/VehiclesApi.js +1 -1
  92. package/src/vendordfinventory_v1/ApiClient.js +152 -70
  93. package/src/vendordfinventory_v1/api/UpdateInventoryApi.js +1 -1
  94. package/src/vendordforders_v2021_12_28/ApiClient.js +152 -70
  95. package/src/vendordforders_v2021_12_28/api/VendorOrdersApi.js +3 -3
  96. package/src/vendordfpayments_v1/ApiClient.js +152 -70
  97. package/src/vendordfpayments_v1/api/VendorInvoiceApi.js +1 -1
  98. package/src/vendordfshipping_v2021_12_28/ApiClient.js +152 -70
  99. package/src/vendordfshipping_v2021_12_28/api/CreateContainerLabelApi.js +1 -1
  100. package/src/vendordfshipping_v2021_12_28/api/CustomerInvoicesApi.js +2 -2
  101. package/src/vendordfshipping_v2021_12_28/api/VendorShippingApi.js +4 -4
  102. package/src/vendordfshipping_v2021_12_28/api/VendorShippingLabelsApi.js +4 -4
  103. package/src/vendordftransactions_v2021_12_28/ApiClient.js +152 -70
  104. package/src/vendordftransactions_v2021_12_28/api/VendorTransactionApi.js +1 -1
  105. package/src/vendorinvoices_v1/ApiClient.js +152 -70
  106. package/src/vendorinvoices_v1/api/VendorPaymentsApi.js +1 -1
  107. package/src/vendororders_v1/ApiClient.js +152 -70
  108. package/src/vendororders_v1/api/VendorOrdersApi.js +4 -4
  109. package/src/vendorshipments_v1/ApiClient.js +152 -70
  110. package/src/vendorshipments_v1/api/VendorShippingApi.js +4 -4
  111. package/src/vendortransactionstatus_v1/ApiClient.js +152 -70
  112. package/src/vendortransactionstatus_v1/api/VendorTransactionApi.js +1 -1
  113. 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
+ }
@@ -1,31 +1,39 @@
1
1
  import superagent from 'superagent';
2
2
 
3
3
  export class LwaAuthClient {
4
- #clientId = null;
5
- #clientSecret = null;
6
- #refreshToken = null;
7
- #accessToken = null;
8
- #accessTokenExpiry = null;
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 || typeof refreshToken !== 'string') {
24
- throw new Error(`invalid refreshToken`);
31
+ if ((!refreshToken && !scope) || (refreshToken && scope)) {
32
+ throw new Error(`Either refreshToken or scope must be defined`);
25
33
  }
26
- this.#clientId = clientId;
27
- this.#clientSecret = clientSecret;
28
- this.#refreshToken = refreshToken;
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
- if (this.#accessToken && this.#accessToken && this.#accessTokenExpiry > Date.now()) {
37
- console.log(`LWA access token already exists and is valid. ${this.#accessTokenExpiry}`);
38
- return Promise.resolve(this.#accessToken);
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
- const token = await this.#doRefresh();
41
- if (!token || !token.access_token) {
42
- throw new Error(`Failed to refresh LWA token.`);
61
+
62
+ const res = await this.#doRefresh();
63
+ if (!this.#cachedTokenMap) {
64
+ this.#cachedTokenMap = new Map();
43
65
  }
44
- this.#accessToken = token.access_token;
45
- this.#accessTokenExpiry = new Date().getTime() + (token.expires_in * 1000);
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(`grant_type=refresh_token&refresh_token=${this.#refreshToken}&client_id=${this.#clientId}&client_secret=${this.#clientSecret}`)
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
+ }