From f3fea1ed941a5aaf64f476ebcb4c41cedd0e2dc0 Mon Sep 17 00:00:00 2001 From: Tom Chinery Date: Thu, 30 Jan 2020 09:12:26 +0000 Subject: [PATCH] feat(api-extension): add support for open api extensions via decorator This commit adds support for Open API Extensions via a @ApiExtension decorator. Usage: ``` @ApiExtension(, ) ``` Example: ``` @ApiExtension('x-foo', { hello: 'world' }) ``` --- e2e/api-spec.json | 3 +++ e2e/src/cats/cats.controller.ts | 2 ++ lib/constants.ts | 3 ++- lib/decorators/api-extension.decorator.ts | 18 ++++++++++++++++++ lib/decorators/index.ts | 1 + lib/swagger-explorer.ts | 4 +++- 6 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 lib/decorators/api-extension.decorator.ts diff --git a/e2e/api-spec.json b/e2e/api-spec.json index ae35f9684..8be0dc8c6 100644 --- a/e2e/api-spec.json +++ b/e2e/api-spec.json @@ -224,6 +224,9 @@ "/api/cats": { "post": { "operationId": "CatsController_create", + "x-foo": { + "test": "bar " + }, "summary": "Create cat", "parameters": [], "requestBody": { diff --git a/e2e/src/cats/cats.controller.ts b/e2e/src/cats/cats.controller.ts index 19f7e08e9..6d1895484 100644 --- a/e2e/src/cats/cats.controller.ts +++ b/e2e/src/cats/cats.controller.ts @@ -5,6 +5,7 @@ import { ApiOperation, ApiResponse, ApiSecurity, + ApiExtension, ApiTags } from '../../../lib'; import { CatsService } from './cats.service'; @@ -28,6 +29,7 @@ export class CatsController { type: () => Cat }) @ApiResponse({ status: 403, description: 'Forbidden.' }) + @ApiExtension('x-foo', { test: 'bar ' }) async create(@Body() createCatDto: CreateCatDto): Promise { return this.catsService.create(createCatDto); } diff --git a/lib/constants.ts b/lib/constants.ts index e85b72304..e928001a3 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -11,5 +11,6 @@ export const DECORATORS = { API_MODEL_PROPERTIES_ARRAY: `${DECORATORS_PREFIX}/apiModelPropertiesArray`, API_SECURITY: `${DECORATORS_PREFIX}/apiSecurity`, API_EXCLUDE_ENDPOINT: `${DECORATORS_PREFIX}/apiExcludeEndpoint`, - API_EXTRA_MODELS: `${DECORATORS_PREFIX}/apiExtraModels` + API_EXTRA_MODELS: `${DECORATORS_PREFIX}/apiExtraModels`, + API_EXTENSION: `${DECORATORS_PREFIX}/apiExtension` }; diff --git a/lib/decorators/api-extension.decorator.ts b/lib/decorators/api-extension.decorator.ts new file mode 100644 index 000000000..9926009e0 --- /dev/null +++ b/lib/decorators/api-extension.decorator.ts @@ -0,0 +1,18 @@ +import { DECORATORS } from '../constants'; +import { createMixedDecorator } from './helpers'; + +export function ApiExtension(extensionKey: string, extensionProperties: any) { + if (!extensionKey.startsWith('x-')) { + throw new Error( + 'Extension key is not prefixed. Please ensure you prefix it with `x-`.' + ); + } + + const extensionObject = { + [extensionKey]: { + ...extensionProperties + } + }; + + return createMixedDecorator(DECORATORS.API_EXTENSION, extensionObject); +} diff --git a/lib/decorators/index.ts b/lib/decorators/index.ts index 67961fce3..e528ec18c 100644 --- a/lib/decorators/index.ts +++ b/lib/decorators/index.ts @@ -20,3 +20,4 @@ export * from './api-query.decorator'; export * from './api-response.decorator'; export * from './api-security.decorator'; export * from './api-use-tags.decorator'; +export * from './api-extension.decorator'; diff --git a/lib/swagger-explorer.ts b/lib/swagger-explorer.ts index 3be438766..aadbd3979 100644 --- a/lib/swagger-explorer.ts +++ b/lib/swagger-explorer.ts @@ -202,10 +202,12 @@ export class SwaggerExplorer { method ) as RequestMethod; const fullPath = globalPath + this.validateRoutePath(routePath); + const apiExtension = Reflect.getMetadata(DECORATORS.API_EXTENSION, method); return { method: RequestMethod[requestMethod].toLowerCase(), path: fullPath === '' ? '/' : fullPath, - operationId: this.getOperationId(instance, method) + operationId: this.getOperationId(instance, method), + ...apiExtension }; }