From 6310278a993800ce66b8a9eea33c94be8a0096fa Mon Sep 17 00:00:00 2001 From: slowquery Date: Fri, 16 Jul 2021 16:16:46 +0900 Subject: [PATCH] feat: same status code multiple response example support --- lib/decorators/api-response.decorator.ts | 6 ++++ lib/services/mimetype-content-wrapper.ts | 7 +++- lib/services/response-object-mapper.ts | 10 ++++-- test/explorer/swagger-explorer.spec.ts | 44 ++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/lib/decorators/api-response.decorator.ts b/lib/decorators/api-response.decorator.ts index 061bfa39d..05cc225a5 100644 --- a/lib/decorators/api-response.decorator.ts +++ b/lib/decorators/api-response.decorator.ts @@ -9,12 +9,18 @@ import { } from '../interfaces/open-api-spec.interface'; import { getTypeIsArrayTuple } from './helpers'; +export interface ApiResponseExmaples { + summary: string; + value: unknown; +} + export interface ApiResponseMetadata extends Omit { status?: number | 'default'; type?: Type | Function | [Function] | string; isArray?: boolean; description?: string; + examples?: { [key: string]: ApiResponseExmaples }; } export interface ApiResponseSchemaHost diff --git a/lib/services/mimetype-content-wrapper.ts b/lib/services/mimetype-content-wrapper.ts index 545d0444c..ce3ff1ff1 100644 --- a/lib/services/mimetype-content-wrapper.ts +++ b/lib/services/mimetype-content-wrapper.ts @@ -6,7 +6,12 @@ export class MimetypeContentWrapper { obj: Record ): Record<'content', ContentObject> { const content = mimetype.reduce( - (acc, item) => ({ ...acc, [item]: obj }), + (acc, item) => { + if (obj.examples === undefined) { + delete obj.examples; + } + return { ...acc, [item]: obj }; + }, {} ); return { content }; diff --git a/lib/services/response-object-mapper.ts b/lib/services/response-object-mapper.ts index 48889da02..9453f8ed0 100644 --- a/lib/services/response-object-mapper.ts +++ b/lib/services/response-object-mapper.ts @@ -11,6 +11,8 @@ export class ResponseObjectMapper { name: string, produces: string[] ) { + const examples = response.examples; + delete response.examples; return { ...response, ...this.mimetypeContentWrapper.wrap(produces, { @@ -19,18 +21,22 @@ export class ResponseObjectMapper { items: { $ref: getSchemaPath(name) } - } + }, + examples }) }; } toRefObject(response: Record, name: string, produces: string[]) { + const examples = response.examples; + delete response.examples; return { ...response, ...this.mimetypeContentWrapper.wrap(produces, { schema: { $ref: getSchemaPath(name) - } + }, + examples }) }; } diff --git a/test/explorer/swagger-explorer.spec.ts b/test/explorer/swagger-explorer.spec.ts index 6313a4b8f..c57f9ce37 100644 --- a/test/explorer/swagger-explorer.spec.ts +++ b/test/explorer/swagger-explorer.spec.ts @@ -79,6 +79,14 @@ describe('SwaggerExplorer', () => { enumArr: LettersEnum; } + class ErrorEntitiesDto { + @ApiProperty() + isError: boolean; + + @ApiProperty() + reason: string; + } + @Controller('') class FooController { @Post('foos') @@ -87,6 +95,26 @@ describe('SwaggerExplorer', () => { type: Foo, description: 'Newly created Foo object' }) + @ApiBadRequestResponse({ + type: Foo, + description: 'Invalid parameter error', + examples: { + ParameterInvalidName: { + summary: 'failure create foo object (invalid name)', + value: { + isError: true, + reason: 'Foo parameter name is invalid' + } + }, + ParameterInvalidEmail: { + summary: 'failure create foo object (invalid email)', + value: { + isError: true, + reason: 'Foo parameter email is invalid' + } + } + } + }) create( @Body() createFoo: CreateFoo, @Query() listEntities: ListEntitiesDto @@ -105,6 +133,22 @@ describe('SwaggerExplorer', () => { } } + it('sees two examples for error responses by same response code', () => { + const explorer = new SwaggerExplorer(schemaObjectFactory); + const routes = explorer.exploreController( + { + instance: new FooController(), + metatype: FooController + } as InstanceWrapper, + new ApplicationConfig(), + 'modulePath', + 'globalPrefix' + ); + + expect((routes[0].responses['400'] as ResponseObject).content['application/json'].examples.ParameterInvalidName).toBeDefined(); + expect((routes[0].responses['400'] as ResponseObject).content['application/json'].examples.ParameterInvalidEmail).toBeDefined(); + }); + it('sees two controller operations and their responses', () => { const explorer = new SwaggerExplorer(schemaObjectFactory); const routes = explorer.exploreController(