diff --git a/packages/rest/src/__tests__/unit/rest.server/fixtures/info.spec.extension.ts b/packages/rest/src/__tests__/unit/rest.server/fixtures/info.spec.extension.ts new file mode 100644 index 000000000000..7c10ba3581fc --- /dev/null +++ b/packages/rest/src/__tests__/unit/rest.server/fixtures/info.spec.extension.ts @@ -0,0 +1,33 @@ +// Copyright IBM Corp. 2019. All Rights Reserved. +// Node module: @loopback/openapi-v3 +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +import {bind} from '@loopback/core'; +import debugModule from 'debug'; +import {inspect} from 'util'; +import { + asSpecEnhancer, + mergeOpenAPISpec, + OASEnhancer, + OpenApiSpec, +} from '../../../..'; + +const debug = debugModule('loopback:openapi:spec-enhancer'); + +/** + * A spec enhancer to add OpenAPI info spec + */ +@bind(asSpecEnhancer) +export class InfoSpecEnhancer implements OASEnhancer { + name = 'info'; + + modifySpec(spec: OpenApiSpec): OpenApiSpec { + const InfoPatchSpec = { + info: {title: 'LoopBack Test Application', version: '1.0.1'}, + }; + const mergedSpec = mergeOpenAPISpec(spec, InfoPatchSpec); + debug(`security spec extension, merged spec: ${inspect(mergedSpec)}`); + return mergedSpec; + } +} diff --git a/packages/rest/src/__tests__/unit/rest.server/rest.server.open-api-spec.unit.ts b/packages/rest/src/__tests__/unit/rest.server/rest.server.open-api-spec.unit.ts index 8bbbd1cd178f..b0855aed25ce 100644 --- a/packages/rest/src/__tests__/unit/rest.server/rest.server.open-api-spec.unit.ts +++ b/packages/rest/src/__tests__/unit/rest.server/rest.server.open-api-spec.unit.ts @@ -3,7 +3,7 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {Application} from '@loopback/core'; +import {Application, createBindingFromClass} from '@loopback/core'; import {anOpenApiSpec, anOperationSpec} from '@loopback/openapi-spec-builder'; import {get, post, requestBody} from '@loopback/openapi-v3'; import {model, property} from '@loopback/repository'; @@ -13,6 +13,7 @@ import { RestComponent, RestServer, } from '../../..'; +import {InfoSpecEnhancer} from './fixtures/info.spec.extension'; describe('RestServer.getApiSpec()', () => { let app: Application; @@ -311,6 +312,16 @@ describe('RestServer.getApiSpec()', () => { }); }); + it('invokes registered oas enhancers', async () => { + const EXPECTED_SPEC_INFO = { + title: 'LoopBack Test Application', + version: '1.0.1', + }; + server.add(createBindingFromClass(InfoSpecEnhancer)); + const spec = await server.getApiSpec(); + expect(spec.info).to.eql(EXPECTED_SPEC_INFO); + }); + async function givenApplication() { app = new Application(); app.component(RestComponent); diff --git a/packages/rest/src/rest.server.ts b/packages/rest/src/rest.server.ts index bb6e2387aedc..86cedb5d74f4 100644 --- a/packages/rest/src/rest.server.ts +++ b/packages/rest/src/rest.server.ts @@ -9,12 +9,15 @@ import { BindingScope, Constructor, Context, + createBindingFromClass, inject, } from '@loopback/context'; import {Application, CoreBindings, Server} from '@loopback/core'; import {HttpServer, HttpServerOptions} from '@loopback/http-server'; import { getControllerSpec, + OASEnhancerService, + OAS_ENHANCER_SERVICE, OpenAPIObject, OpenApiSpec, OperationObject, @@ -130,6 +133,12 @@ export class RestServer extends Context implements Server, HttpServerLike { * @param res - The response. */ + protected _OASEnhancer: OASEnhancerService; + public get OASEnhancer(): OASEnhancerService { + this._setupOASEnhancerIfNeeded(); + return this._OASEnhancer; + } + protected _requestHandler: HttpRequestListener; public get requestHandler(): HttpRequestListener { if (this._requestHandler == null) { @@ -219,6 +228,16 @@ export class RestServer extends Context implements Server, HttpServerLike { this.bind(RestBindings.HANDLER).toDynamicValue(() => this.httpHandler); } + protected _setupOASEnhancerIfNeeded() { + if (this._OASEnhancer != null) return; + this.add( + createBindingFromClass(OASEnhancerService, { + key: OAS_ENHANCER_SERVICE, + }), + ); + this._OASEnhancer = this.getSync(OAS_ENHANCER_SERVICE); + } + protected _setupRequestHandlerIfNeeded() { if (this._expressApp != null) return; this._expressApp = express(); @@ -754,6 +773,10 @@ export class RestServer extends Context implements Server, HttpServerLike { spec = this.updateSpecFromRequest(spec, requestContext); } + // Apply OAS enhancers to the OpenAPI specification + this.OASEnhancer.spec = spec; + spec = await this.OASEnhancer.applyAllEnhancers(); + return spec; }