Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(document-builder): improved types of security scheme methods #2068

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion e2e/fastify.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,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: [] });
Expand Down
4 changes: 2 additions & 2 deletions e2e/validate-schema.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [] })
Expand Down
152 changes: 115 additions & 37 deletions lib/document-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import { isString, isUndefined, negate, pickBy } from 'lodash';
import { buildDocumentBase } from './fixtures/document.base';
import { OpenAPIObject } from './interfaces';
import {
ApiKeySchemeObject,
ExternalDocumentationObject,
HttpSchemeObject,
OAuth2SchemeObject,
SecurityRequirementObject,
SecuritySchemeObject,
ServerVariableObject,
TagObject
} from './interfaces/open-api-spec.interface';

type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;

export class DocumentBuilder {
private readonly logger = new Logger(DocumentBuilder.name);
private readonly document: Omit<OpenAPIObject, 'paths'> = buildDocumentBase();
Expand Down Expand Up @@ -110,74 +115,147 @@ export class DocumentBuilder {
}

public addBearerAuth(
options: SecuritySchemeObject = {
type: 'http'
},
name = 'bearer'
name?: string,
options?: Partial<Optional<HttpSchemeObject, 'type'>>
): this;
/**
* @deprecated Use `addBearerAuth(name, options)` instead
*/
public addBearerAuth(
options?: Partial<Optional<HttpSchemeObject, 'type'>>,
name?: string
): this;
public addBearerAuth(
nameOrOptions:
| string
| Partial<Optional<HttpSchemeObject, 'type'>> = 'bearer',
optionsOrName: string | Partial<Optional<HttpSchemeObject, 'type'>> = {}
): 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 any)
});
return this;
}

public addOAuth2(
options: SecuritySchemeObject = {
type: 'oauth2'
},
name = 'oauth2'
name?: string,
options?: Partial<Optional<OAuth2SchemeObject, 'type'>>
): this;
/**
* @deprecated Use `addOAuth2(name, options)` instead
*/
public addOAuth2(
options?: Partial<Optional<OAuth2SchemeObject, 'type'>>,
name?: string
): this;
public addOAuth2(
nameOrOptions:
| string
| Partial<Optional<OAuth2SchemeObject, 'type'>> = 'oauth2',
optionsOrName: string | Partial<Optional<OAuth2SchemeObject, 'type'>> = {}
): this {
this.addSecurity(name, {
if (typeof nameOrOptions === 'object') {
[nameOrOptions, optionsOrName] = [optionsOrName, nameOrOptions];
}
this.addSecurity(nameOrOptions as string, {
type: 'oauth2',
flows: {},
...options
...(optionsOrName as any)
xTCry marked this conversation as resolved.
Show resolved Hide resolved
});
return this;
}

public addApiKey(
options: SecuritySchemeObject = {
type: 'apiKey'
},
name = 'api_key'
public addBasicAuth(
name?: string,
options?: Partial<Optional<HttpSchemeObject, 'type'>>
): this;
/**
* @deprecated Use `addBasicAuth(name, options)` instead
*/
public addBasicAuth(
options?: Partial<Optional<HttpSchemeObject, 'type'>>,
name?: string
): this;
public addBasicAuth(
nameOrOptions:
| string
| Partial<Optional<HttpSchemeObject, 'type'>> = 'basic',
optionsOrName: string | Partial<Optional<HttpSchemeObject, 'type'>> = {}
): 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 any)
});
return this;
}

public addBasicAuth(
options: SecuritySchemeObject = {
type: 'http'
},
name = 'basic'
public addApiKey(
name?: string,
options?: Partial<Optional<ApiKeySchemeObject, 'type'>>
): this;
/**
* @deprecated Use `addApiKey(name, options)` instead
*/
public addApiKey(
options?: Partial<Optional<ApiKeySchemeObject, 'type'>>,
name?: string
): this;
public addApiKey(
nameOrOptions:
| string
| Partial<Optional<ApiKeySchemeObject, 'type'>> = 'api_key',
optionsOrName: string | Partial<Optional<ApiKeySchemeObject, 'type'>> = {}
): 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,
...(optionsOrName as any)
});
return this;
}

public addCookieAuth(
cookieName?: string,
name?: string,
options?: Partial<Optional<ApiKeySchemeObject, 'type'>>
): this;
/**
* @deprecated Use `addCookieAuth(cookieName, name, options)` instead
*/
public addCookieAuth(
cookieName: string,
options?: Partial<Optional<ApiKeySchemeObject, 'type'>>,
name?: string
): this;
public addCookieAuth(
cookieName = 'connect.sid',
options: SecuritySchemeObject = {
type: 'apiKey'
},
securityName = 'cookie'
nameOrOptions:
| string
| Partial<Optional<ApiKeySchemeObject, 'type'>> = 'cookie',
optionsOrName: string | Partial<Optional<ApiKeySchemeObject, 'type'>> = {}
): 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 any)
});
return this;
}
Expand Down
35 changes: 28 additions & 7 deletions lib/interfaces/open-api-spec.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,16 +250,37 @@ 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;
}

export interface OpenIdConnectSchemeObject {
type: 'openIdConnect';
description?: string;
openIdConnectUrl: string;
}

export interface OAuthFlowsObject {
Expand Down