catalog-backend: async response writing + tests and fixes
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -171,7 +171,7 @@ export async function createRouter(
|
||||
res.setHeader('link', `<${url.pathname}${url.search}>; rel="next"`);
|
||||
}
|
||||
|
||||
writeEntitiesResponse(res, entities);
|
||||
await writeEntitiesResponse(res, entities);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ export async function createRouter(
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
|
||||
writeEntitiesResponse(res, items, entities => ({
|
||||
await writeEntitiesResponse(res, items, entities => ({
|
||||
items: entities,
|
||||
totalItems,
|
||||
pageInfo: {
|
||||
@@ -312,7 +312,9 @@ export async function createRouter(
|
||||
fields: parseEntityTransformParams(req.query, request.fields),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
writeEntitiesResponse(res, items, entities => ({ items: entities }));
|
||||
await writeEntitiesResponse(res, items, entities => ({
|
||||
items: entities,
|
||||
}));
|
||||
})
|
||||
.get('/entity-facets', async (req, res) => {
|
||||
const response = await entitiesCatalog.facets({
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import express from 'express';
|
||||
import { mockErrorHandler } from '@backstage/backend-test-utils';
|
||||
import request from 'supertest';
|
||||
import { writeSingleEntityResponse } from './write';
|
||||
import { writeEntitiesResponse, writeSingleEntityResponse } from './write';
|
||||
|
||||
describe('writeSingleEntityResponse', () => {
|
||||
const app = express();
|
||||
@@ -123,3 +123,159 @@ describe('writeSingleEntityResponse', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('writeEntitiesResponse', () => {
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.get('/echo', (req, res) => {
|
||||
writeEntitiesResponse(res, req.body);
|
||||
});
|
||||
app.get('/wrapped', (req, res) => {
|
||||
writeEntitiesResponse(res, req.body, entities => ({
|
||||
page: 1,
|
||||
items: entities,
|
||||
totalItems: 1337,
|
||||
}));
|
||||
});
|
||||
app.use(mockErrorHandler());
|
||||
|
||||
describe('in object form', () => {
|
||||
it('should return empty list', async () => {
|
||||
const res = await request(app).get('/echo').send({
|
||||
type: 'object',
|
||||
entities: [],
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.type).toBe('application/json');
|
||||
expect(res.header['content-type']).toBe(
|
||||
'application/json; charset=utf-8',
|
||||
);
|
||||
expect(res.body).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return mixed objects', async () => {
|
||||
const res = await request(app)
|
||||
.get('/echo')
|
||||
.send({
|
||||
type: 'object',
|
||||
entities: [{ kind: 'Component' }, null, { kind: 'User' }, null],
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.type).toBe('application/json');
|
||||
expect(res.header['content-type']).toBe(
|
||||
'application/json; charset=utf-8',
|
||||
);
|
||||
expect(res.body).toEqual([
|
||||
{ kind: 'Component' },
|
||||
null,
|
||||
{ kind: 'User' },
|
||||
null,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should wrap response of empty list', async () => {
|
||||
const res = await request(app)
|
||||
.get('/wrapped')
|
||||
.send({ type: 'object', entities: [] });
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.type).toBe('application/json');
|
||||
expect(res.header['content-type']).toBe(
|
||||
'application/json; charset=utf-8',
|
||||
);
|
||||
expect(res.body).toEqual({ page: 1, items: [], totalItems: 1337 });
|
||||
});
|
||||
|
||||
it('should wrap response of mixed list', async () => {
|
||||
const res = await request(app)
|
||||
.get('/wrapped')
|
||||
.send({
|
||||
type: 'object',
|
||||
entities: [{ kind: 'Component' }, null, { kind: 'User' }, null],
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.type).toBe('application/json');
|
||||
expect(res.header['content-type']).toBe(
|
||||
'application/json; charset=utf-8',
|
||||
);
|
||||
expect(res.body).toEqual({
|
||||
page: 1,
|
||||
items: [{ kind: 'Component' }, null, { kind: 'User' }, null],
|
||||
totalItems: 1337,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('in raw form', () => {
|
||||
it('should return empty list', async () => {
|
||||
const res = await request(app).get('/echo').send({
|
||||
type: 'raw',
|
||||
entities: [],
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.type).toBe('application/json');
|
||||
expect(res.header['content-type']).toBe(
|
||||
'application/json; charset=utf-8',
|
||||
);
|
||||
expect(res.body).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return mixed objects', async () => {
|
||||
const res = await request(app)
|
||||
.get('/echo')
|
||||
.send({
|
||||
type: 'raw',
|
||||
entities: ['{"kind":"Component"}', null, '{"kind":"User"}', null],
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.type).toBe('application/json');
|
||||
expect(res.header['content-type']).toBe(
|
||||
'application/json; charset=utf-8',
|
||||
);
|
||||
expect(res.body).toEqual([
|
||||
{ kind: 'Component' },
|
||||
null,
|
||||
{ kind: 'User' },
|
||||
null,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should wrap response of empty list', async () => {
|
||||
const res = await request(app)
|
||||
.get('/wrapped')
|
||||
.send({ type: 'raw', entities: [] });
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.type).toBe('application/json');
|
||||
expect(res.header['content-type']).toBe(
|
||||
'application/json; charset=utf-8',
|
||||
);
|
||||
expect(res.body).toEqual({ page: 1, items: [], totalItems: 1337 });
|
||||
});
|
||||
|
||||
it('should wrap response of mixed list', async () => {
|
||||
const res = await request(app)
|
||||
.get('/wrapped')
|
||||
.send({
|
||||
type: 'raw',
|
||||
entities: ['{"kind":"Component"}', null, '{"kind":"User"}', null],
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.type).toBe('application/json');
|
||||
expect(res.header['content-type']).toBe(
|
||||
'application/json; charset=utf-8',
|
||||
);
|
||||
expect(res.body).toEqual({
|
||||
page: 1,
|
||||
items: [{ kind: 'Component' }, null, { kind: 'User' }, null],
|
||||
totalItems: 1337,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ export function writeSingleEntityResponse(
|
||||
}
|
||||
}
|
||||
|
||||
export function writeEntitiesResponse(
|
||||
export async function writeEntitiesResponse(
|
||||
res: Response,
|
||||
response: EntitiesResponseItems,
|
||||
responseWrapper?: (entities: JsonValue) => JsonValue,
|
||||
@@ -63,7 +63,7 @@ export function writeEntitiesResponse(
|
||||
if (responseWrapper) {
|
||||
const marker = `__MARKER_${Math.random().toString(36).slice(2, 10)}__`;
|
||||
const wrapped = JSON.stringify(responseWrapper(marker));
|
||||
const parts = wrapped.split(marker);
|
||||
const parts = wrapped.split(`"${marker}"`);
|
||||
if (parts.length !== 2) {
|
||||
throw new Error(
|
||||
`Entity items response was incorrectly wrapped into ${parts.length} different parts`,
|
||||
@@ -75,16 +75,18 @@ export function writeEntitiesResponse(
|
||||
|
||||
let first = true;
|
||||
for (const entity of response.entities) {
|
||||
if (first) {
|
||||
res.write('[', 'utf8');
|
||||
first = false;
|
||||
} else {
|
||||
res.write(',', 'utf8');
|
||||
}
|
||||
res.write(entity, 'utf8');
|
||||
}
|
||||
res.end(']');
|
||||
if (trailing) {
|
||||
res.write(trailing, 'utf8');
|
||||
const prefix = first ? '[' : ',';
|
||||
first = false;
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
res.write(prefix + entity, 'utf8', err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
res.end(`${first ? '[' : ''}]${trailing}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user