From 770fee382059b1d01134b22ae00caec4f7136ca6 Mon Sep 17 00:00:00 2001 From: Alaeddine Sridi Date: Sat, 8 Mar 2025 17:59:39 +0100 Subject: [PATCH] Add examples option to ResponseSchema --- __tests__/decorators.test.ts | 152 +++++++++++++++++++++++++++++++++++ src/decorators.ts | 19 ++++- 2 files changed, 170 insertions(+), 1 deletion(-) diff --git a/__tests__/decorators.test.ts b/__tests__/decorators.test.ts index 47ef35e..a6049fb 100644 --- a/__tests__/decorators.test.ts +++ b/__tests__/decorators.test.ts @@ -210,6 +210,69 @@ describe('decorators', () => { fourResponseSchemasMixedStatusCodeWithTwoArraySchemas() { return } + + @Get('/responseSchemaExamples') + @ResponseSchema('BadRequestErrorObject', { + statusCode: 400, + examples: { + TENANT_NOT_FOUND: { + value: { + error: { + reason: 'TENANT_NOT_FOUND', + message: 'We could not find the requested tenant id.', + }, + }, + }, + USER_NOT_FOUND: { + value: { + error: { + reason: 'USER_NOT_FOUND', + message: 'We could not find the requested user id.', + }, + }, + }, + }, + }) + responseSchemaExamples() { + return + } + + @Get('/responseSchemaMultipleExamples') + @ResponseSchema('BadRequestErrorObject1', { + statusCode: 400, + examples: { + TENANT_NOT_FOUND: { + value: { + error: { + reason: 'TENANT_NOT_FOUND', + message: 'We could not find the requested tenant id.', + }, + }, + }, + USER_NOT_FOUND: { + value: { + error: { + reason: 'USER_NOT_FOUND', + message: 'We could not find the requested user id.', + }, + }, + }, + }, + }) + @ResponseSchema('BadRequestErrorObject2', { + statusCode: 400, + examples: { + BANK_NOT_FOUND: { + value: { + error: 'Bad request', + message: 'We could not find the requested bank id.', + }, + }, + }, + }) + responseSchemaMultipleExamples() { + return + } } @Controller('/usershtml') @@ -594,6 +657,95 @@ describe('decorators', () => { }, }) }) + + it('applies @ResponseSchema using examples from options object', () => { + const operation = getOperation(routes.responseSchemaExamples, {}) + expect(operation.responses).toEqual({ + '200': { + content: { + 'application/json': {}, + }, + description: 'Successful response', + }, + '400': { + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/BadRequestErrorObject', + }, + examples: { + TENANT_NOT_FOUND: { + value: { + error: { + message: 'We could not find the requested tenant id.', + reason: 'TENANT_NOT_FOUND', + }, + }, + }, + USER_NOT_FOUND: { + value: { + error: { + message: 'We could not find the requested user id.', + reason: 'USER_NOT_FOUND', + }, + }, + }, + }, + }, + }, + description: '', + }, + }) + }) + + it('applies multiple @ResponseSchema with same status code using examples from options objects', () => { + const operation = getOperation(routes.responseSchemaMultipleExamples, {}) + expect(operation.responses).toEqual({ + '200': { + content: { + 'application/json': {}, + }, + description: 'Successful response', + }, + '400': { + content: { + 'application/json': { + schema: { + oneOf: [ + { $ref: '#/components/schemas/BadRequestErrorObject1' }, + { $ref: '#/components/schemas/BadRequestErrorObject2' }, + ], + }, + examples: { + TENANT_NOT_FOUND: { + value: { + error: { + message: 'We could not find the requested tenant id.', + reason: 'TENANT_NOT_FOUND', + }, + }, + }, + USER_NOT_FOUND: { + value: { + error: { + message: 'We could not find the requested user id.', + reason: 'USER_NOT_FOUND', + }, + }, + }, + BANK_NOT_FOUND: { + value: { + error: 'Bad request', + message: 'We could not find the requested bank id.', + }, + }, + }, + }, + }, + description: '', + }, + }) + }) }) describe('@OpenAPI-decorated class', () => { diff --git a/src/decorators.ts b/src/decorators.ts index 94dd7d9..49505f4 100644 --- a/src/decorators.ts +++ b/src/decorators.ts @@ -1,5 +1,10 @@ import _merge from 'lodash.merge' -import { OperationObject, ReferenceObject, ResponsesObject } from 'openapi3-ts' +import { + ExamplesObject, + OperationObject, + ReferenceObject, + ResponsesObject, +} from 'openapi3-ts' import 'reflect-metadata' import { getContentType, getStatusCode, IRoute } from './index' @@ -91,6 +96,7 @@ export function ResponseSchema( description?: string statusCode?: string | number isArray?: boolean + examples?: ExamplesObject } = {} ) { const setResponseSchema = (source: OperationObject, route: IRoute) => { @@ -98,6 +104,7 @@ export function ResponseSchema( const description = options.description || '' const isArray = options.isArray || false const statusCode = (options.statusCode || getStatusCode(route)) + '' + const examples = options.examples || undefined let responseSchemaName = '' if (typeof responseClass === 'function' && responseClass.name) { @@ -110,12 +117,22 @@ export function ResponseSchema( const reference: ReferenceObject = { $ref: `#/components/schemas/${responseSchemaName}`, } + const schema = isArray ? { items: reference, type: 'array' } : reference + + const oldExamples = + source.responses[statusCode]?.content[contentType]?.examples + + const newExamples = oldExamples + ? { ...oldExamples, ...examples } + : examples + const responses: ResponsesObject = { [statusCode]: { content: { [contentType]: { schema, + examples: newExamples, }, }, description,