fix(proxy/api/ndJsonStream): send header for empty iterables

Introduced by ed987e161
This commit is contained in:
Julien Fontanet 2021-10-29 17:04:36 +02:00
parent 4dc4b635f2
commit 84fdd3fe4b
2 changed files with 38 additions and 31 deletions

View File

@ -14,25 +14,30 @@ import { createLogger } from '@xen-orchestra/log'
const { debug, warn } = createLogger('xo:proxy:api') const { debug, warn } = createLogger('xo:proxy:api')
const ndJsonStream = asyncIteratorToStream(async function* (responseId, iterable) { const ndJsonStream = asyncIteratorToStream(async function*(responseId, iterable) {
let headerSent = false
try { try {
for await (const data of iterable) { let cursor, iterator
if (!headerSent) { try {
yield format.response(responseId, { $responseType: 'ndjson' }) + '\n' const getIterator = iterable[Symbol.iterator] ?? iterable[Symbol.asyncIterator]
headerSent = true iterator = getIterator.call(iterable)
}
cursor = await iterator.next()
yield format.response(responseId, { $responseType: 'ndjson' }) + '\n'
} catch (error) {
yield format.error(responseId, error)
throw error
}
while (!cursor.done) {
try { try {
yield JSON.stringify(data) + '\n' yield JSON.stringify(cursor.value) + '\n'
} catch (error) { } catch (error) {
warn('ndJsonStream, item error', { error }) warn('ndJsonStream, item error', { error })
} }
cursor = await iterator.next()
} }
} catch (error) { } catch (error) {
warn('ndJsonStream, fatal error', { error }) warn('ndJsonStream, fatal error', { error })
if (!headerSent) {
yield format.error(responseId, error)
}
} }
}) })
@ -47,7 +52,7 @@ export default class Api {
ctx.req.setTimeout(0) ctx.req.setTimeout(0)
const profile = await app.authentication.findProfile({ const profile = await app.authentication.findProfile({
authenticationToken: ctx.cookies.get('authenticationToken'), authenticationToken: ctx.cookies.get('authenticationToken')
}) })
if (profile === undefined) { if (profile === undefined) {
ctx.status = 401 ctx.status = 401
@ -118,7 +123,7 @@ export default class Api {
this.addMethods({ this.addMethods({
system: { system: {
getMethodsInfo: [ getMethodsInfo: [
function* () { function*() {
const methods = this._methods const methods = this._methods
for (const name in methods) { for (const name in methods) {
const { description, params = {} } = methods[name] const { description, params = {} } = methods[name]
@ -126,25 +131,25 @@ export default class Api {
} }
}.bind(this), }.bind(this),
{ {
description: 'returns the signatures of all available API methods', description: 'returns the signatures of all available API methods'
}, }
], ],
getServerVersion: [ getServerVersion: [
() => appVersion, () => appVersion,
{ {
description: 'returns the version of xo-server', description: 'returns the version of xo-server'
}, }
], ],
listMethods: [ listMethods: [
function* () { function*() {
const methods = this._methods const methods = this._methods
for (const name in methods) { for (const name in methods) {
yield name yield name
} }
}.bind(this), }.bind(this),
{ {
description: 'returns the name of all available API methods', description: 'returns the name of all available API methods'
}, }
], ],
methodSignature: [ methodSignature: [
({ method: name }) => { ({ method: name }) => {
@ -159,14 +164,14 @@ export default class Api {
{ {
description: 'returns the signature of an API method', description: 'returns the signature of an API method',
params: { params: {
method: { type: 'string' }, method: { type: 'string' }
}, }
}, }
], ]
}, },
test: { test: {
range: [ range: [
function* ({ start = 0, stop, step }) { function*({ start = 0, stop, step }) {
if (step === undefined) { if (step === undefined) {
step = start > stop ? -1 : 1 step = start > stop ? -1 : 1
} }
@ -184,11 +189,11 @@ export default class Api {
params: { params: {
start: { optional: true, type: 'number' }, start: { optional: true, type: 'number' },
step: { optional: true, type: 'number' }, step: { optional: true, type: 'number' },
stop: { type: 'number' }, stop: { type: 'number' }
}, }
}, }
], ]
}, }
}) })
} }
@ -215,7 +220,7 @@ export default class Api {
return required return required
}), }),
type: 'object', type: 'object'
}) })
const m = params => { const m = params => {

View File

@ -27,3 +27,5 @@
> - major: if the change breaks compatibility > - major: if the change breaks compatibility
> >
> In case of conflict, the highest (lowest in previous list) `$version` wins. > In case of conflict, the highest (lowest in previous list) `$version` wins.
- @xen-orchestra/proxy patch