diff --git a/.travis.yml b/.travis.yml index 2a7b90d..8f14d78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,5 @@ node_js: - "6" - "7" - "8" + - "9" sudo: false \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3175cf6..84c329e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +0.0.20 + - Handle for non content type responses + - Bug fix for GetMyPriceForASIN and similar other products API + - Add example, test case for GetMyPriceForASIN + - Add test case for failure/error responses + - Add support for RequestReport API + - Add example for GetMyFeesEstimate in Products + - Add force check for the XML string + 0.0.19 - Correct lint and increase test case timeout - Added Status Code in Error Response. diff --git a/README.md b/README.md index 21cb8fa..b1244a8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # amazon-mws (Amazon Marketplace Web Service) +[![Version](https://img.shields.io/npm/v/amazon-mws.svg)](https://www.npmjs.org/package/amazon-mws) [![Build Status](https://travis-ci.org/bhushankumarl/amazon-mws.svg?branch=master)](https://travis-ci.org/bhushankumarl/amazon-mws) This API supported Amazon Marketplace Web Service(MWS)'s standard REST-style API that accepts/returns JSON requests and Here is the [API reference] (http://docs.developer.amazonservices.com/en_IN/dev_guide/DG_IfNew.html) diff --git a/examples/javaScript/products/getMyFeesEstimate.js b/examples/javaScript/products/getMyFeesEstimate.js new file mode 100644 index 0000000..1d05c8a --- /dev/null +++ b/examples/javaScript/products/getMyFeesEstimate.js @@ -0,0 +1,33 @@ +'use strict'; + +var accessKey = process.env.AWS_ACCESS_KEY_ID || 'YOUR_KEY'; +var accessSecret = process.env.AWS_SECRET_ACCESS_KEY || 'YOUR_SECRET'; + +var amazonMws = require('../../../lib/amazon-mws')(accessKey, accessSecret); + +var productRequest = function () { + amazonMws.products.searchFor({ + 'Version': '2011-10-01', + 'Action': 'GetMyFeesEstimate', + 'SellerId': 'SELLER_ID', + 'MWSAuthToken': 'MWS_AUTH_TOKEN', + 'FeesEstimateRequestList.FeesEstimateRequest.1.MarketplaceId': 'MARKET_PLACE_ID', + 'FeesEstimateRequestList.FeesEstimateRequest.1.IdType': 'ASIN', + 'FeesEstimateRequestList.FeesEstimateRequest.1.IdValue': 'ASIN', + 'FeesEstimateRequestList.FeesEstimateRequest.1.IsAmazonFulfilled': 'true', + 'FeesEstimateRequestList.FeesEstimateRequest.1.Identifier': 'Hello', + 'FeesEstimateRequestList.FeesEstimateRequest.1.PriceToEstimateFees.ListingPrice.CurrencyCode': 'USD', + 'FeesEstimateRequestList.FeesEstimateRequest.1.PriceToEstimateFees.ListingPrice.Amount': '30.00', + 'FeesEstimateRequestList.FeesEstimateRequest.1.PriceToEstimateFees.Shipping.CurrencyCode': 'USD', + 'FeesEstimateRequestList.FeesEstimateRequest.1.PriceToEstimateFees.Shipping.Amount': '3.99', + 'FeesEstimateRequestList.FeesEstimateRequest.1.PriceToEstimateFees.Points.PointsNumber': '0' + }, function (error, response) { + if (error) { + console.log('error products', error); + return; + } + console.log('response ', response); + }); +}; + +productRequest(); \ No newline at end of file diff --git a/examples/javaScript/products/getMyPriceForASIN.js b/examples/javaScript/products/getMyPriceForASIN.js new file mode 100644 index 0000000..2bb7c2d --- /dev/null +++ b/examples/javaScript/products/getMyPriceForASIN.js @@ -0,0 +1,25 @@ +'use strict'; + +var accessKey = process.env.AWS_ACCESS_KEY_ID || 'YOUR_KEY'; +var accessSecret = process.env.AWS_SECRET_ACCESS_KEY || 'YOUR_SECRET'; + +var amazonMws = require('../../../lib/amazon-mws')(accessKey, accessSecret); + +var productRequest = function () { + amazonMws.products.searchFor({ + 'Version': '2011-10-01', + 'Action': 'GetMyPriceForASIN', + 'SellerId': 'SELLER_ID', + 'MWSAuthToken': 'MWS_AUTH_TOKEN', + 'MarketplaceId': 'MARKET_PLACE_ID', + 'ASINList.ASIN.1': 'ASINList_ASIN_1' + }, function (error, response) { + if (error) { + console.log('error products', error); + return; + } + console.log('response ', response); + }); +}; + +productRequest(); \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index 418f5e0..edf1451 100644 --- a/index.d.ts +++ b/index.d.ts @@ -48,6 +48,8 @@ declare class Products extends BaseAmazonMWS { declare class Reports extends BaseAmazonMWS { + submit(params: any): Promise; + } declare class Sellers extends BaseAmazonMWS { diff --git a/lib/AmazonMwsResource.js b/lib/AmazonMwsResource.js index e024734..a2ffb45 100644 --- a/lib/AmazonMwsResource.js +++ b/lib/AmazonMwsResource.js @@ -18,7 +18,7 @@ var Error = require('./Error'); var hasOwn = {}.hasOwnProperty; -var RESPONSE_CONTENT_TYPE = ['text/xml', 'text/xml;charset=utf-8', 'application/xml']; +var RESPONSE_CONTENT_TYPE_XML = ['text/xml', 'text/xml;charset=utf-8', 'application/xml']; // Provide extension mechanism for AmazonMws Resource Sub-Classes AmazonMwsResource.extend = utils.protoExtend; @@ -168,20 +168,31 @@ AmazonMwsResource.prototype = { function processResponseType(res, responseString, callback) { //debug('res %o ', res); - //debug('res.headers %o ', res.headers); - if (RESPONSE_CONTENT_TYPE.indexOf(res.headers['content-type'].toLowerCase()) > -1) { + var xmlParser = new xml2js.Parser({ + mergeAttrs: true, + explicitArray: false, + emptyTag: {} + }); + if (!res.headers['content-type']) { + debug('Content type has not set, so considered it as XML response'); + xmlParser.parseString(responseString, function (err, response) { + //debug('response after parsing JSON %o ', response); + return callback(null, response); + }); + } else if (RESPONSE_CONTENT_TYPE_XML.indexOf(res.headers['content-type'].toLowerCase()) > -1) { debug('It is XML Response'); - var parser = new xml2js.Parser({ - explicitArray: false, - ignoreAttrs: true + xmlParser.parseString(responseString, function (err, response) { + // debug('response after parsing JSON %o ', response); + return callback(null, response); }); - - parser.parseString(responseString, function (err, response) { - //debug('response after parsing JSON %o ', response); + } else if (_.includes(responseString, '?xml')) { + debug('It is XML Response be find out from responseString'); + xmlParser.parseString(responseString, function (err, response) { + // debug('response after parsing JSON %o ', response); return callback(null, response); }); } else { - debug('It is NON-XML Response'); + debug('It is NON-XML Response, so considered it as CSV file'); var TAB_DELIMITER = '\t'; var COMMA_DELIMITER = ','; parseCSVFile(res, responseString, TAB_DELIMITER, function (error, response) { @@ -200,21 +211,26 @@ AmazonMwsResource.prototype = { var dbgResponseBuffer = []; var headers = res.headers; var statusCode = res.statusCode; + var contentType = ''; + if (headers['content-type']) { + contentType = headers['content-type'].toLowerCase(); + } try { statusCode = parseInt(statusCode, 10); } catch (Exception) { debug('Failed to parse statusCode as statusCode not provided in the response. ', statusCode); } var charset = ''; - var content_type = ''; var responseString = ''; - if (headers['content-type'].indexOf('charset') > -1 && headers['content-type'].split(';')[0] && headers['content-type'].split(';')[1]) { - content_type = headers['content-type'].split(';')[0].toLowerCase(); - if (headers['content-type'].split(';')[1].match(/^((\b[^\s=]+)=(([^=]|\\=)+))*$/)[3]) { - charset = headers['content-type'].split(';')[1].match(/^((\b[^\s=]+)=(([^=]|\\=)+))*$/)[3]; + + /** + * Separate the charset & content type + */ + if (contentType.indexOf('charset') > -1 && contentType.split(';')[0] && contentType.split(';')[1]) { + if (contentType.split(';')[1] && contentType.split(';')[1].match(/^((\b[^\s=]+)=(([^=]|\\=)+))*$/)[3]) { + charset = contentType.split(';')[1].match(/^((\b[^\s=]+)=(([^=]|\\=)+))*$/)[3]; } - } else { - content_type = headers['content-type'].toLowerCase(); + contentType = contentType.split(';')[0].toLowerCase(); } var ResponseHeaders = { @@ -222,7 +238,7 @@ AmazonMwsResource.prototype = { 'x-mws-quota-remaining': res.headers['x-mws-quota-remaining'] || 'unknown', 'x-mws-quota-resetson': res.headers['x-mws-quota-resetson'] || 'unknown', 'x-mws-timestamp': res.headers['x-mws-timestamp'], - 'content-type': content_type || 'unknown', + 'content-type': contentType || 'unknown', 'content-charset': charset || 'unknown', 'content-length': res.headers['content-length'] || 'unknown', 'content-md5': res.headers['content-md5'] || 'unknown', @@ -269,21 +285,21 @@ AmazonMwsResource.prototype = { } debug('responseString ', responseString); - debug('content_type ', content_type); + debug('contentType ', contentType); debug('statusCode ', statusCode); - if (!content_type) { - return callback.call(self, new Error.AmazonMwsAPIError({ - message: 'Content Type is not provided in response received from the AmazonMws API', - StatusCode: statusCode || 'unknown' - }), null); - } - try { + var errorResponse = {}; + if (statusCode > 499 && !responseString) { + errorResponse.message = res.statusMessage || 'unknown'; + errorResponse.Headers = ResponseHeaders; + errorResponse.StatusCode = statusCode || 'unknown'; + return callback.call(self, errorResponse, null); + } processResponseType(res, responseString, function (error, response) { if (response.ErrorResponse) { debug('It is ErrorResponse'); - var errorResponse = response.ErrorResponse.Error; + errorResponse = response.ErrorResponse.Error; errorResponse.Headers = ResponseHeaders; errorResponse.StatusCode = statusCode || 'unknown'; errorResponse.RequestId = response.ErrorResponse.RequestID || response.ErrorResponse.RequestId || 'unknown'; @@ -301,7 +317,7 @@ AmazonMwsResource.prototype = { } var ResponseMetadata = {}; - if (RESPONSE_CONTENT_TYPE.indexOf(content_type) > -1) { + if (RESPONSE_CONTENT_TYPE_XML.indexOf(contentType) > -1) { /** * It should execute for only XML response */ diff --git a/lib/resources/Reports.js b/lib/resources/Reports.js index 0d850e1..457ed33 100644 --- a/lib/resources/Reports.js +++ b/lib/resources/Reports.js @@ -8,6 +8,8 @@ module.exports = AmazonMwsResource.extend({ path: 'Reports', search: amazonMwsMethod({ method: 'GET' + }), + submit: amazonMwsMethod({ + method: 'POST' }) - }); diff --git a/package.json b/package.json index 771482c..a74b2d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "amazon-mws", - "version": "0.0.19", + "version": "0.0.20", "description": "Amazon MWS API wrapper", "keywords": [ "Amazon MWS", diff --git a/test/mocha.opts b/test/mocha.opts index 8bbc8c8..2a5fb7d 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -2,6 +2,7 @@ ### mocha.opts ### +--exclude ./test/specs/fulfillmentInboundShipment.spec.js --bail --silly --full-trace diff --git a/test/specs/products.spec.js b/test/specs/products.spec.js index f68ba0e..10dbe56 100644 --- a/test/specs/products.spec.js +++ b/test/specs/products.spec.js @@ -70,4 +70,77 @@ describe('Products', function () { expect(response).to.have.property('Headers').to.have.property('x-mws-quota-resetson'); expect(response).to.have.property('Headers').to.have.property('x-mws-timestamp'); }); + + it('It should get my price for ASIN using getMyPriceForASIN Action', async function () { + var options = { + 'Version': '2011-10-01', + 'Action': 'GetMyPriceForASIN', + 'SellerId': config.SellerId, + 'MWSAuthToken': config.MWSAuthToken, + 'MarketplaceId': config.MarketplaceId, + 'ASINList.ASIN.1': config.ASIN + }; + expect(options.SellerId).to.be.a('string'); + expect(options.MWSAuthToken).to.be.a('string'); + expect(options.MarketplaceId).to.be.a('string'); + expect(options['ASINList.ASIN.1']).to.be.a('string'); + + var response = await amazonMws.products.searchFor(options); + + expect(response).to.be.a('object'); + expect(response).to.have.property('ASIN').to.be.a('string'); + expect(response).to.have.property('status').to.be.a('string'); + expect(response).to.have.property('Product').to.be.a('object'); + expect(response).to.have.property('ResponseMetadata').to.be.a('object'); + expect(response).to.have.property('ResponseMetadata').to.have.property('RequestId'); + expect(response).to.have.property('Headers').to.be.a('object'); + expect(response).to.have.property('Headers').to.have.property('x-mws-quota-max'); + expect(response).to.have.property('Headers').to.have.property('x-mws-quota-remaining'); + expect(response).to.have.property('Headers').to.have.property('x-mws-quota-resetson'); + expect(response).to.have.property('Headers').to.have.property('x-mws-timestamp'); + }); + + it('It should NOT get my price for INVALID ASIN using getMyPriceForASIN Action', async function () { + var options = { + 'Version': '2011-10-01', + 'Action': 'GetMyPriceForASIN', + 'SellerId': config.SellerId, + 'MWSAuthToken': config.MWSAuthToken, + 'MarketplaceId': config.MarketplaceId, + 'ASINList.ASIN.1': undefined + }; + expect(options.SellerId).to.be.a('string'); + expect(options.MWSAuthToken).to.be.a('string'); + expect(options.MarketplaceId).to.be.a('string'); + + try { + var response = await amazonMws.products.searchFor(options); + + expect(response).to.be.a('object'); + expect(response).to.have.property('ASIN').to.be.a('string'); + expect(response).to.have.property('status').to.be.a('string'); + expect(response).to.have.property('Product').to.be.a('object'); + expect(response).to.have.property('ResponseMetadata').to.be.a('object'); + expect(response).to.have.property('ResponseMetadata').to.have.property('RequestId'); + expect(response).to.have.property('Headers').to.be.a('object'); + expect(response).to.have.property('Headers').to.have.property('x-mws-quota-max'); + expect(response).to.have.property('Headers').to.have.property('x-mws-quota-remaining'); + expect(response).to.have.property('Headers').to.have.property('x-mws-quota-resetson'); + expect(response).to.have.property('Headers').to.have.property('x-mws-timestamp'); + } catch (exception) { + console.log('exception ', exception); + expect(exception).to.be.a('object'); + expect(exception).to.have.property('Type').to.be.a('string'); + expect(exception).to.have.property('Message').to.be.a('string'); + expect(exception).to.have.property('Detail').to.be.a('object'); + expect(exception).to.have.property('StatusCode').to.be.a('number'); + expect(exception).to.have.property('RequestId').to.be.a('string'); + expect(exception).to.have.property('Headers').to.be.a('object'); + expect(exception).to.have.property('Headers').to.have.property('x-mws-quota-max'); + expect(exception).to.have.property('Headers').to.have.property('x-mws-quota-remaining'); + expect(exception).to.have.property('Headers').to.have.property('x-mws-quota-resetson'); + expect(exception).to.have.property('Headers').to.have.property('x-mws-timestamp'); + } + + }); }); \ No newline at end of file