[core-http-router] Add helpers for 201, 207 and 422 status codes (#186379)

Closes #186336 by adding helper functions for creating 201, 207 and 422
responses.
This commit is contained in:
Milton Hultgren 2024-06-20 17:39:16 +02:00 committed by GitHub
parent 7304484cf4
commit 02bc5cff27
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 89 additions and 9 deletions

View file

@ -6,13 +6,61 @@
* Side Public License, v 1.
*/
import { fileResponseFactory } from './response';
import { IKibanaResponse } from '@kbn/core-http-server';
import { kibanaResponseFactory } from './response';
describe('kibanaResponseFactory', () => {
describe('status codes', () => {
const tests: Array<[string, number, IKibanaResponse]> = [
['ok', 200, kibanaResponseFactory.ok()],
['created', 201, kibanaResponseFactory.created()],
['accepted', 202, kibanaResponseFactory.accepted()],
['noContent', 204, kibanaResponseFactory.noContent()],
['multiStatus', 207, kibanaResponseFactory.multiStatus()],
['redirected', 302, kibanaResponseFactory.redirected({})],
['notModified', 304, kibanaResponseFactory.notModified({})],
['badRequest', 400, kibanaResponseFactory.badRequest()],
['unauthorized', 401, kibanaResponseFactory.unauthorized()],
['forbidden', 403, kibanaResponseFactory.forbidden()],
['notFound', 404, kibanaResponseFactory.notFound()],
['conflict', 409, kibanaResponseFactory.conflict()],
['unprocessableContent', 422, kibanaResponseFactory.unprocessableContent()],
[
'file',
200,
kibanaResponseFactory.file({
filename: 'test.txt',
body: 'content',
}),
],
[
'custom:205',
205,
kibanaResponseFactory.custom({
statusCode: 205,
}),
],
[
'customError:505',
505,
kibanaResponseFactory.customError({
statusCode: 505,
}),
],
];
it.each(tests)(
'.%s produces a response with status code %i',
(_name, expectedStatusCode, response) => {
expect(response.status).toEqual(expectedStatusCode);
}
);
});
describe('fileResponseFactory', () => {
describe('res.file', () => {
it('returns a kibana response with attachment', () => {
const body = Buffer.from('Attachment content');
const result = fileResponseFactory.file({
const result = kibanaResponseFactory.file({
body,
filename: 'myfile.test',
fileContentSize: 30,
@ -31,7 +79,7 @@ describe('fileResponseFactory', () => {
it('converts string body content to buffer in response', () => {
const body = 'I am a string';
const result = fileResponseFactory.file({ body, filename: 'myfile.test' });
const result = kibanaResponseFactory.file({ body, filename: 'myfile.test' });
expect(result.payload?.toString()).toBe(body);
});
@ -39,7 +87,7 @@ describe('fileResponseFactory', () => {
const isMultiByte = (str: string) => [...str].some((c) => (c.codePointAt(0) || 0) > 255);
const multuByteCharacters = '日本語ダッシュボード.pdf';
const result = fileResponseFactory.file({
const result = kibanaResponseFactory.file({
body: 'content',
filename: multuByteCharacters,
});
@ -72,7 +120,7 @@ describe('fileResponseFactory', () => {
};
const filename = 'myfile.test';
const fileContent = 'content';
const result = fileResponseFactory.file({
const result = kibanaResponseFactory.file({
body: fileContent,
filename,
headers: { ...extraHeaders, ...overrideHeaders },
@ -88,15 +136,15 @@ describe('fileResponseFactory', () => {
describe('content-type', () => {
it('default mime type octet-stream', () => {
const result = fileResponseFactory.file({ body: 'content', filename: 'myfile.unknown' });
const result = kibanaResponseFactory.file({ body: 'content', filename: 'myfile.unknown' });
expect(result.options.headers).toHaveProperty('content-type', 'application/octet-stream');
});
it('gets mime type from filename', () => {
const result = fileResponseFactory.file({ body: 'content', filename: 'myfile.mp4' });
const result = kibanaResponseFactory.file({ body: 'content', filename: 'myfile.mp4' });
expect(result.options.headers).toHaveProperty('content-type', 'video/mp4');
});
it('gets accepts contentType override', () => {
const result = fileResponseFactory.file({
const result = kibanaResponseFactory.file({
body: 'content',
filename: 'myfile.mp4',
fileContentType: 'custom',

View file

@ -40,8 +40,11 @@ export class KibanaResponse<T extends HttpResponsePayload | ResponseError = any>
const successResponseFactory: KibanaSuccessResponseFactory = {
ok: (options: HttpResponseOptions = {}) => new KibanaResponse(200, options.body, options),
created: (options: HttpResponseOptions = {}) => new KibanaResponse(201, options.body, options),
accepted: (options: HttpResponseOptions = {}) => new KibanaResponse(202, options.body, options),
noContent: (options: HttpResponseOptions = {}) => new KibanaResponse(204, undefined, options),
multiStatus: (options: HttpResponseOptions = {}) =>
new KibanaResponse(207, options.body, options),
};
const redirectionResponseFactory: KibanaRedirectionResponseFactory = {
@ -63,6 +66,8 @@ const errorResponseFactory: KibanaErrorResponseFactory = {
new KibanaResponse(404, options.body || 'Not Found', options),
conflict: (options: ErrorHttpResponseOptions = {}) =>
new KibanaResponse(409, options.body || 'Conflict', options),
unprocessableContent: (options: ErrorHttpResponseOptions = {}) =>
new KibanaResponse(422, options.body || 'Unprocessable Content', options),
customError: (options: CustomHttpResponseOptions<ResponseError | Buffer | Stream>) => {
if (!options || !options.statusCode) {
throw new Error(

View file

@ -119,8 +119,10 @@ function createKibanaRequestMock<P = any, Q = any, B = any>({
const createResponseFactoryMock = (): jest.Mocked<KibanaResponseFactory> => ({
ok: jest.fn(),
created: jest.fn(),
accepted: jest.fn(),
noContent: jest.fn(),
multiStatus: jest.fn(),
notModified: jest.fn(),
custom: jest.fn(),
redirected: jest.fn(),
@ -129,6 +131,7 @@ const createResponseFactoryMock = (): jest.Mocked<KibanaResponseFactory> => ({
forbidden: jest.fn(),
notFound: jest.fn(),
conflict: jest.fn(),
unprocessableContent: jest.fn(),
customError: jest.fn(),
file: jest.fn(),
});

View file

@ -22,6 +22,7 @@ const createLifecycleResponseFactoryMock = (): jest.Mocked<LifecycleResponseFact
forbidden: jest.fn(),
notFound: jest.fn(),
conflict: jest.fn(),
unprocessableContent: jest.fn(),
customError: jest.fn(),
});

View file

@ -31,6 +31,15 @@ export interface KibanaSuccessResponseFactory {
options?: HttpResponseOptions<T>
): IKibanaResponse<T>;
/**
* The request has succeeded and has led to the creation of a resource.
* Status code: `201`.
* @param options - {@link HttpResponseOptions} configures HTTP response body & headers.
*/
created<T extends HttpResponsePayload | ResponseError = any>(
options?: HttpResponseOptions<T>
): IKibanaResponse<T>;
/**
* The request has been accepted for processing.
* Status code: `202`.
@ -46,6 +55,13 @@ export interface KibanaSuccessResponseFactory {
* @param options - {@link HttpResponseOptions} configures HTTP response body & headers.
*/
noContent(options?: HttpResponseOptions): IKibanaResponse;
/**
* The server indicates that there might be a mixture of responses (some tasks succeeded, some failed).
* Status code: `207`.
* @param options - {@link HttpResponseOptions} configures HTTP response body & headers.
*/
multiStatus(options?: HttpResponseOptions): IKibanaResponse;
}
/**
@ -112,6 +128,13 @@ export interface KibanaErrorResponseFactory {
*/
conflict(options?: ErrorHttpResponseOptions): IKibanaResponse;
/**
* The server understands the content type of the request entity, and the syntax of the request entity is correct, but it was unable to process the contained instructions.
* Status code: `422`.
* @param options - {@link HttpResponseOptions} configures HTTP response headers, error message and other error details to pass to the client
*/
unprocessableContent(options?: ErrorHttpResponseOptions): IKibanaResponse;
/**
* Creates an error response with defined status code and payload.
* @param options - {@link CustomHttpResponseOptions} configures HTTP response headers, error message and other error details to pass to the client