ebay-api
Advanced tools
Comparing version 1.3.0 to 1.4.0
@@ -15,5 +15,7 @@ /** | ||
EbayRequestError = _errors.EbayRequestError, | ||
EbayClientError = _errors.EbayClientError; | ||
EbayClientError = _errors.EbayClientError, | ||
knownArrayKeysByApiEndpoint = require('./key-lists').knownArrayKeysByApiEndpoint; | ||
/* | ||
@@ -49,5 +51,17 @@ helper: find an array containing only special key-value pairs | ||
*/ | ||
function _canFlattenKey(key) { | ||
if (/Array$/.test(key)) return false; | ||
if (key === 'Item') return false; | ||
function _canFlattenKey(key, requestContext) { | ||
//debug('_canFlattenKey?', key, requestContext); | ||
// assume that '*Array' and '*List' elements are arrays. | ||
if (/Array$/.test(key) || /List$/.test(key)) return false; | ||
// per-endpoint blacklist of known array keys. | ||
if (requestContext != null && requestContext.serviceName && requestContext.opType && | ||
knownArrayKeysByApiEndpoint[requestContext.serviceName] != null && | ||
knownArrayKeysByApiEndpoint[requestContext.serviceName][requestContext.opType] != null && | ||
_.contains(knownArrayKeysByApiEndpoint[requestContext.serviceName][requestContext.opType], key)) { | ||
return false; | ||
} | ||
// otherwise assume it can be flattened if there's a single value. | ||
return true; | ||
@@ -59,90 +73,134 @@ } | ||
convert `OrderArray: [ Order: [...] ]` structure to `Orders: []`. | ||
@param obj: the obj *containing* the key to be restructured. | ||
@param parentObj: the obj *containing* the key to be restructured. | ||
@param key: the key within the object to fix. | ||
modifies byref - no return. | ||
*/ | ||
function _flattenSillyArray(obj, key) { | ||
function _flattenSillyArray(parentObj, key, requestContext) { | ||
//debug('_flattenSillyArray', key, parentObj[key]); | ||
var subKey = key.replace(/Array$/, ''); // e.g. 'Order' from 'OrderArray' | ||
var newKey = subKey + 's'; // e.g. 'Orders' | ||
obj[newKey] = obj[key][0][subKey]; | ||
delete obj[key]; | ||
parentObj[newKey] = parentObj[key][0][subKey]; | ||
delete parentObj[key]; | ||
obj[newKey] = obj[newKey].map(function(subObj) { | ||
return exports.flatten(subObj); | ||
// might have already been flattened... | ||
if (!_.isArray(parentObj[newKey])) parentObj[newKey] = [ parentObj[newKey] ]; | ||
parentObj[newKey] = parentObj[newKey].map(function(subObj) { | ||
return exports.flatten(subObj, -1, requestContext); | ||
}); | ||
return obj; | ||
} | ||
function _convertAmountStructure(obj) { | ||
if (obj.hasOwnProperty('_')) { | ||
obj.amount = obj._; | ||
delete obj._; | ||
function _convertAmountStructure(el, requestContext) { | ||
if (_.isArray(el)) { | ||
return el.map(function(subEl) { | ||
return _convertAmountStructure(subEl); | ||
}); | ||
} | ||
if (obj.hasOwnProperty('$')) { | ||
_.extend(obj, obj['$']); | ||
delete obj['$']; | ||
if (el.hasOwnProperty('_') && el.hasOwnProperty('$')) { | ||
el.amount = +el._; | ||
delete el._; | ||
_.extend(el, el['$']); // {currencyID} | ||
delete el['$']; | ||
} | ||
return obj; | ||
return el; | ||
} | ||
function _castTypes(el) { | ||
if (_.isString(el)) { | ||
if (!isNaN(el)) el = +el; // numeric string to number | ||
else if (el === 'true') el = true; | ||
else if (el === 'false') el = false; | ||
} | ||
return el; | ||
} | ||
/* | ||
helper: RECURSIVELY turn 1-element arrays/objects into flat vars | ||
(different from _.flatten() which returns an array) | ||
*/ | ||
exports.flatten = function flatten(el, maxDepth, _depth) { | ||
recursively turn 1-element arrays/objects into flat values/objects. | ||
intended to handle flaw of XML->JSON conversion, that everything becomes an array. | ||
NOTE this is risky/complicated, because it has to make assumptions | ||
about what *should* remain an array, | ||
so some items might be structured differently depending on number of values. | ||
helpers like `canFlattenKey()` try to mitigate this risk. | ||
also transforms numbers and booleans from strings to types. | ||
*/ | ||
exports.flatten = function flatten(el, maxDepth, requestContext, _depth) { | ||
if (_.isUndefined(_depth)) _depth = 0; | ||
if (_.isUndefined(maxDepth)) maxDepth = 10; | ||
if (_.isUndefined(maxDepth) || maxDepth < 1) maxDepth = 10; | ||
if (_depth === 0) debug('flattening', el, {maxDepth: maxDepth}); | ||
if (_depth === 0) debug('flattening', el, {maxDepth: maxDepth, requestContext: requestContext}); | ||
if (_depth > maxDepth) return el; | ||
if (_depth <= maxDepth) { | ||
// flatten 1-item arrays | ||
if (_.isArray(el) && el.length === 1) { | ||
el = _.first(el); | ||
} | ||
// flatten 1-item arrays. | ||
// note: this is dangerous, means responses w/ single value can look different from multiple values. | ||
// trying to mitigate with `canFlattenKey()` check below. | ||
if (_.isArray(el) && el.length === 1) { | ||
el = _.first(el); | ||
} | ||
// special value-pair structure in the ebay API: turn { @key:KEY, __value__:VALUE } into { KEY: VALUE } | ||
if (_isValuePair(el)) { | ||
var values = _.values(el); | ||
debug('found special element', el); | ||
el = {}; | ||
el[ values[0] ] = values[1]; | ||
debug('converted element:', el); | ||
} | ||
// weird value-pair structure: | ||
// turn `{ @key:KEY, __value__:VALUE }` into `{ KEY: VALUE }` | ||
if (_isValuePair(el)) { | ||
var values = _.values(el); | ||
debug('converting key-value pair', el); | ||
el = {}; | ||
el[ values[0] ] = values[1]; | ||
} | ||
// previous fix just creates an array of these. we want a clean key:val obj. | ||
// so, is this an array of special value-pairs? | ||
if (_isArrayOfValuePairs(el)) { | ||
var fixEl = {}; | ||
_(el).each(function(pair) { | ||
_.extend(fixEl, flatten(pair, maxDepth, _depth+1)); // fix each, combine | ||
}); | ||
el = fixEl; | ||
} | ||
//// previous fix just creates an array of these. we want a clean key:val obj. | ||
//// so, is this an array of special value-pairs? | ||
//// TODO - disabled this b/c old and inefficient - understand where it was needed, and try to optimize. | ||
//if (_isArrayOfValuePairs(el)) { | ||
// var fixEl = {}; | ||
// _(el).each(function(pair) { | ||
// _.extend(fixEl, flatten(pair, maxDepth, requestContext, _depth + 1)); // fix each, combine | ||
// }); | ||
// el = fixEl; | ||
//} | ||
// flatten sub-elements | ||
if (_.isArray(el)) { | ||
_.each(el, function(subEl, subInd) { | ||
el[subInd] = flatten(el[subInd], maxDepth, _depth+1); | ||
}); | ||
} | ||
else if (_.isObject(el)) { | ||
_.each(el, function(subEl, subKey) { | ||
// special cases | ||
if (/Array$/.test(subKey)) { | ||
el = _flattenSillyArray(el, subKey); | ||
} | ||
else if (/Amount/.test(subKey)) { | ||
el[subKey] = _convertAmountStructure(el[subKey]); | ||
} | ||
else if (_canFlattenKey(subKey)) { | ||
el[subKey] = flatten(el[subKey], maxDepth, _depth + 1); | ||
} | ||
}); | ||
} | ||
// flatten sub-elements | ||
if (_.isObject(el) && !_.isArray(el)) { | ||
debug('--is an object', el); | ||
_.forOwn(el, function(childEl, childKey) { | ||
debug('--child', childKey, childEl); | ||
// special cases | ||
if (/Array$/.test(childKey)) { | ||
_flattenSillyArray(el, childKey, requestContext); // on parent, byref | ||
} | ||
else if (/(Amount|Cost|Price)/.test(childKey)) { | ||
el[childKey] = _convertAmountStructure(childEl, requestContext); | ||
} | ||
if (_canFlattenKey(childKey, requestContext)) { | ||
el[childKey] = flatten(childEl, maxDepth, requestContext, _depth + 1); | ||
} | ||
// can't flatten [presumed] array itself, but can still flatten its children. | ||
// @REVIEW: this causes weird skipping behavior, where grandchildren are flattened before higher levels, | ||
// so can't assume that lower levels haven't been flattened yet! | ||
else if (_.isArray(childEl)) { | ||
debug('---grandchildren', childKey, childEl, el[childKey]===childEl, typeof childEl.map); | ||
el[childKey] = childEl.map(function(grandChildEl) { | ||
return flatten(grandChildEl, maxDepth, requestContext, _depth + 1); | ||
}); | ||
} | ||
}); | ||
} | ||
if (_.isArray(el)) { | ||
el = el.map(function(childEl) { | ||
return flatten(childEl, maxDepth, requestContext, _depth + 1); | ||
}); | ||
} | ||
} //depth | ||
el = _castTypes(el); | ||
debug('flattened to', el); | ||
return el; | ||
@@ -155,3 +213,3 @@ }; | ||
@param data: response, converted to (or originally in) JSON. | ||
@param options: context on the request. | ||
@param requestContext: context on the request. | ||
@param callback: gets `null, data` in success case, and `error, data` on error case. | ||
@@ -161,19 +219,23 @@ - error can be from response or parsing failure. (see error types.) | ||
*/ | ||
exports.parseResponseJson = function(data, options, callback) { | ||
debug('parsing items', data); | ||
exports.parseResponseJson = function(data, requestContext, callback) { | ||
debug('parsing response json', data, requestContext); | ||
var flatten = exports.flatten; | ||
data = flatten(data, 1); // top level first | ||
debug('flattened', data); | ||
// flattening can be slow with big responses; | ||
// don't necessarily want to flatten all the way up front. | ||
// (maybe better to let app pick the keys it wants and flatten only them.) | ||
// depth here is arbitrary. | ||
data = flatten(data, 5, requestContext); | ||
// find the response key. | ||
// (is probably `{options.opType}Response`) | ||
// (is probably `{requestContext.opType}Response`) | ||
var responseKey = _(data).keys().find(function(key) { | ||
return /[a-zA-Z]+Response$/.test(key); | ||
}) || options.opType + 'Response'; | ||
}) || requestContext.opType + 'Response'; | ||
debug('looking for response key', responseKey); | ||
data = flatten(data[responseKey], 1); // drill down 1 more level | ||
data = data[responseKey]; | ||
if (_.isUndefined(data)) { | ||
@@ -199,3 +261,3 @@ // assume this is a failure of the client to parse the response properly. | ||
if (!_.isUndefined(data.Ack)) { | ||
data.Ack = flatten(data.Ack); | ||
data.Ack = flatten(data.Ack, -1, requestContext); | ||
} | ||
@@ -221,3 +283,3 @@ | ||
errorMessage = errors.map(function(errorObj) { | ||
errorObj = flatten(errorObj); | ||
errorObj = flatten(errorObj, -1, requestContext); | ||
if (errorObj.ErrorClassification === 'SystemError') { | ||
@@ -233,3 +295,3 @@ errorClassification = 'SystemError'; // trumps RequestError | ||
else if (!_.isUndefined(data.errorMessage)) { | ||
errorMessage = flatten(data.errorMessage); | ||
errorMessage = flatten(data.errorMessage, -1, requestContext); | ||
if (_.isObject(errorMessage)) errorMessage = util.inspect(errorMessage, true, 3); | ||
@@ -251,10 +313,2 @@ // TODO error code and classification in this format? | ||
// flatten a little more. | ||
// see `_canFlattenKey()` for exceptions. | ||
data = flatten(data, 1); | ||
if (data.paginationOutput) { | ||
data.paginationOutput = flatten(data.paginationOutput); | ||
} | ||
// | ||
@@ -261,0 +315,0 @@ // PER-OP PARSING |
{ | ||
"name": "ebay-api", | ||
"description": "eBay API Client", | ||
"version": "1.3.0", | ||
"version": "1.4.0", | ||
"homepage": "https://github.com/newleafdigital/nodejs-ebay-api", | ||
@@ -6,0 +6,0 @@ "author": "Ben Buckman <ben@newleafdigital.com> (http://newleafdigital.com)", |
@@ -30,5 +30,5 @@ #! /usr/bin/env node | ||
}, | ||
function(data, next) { | ||
parseResponseJson(data, {}, next); | ||
} | ||
//function(data, next) { | ||
// parseResponseJson(data, {}, next); | ||
//} | ||
], | ||
@@ -35,0 +35,0 @@ function(error, parsedJson) { |
@@ -1,6 +0,3 @@ | ||
var | ||
chai = require('chai'), | ||
expect = chai.expect; | ||
require('./helpers'); | ||
describe('`deepToArray`', function() { | ||
@@ -7,0 +4,0 @@ var deepToArray = require('../lib/deep-to-array').deepToArray; |
@@ -0,3 +1,5 @@ | ||
require('./helpers'); | ||
describe('`paginatedRequest`', function() { | ||
it('#TODO') | ||
}); |
@@ -1,8 +0,3 @@ | ||
var | ||
chai = require('chai'), | ||
expect = chai.expect, | ||
sinon = require('sinon'); | ||
require('./helpers'); | ||
chai.use(require("sinon-chai")); | ||
var | ||
@@ -14,10 +9,2 @@ request = require('request'), | ||
describe('XML requests', function() { | ||
beforeEach('sandbox', function() { | ||
this.sinon = sinon.sandbox.create(); | ||
}); | ||
afterEach('unstub', function() { | ||
this.sinon.restore(); | ||
}); | ||
describe('building requests', function() { | ||
@@ -390,6 +377,1 @@ beforeEach('stub request', function () { | ||
}); | ||
// @TODO try to hit an actual sandbox endpoint ...? | ||
// (can a sandbox key be hard-coded in this module?) | ||
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
81598
29
1423
1