From c2389a34a71968bb6bb8355439b09d9af841ed61 Mon Sep 17 00:00:00 2001 From: Tommy Gunnarsson Date: Tue, 20 Aug 2019 14:06:46 +0200 Subject: [PATCH] fix: add security as a component --- lib/bagger.ts | 8 ++- lib/component.ts | 64 ++++++++++++++++++- lib/configuration.ts | 48 ++++++++------ test/__snapshots__/component.test.ts.snap | 1 + test/__snapshots__/configuration.test.ts.snap | 3 + test/configuration.test.ts | 15 +++++ 6 files changed, 115 insertions(+), 24 deletions(-) diff --git a/lib/bagger.ts b/lib/bagger.ts index 65e53cf..48695c5 100644 --- a/lib/bagger.ts +++ b/lib/bagger.ts @@ -2,9 +2,9 @@ import { BaggerResponse } from './response'; import { BaggerRequest, Method } from './request'; import { BaggerRequestBody } from './request_body'; import { BaggerConfiguration, BaggerConfigurationInternal } from './configuration'; -import { OpenAPIObject } from 'openapi3-ts'; +import { OpenAPIObject, SecuritySchemeType } from 'openapi3-ts'; import { BaggerParameter, ParameterType } from './parameters'; -import { BaggerComponentAdder, BaggerComponentGetter } from './component'; +import { BaggerComponentAdder, BaggerComponentGetter, BaggerSecurityComponent } from './component'; import { schemaStorage, SchemaDefinition } from './schema_storage'; const internalConfiguration = new BaggerConfigurationInternal(); @@ -66,6 +66,10 @@ export function addRequest(path: string, method: Method): BaggerRequest { return request; } +export function securityComponent(name: string, type: SecuritySchemeType): BaggerSecurityComponent { + return new BaggerSecurityComponent(name, type); +} + /** * Add a reusable component that can be referenced to. */ diff --git a/lib/component.ts b/lib/component.ts index 21cadb9..a566610 100644 --- a/lib/component.ts +++ b/lib/component.ts @@ -1,5 +1,5 @@ import { Schema } from '@hapi/joi'; -import { SchemaObject, ReferenceObject } from 'openapi3-ts'; +import { SchemaObject, ReferenceObject, SecuritySchemeType, SecuritySchemeObject } from 'openapi3-ts'; import { createSwaggerDefinition } from './utils/create_swagger_definition'; import { BaggerConfigurationInternal } from './configuration'; @@ -9,11 +9,68 @@ export interface SchemaComponentObject { [schema: string]: SchemaObject | ReferenceObject; } +export interface SecuritySchemeComponentObject { + [name: string]: SecuritySchemeObject; +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any function isReference(value: any): value is ReferenceObject { return value.$ref && typeof value.$ref === 'string'; } +export class BaggerSecurityComponent { + private key: string; + private _name?: string; + private type: SecuritySchemeType; + private _scheme?: string; + private _in?: string; + private _openIdConnectUrl?: string; + private _description?: string; + + public constructor(key: string, type: SecuritySchemeType) { + this.key = key; + this.type = type; + } + + public scheme(scheme: string): BaggerSecurityComponent { + this._scheme = scheme; + return this; + } + + public in(location: string): BaggerSecurityComponent { + this._in = location; + return this; + } + + public openIdConnectUrl(openIdConnectUrl: string): BaggerSecurityComponent { + this._openIdConnectUrl = openIdConnectUrl; + return this; + } + + public description(description: string): BaggerSecurityComponent { + this._description = description; + return this; + } + + public name(name: string): BaggerSecurityComponent { + this._name = name; + return this; + } + + public compile(): { [key: string]: SecuritySchemeObject } { + return { + [this.key]: { + type: this.type, + description: this._description, + in: this._in, + scheme: this._scheme, + openIdConnectUrl: this._openIdConnectUrl, + name: this._name + } + }; + } +} + export class BaggerSchemaComponent { private name: string; private schema: Schema | ReferenceObject; @@ -54,6 +111,11 @@ export class BaggerComponentAdder { this.internalConfiguration = internalConfiguration; } + public security(component: BaggerSecurityComponent): BaggerSecurityComponent { + this.internalConfiguration.addSecurityComponent(component); + return component; + } + /** * Add a reusable data model (schema). * @param name A unique id that is used to referance the component. diff --git a/lib/configuration.ts b/lib/configuration.ts index 2a2b7a1..70230a9 100644 --- a/lib/configuration.ts +++ b/lib/configuration.ts @@ -1,7 +1,6 @@ import { BaggerRequest } from './request'; import { ExternalDocumentationObject, - SecurityRequirementObject, ServerObject, InfoObject, OpenAPIObject, @@ -10,7 +9,12 @@ import { } from 'openapi3-ts'; import { cleanObject } from './utils/clean_object'; import { validateSchema } from './utils/validate_schema'; -import { BaggerSchemaComponent, SchemaComponentObject } from './component'; +import { + BaggerSchemaComponent, + SchemaComponentObject, + BaggerSecurityComponent, + SecuritySchemeComponentObject +} from './component'; import { Schema } from '@hapi/joi'; class BaggerMultipleComponentsWithSameNameFoundError extends Error {} @@ -19,7 +23,6 @@ class BaggerComponentNotFoundError extends Error {} interface SwaggerConfiguration { info: InfoObject; servers?: ServerObject[]; - security?: SecurityRequirementObject[]; externalDocs?: ExternalDocumentationObject; } @@ -48,15 +51,6 @@ export class BaggerConfiguration { return this; } - /** - * Add an authentication component to the OpenAPI schema. - * @param security The authentication object. - */ - public addSecurity(security: SecurityRequirementObject): BaggerConfiguration { - this.internalConfiguration.addSecurity(security); - return this; - } - /** * Define external documentation for the OpenAPI schema. * @param externalDocs The external documentation object. Containing an URL. @@ -69,12 +63,14 @@ export class BaggerConfiguration { interface BaggerComponents { schemas: BaggerSchemaComponent[]; + securitySchemes: BaggerSecurityComponent[]; } export class BaggerConfigurationInternal { private requests: BaggerRequest[] = []; private components: BaggerComponents = { - schemas: [] + schemas: [], + securitySchemes: [] }; private configuration: SwaggerConfiguration = { info: { @@ -87,6 +83,10 @@ export class BaggerConfigurationInternal { this.requests.push(request); } + public addSecurityComponent(component: BaggerSecurityComponent): void { + this.components.securitySchemes.push(component); + } + public addSchemaComponent(component: BaggerSchemaComponent): void { this.components.schemas.push(component); } @@ -113,13 +113,6 @@ export class BaggerConfigurationInternal { this.configuration.servers.push(server); } - public addSecurity(security: SecurityRequirementObject): void { - if (!this.configuration.security) { - this.configuration.security = []; - } - this.configuration.security.push(security); - } - public setExternalDocs(externalDocs: ExternalDocumentationObject): void { this.configuration.externalDocs = externalDocs; } @@ -150,10 +143,23 @@ export class BaggerConfigurationInternal { }, {}); } + public compileSecurityComponents(): SecuritySchemeComponentObject { + const unmergedComponents = this.components.securitySchemes.map(component => component.compile()); + return unmergedComponents.reduce((securitySchemes: SecuritySchemeComponentObject, component) => { + securitySchemes = { + ...securitySchemes, + ...component + }; + return securitySchemes; + }, {}); + } + private compileComponents(): ComponentsObject { const schemas = this.compileSchemaComponents(); + const securitySchemes = this.compileSecurityComponents(); return { - schemas + schemas, + securitySchemes }; } diff --git a/test/__snapshots__/component.test.ts.snap b/test/__snapshots__/component.test.ts.snap index c60145c..d67767c 100644 --- a/test/__snapshots__/component.test.ts.snap +++ b/test/__snapshots__/component.test.ts.snap @@ -20,6 +20,7 @@ Object { "type": "object", }, }, + "securitySchemes": Object {}, }, "info": Object { "title": "", diff --git a/test/__snapshots__/configuration.test.ts.snap b/test/__snapshots__/configuration.test.ts.snap index 3a4c7e8..c816cb7 100644 --- a/test/__snapshots__/configuration.test.ts.snap +++ b/test/__snapshots__/configuration.test.ts.snap @@ -4,6 +4,7 @@ exports[`Bagger compiler A request can be included 1`] = ` Object { "components": Object { "schemas": Object {}, + "securitySchemes": Object {}, }, "info": Object { "description": "Provides resources related to building swagger definitions", @@ -33,6 +34,7 @@ exports[`Bagger compiler Add a request and get the schema 1`] = ` Object { "components": Object { "schemas": Object {}, + "securitySchemes": Object {}, }, "info": Object { "description": "Provides resources related to building swagger definitions", @@ -94,6 +96,7 @@ exports[`Bagger compiler An empty definition should compile 1`] = ` Object { "components": Object { "schemas": Object {}, + "securitySchemes": Object {}, }, "info": Object { "description": "Provides resources related to building swagger definitions", diff --git a/test/configuration.test.ts b/test/configuration.test.ts index c576a80..f08e3eb 100644 --- a/test/configuration.test.ts +++ b/test/configuration.test.ts @@ -39,4 +39,19 @@ describe('Bagger compiler', () => { expect(bagger.getRequestSchema('/bags', 'post')).toEqual({ body: schema }); expect(bagger.compile()).toMatchSnapshot(); }); + + test('Add security scheme and compile', () => { + bagger.configure().info(defaultInfo); + bagger.addComponent().security( + bagger + .securityComponent('ApiKeyAuth', 'apiKey') + .in('header') + .name('Authorization') + ); + bagger + .addRequest('/bags', 'get') + .addTag('bags') + .addResponse(bagger.response(200).description('Got bags!')); + //expect(bagger.compile()).toMatchSnapshot(); + }); });