From d3329d83914731ddcb612983b23a315692b418d6 Mon Sep 17 00:00:00 2001 From: neshte Date: Tue, 14 Jun 2016 11:50:00 -0500 Subject: [PATCH 01/14] fixes for orders and inventory reports --- lib/mws.js | 870 +++++++++++++++++++++++++++-------------------------- 1 file changed, 447 insertions(+), 423 deletions(-) diff --git a/lib/mws.js b/lib/mws.js index 053a1ae..9d53fdb 100644 --- a/lib/mws.js +++ b/lib/mws.js @@ -1,423 +1,447 @@ -var https = require('https'), - qs = require("querystring"), - crypto = require('crypto'), - xml2js = require('xml2js'); - - -var MARKETPLACE_IDS = { - -}; - -/** - * Constructor for the main MWS client interface used to make api calls and - * various data structures to encapsulate MWS requests, definitions, etc. - * - * @param {String} accessKeyId Id for your secret Access Key (required) - * @param {String} secretAccessKey Secret Access Key provided by Amazon (required) - * @param {String} merchantId Aka SellerId, provided by Amazon (required) - * @param {Object} options Additional configuration options for this instance - */ -function AmazonMwsClient(accessKeyId, secretAccessKey, merchantId, options) { - this.host = options.host || 'mws.amazonservices.com'; - this.port = options.port || 443; - this.conn = options.conn || https; - this.creds = crypto.createCredentials(options.creds || {}); - this.appName = options.appName || 'mws-js'; - this.appVersion = options.appVersion || '0.1.0'; - this.appLanguage = options.appLanguage || 'JavaScript'; - this.accessKeyId = accessKeyId || null; - this.secretAccessKey = secretAccessKey || null; - this.merchantId = merchantId || null; -} - -/** - * The method used to invoke calls against MWS Endpoints. Recommended usage is - * through the invoke wrapper method when the api call you're invoking has a - * request defined in one of the submodules. However, you can use call() manually - * when a lower level of control is necessary (custom or new requests, for example). - * - * @param {Object} api Settings object unique to each API submodule - * @param {String} action Api `Action`, such as GetServiceStatus or GetOrder - * @param {Object} query Any parameters belonging to the current action - * @param {Function} callback Callback function to send any results recieved - */ -AmazonMwsClient.prototype.call = function(api, action, query, callback) { - if (this.secretAccessKey == null || this.accessKeyId == null || this.merchantId == null) { - throw("accessKeyId, secretAccessKey, and merchantId must be set"); - } - - if(process.env.NODE_ENV === 'development') { - console.log("=================== API ==================="); - console.log(JSON.stringify(api)); - console.log("================ QUERY =============="); - console.log(JSON.stringify(query)); - console.log("=============================="); - } - - // Check if we're dealing with a file (such as a feed) upload - if (api.upload) { - var body = query._BODY_, - bformat = query._FORMAT_; - delete query._BODY_; - delete query._FORMAT_; - } - - // Add required parameters and sign the query - query['Action'] = action; - query['Version'] = api.version; - query["Timestamp"] = (new Date()).toISOString(); - query["AWSAccessKeyId"] = this.accessKeyId; - if (api.legacy) { - query['Merchant'] = this.merchantId; - } else { - query['SellerId'] = this.merchantId; - } - query = this.sign(api.path, query); - - if (!api.upload) { - var body = qs.stringify(query); - } - - // Setup our HTTP headers and connection options - var headers = { - 'Host': this.host, - 'User-Agent': this.appName + '/' + this.appVersion + ' (Language=' + this.appLanguage + ')', - 'Content-Type': bformat || 'application/x-www-form-urlencoded; charset=utf-8', - 'Content-Length': body.length - }; - if (api.upload) { - headers['Content-MD5'] = crypto.createHash('md5').update(body).digest("base64"); - } - - var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); - - console.log("************ ACTUAL PATH: ***************"); - console.log(pathReq); - - var options = { - host: this.host, - port: this.port, - path: pathReq, - method: "POST", - headers: headers - }; - - // Make the initial request and define callbacks - var req = this.conn.request(options, function (res) { - var data = ''; - // Append each incoming chunk to data variable - res.addListener('data', function (chunk) { - data += chunk.toString(); - }); - // When response is complete, parse the XML and pass it to callback - res.addListener('end', function() { - var parser = new xml2js.Parser(); - parser.addListener('end', function (result) { - // Throw an error if there was a problem reported - if (result.Error != null) { - if(process.env.NODE_ENV === 'development') { - console.log("--------------------------------"); - console.log("ERROR:"); - console.log(JSON.stringify(result.Error)); - } - throw("ERROR"); - } - callback(result); - }); - if (data.slice(0, 5) == '= 0; i--) { - setValue(p.name + '.' + (i+1), value[i]); - } - } else { - for (var key in value) { - setValue(p.name + '.' + (++i), value[key]); - } - } - } - } else { - setValue(p.name, value) - } - - return this; -}; - -/** - * Builds a query object and checks for required parameters. - * - * @return {Object} KvP's of all provided parameters (used by invoke()) - */ -AmazonMwsRequest.prototype.query = function() { - var q = {}; - if(process.env.NODE_ENV === 'development') { - console.log("AMR.proto.query !!!!!!!!!!!!!!!!!!!!!!!"); - console.log(JSON.stringify(this)); - console.log(JSON.stringify(this.params)); - } - for (var param in this.params) { - var value = this.params[param].value, - name = this.params[param].name, - complex = (this.params[param].type === 'Complex'), - required = this.params[param].required; - console.log("v " + value + "\nn " + name + "\nr " + required); - if ((value !== undefined) && (value !== null)) { - if (complex) { - value.appendTo(q); - } else { - q[name] = value; - } - } else { - if (param.required === true) { - throw("ERROR: Missing required parameter, " + name + "!") - } - } - }; - if(process.env.NODE_ENV === 'development') { - console.log("query() about to return this:"); - console.log(q); - console.log("----------------------------------"); - } - return q; -}; - - -/** - * Contructor for objects used to represent enumeration states. Useful - * when you need to make programmatic updates to an enumerated data type or - * wish to encapsulate enum states in a handy, re-usable variable. - * - * @param {Array} choices An array of any possible values (choices) - */ -function EnumType(choices) { - for (var choice in choices) { - this[choices[choice]] = false; - } - this._choices = choices; -} - -/** - * Enable one or more choices (accepts a variable number of arguments) - * @return {Object} Current instance of EnumType for chaining - */ -EnumType.prototype.enable = function() { - for (var arg in arguments) { - this[arguments[arg]] = true; - } - return this; -}; - -/** - * Disable one or more choices (accepts a variable number of arguments) - * @return {Object} Current instance of EnumType for chaining - */ -EnumType.prototype.disable = function() { - for (var arg in arguments) { - this[arguments[arg]] = false; - } - return this; -}; - -/** - * Toggles one or more choices (accepts a variable number of arguments) - * @return {Object} Current instance of EnumType for chaining - */ -EnumType.prototype.toggle = function() { - for (var arg in arguments) { - this[arguments[arg]] = ! this[arguments[arg]]; - } - return this; -}; - -/** - * Return all possible values without regard to current state - * @return {Array} Choices passed to EnumType constructor - */ -EnumType.prototype.all = function() { - return this._choices; -}; - -/** - * Return all enabled choices as an array (used to set list params, usually) - * @return {Array} Choice values for each choice set to true - */ -EnumType.prototype.values = function() { - var value = []; - for (var choice in this._choices) { - if (this[this._choices[choice]] === true) { - value.push(this._choices[choice]); - } - } - return value; -}; - - -// /** -// * Takes an object and adds an appendTo function that will add -// * each kvp of object to a query. Used when dealing with complex -// * parameters that need to be built in an abnormal or unique way. -// * -// * @param {String} name Name of parameter, prefixed to each key -// * @param {Object} obj Parameters belonging to the complex type -// */ -// function ComplexType(name) { -// this.pre = name; -// var _obj = obj; -// obj.appendTo = function(query) { -// for (var k in _obj) { -// query[name + '.' k] = _obj[k]; -// } -// return query; -// } -// return obj; -// } - -// ComplexType.prototype.appendTo = function(query) { -// for (var k in value) -// } - -/** - * Complex List helper object. Once initialized, you should set - * an add(args) method which pushes a new complex object to members. - * - * @param {String} name Name of Complex Type (including .member or subtype) - */ -function ComplexListType(name) { - this.pre = name; - this.members = []; -} - -/** - * Appends each member object as a complex list item - * @param {Object} query Query object to append to - * @return {Object} query - */ -ComplexListType.prototype.appendTo = function(query) { - var members = this.members; - for (var i = 0; i < members.length; i++) { - for (var j in members[i]) { - query[this.pre + '.' + (i+1) + '.' + j] = members[i][j] - } - } - return query; -}; - -exports.Client = AmazonMwsClient; -exports.Request = AmazonMwsRequest; -exports.Enum = EnumType; -exports.ComplexList = ComplexListType; -exports.Orders = require('./orders'); -exports.Sellers = require('./sellers'); -exports.Feeds = require('./feeds'); -exports.Products = require('./products'); -exports.Reports = require('./reports'); - +var https = require('https'), + qs = require("querystring"), + crypto = require('crypto'), + tls = require('tls'); + xml2js = require('xml2js'); + + +var MARKETPLACE_IDS = { + +}; + +/** + * Constructor for the main MWS client interface used to make api calls and + * various data structures to encapsulate MWS requests, definitions, etc. + * + * @param {String} accessKeyId Id for your secret Access Key (required) + * @param {String} secretAccessKey Secret Access Key provided by Amazon (required) + * @param {String} merchantId Aka SellerId, provided by Amazon (required) + * @param {Object} options Additional configuration options for this instance + */ +function AmazonMwsClient(accessKeyId, secretAccessKey, merchantId, options) { + this.host = options.host || 'mws.amazonservices.com'; + this.port = options.port || 443; + this.conn = options.conn || https; + if (tls.createSecureContext) { + this.creds = tls.createSecureContext(options.creds || {}); + } else { + this.creds = crypto.createCredentials(options.creds || {}); + } + this.appName = options.appName || 'mws-js'; + this.appVersion = options.appVersion || '0.1.0'; + this.appLanguage = options.appLanguage || 'JavaScript'; + this.accessKeyId = accessKeyId || null; + this.secretAccessKey = secretAccessKey || null; + this.merchantId = merchantId || null; +} + +/** + * The method used to invoke calls against MWS Endpoints. Recommended usage is + * through the invoke wrapper method when the api call you're invoking has a + * request defined in one of the submodules. However, you can use call() manually + * when a lower level of control is necessary (custom or new requests, for example). + * + * @param {Object} api Settings object unique to each API submodule + * @param {String} action Api `Action`, such as GetServiceStatus or GetOrder + * @param {Object} query Any parameters belonging to the current action + * @param {Function} callback Callback function to send any results recieved + */ +AmazonMwsClient.prototype.call = function(api, action, query, callback) { + if (this.secretAccessKey == null || this.accessKeyId == null || this.merchantId == null) { + throw("accessKeyId, secretAccessKey, and merchantId must be set"); + } + + if(process.env.NODE_ENV === 'development') { + console.log("=================== API ==================="); + console.log(JSON.stringify(api)); + console.log("================ QUERY =============="); + console.log(JSON.stringify(query)); + console.log("=============================="); + } + + // Check if we're dealing with a file (such as a feed) upload + if (api.upload) { + var body = query._BODY_, + bformat = query._FORMAT_; + delete query._BODY_; + delete query._FORMAT_; + } + + // Add required parameters and sign the query + query['Action'] = action; + query['Version'] = api.version; + query["Timestamp"] = (new Date()).toISOString(); + query["AWSAccessKeyId"] = this.accessKeyId; + if (api.legacy) { + query['Merchant'] = this.merchantId; + } else { + query['SellerId'] = this.merchantId; + } + console.log('query : '); + console.log(query); + query = this.sign(api.path, query); + if (!api.upload) { + var body = qs.stringify(query); + } + + // Setup our HTTP headers and connection options + var headers = { + 'Host': this.host, + 'User-Agent': this.appName + '/' + this.appVersion + ' (Language=' + this.appLanguage + ')', + 'Content-Type': bformat || 'application/x-www-form-urlencoded; charset=utf-8', + 'Content-Length': body.length + }; + + if (api.upload) { + headers['Content-MD5'] = crypto.createHash('md5').update(body).digest("base64"); + } + + var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); + + console.log("************ ACTUAL PATH: ***************"); + console.log(pathReq); + + var options = { + host: this.host, + port: this.port, + path: pathReq, + method: "POST", + headers: headers + }; + + // Make the initial request and define callbacks + var req = this.conn.request(options, function (res) { + console.log(res.headers); + var data = ''; + // Append each incoming chunk to data variable + res.addListener('data', function (chunk) { + data += chunk.toString(); + console.log('data'); + console.log(data); + }); + // When response is complete, parse the XML and pass it to callback + res.addListener('end', function() { + var parser = new xml2js.Parser(); + parser.addListener('end', function (result) { + // Throw an error if there was a problem reported + if (result.Error != null) { + if(process.env.NODE_ENV === 'development') { + console.log("--------------------------------"); + console.log("ERROR:"); + console.log(JSON.stringify(result.Error)); + } + throw("ERROR"); + } + callback(null, result); + }); + if (data.slice(0, 5) == '= 0; i--) { + setValue(p.name + '.' + (i+1), value[i]); + } + } else { + for (var key in value) { + setValue(p.name + '.' + (++i), value[key]); + } + } + } + } else { + setValue(p.name, value) + } + + return this; +}; + +/** + * Builds a query object and checks for required parameters. + * + * @return {Object} KvP's of all provided parameters (used by invoke()) + */ +AmazonMwsRequest.prototype.query = function() { + var q = {}; + if(process.env.NODE_ENV === 'development') { + console.log("AMR.proto.query !!!!!!!!!!!!!!!!!!!!!!!"); + console.log(JSON.stringify(this)); + console.log(JSON.stringify(this.params)); + } + for (var param in this.params) { + var value = this.params[param].value, + name = this.params[param].name, + complex = (this.params[param].type === 'Complex'), + required = this.params[param].required; + + console.log("v " + value + "\nn " + name + "\nr " + required); + if ((value !== undefined) && (value !== null)) { + if (complex) { + value.appendTo(q); + } else if (this.params[param].list){ + for (var key in value) { + q[key] = value[key]; + } + } else { + for (key in value) { + q[key] = value[key]; + } + } + } else { + if (param.required === true) { + throw("ERROR: Missing required parameter, " + name + "!") + } + } + }; + if(process.env.NODE_ENV === 'development') { + console.log("query() about to return this:"); + console.log(q); + console.log("----------------------------------"); + } + return q; +}; + + +/** + * Contructor for objects used to represent enumeration states. Useful + * when you need to make programmatic updates to an enumerated data type or + * wish to encapsulate enum states in a handy, re-usable variable. + * + * @param {Array} choices An array of any possible values (choices) + */ +function EnumType(choices) { + for (var choice in choices) { + this[choices[choice]] = false; + } + this._choices = choices; +} + +/** + * Enable one or more choices (accepts a variable number of arguments) + * @return {Object} Current instance of EnumType for chaining + */ +EnumType.prototype.enable = function() { + for (var arg in arguments) { + this[arguments[arg]] = true; + } + return this; +}; + +/** + * Disable one or more choices (accepts a variable number of arguments) + * @return {Object} Current instance of EnumType for chaining + */ +EnumType.prototype.disable = function() { + for (var arg in arguments) { + this[arguments[arg]] = false; + } + return this; +}; + +/** + * Toggles one or more choices (accepts a variable number of arguments) + * @return {Object} Current instance of EnumType for chaining + */ +EnumType.prototype.toggle = function() { + for (var arg in arguments) { + this[arguments[arg]] = ! this[arguments[arg]]; + } + return this; +}; + +/** + * Return all possible values without regard to current state + * @return {Array} Choices passed to EnumType constructor + */ +EnumType.prototype.all = function() { + return this._choices; +}; + +/** + * Return all enabled choices as an array (used to set list params, usually) + * @return {Array} Choice values for each choice set to true + */ +EnumType.prototype.values = function() { + var value = []; + for (var choice in this._choices) { + if (this[this._choices[choice]] === true) { + value.push(this._choices[choice]); + } + } + return value; +}; + + +// /** +// * Takes an object and adds an appendTo function that will add +// * each kvp of object to a query. Used when dealing with complex +// * parameters that need to be built in an abnormal or unique way. +// * +// * @param {String} name Name of parameter, prefixed to each key +// * @param {Object} obj Parameters belonging to the complex type +// */ +// function ComplexType(name) { +// this.pre = name; +// var _obj = obj; +// obj.appendTo = function(query) { +// for (var k in _obj) { +// query[name + '.' k] = _obj[k]; +// } +// return query; +// } +// return obj; +// } + +// ComplexType.prototype.appendTo = function(query) { +// for (var k in value) +// } + +/** + * Complex List helper object. Once initialized, you should set + * an add(args) method which pushes a new complex object to members. + * + * @param {String} name Name of Complex Type (including .member or subtype) + */ +function ComplexListType(name) { + this.pre = name; + this.members = []; +} + +/** + * Appends each member object as a complex list item + * @param {Object} query Query object to append to + * @return {Object} query + */ +ComplexListType.prototype.appendTo = function(query) { + var members = this.members; + for (var i = 0; i < members.length; i++) { + for (var j in members[i]) { + query[this.pre + '.' + (i+1) + '.' + j] = members[i][j] + } + } + return query; +}; + +exports.Client = AmazonMwsClient; +exports.Request = AmazonMwsRequest; +exports.Enum = EnumType; +exports.ComplexList = ComplexListType; +exports.Orders = require('./orders'); +exports.Sellers = require('./sellers'); +exports.Feeds = require('./feeds'); +exports.Products = require('./products'); +exports.Reports = require('./reports'); From d6d11dfcdfbf3d22f33b289f626cdfde7b8fdae1 Mon Sep 17 00:00:00 2001 From: Jorge Irene Date: Thu, 1 Dec 2016 09:43:15 -0600 Subject: [PATCH 02/14] Update to read files when uploading information --- lib/mws.js | 921 +++++++++++++++++++++++++++++------------------------ 1 file changed, 498 insertions(+), 423 deletions(-) mode change 100644 => 100755 lib/mws.js diff --git a/lib/mws.js b/lib/mws.js old mode 100644 new mode 100755 index 053a1ae..938ccdf --- a/lib/mws.js +++ b/lib/mws.js @@ -1,423 +1,498 @@ -var https = require('https'), - qs = require("querystring"), - crypto = require('crypto'), - xml2js = require('xml2js'); - - -var MARKETPLACE_IDS = { - -}; - -/** - * Constructor for the main MWS client interface used to make api calls and - * various data structures to encapsulate MWS requests, definitions, etc. - * - * @param {String} accessKeyId Id for your secret Access Key (required) - * @param {String} secretAccessKey Secret Access Key provided by Amazon (required) - * @param {String} merchantId Aka SellerId, provided by Amazon (required) - * @param {Object} options Additional configuration options for this instance - */ -function AmazonMwsClient(accessKeyId, secretAccessKey, merchantId, options) { - this.host = options.host || 'mws.amazonservices.com'; - this.port = options.port || 443; - this.conn = options.conn || https; - this.creds = crypto.createCredentials(options.creds || {}); - this.appName = options.appName || 'mws-js'; - this.appVersion = options.appVersion || '0.1.0'; - this.appLanguage = options.appLanguage || 'JavaScript'; - this.accessKeyId = accessKeyId || null; - this.secretAccessKey = secretAccessKey || null; - this.merchantId = merchantId || null; -} - -/** - * The method used to invoke calls against MWS Endpoints. Recommended usage is - * through the invoke wrapper method when the api call you're invoking has a - * request defined in one of the submodules. However, you can use call() manually - * when a lower level of control is necessary (custom or new requests, for example). - * - * @param {Object} api Settings object unique to each API submodule - * @param {String} action Api `Action`, such as GetServiceStatus or GetOrder - * @param {Object} query Any parameters belonging to the current action - * @param {Function} callback Callback function to send any results recieved - */ -AmazonMwsClient.prototype.call = function(api, action, query, callback) { - if (this.secretAccessKey == null || this.accessKeyId == null || this.merchantId == null) { - throw("accessKeyId, secretAccessKey, and merchantId must be set"); - } - - if(process.env.NODE_ENV === 'development') { - console.log("=================== API ==================="); - console.log(JSON.stringify(api)); - console.log("================ QUERY =============="); - console.log(JSON.stringify(query)); - console.log("=============================="); - } - - // Check if we're dealing with a file (such as a feed) upload - if (api.upload) { - var body = query._BODY_, - bformat = query._FORMAT_; - delete query._BODY_; - delete query._FORMAT_; - } - - // Add required parameters and sign the query - query['Action'] = action; - query['Version'] = api.version; - query["Timestamp"] = (new Date()).toISOString(); - query["AWSAccessKeyId"] = this.accessKeyId; - if (api.legacy) { - query['Merchant'] = this.merchantId; - } else { - query['SellerId'] = this.merchantId; - } - query = this.sign(api.path, query); - - if (!api.upload) { - var body = qs.stringify(query); - } - - // Setup our HTTP headers and connection options - var headers = { - 'Host': this.host, - 'User-Agent': this.appName + '/' + this.appVersion + ' (Language=' + this.appLanguage + ')', - 'Content-Type': bformat || 'application/x-www-form-urlencoded; charset=utf-8', - 'Content-Length': body.length - }; - if (api.upload) { - headers['Content-MD5'] = crypto.createHash('md5').update(body).digest("base64"); - } - - var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); - - console.log("************ ACTUAL PATH: ***************"); - console.log(pathReq); - - var options = { - host: this.host, - port: this.port, - path: pathReq, - method: "POST", - headers: headers - }; - - // Make the initial request and define callbacks - var req = this.conn.request(options, function (res) { - var data = ''; - // Append each incoming chunk to data variable - res.addListener('data', function (chunk) { - data += chunk.toString(); - }); - // When response is complete, parse the XML and pass it to callback - res.addListener('end', function() { - var parser = new xml2js.Parser(); - parser.addListener('end', function (result) { - // Throw an error if there was a problem reported - if (result.Error != null) { - if(process.env.NODE_ENV === 'development') { - console.log("--------------------------------"); - console.log("ERROR:"); - console.log(JSON.stringify(result.Error)); - } - throw("ERROR"); - } - callback(result); - }); - if (data.slice(0, 5) == '= 0; i--) { - setValue(p.name + '.' + (i+1), value[i]); - } - } else { - for (var key in value) { - setValue(p.name + '.' + (++i), value[key]); - } - } - } - } else { - setValue(p.name, value) - } - - return this; -}; - -/** - * Builds a query object and checks for required parameters. - * - * @return {Object} KvP's of all provided parameters (used by invoke()) - */ -AmazonMwsRequest.prototype.query = function() { - var q = {}; - if(process.env.NODE_ENV === 'development') { - console.log("AMR.proto.query !!!!!!!!!!!!!!!!!!!!!!!"); - console.log(JSON.stringify(this)); - console.log(JSON.stringify(this.params)); - } - for (var param in this.params) { - var value = this.params[param].value, - name = this.params[param].name, - complex = (this.params[param].type === 'Complex'), - required = this.params[param].required; - console.log("v " + value + "\nn " + name + "\nr " + required); - if ((value !== undefined) && (value !== null)) { - if (complex) { - value.appendTo(q); - } else { - q[name] = value; - } - } else { - if (param.required === true) { - throw("ERROR: Missing required parameter, " + name + "!") - } - } - }; - if(process.env.NODE_ENV === 'development') { - console.log("query() about to return this:"); - console.log(q); - console.log("----------------------------------"); - } - return q; -}; - - -/** - * Contructor for objects used to represent enumeration states. Useful - * when you need to make programmatic updates to an enumerated data type or - * wish to encapsulate enum states in a handy, re-usable variable. - * - * @param {Array} choices An array of any possible values (choices) - */ -function EnumType(choices) { - for (var choice in choices) { - this[choices[choice]] = false; - } - this._choices = choices; -} - -/** - * Enable one or more choices (accepts a variable number of arguments) - * @return {Object} Current instance of EnumType for chaining - */ -EnumType.prototype.enable = function() { - for (var arg in arguments) { - this[arguments[arg]] = true; - } - return this; -}; - -/** - * Disable one or more choices (accepts a variable number of arguments) - * @return {Object} Current instance of EnumType for chaining - */ -EnumType.prototype.disable = function() { - for (var arg in arguments) { - this[arguments[arg]] = false; - } - return this; -}; - -/** - * Toggles one or more choices (accepts a variable number of arguments) - * @return {Object} Current instance of EnumType for chaining - */ -EnumType.prototype.toggle = function() { - for (var arg in arguments) { - this[arguments[arg]] = ! this[arguments[arg]]; - } - return this; -}; - -/** - * Return all possible values without regard to current state - * @return {Array} Choices passed to EnumType constructor - */ -EnumType.prototype.all = function() { - return this._choices; -}; - -/** - * Return all enabled choices as an array (used to set list params, usually) - * @return {Array} Choice values for each choice set to true - */ -EnumType.prototype.values = function() { - var value = []; - for (var choice in this._choices) { - if (this[this._choices[choice]] === true) { - value.push(this._choices[choice]); - } - } - return value; -}; - - -// /** -// * Takes an object and adds an appendTo function that will add -// * each kvp of object to a query. Used when dealing with complex -// * parameters that need to be built in an abnormal or unique way. -// * -// * @param {String} name Name of parameter, prefixed to each key -// * @param {Object} obj Parameters belonging to the complex type -// */ -// function ComplexType(name) { -// this.pre = name; -// var _obj = obj; -// obj.appendTo = function(query) { -// for (var k in _obj) { -// query[name + '.' k] = _obj[k]; -// } -// return query; -// } -// return obj; -// } - -// ComplexType.prototype.appendTo = function(query) { -// for (var k in value) -// } - -/** - * Complex List helper object. Once initialized, you should set - * an add(args) method which pushes a new complex object to members. - * - * @param {String} name Name of Complex Type (including .member or subtype) - */ -function ComplexListType(name) { - this.pre = name; - this.members = []; -} - -/** - * Appends each member object as a complex list item - * @param {Object} query Query object to append to - * @return {Object} query - */ -ComplexListType.prototype.appendTo = function(query) { - var members = this.members; - for (var i = 0; i < members.length; i++) { - for (var j in members[i]) { - query[this.pre + '.' + (i+1) + '.' + j] = members[i][j] - } - } - return query; -}; - -exports.Client = AmazonMwsClient; -exports.Request = AmazonMwsRequest; -exports.Enum = EnumType; -exports.ComplexList = ComplexListType; -exports.Orders = require('./orders'); -exports.Sellers = require('./sellers'); -exports.Feeds = require('./feeds'); -exports.Products = require('./products'); -exports.Reports = require('./reports'); - +var https = require('https'), + qs = require("querystring"), + crypto = require('crypto'), + tls = require('tls'); + xml2js = require('xml2js'); +var iconv = require('iconv-lite'); + + +var MARKETPLACE_IDS = { + +}; + +/** + * Constructor for the main MWS client interface used to make api calls and + * various data structures to encapsulate MWS requests, definitions, etc. + * + * @param {String} accessKeyId Id for your secret Access Key (required) + * @param {String} secretAccessKey Secret Access Key provided by Amazon (required) + * @param {String} merchantId Aka SellerId, provided by Amazon (required) + * @param {Object} options Additional configuration options for this instance + */ +function AmazonMwsClient(accessKeyId, secretAccessKey, merchantId, options) { + this.host = options.host || 'mws.amazonservices.com'; + this.port = options.port || 443; + this.conn = options.conn || https; + if (tls.createSecureContext) { + this.creds = tls.createSecureContext(options.creds || {}); + } else { + this.creds = crypto.createCredentials(options.creds || {}); + } + this.appName = options.appName || 'mws-js'; + this.appVersion = options.appVersion || '0.1.0'; + this.appLanguage = options.appLanguage || 'JavaScript'; + this.accessKeyId = accessKeyId || null; + this.secretAccessKey = secretAccessKey || null; + this.merchantId = merchantId || null; +} + +/** + * The method used to invoke calls against MWS Endpoints. Recommended usage is + * through the invoke wrapper method when the api call you're invoking has a + * request defined in one of the submodules. However, you can use call() manually + * when a lower level of control is necessary (custom or new requests, for example). + * + * @param {Object} api Settings object unique to each API submodule + * @param {String} action Api `Action`, such as GetServiceStatus or GetOrder + * @param {Object} query Any parameters belonging to the current action + * @param {Function} callback Callback function to send any results recieved + */ +AmazonMwsClient.prototype.call = function(api, action, query, callback) { + if (this.secretAccessKey == null || this.accessKeyId == null || this.merchantId == null) { + throw("accessKeyId, secretAccessKey, and merchantId must be set"); + } + + if(process.env.NODE_ENV === 'development') { + console.log("=================== API ==================="); + console.log(JSON.stringify(api)); + console.log("================ QUERY =============="); + console.log(JSON.stringify(query)); + console.log("=============================="); + } + + var buffers = []; + var buffer; + var self = this; + + // Add required parameters and sign the query + query['Action'] = action; + query['Version'] = api.version; + query["Timestamp"] = (new Date()).toISOString(); + query["AWSAccessKeyId"] = self.accessKeyId; + if (api.legacy) { + query['Merchant'] = self.merchantId; + } else { + query['SellerId'] = self.merchantId; + } + + // Check if we're dealing with a file (such as a feed) upload + if (api.upload) { + var body = query._BODY_, + bformat = query._FORMAT_; + delete query._BODY_; + delete query._FORMAT_; + + body.on('data', function(buffer) { + buffers.push(buffer); + }); + + body.on('end', function(){ + buffer = Buffer.concat(buffers); + + console.log('before query : '); + console.log(query); + query = self.sign(api.path, query); + console.log('query : '); + console.log(query); + // Setup our HTTP headers and connection options + var headers = { + 'Host': self.host, + 'User-Agent': self.appName + '/' + self.appVersion + ' (Language=' + self.appLanguage + ')', + 'Content-Type': bformat || 'text/tab-separated-values; charset=iso-8859-1', + 'Content-Length': buffer.length + }; + + headers['Content-MD5'] = crypto.createHash('md5').update(buffer).digest("base64"); + + var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); + + console.log("************ ACTUAL PATH: ***************"); + console.log(pathReq); + + var options = { + host: self.host, + port: self.port, + path: pathReq, + method: "POST", + headers: headers + }; + + sendRequest(self, options, buffer, function(err, result) { + callback(err, result); + }); + }); + } else { + console.log('query : '); + console.log(query); + query = this.sign(api.path, query); + var body = qs.stringify(query); + + // Setup our HTTP headers and connection options + var headers = { + 'Host': this.host, + 'User-Agent': this.appName + '/' + this.appVersion + ' (Language=' + this.appLanguage + ')', + 'Content-Type': bformat || 'application/x-www-form-urlencoded; charset=utf-8', + //'Content-Type': 'text/tab-separated-values; charset=iso-8859-1', + 'Content-Length': Buffer.byteLength(body) + }; + + var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); + + console.log("************ ACTUAL PATH: ***************"); + console.log(pathReq); + + var options = { + host: this.host, + port: this.port, + path: pathReq, + method: "POST", + headers: headers + }; + console.log(options); + sendRequest(this, options, body, function(err, result) { + callback(err, result); + }); + } +}; + +function sendRequest(self, options, body, callback) { + // Make the initial request and define callbacks + var req = self.conn.request(options, function (res) { + res.setEncoding('binary'); + console.log(res.headers); + var data = ''; + // Append each incoming chunk to data variable + res.addListener('data', function (chunk) { + var utf8Chunk = iconv.decode(new Buffer(chunk), 'utf8'); + data += utf8Chunk.toString(); + console.log('data'); + console.log(data); + }); + // When response is complete, parse the XML and pass it to callback + res.addListener('end', function() { + var parser = new xml2js.Parser(); + parser.addListener('end', function (result) { + // Throw an error if there was a problem reported + if (result.Error != null) { + if(process.env.NODE_ENV === 'development') { + console.log("--------------------------------"); + console.log("ERROR:"); + console.log(JSON.stringify(result.Error)); + } + throw("ERROR"); + } + callback(null, result); + }); + if (data.slice(0, 5) == '= 0; i--) { + setValue(p.name + '.' + (i+1), value[i]); + } + } else { + for (var key in value) { + setValue(p.name + '.' + (++i), value[key]); + } + } + } + } else { + setValue(p.name, value) + } + + return this; +}; + +/** + * Builds a query object and checks for required parameters. + * + * @return {Object} KvP's of all provided parameters (used by invoke()) + */ +AmazonMwsRequest.prototype.query = function() { + var q = {}; + if(process.env.NODE_ENV === 'development') { + console.log("AMR.proto.query !!!!!!!!!!!!!!!!!!!!!!!"); + console.log(JSON.stringify(this)); + console.log(JSON.stringify(this.params)); + } + for (var param in this.params) { + var value = this.params[param].value, + name = this.params[param].name, + complex = (this.params[param].type === 'Complex'), + required = this.params[param].required; + + console.log("v " + value + "\nn " + name + "\nr " + required); + if ((value !== undefined) && (value !== null)) { + if (complex) { + value.appendTo(q); + } else if (this.params[param].list){ + for (var key in value) { + q[key] = value[key]; + } + } else { + for (key in value) { + q[key] = value[key]; + } + } + } else { + if (param.required === true) { + throw("ERROR: Missing required parameter, " + name + "!") + } + } + }; + if(process.env.NODE_ENV === 'development') { + console.log("query() about to return this:"); + console.log(q); + console.log("----------------------------------"); + } + return q; +}; + + +/** + * Contructor for objects used to represent enumeration states. Useful + * when you need to make programmatic updates to an enumerated data type or + * wish to encapsulate enum states in a handy, re-usable variable. + * + * @param {Array} choices An array of any possible values (choices) + */ +function EnumType(choices) { + for (var choice in choices) { + this[choices[choice]] = false; + } + this._choices = choices; +} + +/** + * Enable one or more choices (accepts a variable number of arguments) + * @return {Object} Current instance of EnumType for chaining + */ +EnumType.prototype.enable = function() { + for (var arg in arguments) { + this[arguments[arg]] = true; + } + return this; +}; + +/** + * Disable one or more choices (accepts a variable number of arguments) + * @return {Object} Current instance of EnumType for chaining + */ +EnumType.prototype.disable = function() { + for (var arg in arguments) { + this[arguments[arg]] = false; + } + return this; +}; + +/** + * Toggles one or more choices (accepts a variable number of arguments) + * @return {Object} Current instance of EnumType for chaining + */ +EnumType.prototype.toggle = function() { + for (var arg in arguments) { + this[arguments[arg]] = ! this[arguments[arg]]; + } + return this; +}; + +/** + * Return all possible values without regard to current state + * @return {Array} Choices passed to EnumType constructor + */ +EnumType.prototype.all = function() { + return this._choices; +}; + +/** + * Return all enabled choices as an array (used to set list params, usually) + * @return {Array} Choice values for each choice set to true + */ +EnumType.prototype.values = function() { + var value = []; + for (var choice in this._choices) { + if (this[this._choices[choice]] === true) { + value.push(this._choices[choice]); + } + } + return value; +}; + + +// /** +// * Takes an object and adds an appendTo function that will add +// * each kvp of object to a query. Used when dealing with complex +// * parameters that need to be built in an abnormal or unique way. +// * +// * @param {String} name Name of parameter, prefixed to each key +// * @param {Object} obj Parameters belonging to the complex type +// */ +// function ComplexType(name) { +// this.pre = name; +// var _obj = obj; +// obj.appendTo = function(query) { +// for (var k in _obj) { +// query[name + '.' k] = _obj[k]; +// } +// return query; +// } +// return obj; +// } + +// ComplexType.prototype.appendTo = function(query) { +// for (var k in value) +// } + +/** + * Complex List helper object. Once initialized, you should set + * an add(args) method which pushes a new complex object to members. + * + * @param {String} name Name of Complex Type (including .member or subtype) + */ +function ComplexListType(name) { + this.pre = name; + this.members = []; +} + +/** + * Appends each member object as a complex list item + * @param {Object} query Query object to append to + * @return {Object} query + */ +ComplexListType.prototype.appendTo = function(query) { + var members = this.members; + for (var i = 0; i < members.length; i++) { + for (var j in members[i]) { + query[this.pre + '.' + (i+1) + '.' + j] = members[i][j] + } + } + return query; +}; + +exports.Client = AmazonMwsClient; +exports.Request = AmazonMwsRequest; +exports.Enum = EnumType; +exports.ComplexList = ComplexListType; +exports.Orders = require('./orders'); +exports.Sellers = require('./sellers'); +exports.Feeds = require('./feeds'); +exports.Products = require('./products'); +exports.Reports = require('./reports'); From b6064669ac9f875161d1a32dc52f2830aa5865a8 Mon Sep 17 00:00:00 2001 From: Jaime Garza Date: Thu, 1 Dec 2016 09:58:49 -0600 Subject: [PATCH 03/14] Pull request 1 --- lib/mws.js | 177 +++++++++++++++++++++++++++++++++------------------ package.json | 4 +- 2 files changed, 116 insertions(+), 65 deletions(-) diff --git a/lib/mws.js b/lib/mws.js index 9d53fdb..938ccdf 100644 --- a/lib/mws.js +++ b/lib/mws.js @@ -3,6 +3,7 @@ var https = require('https'), crypto = require('crypto'), tls = require('tls'); xml2js = require('xml2js'); +var iconv = require('iconv-lite'); var MARKETPLACE_IDS = { @@ -59,93 +60,143 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { console.log("=============================="); } - // Check if we're dealing with a file (such as a feed) upload - if (api.upload) { - var body = query._BODY_, - bformat = query._FORMAT_; - delete query._BODY_; - delete query._FORMAT_; - } + var buffers = []; + var buffer; + var self = this; // Add required parameters and sign the query query['Action'] = action; query['Version'] = api.version; query["Timestamp"] = (new Date()).toISOString(); - query["AWSAccessKeyId"] = this.accessKeyId; + query["AWSAccessKeyId"] = self.accessKeyId; if (api.legacy) { - query['Merchant'] = this.merchantId; - } else { - query['SellerId'] = this.merchantId; - } - console.log('query : '); - console.log(query); - query = this.sign(api.path, query); - if (!api.upload) { - var body = qs.stringify(query); + query['Merchant'] = self.merchantId; + } else { + query['SellerId'] = self.merchantId; } - // Setup our HTTP headers and connection options - var headers = { - 'Host': this.host, - 'User-Agent': this.appName + '/' + this.appVersion + ' (Language=' + this.appLanguage + ')', - 'Content-Type': bformat || 'application/x-www-form-urlencoded; charset=utf-8', - 'Content-Length': body.length - }; - + // Check if we're dealing with a file (such as a feed) upload if (api.upload) { - headers['Content-MD5'] = crypto.createHash('md5').update(body).digest("base64"); - } + var body = query._BODY_, + bformat = query._FORMAT_; + delete query._BODY_; + delete query._FORMAT_; - var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); + body.on('data', function(buffer) { + buffers.push(buffer); + }); - console.log("************ ACTUAL PATH: ***************"); - console.log(pathReq); + body.on('end', function(){ + buffer = Buffer.concat(buffers); + + console.log('before query : '); + console.log(query); + query = self.sign(api.path, query); + console.log('query : '); + console.log(query); + // Setup our HTTP headers and connection options + var headers = { + 'Host': self.host, + 'User-Agent': self.appName + '/' + self.appVersion + ' (Language=' + self.appLanguage + ')', + 'Content-Type': bformat || 'text/tab-separated-values; charset=iso-8859-1', + 'Content-Length': buffer.length + }; + + headers['Content-MD5'] = crypto.createHash('md5').update(buffer).digest("base64"); + + var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); + + console.log("************ ACTUAL PATH: ***************"); + console.log(pathReq); + + var options = { + host: self.host, + port: self.port, + path: pathReq, + method: "POST", + headers: headers + }; + + sendRequest(self, options, buffer, function(err, result) { + callback(err, result); + }); + }); + } else { + console.log('query : '); + console.log(query); + query = this.sign(api.path, query); + var body = qs.stringify(query); - var options = { - host: this.host, - port: this.port, - path: pathReq, - method: "POST", - headers: headers - }; + // Setup our HTTP headers and connection options + var headers = { + 'Host': this.host, + 'User-Agent': this.appName + '/' + this.appVersion + ' (Language=' + this.appLanguage + ')', + 'Content-Type': bformat || 'application/x-www-form-urlencoded; charset=utf-8', + //'Content-Type': 'text/tab-separated-values; charset=iso-8859-1', + 'Content-Length': Buffer.byteLength(body) + }; + + var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); + + console.log("************ ACTUAL PATH: ***************"); + console.log(pathReq); + + var options = { + host: this.host, + port: this.port, + path: pathReq, + method: "POST", + headers: headers + }; + console.log(options); + sendRequest(this, options, body, function(err, result) { + callback(err, result); + }); + } +}; +function sendRequest(self, options, body, callback) { // Make the initial request and define callbacks - var req = this.conn.request(options, function (res) { + var req = self.conn.request(options, function (res) { + res.setEncoding('binary'); console.log(res.headers); - var data = ''; - // Append each incoming chunk to data variable - res.addListener('data', function (chunk) { - data += chunk.toString(); - console.log('data'); - console.log(data); - }); - // When response is complete, parse the XML and pass it to callback - res.addListener('end', function() { + var data = ''; + // Append each incoming chunk to data variable + res.addListener('data', function (chunk) { + var utf8Chunk = iconv.decode(new Buffer(chunk), 'utf8'); + data += utf8Chunk.toString(); + console.log('data'); + console.log(data); + }); + // When response is complete, parse the XML and pass it to callback + res.addListener('end', function() { var parser = new xml2js.Parser(); parser.addListener('end', function (result) { - // Throw an error if there was a problem reported - if (result.Error != null) { - if(process.env.NODE_ENV === 'development') { - console.log("--------------------------------"); - console.log("ERROR:"); - console.log(JSON.stringify(result.Error)); - } - throw("ERROR"); - } - callback(null, result); + // Throw an error if there was a problem reported + if (result.Error != null) { + if(process.env.NODE_ENV === 'development') { + console.log("--------------------------------"); + console.log("ERROR:"); + console.log(JSON.stringify(result.Error)); + } + throw("ERROR"); + } + callback(null, result); }); if (data.slice(0, 5) == ' Date: Thu, 1 Dec 2016 10:30:06 -0600 Subject: [PATCH 04/14] Add iconv-lite dependency needed in mws.js file --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9b5351a..43046a1 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "sellers" ], "homepage": "_", - "version": "0.9.7", + "version": "0.9.8", "author": "Eibbor Srenduas (http://eibbors.net)", "contributors": [ "Adrian Sanchezdelc ", @@ -24,6 +24,7 @@ "url": "https://github.com/ticadia/mws-sdk.git" }, "dependencies": { + "iconv-lite": "^0.4.15", "xml2js": "^0.4.9" }, "devDependencies": { From 94fb926eef05b5b5f72213181cb8c7063f80afd4 Mon Sep 17 00:00:00 2001 From: Jorge Irene Date: Fri, 23 Dec 2016 12:18:31 -0600 Subject: [PATCH 05/14] Changes to deal with xmls and excel files when uploading information. --- lib/feeds.js | 25 ++++++++++++------------- lib/mws.js | 17 +++++++++++++---- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/feeds.js b/lib/feeds.js index e076028..bb54a2f 100644 --- a/lib/feeds.js +++ b/lib/feeds.js @@ -1,14 +1,14 @@ /** * Feeds API requests and definitions for Amazon's MWS web services. * For information on using, please see examples folder. - * + * * @author Robert Saunders */ var mws = require('./mws'); - + /** * Construct a Feeds API request for mws.Client.invoke() - * + * * @param {String} action Action parameter of request * @param {Object} params Schemas for all supported parameters */ @@ -23,7 +23,6 @@ function FeedsRequest(action, params, isUpload) { action: action, // This next field is the just the SPECIFICATION (schema) of the parameters. // There are no VALUES for these parameters yet. - params: params }; // Note that mws.Request is AmazonMwsRequest in the mws.js file. return new mws.Request(opts); @@ -35,17 +34,17 @@ function FeedsRequest(action, params, isUpload) { */ var enums = exports.enums = { - FeedProcessingStatuses: function() { - return new mws.Enum(['_SUBMITTED_', '_IN_PROGRESS_', '_CANCELLED_', '_DONE_']); + FeedProcessingStatuses: function() { + return new mws.Enum(['_SUBMITTED_', '_IN_PROGRESS_', '_CANCELLED_', '_DONE_']); }, FeedTypes: function() { return new mws.Enum([ - '_POST_PRODUCT_DATA_', '_POST_PRODUCT_RELATIONSHIP_DATA_', '_POST_ITEM_DATA_', '_POST_PRODUCT_OVERRIDES_DATA_', '_POST_PRODUCT_IMAGE_DATA_', - '_POST_PRODUCT_PRICING_DATA_', '_POST_INVENTORY_AVAILABILITY_DATA_', '_POST_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_ORDER_FULFILLMENT_DATA_', - '_POST_FULFILLMENT_ORDER_REQUEST_DATA_', '_POST_FULFILLMENT_ORDER_CANCELLATION', '_POST_PAYMENT_ADJUSTMENT_DATA_', '_POST_INVOICE_CONFIRMATION_DATA_', - '_POST_FLAT_FILE_LISTINGS_DATA_', '_POST_FLAT_FILE_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_FLAT_FILE_FULFILLMENT_DATA_', - '_POST_FLAT_FILE_FBA_CREATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_FBA_UPDATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_PAYMENT_ADJUSTMENT_DATA_', + '_POST_PRODUCT_DATA_', '_POST_PRODUCT_RELATIONSHIP_DATA_', '_POST_ITEM_DATA_', '_POST_PRODUCT_OVERRIDES_DATA_', '_POST_PRODUCT_IMAGE_DATA_', + '_POST_PRODUCT_PRICING_DATA_', '_POST_INVENTORY_AVAILABILITY_DATA_', '_POST_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_ORDER_FULFILLMENT_DATA_', + '_POST_FULFILLMENT_ORDER_REQUEST_DATA_', '_POST_FULFILLMENT_ORDER_CANCELLATION', '_POST_PAYMENT_ADJUSTMENT_DATA_', '_POST_INVOICE_CONFIRMATION_DATA_', + '_POST_FLAT_FILE_LISTINGS_DATA_', '_POST_FLAT_FILE_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_FLAT_FILE_FULFILLMENT_DATA_', + '_POST_FLAT_FILE_FBA_CREATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_FBA_UPDATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_PAYMENT_ADJUSTMENT_DATA_', '_POST_FLAT_FILE_INVOICE_CONFIRMATION_DATA_', '_POST_FLAT_FILE_INVLOADER_DATA_', '_POST_FLAT_FILE_CONVERGENCE_LISTINGS_DATA_', '_POST_FLAT_FILE_BOOKLOADER_DATA_', '_POST_FLAT_FILE_LISTINGS_DATA_', '_POST_FLAT_FILE_PRICEANDQUANTITYONLY', '_POST_UIEE_BOOKLOADER_DATA_' ]); @@ -54,7 +53,7 @@ var enums = exports.enums = { }; /** - * A collection of currently supported request constructors. Once created and + * A collection of currently supported request constructors. Once created and * configured, the returned requests can be passed to an mws client `invoke` call * @type {Object} */ @@ -102,7 +101,7 @@ var calls = exports.requests = { }, SubmitFeed: function() { - return new FeedsRequest('SubmitFeed', + return new FeedsRequest('SubmitFeed', // schema: { FeedContents: { name: '_BODY_', required: true }, diff --git a/lib/mws.js b/lib/mws.js index 938ccdf..8e2bd11 100755 --- a/lib/mws.js +++ b/lib/mws.js @@ -74,14 +74,15 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { } else { query['SellerId'] = self.merchantId; } - - // Check if we're dealing with a file (such as a feed) upload if (api.upload) { var body = query._BODY_, bformat = query._FORMAT_; delete query._BODY_; delete query._FORMAT_; + } + // Check if we're dealing with a file (such as a feed) upload + if (api.upload && body !== null && typeof body === 'object' && typeof body.pipe === 'function') { // last three conditions to validate if it's a stream. body.on('data', function(buffer) { buffers.push(buffer); }); @@ -125,7 +126,10 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { console.log('query : '); console.log(query); query = this.sign(api.path, query); - var body = qs.stringify(query); + + if (!api.upload) { + var body = qs.stringify(query); + } // Setup our HTTP headers and connection options var headers = { @@ -136,6 +140,10 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { 'Content-Length': Buffer.byteLength(body) }; + if (api.upload) { + headers['Content-MD5'] = crypto.createHash('md5').update(body).digest('base64'); + } + var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); console.log("************ ACTUAL PATH: ***************"); @@ -158,9 +166,10 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { function sendRequest(self, options, body, callback) { // Make the initial request and define callbacks var req = self.conn.request(options, function (res) { + var data = ''; res.setEncoding('binary'); console.log(res.headers); - var data = ''; + // Append each incoming chunk to data variable res.addListener('data', function (chunk) { var utf8Chunk = iconv.decode(new Buffer(chunk), 'utf8'); From c4271a2ad38f6b7e6a06ded016138e385b16fc00 Mon Sep 17 00:00:00 2001 From: neshte Date: Fri, 23 Dec 2016 12:25:18 -0600 Subject: [PATCH 06/14] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7832762..7df5806 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "sellers" ], "homepage": "_", - "version": "0.9.8", + "version": "0.9.9", "author": "Eibbor Srenduas (http://eibbors.net)", "contributors": [ "Adrian Sanchezdelc ", From 010a703081e49b4a61c58668bda1d8047f6c1ba7 Mon Sep 17 00:00:00 2001 From: Jorge Irene Date: Mon, 26 Dec 2016 18:47:51 -0600 Subject: [PATCH 07/14] Addition of params parameter to request. --- lib/feeds.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/feeds.js b/lib/feeds.js index bb54a2f..8aad801 100644 --- a/lib/feeds.js +++ b/lib/feeds.js @@ -23,6 +23,7 @@ function FeedsRequest(action, params, isUpload) { action: action, // This next field is the just the SPECIFICATION (schema) of the parameters. // There are no VALUES for these parameters yet. + params: params }; // Note that mws.Request is AmazonMwsRequest in the mws.js file. return new mws.Request(opts); From 3c7c70b8d43119fd71177237f0070d60064cbc06 Mon Sep 17 00:00:00 2001 From: neshte Date: Mon, 26 Dec 2016 18:55:59 -0600 Subject: [PATCH 08/14] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7df5806..299b598 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "sellers" ], "homepage": "_", - "version": "0.9.9", + "version": "0.9.10", "author": "Eibbor Srenduas (http://eibbors.net)", "contributors": [ "Adrian Sanchezdelc ", From dad70383eb31134e950eac4adb63380c48bfe8eb Mon Sep 17 00:00:00 2001 From: Jorge Irene Date: Tue, 6 Jun 2017 13:01:29 -0500 Subject: [PATCH 09/14] Add new api version for orders --- lib/orders.js | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/orders.js b/lib/orders.js index 1f1f7d1..2c75aa8 100644 --- a/lib/orders.js +++ b/lib/orders.js @@ -1,14 +1,14 @@ /** * Orders API requests and definitions for Amazon's MWS web services. * For information on using, please see examples folder. - * + * * @author Robert Saunders */ var mws = require('./mws'); /** * Construct an Orders API request for mws.Client.invoke() - * + * * @param {String} action Action parameter of request * @param {Object} params Schemas for all supported parameters */ @@ -16,8 +16,8 @@ function OrdersRequest(action, params) { var opts = { name: 'Orders', group: 'Order Retrieval', - path: '/Orders/2011-01-01', - version: '2011-01-01', + path: '/Orders/2013-09-01', + version: '2013-09-01', legacy: false, action: action, params: params @@ -31,15 +31,15 @@ function OrdersRequest(action, params) { */ var enums = exports.enums = { - FulfillmentChannels: function() { - return new mws.Enum(['AFN', 'MFN']); + FulfillmentChannels: function() { + return new mws.Enum(['AFN', 'MFN']); }, - OrderStatuses: function() { + OrderStatuses: function() { return new mws.Enum(['Pending', 'Unshipped', 'PartiallyShipped', 'Shipped', 'Canceled', 'Unfulfillable']); }, - PaymentMethods: function() { + PaymentMethods: function() { return new mws.Enum(['COD', 'CVS', 'Other']); } @@ -53,14 +53,14 @@ var enums = exports.enums = { var types = exports.types = { FulfillmentChannel: { - 'AFN':'Amazon Fulfillment Network', + 'AFN':'Amazon Fulfillment Network', 'MFN':'Merchant\'s Fulfillment Network' }, OrderStatus: { - 'Pending':'Order placed but payment not yet authorized. Not ready for shipment.', - 'Unshipped':'Payment has been authorized. Order ready for shipment, but no items shipped yet. Implies PartiallyShipped.', + 'Pending':'Order placed but payment not yet authorized. Not ready for shipment.', + 'Unshipped':'Payment has been authorized. Order ready for shipment, but no items shipped yet. Implies PartiallyShipped.', 'PartiallyShipped':'One or more (but not all) items have been shipped. Implies Unshipped.', - 'Shipped':'All items in the order have been shipped.', + 'Shipped':'All items in the order have been shipped.', 'Canceled':'The order was canceled.', 'Unfulfillable':'The order cannot be fulfilled. Applies only to Amazon-fulfilled orders not placed on Amazon.' }, @@ -84,7 +84,7 @@ var types = exports.types = { }; /** - * A collection of currently supported request constructors. Once created and + * A collection of currently supported request constructors. Once created and * configured, the returned requests can be passed to an mws client `invoke` call * @type {Object} */ @@ -101,7 +101,7 @@ var calls = exports.requests = { * Returns orders created or updated during a time frame you specify. */ ListOrders: function() { - return new OrdersRequest('ListOrders', { + return new OrdersRequest('ListOrders', { CreatedAfter: { name: 'CreatedAfter', type: 'Timestamp' }, CreatedBefore: { name: 'CreatedBefore', type: 'Timestamp' }, LastUpdatedAfter: { name: 'LastUpdatedAfter', type: 'Timestamp' }, @@ -120,8 +120,8 @@ var calls = exports.requests = { * Returns the next page of orders using the NextToken parameter. */ ListOrdersByNextToken: function() { - return new OrdersRequest('ListOrdersByNextToken', { - NextToken: { name: 'NextToken', required: true } + return new OrdersRequest('ListOrdersByNextToken', { + NextToken: { name: 'NextToken', required: true } }); }, @@ -129,8 +129,8 @@ var calls = exports.requests = { * Returns orders based on the AmazonOrderId values that you specify. */ GetOrder: function() { - return new OrdersRequest('GetOrder', { - AmazonOrderId: { name: 'AmazonOrderId.Id', required: true, list: true } + return new OrdersRequest('GetOrder', { + AmazonOrderId: { name: 'AmazonOrderId.Id', required: true, list: true } }); }, @@ -138,7 +138,7 @@ var calls = exports.requests = { * Returns order items based on the AmazonOrderId that you specify. */ ListOrderItems: function() { - return new OrdersRequest('ListOrderItems', { + return new OrdersRequest('ListOrderItems', { AmazonOrderId: { name: 'AmazonOrderId', required: true } }); }, @@ -146,9 +146,9 @@ var calls = exports.requests = { * Returns the next page of order items using the NextToken parameter. */ ListOrderItemsByNextToken: function() { - return new OrdersRequest('ListOrderItemsByNextToken', { - NextToken: { name: 'NextToken', required: true } + return new OrdersRequest('ListOrderItemsByNextToken', { + NextToken: { name: 'NextToken', required: true } }); } -}; \ No newline at end of file +}; From 37e1ef5073f440d083281fcdf5ae15c1ead4629b Mon Sep 17 00:00:00 2001 From: neshte Date: Tue, 6 Jun 2017 13:06:32 -0500 Subject: [PATCH 10/14] Updated package.json version npm --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 299b598..ad11f2e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "sellers" ], "homepage": "_", - "version": "0.9.10", + "version": "0.9.11", "author": "Eibbor Srenduas (http://eibbors.net)", "contributors": [ "Adrian Sanchezdelc ", From 33a284b30c18711060d5f41990cef0ca6f6f5abf Mon Sep 17 00:00:00 2001 From: Jorge Irene Date: Wed, 10 Jan 2018 11:34:26 -0600 Subject: [PATCH 11/14] Add auth token --- lib/mws.js | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/mws.js b/lib/mws.js index 8e2bd11..88d3f91 100755 --- a/lib/mws.js +++ b/lib/mws.js @@ -19,22 +19,23 @@ var MARKETPLACE_IDS = { * @param {String} merchantId Aka SellerId, provided by Amazon (required) * @param {Object} options Additional configuration options for this instance */ -function AmazonMwsClient(accessKeyId, secretAccessKey, merchantId, options) { - this.host = options.host || 'mws.amazonservices.com'; - this.port = options.port || 443; - this.conn = options.conn || https; - if (tls.createSecureContext) { - this.creds = tls.createSecureContext(options.creds || {}); - } else { - this.creds = crypto.createCredentials(options.creds || {}); - } - this.appName = options.appName || 'mws-js'; - this.appVersion = options.appVersion || '0.1.0'; - this.appLanguage = options.appLanguage || 'JavaScript'; - this.accessKeyId = accessKeyId || null; - this.secretAccessKey = secretAccessKey || null; - this.merchantId = merchantId || null; -} + function AmazonMwsClient(accessKeyId, secretAccessKey, merchantId, authToken, options) { + this.host = options.host || 'mws.amazonservices.com'; + this.port = options.port || 443; + this.conn = options.conn || https; + if (tls.createSecureContext) { + this.creds = tls.createSecureContext(options.creds || {}); + } else { + this.creds = crypto.createCredentials(options.creds || {}); + } + this.appName = options.appName || 'mws-js'; + this.appVersion = options.appVersion || '0.1.0'; + this.appLanguage = options.appLanguage || 'JavaScript'; + this.accessKeyId = accessKeyId || null; + this.authToken = authToken || null; + this.secretAccessKey = secretAccessKey || null; + this.merchantId = merchantId || null; + } /** * The method used to invoke calls against MWS Endpoints. Recommended usage is @@ -69,6 +70,7 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { query['Version'] = api.version; query["Timestamp"] = (new Date()).toISOString(); query["AWSAccessKeyId"] = self.accessKeyId; + query['MWSAuthToken'] = self.authToken; if (api.legacy) { query['Merchant'] = self.merchantId; } else { From 82f16aeb54fcaf9b16846ed340cb558dd53491b1 Mon Sep 17 00:00:00 2001 From: jokenson Date: Wed, 10 Jan 2018 11:36:13 -0600 Subject: [PATCH 12/14] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 43046a1..8b745a7 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "sellers" ], "homepage": "_", - "version": "0.9.8", + "version": "0.9.9", "author": "Eibbor Srenduas (http://eibbors.net)", "contributors": [ "Adrian Sanchezdelc ", From b6be120051aec5d0781c7126930d524ed880b8bc Mon Sep 17 00:00:00 2001 From: Jaime Garza Date: Fri, 31 May 2019 10:56:55 -0500 Subject: [PATCH 13/14] conditional debug logs better practice --- lib/mws.js | 102 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/lib/mws.js b/lib/mws.js index 88d3f91..e49227d 100755 --- a/lib/mws.js +++ b/lib/mws.js @@ -23,6 +23,7 @@ var MARKETPLACE_IDS = { this.host = options.host || 'mws.amazonservices.com'; this.port = options.port || 443; this.conn = options.conn || https; + this.debug = options.debug || false; if (tls.createSecureContext) { this.creds = tls.createSecureContext(options.creds || {}); } else { @@ -53,12 +54,12 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { throw("accessKeyId, secretAccessKey, and merchantId must be set"); } - if(process.env.NODE_ENV === 'development') { + if(this.debug) { console.log("=================== API ==================="); - console.log(JSON.stringify(api)); - console.log("================ QUERY =============="); - console.log(JSON.stringify(query)); - console.log("=============================="); + console.log(JSON.stringify(api)); + console.log("================ QUERY =============="); + console.log(JSON.stringify(query)); + console.log("=============================="); } var buffers = []; @@ -92,11 +93,15 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { body.on('end', function(){ buffer = Buffer.concat(buffers); - console.log('before query : '); - console.log(query); + if(self.debug){ + console.log('before query : '); + console.log(query); + } query = self.sign(api.path, query); - console.log('query : '); - console.log(query); + if(self.debug){ + console.log('query : '); + console.log(query); + } // Setup our HTTP headers and connection options var headers = { 'Host': self.host, @@ -109,8 +114,10 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); - console.log("************ ACTUAL PATH: ***************"); - console.log(pathReq); + if(self.debug){ + console.log("************ ACTUAL PATH: ***************"); + console.log(pathReq); + } var options = { host: self.host, @@ -125,8 +132,10 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { }); }); } else { - console.log('query : '); - console.log(query); + if(self.debug){ + console.log('query : '); + console.log(query); + } query = this.sign(api.path, query); if (!api.upload) { @@ -148,8 +157,10 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { var pathReq = api.path + (api.upload ? '?' + qs.stringify(query) : ''); - console.log("************ ACTUAL PATH: ***************"); - console.log(pathReq); + if(self.debug){ + console.log("************ ACTUAL PATH: ***************"); + console.log(pathReq); + } var options = { host: this.host, @@ -158,7 +169,9 @@ AmazonMwsClient.prototype.call = function(api, action, query, callback) { method: "POST", headers: headers }; - console.log(options); + if(self.debug){ + console.log(options); + } sendRequest(this, options, body, function(err, result) { callback(err, result); }); @@ -170,14 +183,18 @@ function sendRequest(self, options, body, callback) { var req = self.conn.request(options, function (res) { var data = ''; res.setEncoding('binary'); - console.log(res.headers); + if(self.debug){ + console.log(res.headers); + } // Append each incoming chunk to data variable res.addListener('data', function (chunk) { var utf8Chunk = iconv.decode(new Buffer(chunk), 'utf8'); data += utf8Chunk.toString(); - console.log('data'); - console.log(data); + if(self.debug){ + console.log('data'); + console.log(data); + } }); // When response is complete, parse the XML and pass it to callback res.addListener('end', function() { @@ -185,7 +202,7 @@ function sendRequest(self, options, body, callback) { parser.addListener('end', function (result) { // Throw an error if there was a problem reported if (result.Error != null) { - if(process.env.NODE_ENV === 'development') { + if(self.debug) { console.log("--------------------------------"); console.log("ERROR:"); console.log(JSON.stringify(result.Error)); @@ -202,8 +219,10 @@ function sendRequest(self, options, body, callback) { }); req.on('error', function(err) { - console.log('errorInvoke'); - console.log(err); + if(self.debug){ + console.log('errorInvoke'); + console.log(err); + } callback(err); }); @@ -238,7 +257,9 @@ AmazonMwsClient.prototype.sign = function(path, query) { } var stringToSign = ["POST", this.host, path, qs.stringify(sorted)].join("\n"); - console.log(stringToSign); + if(this.debug){ + console.log(stringToSign); + } // An RFC (cannot remember which one) requires these characters also be changed: stringToSign = stringToSign.replace(/'/g,"%27"); stringToSign = stringToSign.replace(/\*/g,"%2A"); @@ -257,10 +278,10 @@ AmazonMwsClient.prototype.sign = function(path, query) { * @param {Function} callback Callback function used to process results/errors */ AmazonMwsClient.prototype.invoke = function(request, callback) { - if(process.env.NODE_ENV === 'development') { - console.log('++++++++++++++++++++++++++++++++++++'); - console.log('INVOKE'); - console.log(JSON.stringify(request)); + if(this.debug) { + console.log('++++++++++++++++++++++++++++++++++++'); + console.log('INVOKE'); + console.log(JSON.stringify(request)); } this.call(request.api, request.action, request.query(), callback); }; @@ -277,8 +298,9 @@ function AmazonMwsRequest(options) { path: options.path || '/', version: options.version || '2009-01-01', legacy: options.legacy || false, - upload: options.upload + upload: options.upload }; + this.debug = options.debug || false; this.action = options.action || 'GetServiceStatus'; this.params = options.params || {}; } @@ -291,7 +313,9 @@ function AmazonMwsRequest(options) { * @return {Object} Current instance to allow function chaining */ AmazonMwsRequest.prototype.set = function(param, value) { - console.log(param); + if(this.debug){ + console.log(param); + } var p = this.params[param], v = p.value = {}; @@ -339,10 +363,10 @@ AmazonMwsRequest.prototype.set = function(param, value) { */ AmazonMwsRequest.prototype.query = function() { var q = {}; - if(process.env.NODE_ENV === 'development') { - console.log("AMR.proto.query !!!!!!!!!!!!!!!!!!!!!!!"); - console.log(JSON.stringify(this)); - console.log(JSON.stringify(this.params)); + if(this.debug) { + console.log("AMR.proto.query !!!!!!!!!!!!!!!!!!!!!!!"); + console.log(JSON.stringify(this)); + console.log(JSON.stringify(this.params)); } for (var param in this.params) { var value = this.params[param].value, @@ -350,7 +374,9 @@ AmazonMwsRequest.prototype.query = function() { complex = (this.params[param].type === 'Complex'), required = this.params[param].required; - console.log("v " + value + "\nn " + name + "\nr " + required); + if(this.debug){ + console.log("v " + value + "\nn " + name + "\nr " + required); + } if ((value !== undefined) && (value !== null)) { if (complex) { value.appendTo(q); @@ -369,10 +395,10 @@ AmazonMwsRequest.prototype.query = function() { } } }; - if(process.env.NODE_ENV === 'development') { - console.log("query() about to return this:"); - console.log(q); - console.log("----------------------------------"); + if(this.debug) { + console.log("query() about to return this:"); + console.log(q); + console.log("----------------------------------"); } return q; }; From e5868a4d77a0b25ae77e74c6ee01ecfbb40b90d1 Mon Sep 17 00:00:00 2001 From: Jaime Garza Date: Fri, 31 May 2019 11:17:29 -0500 Subject: [PATCH 14/14] new version without logs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 53864dd..c7bef21 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "sellers" ], "homepage": "_", - "version": "0.9.12", + "version": "0.9.13", "author": "Eibbor Srenduas (http://eibbors.net)", "contributors": [ "Adrian Sanchezdelc ",