Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

amazon-sp-api

Package Overview
Dependencies
Maintainers
1
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

amazon-sp-api - npm Package Compare versions

Comparing version 0.1.1 to 0.2.0

lib/operations.js

15

lib/request.js

@@ -7,3 +7,3 @@ const https = require('https');

let options = {
method:req_options.method || 'GET',
method:req_options.method,
port:443,

@@ -15,5 +15,5 @@ hostname:url.hostname,

let post_params;
if (['post','put'].includes(req_options.method.toLowerCase()) && req_options.query){
post_params = req_options.query;
options.headers['Content-Length'] = post_params.length;
if (req_options.body){
post_params = req_options.body;
options.headers['Content-Length'] = Buffer.byteLength(post_params);
}

@@ -26,3 +26,6 @@ let req = https.request(options, (res) => {

res.on('end', () => {
resolve(body);
resolve({
body:body,
statusCode:res.statusCode
});
});

@@ -35,3 +38,3 @@ });

if (post_params){
req.write(post_params);
req.write(post_params, 'utf8');
}

@@ -38,0 +41,0 @@ req.end();

64

lib/SellingPartner.js

@@ -6,2 +6,3 @@ const CustomError = require('./CustomError');

const credentials = require('./credentials');
const operations = require('./operations');

@@ -27,2 +28,3 @@ // Provide credentials as environment variables OR create a path and file ~/.amzspapi/credentials (located in your user folder)

// auto_request_tokens:true // Optional: Whether or not the client should retrieve new access and role credentials if non given or expired. Default is true
// auto_request_throttled:true // Optional: Whether or not the client should automatically retry a request when throttled. Default is true
// }

@@ -47,3 +49,4 @@ constructor(config){

this._options = Object.assign({
auto_request_tokens:true
auto_request_tokens:true,
auto_request_throttled:true
}, config.options);

@@ -62,2 +65,8 @@

async _wait(restore_rate){
return new Promise((resolve, reject) => {
setTimeout(resolve, restore_rate * 1000);
});
}
async refreshAccessToken(){

@@ -67,3 +76,3 @@ let res = await request({

url:'https://api.amazon.com/auth/o2/token',
query:JSON.stringify({
body:JSON.stringify({
grant_type:'refresh_token',

@@ -80,7 +89,7 @@ refresh_token:this._refresh_token,

try {
json_res = JSON.parse(res);
json_res = JSON.parse(res.body);
} catch (e){
throw new CustomError({
code:'REFRESH_ACCESS_TOKEN_PARSE_ERROR',
message:res
message:res.body
});

@@ -98,3 +107,3 @@ }

code:'UNKNOWN_REFRESH_ACCESS_TOKEN_ERROR',
message:res
message:res.body
});

@@ -109,7 +118,7 @@ }

try {
json_res = xml_parser.parse(res);
json_res = xml_parser.parse(res.body);
} catch(e){
throw new CustomError({
code:'XML_PARSE_ERROR',
message:res
message:res.body
});

@@ -132,3 +141,3 @@ }

code:'NO_ROLE_CREDENTIALS_RECEIVED',
message:res
message:res.body
});

@@ -138,13 +147,20 @@ }

// req_params object params:
// path: Required, the API path you want to request, [see SP API References](https://github.com/amzn/selling-partner-api-docs/tree/main/references)
// method: Optional, HTTP Method of the call, default is GET
// query: Optional, the input parameters of the call
// req_params object:
// * operation: Required, the operation you want to request [see SP API References](https://github.com/amzn/selling-partner-api-docs/tree/main/references)
// * path: The input paramaters added to the path of the operation
// * query: The input parameters added to the query string of the operation
// * body: The input parameters added to the body of the operation
async callAPI(req_params){
if (!req_params.path){
if (!req_params.operation){
throw new CustomError({
code:'NO_API_PATH_GIVEN',
message:'Please provide an API path to call'
code:'NO_OPERATION_GIVEN',
message:'Please provide an operation to call'
});
}
if (!operations[req_params.operation]){
throw new CustomError({
code:'OPERATION_NOT_FOUND',
message:'No operation found: ' + req_params.operation
});
}
if (this._options.auto_request_tokens){

@@ -164,12 +180,15 @@ if (!this._access_token){

}
req_params = operations[req_params.operation](req_params);
let signed_request = new Signer(this._region).signAPIRequest(this._access_token, this._role_credentials, req_params);
let res = await request(signed_request);
let json_res;
if (res.statusCode === 204 && req_params.method === 'DELETE'){
return {success:true};
}
try {
// Replace newlines in json result because some errors are returned with newline characters, i.e. "InvalidSignature"
json_res = JSON.parse(res.replace(/\n/g, ''));
json_res = JSON.parse(res.body.replace(/\n/g, ''));
} catch (e){
throw new CustomError({
code:'JSON_PARSE_ERROR',
message:res
message:res.body
});

@@ -180,3 +199,3 @@ }

// Refresh tokens when expired and auto_request_tokens is true
if (error.code === 'Unauthorized'){
if (res.statusCode === 403 && error.code === 'Unauthorized'){
if (this._options.auto_request_tokens){

@@ -191,6 +210,11 @@ if (/access token.*expired/.test(error.details)){

}
// Retry when call is throttled and auto_request_throttled is true
} else if (res.statusCode === 429 && error.code === 'QuotaExceeded' && this._options.auto_request_throttled){
await this._wait(req_params.restore_rate);
return await this.callAPI(req_params);
}
throw new CustomError(error);
}
return json_res.payload;
// Some calls do not return response in payload but directly (i.e. operation "getSmallAndLightEligibilityBySellerSKU")!
return json_res.payload || json_res;
}

@@ -197,0 +221,0 @@

@@ -68,14 +68,13 @@ const crypto = require('crypto-js');

_constructCanonicalRequestForAPI(access_token, method, path, encoded_query_string, query){
_constructCanonicalRequestForAPI(access_token, params, encoded_query_string){
let canonical = [];
canonical.push(method);
canonical.push(path);
canonical.push(method.toLowerCase() === 'get' ? encoded_query_string : '');
canonical.push(params.method);
canonical.push(params.api_path);
canonical.push(encoded_query_string);
canonical.push('host:' + this._api_endpoint);
canonical.push('x-amz-access-token:' + access_token);
canonical.push('x-amz-content-sha256:' + crypto.SHA256(encoded_query_string || ''));
canonical.push('x-amz-date:' + this._iso_date.full);
canonical.push('');
canonical.push('host;x-amz-access-token;x-amz-content-sha256;x-amz-date');
canonical.push(crypto.SHA256(method.toLowerCase() === 'get' ? '' : JSON.stringify(query)));
canonical.push('host;x-amz-access-token;x-amz-date');
canonical.push(crypto.SHA256(params.body ? JSON.stringify(params.body) : ''));
return canonical.join('\n');

@@ -101,5 +100,12 @@ }

_constructURL(req_params, encoded_query_string){
let url = 'https://' + this._api_endpoint + req_params.api_path;
if (encoded_query_string !== ''){
url += '?' + encoded_query_string;
}
return url;
}
signAPIRequest(access_token, role_credentials, req_params){
req_params.method = req_params.method || 'GET';
req_params.query = this._sortQuery(req_params.query);

@@ -110,19 +116,16 @@

let encoded_query_string = this._constructEncodedQueryString(req_params.query);
let canonical_request = this._constructCanonicalRequestForAPI(access_token, req_params.method, req_params.path, encoded_query_string, req_params.query);
let canonical_request = this._constructCanonicalRequestForAPI(access_token, req_params, encoded_query_string);
let string_to_sign = this._constructStringToSign(this._aws_regions[this._region], 'execute-api', canonical_request);
let signature = this._constructSignature(this._aws_regions[this._region], 'execute-api', string_to_sign, role_credentials.secret);
let get_params = (req_params.method.toLowerCase() !== 'get' || encoded_query_string === '') ? '' : ('?' + encoded_query_string);
return {
method:req_params.method,
url:'https://' + this._api_endpoint + req_params.path + get_params,
query:JSON.stringify(req_params.query),
url:this._constructURL(req_params, encoded_query_string),
body:req_params.body ? JSON.stringify(req_params.body) : null,
headers:{
'Authorization':'AWS4-HMAC-SHA256 Credential=' + role_credentials.id + '/' + this._iso_date.short + '/' + this._aws_regions[this._region] + '/execute-api/aws4_request, SignedHeaders=host;x-amz-access-token;x-amz-content-sha256;x-amz-date, Signature=' + signature,
'Content-Type': 'application/json',
'Authorization':'AWS4-HMAC-SHA256 Credential=' + role_credentials.id + '/' + this._iso_date.short + '/' + this._aws_regions[this._region] + '/execute-api/aws4_request, SignedHeaders=host;x-amz-access-token;x-amz-date, Signature=' + signature,
'Content-Type': 'application/json; charset=utf-8',
'host':this._api_endpoint,
'x-amz-access-token':access_token,
'x-amz-security-token':role_credentials.security_token,
'x-amz-content-sha256':crypto.SHA256(encoded_query_string).toString(crypto.enc.Hex),
'x-amz-date':this._iso_date.full

@@ -154,3 +157,3 @@ }

url:'https://sts.amazonaws.com',
query:encoded_query_string,
body:encoded_query_string,
headers:{

@@ -157,0 +160,0 @@ 'Authorization':'AWS4-HMAC-SHA256 Credential=' + aws_user.id + '/' + this._iso_date.short + '/us-east-1/sts/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=' + signature,

{
"name": "amazon-sp-api",
"version": "0.1.1",
"version": "0.2.0",
"description": "Amazon Selling Partner API client",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -70,3 +70,4 @@ # amazon-sp-api (client for the Amazon Selling Partner API)

credentials_path:'<YOUR_CUSTOM_ABSOLUTE_PATH>', // Optional, a custom absolute path to your credentials file location
auto_request_tokens:true // Optional, whether or not the client should retrieve new access and role credentials if non given or expired. Default is true
auto_request_tokens:true, // Optional, whether or not the client should retrieve new access and role credentials if non given or expired. Default is true
auto_request_throttled:true // Optional: Whether or not the client should automatically retry a request when throttled. Default is true
}

@@ -107,11 +108,13 @@ }

The .callAPI() function takes an object as input:
* path: Required, the API path you want to request, [see SP API References](https://github.com/amzn/selling-partner-api-docs/tree/main/references)
* method: Optional, HTTP Method of the call, default is GET
* query: Optional, the input parameters of the call
* operation: Required, the operation you want to request [see SP API References](https://github.com/amzn/selling-partner-api-docs/tree/main/references)
* path: The input paramaters added to the path of the operation
* query: The input parameters added to the query string of the operation
* body: The input parameters added to the body of the operation
## Examples
```javascript
let res = await sellingPartner.callAPI({
path:'/sales/v1/orderMetrics',
method:'GET',
operation:'getOrderMetrics',
query:{
marketplaceIds:'A1PA6795UKMFR9',
marketplaceIds:['A1PA6795UKMFR9'],
interval:'2020-10-01T00:00:00-07:00--2020-10-01T20:00:00-07:00',

@@ -122,4 +125,24 @@ granularity:'Hour'

```
```javascript
let res = await sellingPartner.callAPI({
operation:'getCatalogItem',
path:{
asin:'B084J4QQFT'
},
query:{
MarketplaceId:'A1PA6795UKMFR9'
}
});
```
```javascript
let res = await sellingPartner.callAPI({
operation:'createReport',
body:{
reportType:'GET_FLAT_FILE_OPEN_LISTINGS_DATA',
marketplaceIds:['A1PA6795UKMFR9']
}
});
```
## Known Issues
Since the Selling Partner API is still pretty new, not all API paths and endpoints have been tested for full functionality. If you find any calls not working please open up a new issue.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc