Skip to content

Commit

Permalink
fix!: parse attributes on eBay xml response
Browse files Browse the repository at this point in the history
  • Loading branch information
dantio committed Jan 23, 2021
1 parent c13f84b commit 6970ae6
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 121 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"axios": "^0.21.1",
"axios-rate-limit": "^1.1.2",
"debug": "^2.1.1",
"fast-xml-parser": "^3.13.0",
"fast-xml-parser": "^3.17.6",
"nanoevents": "^2.0.0",
"qs": "^6.8.0"
},
Expand Down
57 changes: 0 additions & 57 deletions src/api/traditional/Parser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import parser from 'fast-xml-parser';
import {dateTimeNodes, numericNodes} from './nodes';

const EXTRANEOUS = [
'@',
Expand All @@ -26,62 +25,6 @@ export default class Parser {
return parser.parse(xml, parseOptions);
}

/**
* Casts text representations to Javascript representations
*
* @param {String} value The value
* @param {String} key The key
* @return {Date|Number} The cast value
*/
public static cast(value: any, key: any) {
if (value === 'true') {
return true;
}

if (value === 'false') {
return false;
}

if (typeof key === 'string') {
if (dateTimeNodes[key.toLowerCase()]) {
return new Date(value);
}

if (!isNaN(value) && numericNodes[key.toLowerCase()]) {
return Number(value);
}
}

return value;
}

/**
* recursively flattens `value` keys in the XML -> JSON conversion
* we can do this because we don't need to worry about XML attributes from eBay
*
* @param {Object} o the object output from the XML parser
* @param {Object} key the key
* @return {Object} the flattened output
*/
public static flatten(o: any, key?: any): any {
if (o && o.value) {
return Parser.cast(o.value, key);
}

if (Array.isArray(o)) {
return o.map(Parser.flatten);
}

if (typeof o !== 'object') {
return Parser.cast(o, key);
}

return Object.keys(o).reduce((deflated: any, fKey) => {
deflated[fKey] = Parser.flatten(o[fKey], fKey);
return deflated;
}, {});
}

/**
* flattens the eBay pagination object to be easier to deal with
*
Expand Down
9 changes: 7 additions & 2 deletions src/api/traditional/XMLRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ const defaultXmlOptions = {
const parser = new j2xParser(defaultXmlOptions);

const defaultParseOptions = {
textNodeName: 'value'
attributeNamePrefix: '',
textNodeName: 'val',
ignoreAttributes: false,
parseAttributeValue: true,
parseNodeValue: true,
ignoreNameSpace: true
};

export type Options = {
Expand Down Expand Up @@ -159,7 +164,7 @@ export default class XMLRequest {

// Unwrap
if (json[this.responseWrapper]) {
json = Parser.flatten(json[this.responseWrapper]);
json = json[this.responseWrapper]
}

this.handleEBayJsonError(json);
Expand Down
133 changes: 76 additions & 57 deletions test/api/traditional/xmlRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,83 +7,102 @@ import XMLRequest, {Config} from '../../../src/api/traditional/XMLRequest';

describe('XMLRequestTest', () => {

const config: Config = {
headers: {
CALL: 'CALL'
},
endpoint: 'endpoint',
xmlns: 'xmlns',
eBayAuthToken: 'eBayAuthToken'
};
const config: Config = {
headers: {
CALL: 'CALL'
},
endpoint: 'endpoint',
xmlns: 'xmlns',
eBayAuthToken: 'eBayAuthToken'
};

const apiResponse = '<CALL>response</CALL>';
const req = {
get: sinon.stub().returns(Promise.resolve()),
delete: sinon.stub().returns(Promise.resolve()),
put: sinon.stub().returns(Promise.resolve()),
post: sinon.stub().returns(Promise.resolve(apiResponse)),
postForm: sinon.stub().returns(Promise.resolve())
};
const apiResponse = '<CALL>response</CALL>';
const req = {
get: sinon.stub().returns(Promise.resolve()),
delete: sinon.stub().returns(Promise.resolve()),
put: sinon.stub().returns(Promise.resolve()),
post: sinon.stub().returns(Promise.resolve(apiResponse)),
postForm: sinon.stub().returns(Promise.resolve())
};

afterEach(() => {
sinon.reset();
});
afterEach(() => {
sinon.reset();
});

it('Return Raw Response XML', () => {
const request = new XMLRequest('CALL', {}, config, req);
return request.fetch({raw: true}).then(result => {
expect(result).to.equal(apiResponse);
});
it('Return Raw Response XML', () => {
const request = new XMLRequest('CALL', {}, config, req);
return request.fetch({raw: true}).then(result => {
expect(result).to.equal(apiResponse);
});
});

it('Calls correct endpoint', () => {
const request = new XMLRequest('CALL', {Param: 'Param'}, config, req);
return request.fetch({raw: true}).then(() => {
expect(req.post.args[0][0]).to.equal('endpoint');
});
it('Calls correct endpoint', () => {
const request = new XMLRequest('CALL', {Param: 'Param'}, config, req);
return request.fetch({raw: true}).then(() => {
expect(req.post.args[0][0]).to.equal('endpoint');
});
});

it('Adds eBayAuthToken', () => {
const request = new XMLRequest('CALL', {Param: 'Param'}, config, req);
return request.fetch({raw: true}).then(() => {
expect(req.post.args[0][1]).to.equal([
'<?xml version="1.0" encoding="utf-8"?>',
'<CALLRequest xmlns="xmlns">',
'<RequesterCredentials><eBayAuthToken>eBayAuthToken</eBayAuthToken></RequesterCredentials>',
'<Param>Param</Param>',
'</CALLRequest>'
].join(''));
});
it('Adds eBayAuthToken', () => {
const request = new XMLRequest('CALL', {Param: 'Param'}, config, req);
return request.fetch({raw: true}).then(() => {
expect(req.post.args[0][1]).to.equal([
'<?xml version="1.0" encoding="utf-8"?>',
'<CALLRequest xmlns="xmlns">',
'<RequesterCredentials><eBayAuthToken>eBayAuthToken</eBayAuthToken></RequesterCredentials>',
'<Param>Param</Param>',
'</CALLRequest>'
].join(''));
});
});

it('Removes Extraneous nodes', () => {
const response = `<?xml version="1.0" encoding="utf-8"?>
it('Removes Extraneous nodes', () => {
const response = `<?xml version="1.0" encoding="utf-8"?>
<CALLResponse xmlns="urn:ebay:apis:eBLBaseComponents">
<Ack>Ack</Timestamp>
<ack>ack</Ack>
<Version>Version</Version>
<Build>Build</Build>
</CALLResponse>`;

req.post = sinon.stub().returns(Promise.resolve(response));
const request = new XMLRequest('CALL', {}, config, req);
return request.fetch().then(result => {
expect(result).to.deep.equal({});
});
req.post = sinon.stub().returns(Promise.resolve(response));
const request = new XMLRequest('CALL', {}, config, req);
return request.fetch().then(result => {
expect(result).to.deep.equal({});
});
});

it('Unwraps Response', () => {
const response = `<?xml version="1.0" encoding="utf-8"?>
<CALLResponse xmlns="urn:ebay:apis:eBLBaseComponents">
<Item>Item</Item>
</CALLResponse>`;

req.post = sinon.stub().returns(Promise.resolve(response));
const request = new XMLRequest('CALL', {}, config, req);
return request.fetch().then(result => {
expect({
Item: 'Item'
}).to.deep.equal(result);
});
});

it('Unwraps Response', () => {
const response = `<?xml version="1.0" encoding="utf-8"?>
it('Parse Attributes', () => {
const response = `<?xml version="1.0" encoding="utf-8"?>
<CALLResponse xmlns="urn:ebay:apis:eBLBaseComponents">
<Item>Item</Timestamp>
<Price currency="EUR">2.99</Price>
</CALLResponse>`;

req.post = sinon.stub().returns(Promise.resolve(response));
const request = new XMLRequest('CALL', {}, config, req);
return request.fetch().then(result => {
expect({
Item: 'Item'
}).to.deep.equal(result);
});
req.post = sinon.stub().returns(Promise.resolve(response));
const request = new XMLRequest('CALL', {}, config, req);
return request.fetch().then(result => {
console.log(result, null, 2)
expect({
Price: {
currency: 'EUR',
val: 2.99
}
}).to.deep.equal(result);
});
});
});

0 comments on commit 6970ae6

Please sign in to comment.