diff --git a/.gitignore b/.gitignore index cb72281e8..9ae7e08cf 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,4 @@ npm-debug.log dist index.js index.d.ts -/sample \ No newline at end of file +/sample diff --git a/lib/constants.ts b/lib/constants.ts index 6c513dffc..ea7715477 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -12,6 +12,7 @@ export const DECORATORS = { API_MODEL_PROPERTIES_ARRAY: `${DECORATORS_PREFIX}/apiModelPropertiesArray`, API_SECURITY: `${DECORATORS_PREFIX}/apiSecurity`, API_EXCLUDE_ENDPOINT: `${DECORATORS_PREFIX}/apiExcludeEndpoint`, + API_INCLUDE_ENDPOINT: `${DECORATORS_PREFIX}/apiIncludeEndpoint`, API_EXCLUDE_CONTROLLER: `${DECORATORS_PREFIX}/apiExcludeController`, API_EXTRA_MODELS: `${DECORATORS_PREFIX}/apiExtraModels`, API_EXTENSION: `${DECORATORS_PREFIX}/apiExtension`, diff --git a/lib/decorators/api-include-endpoint.decorator.ts b/lib/decorators/api-include-endpoint.decorator.ts new file mode 100644 index 000000000..e82d3cde6 --- /dev/null +++ b/lib/decorators/api-include-endpoint.decorator.ts @@ -0,0 +1,11 @@ +import { DECORATORS } from '../constants'; +import { createMethodDecorator } from './helpers'; + +/** + * @publicApi + */ +export function ApiIncludeEndpoint(disable = true): MethodDecorator { + return createMethodDecorator(DECORATORS.API_INCLUDE_ENDPOINT, { + disable + }); +} diff --git a/lib/decorators/index.ts b/lib/decorators/index.ts index e62f1fe34..15c7630d7 100644 --- a/lib/decorators/index.ts +++ b/lib/decorators/index.ts @@ -5,6 +5,7 @@ export * from './api-consumes.decorator'; export * from './api-cookie.decorator'; export * from './api-default-getter.decorator'; export * from './api-exclude-endpoint.decorator'; +export * from './api-include-endpoint.decorator'; export * from './api-exclude-controller.decorator'; export * from './api-extra-models.decorator'; export * from './api-header.decorator'; diff --git a/lib/explorers/api-include-endpoint.explorer.ts b/lib/explorers/api-include-endpoint.explorer.ts new file mode 100644 index 000000000..3443983da --- /dev/null +++ b/lib/explorers/api-include-endpoint.explorer.ts @@ -0,0 +1,8 @@ +import { Type } from '@nestjs/common'; +import { DECORATORS } from '../constants'; + +export const exploreApiIncludeEndpointMetadata = ( + instance: object, + prototype: Type, + method: object +) => Reflect.getMetadata(DECORATORS.API_INCLUDE_ENDPOINT, method); diff --git a/lib/extra/swagger-shim.ts b/lib/extra/swagger-shim.ts index c32062826..f07150314 100644 --- a/lib/extra/swagger-shim.ts +++ b/lib/extra/swagger-shim.ts @@ -25,6 +25,9 @@ export function ApiCookieAuth() { export function ApiExcludeEndpoint() { return () => {}; } +export function ApiIncludeEndpoint() { + return () => {}; +} export function ApiExcludeController() { return () => {}; } diff --git a/lib/interfaces/swagger-document-options.interface.ts b/lib/interfaces/swagger-document-options.interface.ts index 3bd0db189..b782252fd 100644 --- a/lib/interfaces/swagger-document-options.interface.ts +++ b/lib/interfaces/swagger-document-options.interface.ts @@ -56,4 +56,10 @@ export interface SwaggerDocumentOptions { * @default true */ autoTagControllers?: boolean; + + /** + * If `true`, swagger will only include routes that are decorated with the `@ApiIncludeEndpoint()` decorator + * @default false + */ + onlyIncludeDecoratedEndpoints?: boolean; } diff --git a/lib/swagger-explorer.ts b/lib/swagger-explorer.ts index 81c261a24..8e0de6913 100644 --- a/lib/swagger-explorer.ts +++ b/lib/swagger-explorer.ts @@ -36,6 +36,7 @@ import { DECORATORS } from './constants'; import { exploreApiCallbacksMetadata } from './explorers/api-callbacks.explorer'; import { exploreApiExcludeControllerMetadata } from './explorers/api-exclude-controller.explorer'; import { exploreApiExcludeEndpointMetadata } from './explorers/api-exclude-endpoint.explorer'; +import { exploreApiIncludeEndpointMetadata } from './explorers/api-include-endpoint.explorer'; import { exploreApiExtraModelsMetadata, exploreGlobalApiExtraModelsMetadata @@ -113,6 +114,7 @@ export class SwaggerExplorer { fieldKey: string ) => string; autoTagControllers?: boolean; + onlyIncludeDecoratedEndpoints?: boolean; } ) { const { operationIdFactory, linkNameFactory } = options; @@ -167,6 +169,7 @@ export class SwaggerExplorer { modulePath?: string; globalPrefix?: string; autoTagControllers?: boolean; + onlyIncludeDecoratedEndpoints?: boolean; } ): DenormalizedDoc[] { // eslint-disable-next-line @typescript-eslint/no-this-alias @@ -187,6 +190,14 @@ export class SwaggerExplorer { DenormalizedDoc[] >(instance, prototype, (name) => { const targetCallback = prototype[name]; + const includeEndpoint = exploreApiIncludeEndpointMetadata( + instance, + prototype, + targetCallback + ); + if (options.onlyIncludeDecoratedEndpoints && !includeEndpoint) { + return; + } const excludeEndpoint = exploreApiExcludeEndpointMetadata( instance, prototype, diff --git a/lib/swagger-scanner.ts b/lib/swagger-scanner.ts index 168204c9d..c14241dd8 100644 --- a/lib/swagger-scanner.ts +++ b/lib/swagger-scanner.ts @@ -41,7 +41,8 @@ export class SwaggerScanner { ignoreGlobalPrefix = false, operationIdFactory, linkNameFactory, - autoTagControllers = true + autoTagControllers = true, + onlyIncludeDecoratedEndpoints = false } = options; const untypedApp = app as any; @@ -80,7 +81,8 @@ export class SwaggerScanner { globalPrefix, operationIdFactory, linkNameFactory, - autoTagControllers + autoTagControllers, + onlyIncludeDecoratedEndpoints }) ); }); @@ -92,7 +94,8 @@ export class SwaggerScanner { globalPrefix, operationIdFactory, linkNameFactory, - autoTagControllers + autoTagControllers, + onlyIncludeDecoratedEndpoints }) ); } @@ -122,6 +125,7 @@ export class SwaggerScanner { fieldKey: string ) => string; autoTagControllers?: boolean; + onlyIncludeDecoratedEndpoints?: boolean; } ): ModuleRoute[] { const denormalizedArray = [...controller.values()].map((ctrl) =>