diff --git a/e2e/fastify.e2e-spec.ts b/e2e/fastify.e2e-spec.ts index fabb58ab6..30a7a99b7 100644 --- a/e2e/fastify.e2e-spec.ts +++ b/e2e/fastify.e2e-spec.ts @@ -30,7 +30,8 @@ describe('Fastify Swagger', () => { .addOAuth2() .addApiKey() .addApiKey({ type: 'apiKey' }, 'key1') - .addApiKey({ type: 'apiKey' }, 'key2') + .addApiKey({}, 'key2') + .addApiKey('key3', { type: 'apiKey' }) .addCookieAuth() .addSecurityRequirements('bearer') .addSecurityRequirements({ basic: [], cookie: [] }) diff --git a/e2e/validate-schema.e2e-spec.ts b/e2e/validate-schema.e2e-spec.ts index 198f2b06f..eeb312a70 100644 --- a/e2e/validate-schema.e2e-spec.ts +++ b/e2e/validate-schema.e2e-spec.ts @@ -35,8 +35,8 @@ describe('Validate OpenAPI schema', () => { .addBearerAuth() .addOAuth2() .addApiKey() - .addApiKey({ type: 'apiKey' }, 'key1') - .addApiKey({ type: 'apiKey' }, 'key2') + .addApiKey({}, 'key1') + .addApiKey({}, 'key2') .addCookieAuth() .addSecurityRequirements('bearer') .addSecurityRequirements({ basic: [], cookie: [] }) diff --git a/lib/document-builder.ts b/lib/document-builder.ts index 442b12848..88266961f 100644 --- a/lib/document-builder.ts +++ b/lib/document-builder.ts @@ -3,15 +3,22 @@ import { clone, isString, isUndefined, negate, pickBy } from 'lodash'; import { buildDocumentBase } from './fixtures/document.base'; import { OpenAPIObject } from './interfaces'; import { + ApiKeySchemeObject, + HttpSchemeObject, + OAuth2SchemeObject, + SecuritySchemeObject as SSObject, ExternalDocumentationObject, ParameterObject, SecurityRequirementObject, - SecuritySchemeObject, ServerVariableObject, TagObject } from './interfaces/open-api-spec.interface'; import { GlobalParametersStorage } from './storages/global-parameters.storage'; +type Optional = Pick, K> & Omit; + +type OptSObject> = Partial>; + export class DocumentBuilder { private readonly logger = new Logger(DocumentBuilder.name); private readonly document: Omit = buildDocumentBase(); @@ -97,7 +104,7 @@ export class DocumentBuilder { return this; } - public addSecurity(name: string, options: SecuritySchemeObject): this { + public addSecurity(name: string, options: SSObject): this { this.document.components.securitySchemes = { ...(this.document.components.securitySchemes || {}), [name]: options @@ -128,75 +135,138 @@ export class DocumentBuilder { return this; } - public addBearerAuth( - options: SecuritySchemeObject = { - type: 'http' - }, - name = 'bearer' + public addBearerAuth( + name?: string, + options?: OptSObject + ): this; + /** + * @deprecated Use `addBearerAuth(name, options)` instead + */ + public addBearerAuth( + options?: OptSObject, + name?: string + ): this; + public addBearerAuth( + nameOrOptions: string | OptSObject = 'bearer', + optionsOrName: string | OptSObject = {} ): this { - this.addSecurity(name, { + if (typeof nameOrOptions === 'object') { + [nameOrOptions, optionsOrName] = [optionsOrName, nameOrOptions]; + } + this.addSecurity(nameOrOptions as string, { + type: 'http', scheme: 'bearer', bearerFormat: 'JWT', - ...options + ...(optionsOrName as OptSObject) }); return this; } - public addOAuth2( - options: SecuritySchemeObject = { - type: 'oauth2' - }, - name = 'oauth2' + public addOAuth2( + name?: string, + options?: OptSObject + ): this; + /** + * @deprecated Use `addOAuth2(name, options)` instead + */ + public addOAuth2( + options?: OptSObject, + name?: string + ): this; + public addOAuth2( + nameOrOptions: string | OptSObject = 'oauth2', + optionsOrName: string | OptSObject = {} ): this { - this.addSecurity(name, { + if (typeof nameOrOptions === 'object') { + [nameOrOptions, optionsOrName] = [optionsOrName, nameOrOptions]; + } + this.addSecurity(nameOrOptions as string, { type: 'oauth2', flows: {}, - ...options + ...(optionsOrName as OptSObject) }); return this; } - public addApiKey( - options: SecuritySchemeObject = { - type: 'apiKey' - }, - name = 'api_key' + public addBasicAuth( + name?: string, + options?: OptSObject + ): this; + /** + * @deprecated Use `addBasicAuth(name, options)` instead + */ + public addBasicAuth( + options?: OptSObject, + name?: string + ): this; + public addBasicAuth( + nameOrOptions: string | OptSObject = 'basic', + optionsOrName: string | OptSObject = {} ): this { - this.addSecurity(name, { - type: 'apiKey', - in: 'header', - name, - ...options + if (typeof nameOrOptions === 'object') { + [nameOrOptions, optionsOrName] = [optionsOrName, nameOrOptions]; + } + this.addSecurity(nameOrOptions as string, { + type: 'http', + scheme: 'basic', + ...(optionsOrName as OptSObject) }); return this; } - public addBasicAuth( - options: SecuritySchemeObject = { - type: 'http' - }, - name = 'basic' + public addApiKey( + name?: string, + options?: OptSObject + ): this; + /** + * @deprecated Use `addApiKey(name, options)` instead + */ + public addApiKey( + options?: OptSObject, + name?: string + ): this; + public addApiKey( + nameOrOptions: string | OptSObject = 'api_key', + optionsOrName: string | OptSObject = {} ): this { - this.addSecurity(name, { - type: 'http', - scheme: 'basic', - ...options + if (typeof nameOrOptions === 'object') { + [nameOrOptions, optionsOrName] = [optionsOrName, nameOrOptions]; + } + this.addSecurity(nameOrOptions as string, { + type: 'apiKey', + in: 'header', + name: nameOrOptions as string, + ...(optionsOrName as OptSObject) }); return this; } - public addCookieAuth( + public addCookieAuth( + cookieName?: string, + name?: string, + options?: OptSObject + ): this; + /** + * @deprecated Use `addCookieAuth(cookieName, name, options)` instead + */ + public addCookieAuth( + cookieName: string, + options?: OptSObject, + name?: string + ): this; + public addCookieAuth( cookieName = 'connect.sid', - options: SecuritySchemeObject = { - type: 'apiKey' - }, - securityName = 'cookie' + nameOrOptions: string | OptSObject = 'cookie', + optionsOrName: string | OptSObject = {} ): this { - this.addSecurity(securityName, { + if (typeof nameOrOptions === 'object') { + [nameOrOptions, optionsOrName] = [optionsOrName, nameOrOptions]; + } + this.addSecurity(nameOrOptions as string, { type: 'apiKey', in: 'cookie', name: cookieName, - ...options + ...(optionsOrName as OptSObject) }); return this; } diff --git a/lib/interfaces/open-api-spec.interface.ts b/lib/interfaces/open-api-spec.interface.ts index 2cbfe64fd..a248fae85 100644 --- a/lib/interfaces/open-api-spec.interface.ts +++ b/lib/interfaces/open-api-spec.interface.ts @@ -250,19 +250,41 @@ export interface XmlObject { } export type SecuritySchemeType = 'apiKey' | 'http' | 'oauth2' | 'openIdConnect'; +export type ApiKeyLocation = 'query' | 'header' | 'cookie'; -export interface SecuritySchemeObject { - type: SecuritySchemeType; +export type SecuritySchemeObject = + | ApiKeySchemeObject + | HttpSchemeObject + | OAuth2SchemeObject + | OpenIdConnectSchemeObject; + +export interface ApiKeySchemeObject { + type: 'apiKey'; description?: string; - name?: string; - in?: string; - scheme?: string; + name: string; + in: ApiKeyLocation; +} + +export interface HttpSchemeObject { + type: 'http'; + description?: string; + scheme: string; bearerFormat?: string; - flows?: OAuthFlowsObject; - openIdConnectUrl?: string; +} + +export interface OAuth2SchemeObject { + type: 'oauth2'; + description?: string; + flows: OAuthFlowsObject; 'x-tokenName'?: string; } +export interface OpenIdConnectSchemeObject { + type: 'openIdConnect'; + description?: string; + openIdConnectUrl: string; +} + export interface OAuthFlowsObject { implicit?: OAuthFlowObject; password?: OAuthFlowObject;