-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Delete BaseHTTPService and implement new BaseXmlService (affects [eclipse-marketplace f-droid]; also testing on [uptimerobot circleci]) #2037
Changes from 1 commit
06dac0e
b9b6a84
5ec6bfe
ea91b5f
5a0737a
78cede5
7bc9c0b
5de279e
4dd47ec
3dbbce2
9bf7c33
5dc426d
45f9e8b
a0de9f9
372a382
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
'use-strict' | ||
|
||
const { InvalidResponse } = require('../services/errors') | ||
const xml2js = require('xml2js') | ||
|
||
async function asJson({ buffer, res }) { | ||
try { | ||
return JSON.parse(buffer) | ||
} catch (err) { | ||
throw new InvalidResponse({ | ||
prettyMessage: 'unparseable json response', | ||
underlyingError: err, | ||
}) | ||
} | ||
} | ||
|
||
async function asXml({ buffer, res }) { | ||
let xml | ||
xml2js.parseString(buffer, (err, parsedData) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This uses a callback and works fine because the underlying implementation is not async (see Leonidas-from-XIV/node-xml2js#159 (comment)), but there is probably a better way of doing this. Any ideas/guidance? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aw, that is a sadly unpleasant thread. Two thoughts:
const { promisify } = require('util')
const parseXml = promisify(require('xml2js').parseString)
let xml
try {
// `parseXml` is returning an awaitable promise so if you were assigning its
// return value to a variable, you'd need `await`. However on a return from
// an async function, there's no need to specify `await`.
return parseXml(buffer)
} catch (err) {
throw …
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tied solution 1, switching to
Bad point:
I ran |
||
if (err != null) { | ||
throw new InvalidResponse({ | ||
prettyMessage: 'unparseable xml response', | ||
underlyingError: err, | ||
}) | ||
} | ||
xml = parsedData | ||
}) | ||
return xml | ||
} | ||
|
||
module.exports = { | ||
asJson, | ||
asXml, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
'use strict' | ||
|
||
const { expect } = require('chai') | ||
const { asJson, asXml } = require('./response-parsers') | ||
const { InvalidResponse } = require('../services/errors') | ||
|
||
describe('Json parser', function() { | ||
it('parses json', async function() { | ||
const json = await asJson({ buffer: '{"foo": "bar"}' }) | ||
|
||
expect(json).to.deep.equal({ foo: 'bar' }) | ||
}) | ||
|
||
it('thows invalid response if json parsing fails', async function() { | ||
try { | ||
await asJson({ buffer: 'not json' }) | ||
expect.fail('Expected to throw') | ||
} catch (e) { | ||
expect(e).to.be.an.instanceof(InvalidResponse) | ||
expect(e.message).to.equal( | ||
'Invalid Response: Unexpected token o in JSON at position 1' | ||
) | ||
expect(e.prettyMessage).to.equal('unparseable json response') | ||
} | ||
}) | ||
|
||
describe('Xml parser', function() { | ||
it('parses xml', async function() { | ||
const xml = await asXml({ buffer: '<foo>bar</foo>' }) | ||
|
||
expect(xml).to.deep.include({ foo: 'bar' }) | ||
}) | ||
|
||
it('thows invalid response if xml parsing fails', async function() { | ||
try { | ||
await asXml({ buffer: 'not xml' }) | ||
expect.fail('Expected to throw') | ||
} catch (e) { | ||
expect(e).to.be.an.instanceof(InvalidResponse) | ||
expect(e.message).to.equal( | ||
'Invalid Response: Non-whitespace before first tag.\nLine: 0\nColumn: 1\nChar: n' | ||
) | ||
expect(e.prettyMessage).to.equal('unparseable xml response') | ||
} | ||
}) | ||
}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for adding these tests! Well worth keeping and adjusting if this functionality is moved inline, which would widen the bracket a bit. We aren't using |
||
}) |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,59 +2,23 @@ | |
|
||
// See available emoji at http://emoji.muan.co/ | ||
const emojic = require('emojic') | ||
const Joi = require('joi') | ||
const { asJson } = require('../lib/error-helper') | ||
const BaseHTTPService = require('./base-http') | ||
const { InvalidResponse } = require('./errors') | ||
const { asJson } = require('../lib/response-parsers') | ||
const BaseService = require('./base') | ||
const trace = require('./trace') | ||
|
||
class BaseJsonService extends BaseHTTPService { | ||
static _validate(json, schema) { | ||
const { error, value } = Joi.validate(json, schema, { | ||
allowUnknown: true, | ||
stripUnknown: true, | ||
}) | ||
if (error) { | ||
trace.logTrace( | ||
'validate', | ||
emojic.womanShrugging, | ||
'Response did not match schema', | ||
error.message | ||
) | ||
throw new InvalidResponse({ | ||
prettyMessage: 'invalid json response', | ||
underlyingError: error, | ||
}) | ||
} else { | ||
trace.logTrace( | ||
'validate', | ||
emojic.bathtub, | ||
'JSON after validation', | ||
value, | ||
{ deep: true } | ||
) | ||
return value | ||
} | ||
} | ||
|
||
class BaseJsonService extends BaseService { | ||
async _requestJson({ schema, url, options = {}, errorMessages = {} }) { | ||
const logTrace = (...args) => trace.logTrace('fetch', ...args) | ||
if (!schema || !schema.isJoi) { | ||
throw Error('A Joi schema is required') | ||
} | ||
const mergedOptions = { | ||
...{ headers: { Accept: 'application/json' } }, | ||
...options, | ||
} | ||
return this._requestHTTP({ url, mergedOptions, errorMessages }) | ||
.then(asJson) | ||
.then(json => { | ||
logTrace(emojic.dart, 'Response JSON (before validation)', json, { | ||
deep: true, | ||
}) | ||
return json | ||
}) | ||
.then(json => this.constructor._validate(json, schema)) | ||
const jsonData = await this._request({ url, mergedOptions, errorMessages }) | ||
const json = await asJson(jsonData) | ||
logTrace(emojic.dart, 'Response JSON (before validation)', json, { | ||
deep: true, | ||
}) | ||
return this.constructor._validate(json, schema) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, this is much clearer! |
||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
'use strict' | ||
|
||
// See available emoji at http://emoji.muan.co/ | ||
const emojic = require('emojic') | ||
const { asXml } = require('../lib/response-parsers') | ||
const BaseService = require('./base') | ||
const trace = require('./trace') | ||
|
||
class BaseXmlService extends BaseService { | ||
async _requestXml({ schema, url, options = {}, errorMessages = {} }) { | ||
const logTrace = (...args) => trace.logTrace('fetch', ...args) | ||
const mergedOptions = { | ||
...{ headers: { Accept: 'application/xml, text/xml' } }, | ||
...options, | ||
} | ||
const xmlData = await this._request({ url, mergedOptions, errorMessages }) | ||
const xml = await asXml(xmlData) | ||
logTrace(emojic.dart, 'Response XML (before validation)', xml, { | ||
deep: true, | ||
}) | ||
return this.constructor._validate(xml, schema) | ||
} | ||
} | ||
|
||
module.exports = BaseXmlService |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're only using this code in one place. Would it be more direct to move it there, inline?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With
fast-xml-parser
, the parser is now used in two places, so keeping therequire
up there. 😄There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry, I meant the functions themselves, not the
require
s. Instead of having these in a separate module, could they just be inlined into the_requestX
methods in the base services?