ebay-api
Advanced tools
Comparing version 1.0.0-alpha to 1.0.0
@@ -6,31 +6,28 @@ // example paginated request to FindingService:findItemsAdvanced | ||
var params = {}; | ||
var params = { | ||
categoryId: [ "47123", "14948" ], // Electronics & Gadgets | ||
params.categoryId = [ "47123", "14948" ]; // Electronics & Gadgets | ||
// include SellerInfo in response | ||
outputSelector: [ 'SellerInfo' ], | ||
// include SellerInfo in response | ||
params.outputSelector = [ 'SellerInfo' ]; | ||
//// for affiliate links (see https://www.x.com/developers/ebay/use-cases/affiliate-tracking) | ||
affiliate: { | ||
trackingID: '1234567890', // CAMPAIGN ID | ||
networkId: '9', // "If you are registered with eBay Partner Network, the networkId is 9" | ||
customId: '1' // arbitrary, to differentiate sub-campaigns | ||
}, | ||
//// for affiliate links (see https://www.x.com/developers/ebay/use-cases/affiliate-tracking) | ||
// params['affiliate.trackingId'] = '1234567890'; // CAMPAIGN ID | ||
// params['affiliate.networkId'] = '9'; // "If you are registered with eBay Partner Network, the networkId is 9" | ||
// params['affiliate.customId'] = '1'; // arbitrary, to differentiate sub-campaigns | ||
itemFilter: [ | ||
// (most of these can be 1 item or array of multiple items, depending on the service) | ||
{name: "ExcludeCategory", value: "14998"}, // no Vintage Electronics | ||
{name: "ListingType", value: "Auction"}, | ||
var filters = {}; | ||
// example of 2-part filter | ||
{name: 'MinPrice', value: '20', paramName: 'Currency', paramValue: 'USD'}, | ||
{name: 'MaxPrice', value: '300', paramName: 'Currency', paramValue: 'USD'} | ||
] | ||
}; | ||
filters.itemFilter = [ | ||
// (most of these can be 1 item or array of multiple items, depending on the service) | ||
new ebay.ItemFilter("ExcludeCategory", ["14998"]), // no Vintage Electronics | ||
// new ebay.ItemFilter("ExcludeSeller", ["somebadseller"]), | ||
new ebay.ItemFilter("ListingType", ["Auction"]), | ||
new ebay.ItemFilter('MinPrice', '20', 'Currency', 'USD'), | ||
new ebay.ItemFilter('MaxPrice', '300', 'Currency', 'USD'), // example of 2-part filter | ||
]; | ||
// run it (paginated) | ||
var pages = 2, | ||
perPage = 3; // max 100x100 (10k items) | ||
ebay.paginateGetRequest({ | ||
ebay.paginatedRequest({ | ||
serviceName: 'Finding', | ||
@@ -40,5 +37,4 @@ opType: 'findItemsAdvanced', | ||
params: params, | ||
filters: filters, | ||
pages: pages, | ||
perPage: perPage, | ||
pages: 2, | ||
perPage: 3, // max 100x100 (10k items) | ||
parser: ebay.parseResponse | ||
@@ -45,0 +41,0 @@ }, |
@@ -5,6 +5,4 @@ // eBay API client for Node.js | ||
exports.paginateGetRequest = require('./lib/pagination').paginateGetRequest; | ||
exports.paginatedRequest = require('./lib/pagination').paginatedRequest; | ||
exports.ItemFilter = require('./lib/filters').ItemFilter; | ||
exports.parseResponse = require('./lib/parser').parseResponse; |
@@ -8,16 +8,3 @@ var | ||
default params per service type. | ||
for GET requests these go into URL. for POST requests these go into headers. | ||
options differ by service, see below. | ||
@option appId | ||
@option opType | ||
@option version | ||
@option globalId ? | ||
@option siteId ? | ||
@option devName | ||
@option cert | ||
... | ||
@param {object} options: request context. see `xmlRequest()` docs, and code below. | ||
*/ | ||
@@ -52,10 +39,9 @@ exports.getDefaultHeaders = function(options) { | ||
return { | ||
'SERVICE-NAME': options.serviceName, | ||
'CONSUMER-ID': options.appId ? options.appId : null, | ||
// based on response data | ||
'X-EBAY-SOA-SERVICE-NAME': 'MerchandisingService', | ||
'X-EBAY-SOA-OPERATION-NAME': options.opType, | ||
'EBAY-SOA-CONSUMER-ID': options.appId ? options.appId : null, | ||
'SERVICE-VERSION': options.version ? options.version : '1.5.0', | ||
'OPERATION-NAME': options.opType, | ||
'GLOBAL-ID': options.globalId ? options.globalId : 'EBAY-US', | ||
'RESPONSE-DATA-FORMAT': 'XML', | ||
'REST-PAYLOAD': null // (not sure what this does) | ||
'X-EBAY-SOA-REQUEST-DATA-FORMAT': 'XML', | ||
//'RESPONSE-DATA-FORMAT': 'XML', | ||
//'REST-PAYLOAD': null // (not sure what this does) | ||
}; | ||
@@ -62,0 +48,0 @@ |
@@ -9,3 +9,3 @@ var | ||
// PAGINATE multiple GET/JSON requests in parallel (max 100 per page, 100 pages = 10k items) | ||
exports.paginateGetRequest = function(options, callback) { | ||
exports.paginatedRequest = function(options, callback) { | ||
throw new Error("TODO re-implement/refactor me!"); | ||
@@ -18,3 +18,2 @@ | ||
//options.params = options.params || {}; | ||
//options.filters = options.filters || {}; | ||
//options.reqOptions = options.reqOptions || {}; | ||
@@ -21,0 +20,0 @@ //options.pages = options.pages || 2; |
@@ -148,2 +148,4 @@ var | ||
var flatten = exports.flatten; | ||
data = flatten(data, 1); // top level first | ||
@@ -188,2 +190,6 @@ debug('flattened', data); | ||
if (data.paginationOutput) { | ||
data.paginationOutput = flatten(data.paginationOutput); | ||
} | ||
// | ||
@@ -194,33 +200,3 @@ // PER-OP PARSING | ||
//var items = []; | ||
//if (typeof data.Item !== 'undefined') { // e.g. for Shopping::GetSingleItem | ||
// items = [ data.Item ]; // preserve array for standardization (?) | ||
//} | ||
// | ||
//else if (typeof data.searchResult !== 'undefined') { // e.g. for FindingService | ||
// // reduce in steps so successful-but-empty responses don't throw error | ||
// if (!_.isEmpty(data.searchResult)) { | ||
// data = _(data.searchResult).first(); | ||
// if (typeof data !== 'undefined') { | ||
// if (typeof data.item !== 'undefined') { | ||
// items = data.item; | ||
// } | ||
// } | ||
// } | ||
//} | ||
//else if (typeof data.itemRecommendations !== 'undefined') { | ||
// if (typeof data.itemRecommendations !== 'undefined') { | ||
// if (typeof data.itemRecommendations.item !== 'undefined') { | ||
// items = _.isArray(data.itemRecommendations.item) ? data.itemRecommendations.item : []; | ||
// } | ||
// } | ||
//} | ||
// | ||
//// recursively flatten 1-level arrays and "@key:__VALUE__" pairs | ||
//items = _(items).map(function(item) { | ||
// return flatten(item); | ||
//}); | ||
return data; | ||
}; |
116
lib/urls.js
@@ -6,111 +6,41 @@ var | ||
// [internal] convert params hash to url string. | ||
// some items may be arrays, use key(0)..(n) | ||
// param usage: | ||
// - use null values for plain params | ||
// - use arrays for repeating keys | ||
function buildUrlParams(obj, prefix) { | ||
var i, k, keys, str, _fn, _i, _j, _len, _len1; | ||
str = []; | ||
if (typeof prefix === "undefined") { | ||
prefix = ""; | ||
} | ||
if (obj === null) { return prefix; } | ||
if (obj.constructor.toString().match(/^function\sarray/i)) { | ||
_fn = function(o) { | ||
return str.push(buildUrlParams(o, prefix + "(" + i + ")")); | ||
}; | ||
for (i = _i = 0, _len = obj.length; _i < _len; i = ++_i) { | ||
k = obj[i]; | ||
_fn(k); | ||
} | ||
} else if (obj.constructor.toString().match(/^function\sobject/i)) { | ||
if (prefix !== "") { | ||
prefix += "."; | ||
} | ||
keys = Object.keys(obj); | ||
for (i = _j = 0, _len1 = keys.length; _j < _len1; i = ++_j) { | ||
k = keys[i]; | ||
str.push(buildUrlParams(obj[k], prefix + k)); | ||
} | ||
} else { | ||
str.push(prefix + "=" + obj); | ||
debug(prefix + " = " + obj); | ||
} | ||
return str.join("&"); | ||
} | ||
// [internal] convert a filters array to a url string | ||
// adapted from client-side JS example in ebay docs | ||
function buildFilters(filterType, filters) { | ||
var urlFilter = ''; | ||
_(filters).each(function eachItemFilter(filter, filterInd) { | ||
// each parameter in each item filter | ||
_(filter).each(function eachItemParam(paramVal, paramKey) { | ||
// Check to see if the paramter has a value (some don't) | ||
if (paramVal !== "") { | ||
// multi-value param | ||
if (_.isArray(paramVal)) { | ||
_(paramVal).each(function eachSubFilter(paramSubVal, paramSubIndex) { | ||
urlFilter += '&' + filterType + '(' + filterInd + ').' + paramKey + '(' + paramSubIndex + ')=' + paramSubVal; | ||
}); | ||
} | ||
// single-value param | ||
else { | ||
urlFilter += '&' + filterType + '(' + filterInd + ').' + paramKey + '=' + paramVal; | ||
} | ||
} | ||
}); | ||
}); | ||
return urlFilter; | ||
} | ||
/* | ||
build URL to API endpoints | ||
set sandbox=true for sandbox, otherwise production | ||
- params is a 1D obj | ||
- filters is an obj of { filterType:[filters] } (where filters is an array of ItemFilter) | ||
params,filters only apply to GET requests; for POST pass in empty {} or null | ||
@TODO does this need `params` or `filters` anymore? -- if everything is XML?? | ||
build URL to API endpoints. | ||
@param options: request context (see | ||
*/ | ||
exports.buildRequestUrl = function(serviceName, params, filters, sandbox) { | ||
exports.buildRequestUrl = function(options) { | ||
var url; | ||
params = params || {}; | ||
filters = filters || {}; | ||
sandbox = (typeof sandbox === 'boolean') ? sandbox : false; | ||
options = options || {}; | ||
var serviceName = options.serviceName.replace(/Service$/, ''); | ||
switch (serviceName) { | ||
case 'Finding': | ||
if (sandbox) { | ||
throw new Error("There is no sandbox environment for the FindingService."); | ||
} | ||
else url = "https://svcs.ebay.com/services/search/FindingService/v1?"; | ||
if (options.sandbox) url = 'http://svcs.sandbox.ebay.com/services/search/FindingService/v1'; | ||
else url = 'https://svcs.ebay.com/services/search/FindingService/v1'; | ||
break; | ||
case 'Product': | ||
if (sandbox) url = "http://svcs.sandbox.ebay.com/services/marketplacecatalog/" + serviceName + "/v1?"; | ||
else url = "http://svcs.ebay.com/services/marketplacecatalog/" + serviceName + "/v1?"; | ||
if (options.sandbox) url = 'http://svcs.sandbox.ebay.com/services/marketplacecatalog/' + serviceName + '/v1'; | ||
else url = 'https://svcs.ebay.com/services/marketplacecatalog/' + serviceName + '/v1'; | ||
break; | ||
case 'Shopping': | ||
if (sandbox) url = 'http://open.api.sandbox.ebay.com/shopping?'; | ||
else url = "https://open.api.ebay.com/shopping?"; | ||
if (options.sandbox) url = 'http://open.api.sandbox.ebay.com/shopping'; | ||
else url = "https://open.api.ebay.com/shopping"; | ||
break; | ||
case 'Trading': // ...and the other XML APIs | ||
if (sandbox) url = 'https://api.sandbox.ebay.com/ws/api.dll'; | ||
case 'Trading': | ||
if (options.sandbox) url = 'https://api.sandbox.ebay.com/ws/api.dll'; | ||
else url = 'https://api.ebay.com/ws/api.dll'; | ||
break; | ||
// params and filters don't apply to URLs w/ these | ||
return url; | ||
// break; | ||
case 'Merchandising': | ||
if (options.sandbox) url = 'http://svcs.sandbox.ebay.com/MerchandisingService'; | ||
else url = "https://svcs.ebay.com/MerchandisingService"; | ||
break; | ||
default: | ||
if (sandbox) { | ||
if (options.sandbox) { | ||
throw new Error("Sandbox endpoint for " + serviceName + " service not yet implemented. Please add."); | ||
@@ -121,9 +51,5 @@ } | ||
url += buildUrlParams(params); // no trailing & | ||
// [removed URL params - all XML based now. if need to bring back, look in git history.] | ||
_(filters).each(function(typeFilters, type) { | ||
url += buildFilters(type, typeFilters); // each has leading & | ||
}); | ||
return url; | ||
}; |
@@ -14,16 +14,33 @@ var | ||
// need arrays all the way down... (quirk of xml module). | ||
/** | ||
* handle quirk of 'xml' module: | ||
* need arrays all the way down, | ||
* and objects can only have a single key:value, | ||
* e.g. {k1:v1,k2:v2} need to become [{k1:[v1]}, {k2:[v2]}]. | ||
* @see tests in xml-request.test.js. | ||
*/ | ||
exports._deepToArray = function _deepToArray(obj) { | ||
var key, value; | ||
if (_.isObject(obj)) { | ||
var key, value, arr = []; | ||
if (_.isArray(obj)) { | ||
// not sure about this: arrays within objects are handled below; | ||
// top level should never be an array; | ||
// but seems ok. change/fix if a scenario comes up that this breaks. | ||
return obj.map(function(value) { | ||
return _deepToArray(value); | ||
}); | ||
} | ||
else if (_.isObject(obj)) { | ||
for (key in obj) { | ||
if (obj.hasOwnProperty(key)) { | ||
value = obj[key]; | ||
if (_.isArray(value)) { // recurse over array children | ||
obj[key] = value.map(function(subValue) { | ||
return _deepToArray(subValue); | ||
// `{foo: [a,b,c]}` => `[{foo:a},{foo:b},{foo:c}]` | ||
if (_.isArray(value)) { | ||
value.forEach(function(subValue) { | ||
arr.push(_.set({}, key, _deepToArray(subValue))); | ||
}); | ||
} | ||
else { // make it an array | ||
obj[key] = [ _deepToArray(value) ]; | ||
else { | ||
arr.push(_.set({}, key, _deepToArray(value))); | ||
} | ||
@@ -33,3 +50,6 @@ } | ||
} | ||
return obj; | ||
else { | ||
arr = [obj]; | ||
} | ||
return arr; | ||
}; | ||
@@ -40,5 +60,3 @@ | ||
build XML input for XML requests (POST). | ||
@param {object} options: request context. | ||
@option {object} params: custom input params. | ||
@param {object} options: request context. see `xmlRequest()` docs. | ||
for repeatable fields, use an array value (see below). | ||
@@ -55,20 +73,15 @@ */ | ||
if (options.serviceName === 'Finding') { | ||
root.push({'_attr' : {'xmlns' : 'http://www.ebay.com/marketplace/search/v1/services'}}); | ||
root.push({'_attr': {'xmlns': 'http://www.ebay.com/marketplace/search/v1/services'}}); | ||
} | ||
else { | ||
root.push({'_attr' : {'xmlns' : 'urn:ebay:apis:eBLBaseComponents'}}); | ||
root.push({'_attr': {'xmlns': 'urn:ebay:apis:eBLBaseComponents'}}); | ||
} | ||
if (options.authToken) { | ||
root.push({ 'RequesterCredentials' : [ { 'eBayAuthToken' : options.authToken } ] }); | ||
root.push({ 'RequesterCredentials': [ { 'eBayAuthToken' : options.authToken } ] }); | ||
} | ||
params = exports._deepToArray(params); | ||
// concat | ||
root.push.apply(root, exports._deepToArray(params)); | ||
_.each(params, function(value, key) { // keys in top object | ||
_.each(params[key], function(value) { // arrays | ||
root.push(_.set({}, key, [value])); | ||
}); | ||
}); | ||
debug('Converting to XML', data); | ||
@@ -87,4 +100,21 @@ | ||
/* | ||
@param {object} @options | ||
@option reqOptions: passed to request | ||
@param {object} @options: | ||
@option {object} reqOptions: passed to request | ||
@option {string} appId | ||
@option {string} opType | ||
@option {string} version | ||
@option {string} devName | ||
@option {string} cert | ||
@option {string} authToken | ||
@option {string} globalId | ||
@option {string} siteId | ||
@option {object} params: this gets converted to an XML body with request parameters. | ||
all filters go in here. use nested objects where appropriate. see the API docs. | ||
... | ||
@param {function} callback: gets response | ||
@@ -104,4 +134,3 @@ */ | ||
var reqOptions = _.extend({}, options.reqOptions, { | ||
url: buildRequestUrl(options.serviceName, {}, {}, options.sandbox || false), | ||
// @see note in buildXmlInput() re: nested elements (...??) | ||
url: buildRequestUrl(options), | ||
body: buildXmlInput(options), | ||
@@ -108,0 +137,0 @@ }); |
{ | ||
"name": "ebay-api", | ||
"description": "eBay API Client", | ||
"version": "1.0.0-alpha", | ||
"version": "1.0.0", | ||
"homepage": "https://github.com/newleafdigital/nodejs-ebay-api", | ||
@@ -30,4 +30,4 @@ "author": "Ben Buckman <ben@newleafdigital.com> (http://newleafdigital.com)", | ||
"engines": { | ||
"node": ">= 0.6" | ||
"node": ">= 0.10" | ||
} | ||
} |
eBay API client for Node.js | ||
=============== | ||
[![Build Status](https://travis-ci.org/benbuckman/nodejs-ebay-api.svg)](https://travis-ci.org/benbuckman/nodejs-ebay-api) | ||
## Intro | ||
@@ -62,4 +64,4 @@ | ||
- `params`: (see examples and API documentation) | ||
- `filters`: (see examples and API documentation.) _might no longer work in 1.x: if you're using this, please submit a PR!_ | ||
- `reqOptions`: passed to the [request](https://github.com/request/request) module, e.g. for additional `headers`. | ||
- `reqOptions`: passed to the [request](https://github.com/request/request) module, | ||
e.g. for additional `headers`, or `timeout`. | ||
- `parser`: function which takes the response data and extracts items (or other units depending on the query). | ||
@@ -97,2 +99,9 @@ _Module includes a default parser._ | ||
## Debugging | ||
This module uses the [debug](https://github.com/visionmedia/debug) module for internal logging. | ||
Run your app (or node REPL) with `DEBUG=ebay* ...` to see output. | ||
## Helpers | ||
@@ -106,2 +115,3 @@ | ||
- The structure of the format `{ @key:KEY, __value__:VALUE }` is flattened to its key:value pair. | ||
- Other weird structures (from the API itself, or the XML->JSON conversion) are simplified. _(See the code for details.)_ | ||
@@ -112,8 +122,7 @@ Its purpose is to make the data easier to handle in code, and to model/query in MongoDB. | ||
The default parser will `flatten()` the response to a finite depth | ||
(because infinite recursion on an indeterminate response size would cause an unnecessary performance hit). | ||
If you want to flatten further, use this method directly. | ||
### `ItemFilter(name, value, paramName, paramValue)` | ||
A class constructor to simplify creating filters. (See the examples) | ||
### `getLatestApiVersions(callback)` | ||
@@ -120,0 +129,0 @@ |
@@ -147,3 +147,3 @@ var | ||
describe('`parseResponse`', function() { | ||
// @TODO | ||
it('#TODO') | ||
}); |
@@ -48,3 +48,3 @@ var | ||
expect(request.post.lastCall.args[0]).to.deep.equal({ | ||
url: 'http://open.api.sandbox.ebay.com/shopping?', | ||
url: 'http://open.api.sandbox.ebay.com/shopping', | ||
headers: { | ||
@@ -125,2 +125,69 @@ 'Content-Type': 'text/xml', | ||
describe('Finding: findItemsByKeywords', function() { | ||
beforeEach('build request', function () { | ||
xmlRequest({ | ||
serviceName: 'Finding', | ||
opType: 'findItemsByKeywords', | ||
sandbox: true, | ||
appId: 'ABCDEF', | ||
raw: true, // no parsing | ||
params: { | ||
keywords: ['Canon', 'Powershot'], | ||
outputSelector: ['AspectHistogram'], | ||
itemFilter: [ | ||
{name: 'FreeShippingOnly', value: 'true'}, | ||
{name: 'MaxPrice', value: '150'}, | ||
], | ||
domainFilter: [ | ||
{name: 'domainName', value: 'Digital_Cameras'} | ||
], | ||
paginationInput: { | ||
entriesPerPage: '5' | ||
} | ||
} | ||
}, function noop() { | ||
}); | ||
}); | ||
it('initiated request with expected parameters', function() { | ||
expect(request.post).to.have.been.calledOnce; | ||
expect(request.post.lastCall.args[0]).to.deep.equal({ | ||
url: 'http://svcs.sandbox.ebay.com/services/search/FindingService/v1', | ||
headers: { | ||
'X-EBAY-SOA-SECURITY-APPNAME': 'ABCDEF', | ||
'X-EBAY-SOA-REQUEST-DATA-FORMAT': 'XML', | ||
'X-EBAY-SOA-RESPONSE-DATA-FORMAT': 'XML', | ||
'X-EBAY-SOA-GLOBAL-ID': 'EBAY-US', | ||
'X-EBAY-SOA-SERVICE-VERSION': '1.13.0', | ||
'X-EBAY-SOA-OPERATION-NAME': 'findItemsByKeywords' | ||
}, | ||
body: | ||
'<?xml version="1.0" encoding="UTF-8"?>\n' + | ||
'<findItemsByKeywordsRequest xmlns="http://www.ebay.com/marketplace/search/v1/services">\n' + | ||
' <keywords>Canon</keywords>\n' + | ||
' <keywords>Powershot</keywords>\n' + | ||
' <outputSelector>AspectHistogram</outputSelector>\n' + | ||
' <itemFilter>\n' + | ||
' <name>FreeShippingOnly</name>\n' + | ||
' <value>true</value>\n' + | ||
' </itemFilter>\n' + | ||
' <itemFilter>\n' + | ||
' <name>MaxPrice</name>\n' + | ||
' <value>150</value>\n' + | ||
' </itemFilter>\n' + | ||
' <domainFilter>\n' + | ||
' <name>domainName</name>\n' + | ||
' <value>Digital_Cameras</value>\n' + | ||
' </domainFilter>\n' + | ||
' <paginationInput>\n' + | ||
' <entriesPerPage>5</entriesPerPage>\n' + | ||
' </paginationInput>\n' + | ||
'</findItemsByKeywordsRequest>' | ||
}); | ||
}); | ||
}); | ||
describe('nested params', function() { | ||
@@ -136,3 +203,4 @@ beforeEach('build request', function() { | ||
'ItemTransactionID': { | ||
'OrderLineItemID': '12345-67890' | ||
'OrderLineItemID': '12345-67890', | ||
'SomeOtherID': 'ABCDEF' | ||
} | ||
@@ -151,2 +219,3 @@ }] | ||
' <OrderLineItemID>12345-67890</OrderLineItemID>\n' + | ||
' <SomeOtherID>ABCDEF</SomeOtherID>\n' + | ||
' </ItemTransactionID>\n' + | ||
@@ -168,7 +237,54 @@ ' </ItemTransactionIDArray>\n' + | ||
it('converts everything to an array', function() { | ||
it('converts plain value to array', function() { | ||
expect(_deepToArray(5)).to.deep.equal([5]); | ||
}); | ||
it('converts object to array', function() { | ||
expect(_deepToArray({ | ||
'a': '1', | ||
'b': '2' | ||
})).to.deep.equal( | ||
[ | ||
{'a': ['1']}, | ||
{'b': ['2']} | ||
] | ||
); | ||
}); | ||
it('splits out array elements into objects', function() { | ||
expect(_deepToArray( | ||
{ | ||
thing: ['a', 'b', 'c'], | ||
itemFilter: [ | ||
{name: 'FreeShippingOnly', value: 'true'}, | ||
{name: 'MaxPrice', value: '150'}, | ||
] | ||
} | ||
)).to.deep.equal( | ||
[ | ||
{thing: ['a']}, | ||
{thing: ['b']}, | ||
{thing: ['c']}, | ||
{ | ||
itemFilter: [ | ||
{name: ['FreeShippingOnly']}, | ||
{value: ['true']}, | ||
] | ||
}, | ||
{ | ||
itemFilter: [ | ||
{name: ['MaxPrice']}, | ||
{value: ['150']}, | ||
] | ||
} | ||
] | ||
); | ||
}); | ||
it('converts everything recursively to arrays', function() { | ||
expect(_deepToArray({ | ||
'Thing': { | ||
'Has': { | ||
'Weird': 'Structure' | ||
'Weird': 'Structure', | ||
'Strange': 'Data' | ||
} | ||
@@ -180,18 +296,38 @@ }, | ||
'More': 'Things', | ||
'Finally': ['Thing1', 'Thing2'] | ||
'Finally': ['Thing1', 'Thing2'], | ||
'Already': [{'An': 'Array'}] | ||
})) | ||
.to.deep.equal({ | ||
'Thing': [{ | ||
'Has': [{ | ||
'Weird': ['Structure'] | ||
}] | ||
}], | ||
'Things': [ | ||
{'foo': ['bar']} | ||
], | ||
'More': ['Things'], | ||
'Finally': ['Thing1', 'Thing2'] | ||
}); | ||
.to.deep.equal( | ||
[ | ||
{ | ||
'Thing': [{ | ||
'Has': [ | ||
{'Weird': ['Structure']}, | ||
{'Strange': ['Data']} | ||
] | ||
}] | ||
}, | ||
{ | ||
'Things': [ | ||
{'foo': ['bar']} | ||
] | ||
}, | ||
{ | ||
'More': ['Things'] | ||
}, | ||
{ | ||
'Finally': ['Thing1'] | ||
}, | ||
{ | ||
'Finally': ['Thing2'] | ||
}, | ||
{ | ||
'Already': [ | ||
{'An': ['Array']} | ||
] | ||
} | ||
] | ||
); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1083
0
140
0
42264
21