diff --git a/packages/openapi-v3/package.json b/packages/openapi-v3/package.json index 003d90b80267..b64876ad9eb6 100644 --- a/packages/openapi-v3/package.json +++ b/packages/openapi-v3/package.json @@ -11,7 +11,6 @@ "debug": "^4.1.1", "http-status": "^1.4.2", "json-merge-patch": "^0.2.3", - "json-schema-compare": "^0.2.2", "lodash": "^4.17.15", "openapi3-ts": "^1.3.0", "tslib": "^1.11.1" @@ -25,7 +24,6 @@ "@types/debug": "^4.1.5", "@types/http-status": "^1.1.2", "@types/json-merge-patch": "0.0.4", - "@types/json-schema-compare": "^0.2.0", "@types/lodash": "^4.14.149", "@types/node": "^10.17.17" }, diff --git a/packages/openapi-v3/src/enhancers/index.ts b/packages/openapi-v3/src/enhancers/index.ts index b82eb0936f1d..6df3b5cb32b8 100644 --- a/packages/openapi-v3/src/enhancers/index.ts +++ b/packages/openapi-v3/src/enhancers/index.ts @@ -3,7 +3,6 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -export * from './extensions/consolidate.spec.extension'; export * from './keys'; export * from './spec-enhancer.service'; export * from './types'; diff --git a/packages/rest/package-lock.json b/packages/rest/package-lock.json index aac7bc96f3da..2459b770a3eb 100644 --- a/packages/rest/package-lock.json +++ b/packages/rest/package-lock.json @@ -94,6 +94,21 @@ "integrity": "sha512-otRe77JNNWzoVGLKw8TCspKswRoQToys4tuL6XYVBFxjgeM0RUrx7m3jkaTdxILxeGry3zM8mGYkGXMeQ02guA==", "dev": true }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, + "@types/json-schema-compare": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@types/json-schema-compare/-/json-schema-compare-0.2.0.tgz", + "integrity": "sha512-TtCXQjsCQi+fcandEbzDJhqyztpVM9c5mtGuk7Hf8yQsdaBpfjEkOicfydAEWB684wGCzUrV5ttvt9hCyDCoxA==", + "dev": true, + "requires": { + "@types/json-schema": "*" + } + }, "@types/lodash": { "version": "4.14.149", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", @@ -845,6 +860,14 @@ "xmlcreate": "^2.0.3" } }, + "json-schema-compare": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz", + "integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==", + "requires": { + "lodash": "^4.17.4" + } + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", diff --git a/packages/rest/package.json b/packages/rest/package.json index e5899f658d54..8bcdc013d4fc 100644 --- a/packages/rest/package.json +++ b/packages/rest/package.json @@ -40,6 +40,7 @@ "debug": "^4.1.1", "express": "^4.17.1", "http-errors": "^1.7.3", + "json-schema-compare": "^0.2.2", "js-yaml": "^3.13.1", "lodash": "^4.17.15", "on-finished": "^2.3.0", @@ -57,6 +58,7 @@ "@loopback/repository": "^2.0.2", "@loopback/testlab": "^2.0.2", "@types/debug": "^4.1.5", + "@types/json-schema-compare": "^0.2.0", "@types/js-yaml": "^3.12.3", "@types/lodash": "^4.14.149", "@types/multer": "^1.4.2", diff --git a/packages/openapi-v3/src/__tests__/unit/enhancers/consolidate.spec.extension.unit.ts b/packages/rest/src/__tests__/unit/rest.server/consolidate.spec.extension.unit.ts similarity index 88% rename from packages/openapi-v3/src/__tests__/unit/enhancers/consolidate.spec.extension.unit.ts rename to packages/rest/src/__tests__/unit/rest.server/consolidate.spec.extension.unit.ts index fdbb4c82d395..fa277b9cc408 100644 --- a/packages/openapi-v3/src/__tests__/unit/enhancers/consolidate.spec.extension.unit.ts +++ b/packages/rest/src/__tests__/unit/rest.server/consolidate.spec.extension.unit.ts @@ -9,7 +9,7 @@ import { OperationSpecBuilder, } from '@loopback/openapi-spec-builder'; import {expect} from '@loopback/testlab'; -import {ConsolidationEnhancer} from '../../..'; +import {ConsolidationEnhancer} from '../../../spec-enhancers/consolidate.spec-enhancer'; const consolidationEnhancer = new ConsolidationEnhancer(); @@ -251,4 +251,33 @@ describe('consolidateSchemaObjects', () => { expect(consolidationEnhancer.modifySpec(inputSpec)).to.eql(expectedSpec); }); + + it('obeys isDisabled option when set to true', () => { + consolidationEnhancer.isDisabled = true; + const EXPECTED_SPEC = new OpenApiSpecBuilder() + .withOperation( + 'get', + '/', + new OperationSpecBuilder().withResponse(200, { + description: 'Example', + content: { + 'application/json': { + schema: { + title: 'loopback.example', + properties: { + test: { + type: 'string', + }, + }, + }, + }, + }, + }), + ) + .build(); + + expect(consolidationEnhancer.modifySpec(EXPECTED_SPEC)).to.eql( + EXPECTED_SPEC, + ); + }); }); 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 b86bc5fcace9..d41848c6880b 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 @@ -5,12 +5,7 @@ import {Application, createBindingFromClass} from '@loopback/core'; import {anOpenApiSpec, anOperationSpec} from '@loopback/openapi-spec-builder'; -import { - ConsolidationEnhancer, - get, - post, - requestBody, -} from '@loopback/openapi-v3'; +import {get, post, requestBody} from '@loopback/openapi-v3'; import {model, property} from '@loopback/repository'; import { expect, @@ -24,6 +19,7 @@ import { RestServerConfig, } from '../../..'; import {RestTags} from '../../../keys'; +import {ConsolidationEnhancer} from '../../../spec-enhancers/consolidate.spec-enhancer'; import {TestInfoSpecEnhancer} from './fixtures/info.spec.extension'; describe('RestServer.getApiSpec()', () => { @@ -331,10 +327,8 @@ describe('RestServer.getApiSpec()', () => { }); }); - it('registers core oas enhancers', async () => { - const enhancer = await server.OASEnhancer.getEnhancerByName( - 'loopback.consolidate.schemas', - ); + it('registers consolidate enhancer', async () => { + const enhancer = await server.OASEnhancer.getEnhancerByName('consolidate'); expect(enhancer).to.be.instanceOf(ConsolidationEnhancer); }); @@ -392,7 +386,7 @@ describe('RestServer.getApiSpec()', () => { expect(spec.info).to.eql(EXPECTED_SPEC_INFO); }); - context('options', () => { + context('OpenApiSpecOptions', () => { it('disables consolidator if disableOASConsolidator is set to true', async () => { const options: {rest: RestServerConfig} = { rest: {openApiSpec: {disableOASConsolidator: true}}, @@ -403,11 +397,11 @@ describe('RestServer.getApiSpec()', () => { server = await app.getServer(RestServer); await server.start(); - // consolidation enhancer to be unset - const enhancer = await server.OASEnhancer.getEnhancerByName( - 'loopback.consolidate.schemas', - ); - expect(enhancer).to.be.undefined(); + const enhancer = (await server.OASEnhancer.getEnhancerByName( + 'consolidate', + )) as ConsolidationEnhancer; + expect(enhancer.isDisabled).to.be.true(); + await server.stop(); }); }); diff --git a/packages/rest/src/rest.component.ts b/packages/rest/src/rest.component.ts index 3aafc378a3b8..5eb1dd1ac964 100644 --- a/packages/rest/src/rest.component.ts +++ b/packages/rest/src/rest.component.ts @@ -42,6 +42,7 @@ import { RestServerConfig, } from './rest.server'; import {DefaultSequence} from './sequence'; +import {ConsolidationEnhancer} from './spec-enhancers/consolidate.spec-enhancer'; import {InfoSpecEnhancer} from './spec-enhancers/info.spec-enhancer'; export class RestComponent implements Component { @@ -83,6 +84,7 @@ export class RestComponent implements Component { RestBindings.REQUEST_BODY_PARSER_STREAM, ), createBindingFromClass(InfoSpecEnhancer), + createBindingFromClass(ConsolidationEnhancer), ]; servers: { [name: string]: Constructor; diff --git a/packages/rest/src/rest.server.ts b/packages/rest/src/rest.server.ts index 8085d5e651fe..ad6152c0e76a 100644 --- a/packages/rest/src/rest.server.ts +++ b/packages/rest/src/rest.server.ts @@ -19,7 +19,6 @@ import { import {Application, CoreBindings, Server} from '@loopback/core'; import {HttpServer, HttpServerOptions} from '@loopback/http-server'; import { - ConsolidationEnhancer, getControllerSpec, OASEnhancerBindings, OASEnhancerService, @@ -247,16 +246,6 @@ export class RestServer extends Context implements Server, HttpServerLike { }), ); this._OASEnhancer = this.getSync(OASEnhancerBindings.OAS_ENHANCER_SERVICE); - this.registerCoreEnhancers(); - } - - /** - * Register core built in OAS Enhancers - */ - registerCoreEnhancers() { - if (!this.config.openApiSpec.disableOASConsolidator) { - this.add(createBindingFromClass(ConsolidationEnhancer)); - } } protected _setupRequestHandlerIfNeeded() { diff --git a/packages/openapi-v3/src/enhancers/extensions/consolidate.spec.extension.ts b/packages/rest/src/spec-enhancers/consolidate.spec-enhancer.ts similarity index 80% rename from packages/openapi-v3/src/enhancers/extensions/consolidate.spec.extension.ts rename to packages/rest/src/spec-enhancers/consolidate.spec-enhancer.ts index 4652ee5f2b8b..fcc0288c56ca 100644 --- a/packages/openapi-v3/src/enhancers/extensions/consolidate.spec.extension.ts +++ b/packages/rest/src/spec-enhancers/consolidate.spec-enhancer.ts @@ -1,14 +1,29 @@ -import {bind} from '@loopback/core'; -import compare from 'json-schema-compare'; -import _ from 'lodash'; +// Copyright IBM Corp. 2020. All Rights Reserved. +// Node module: @loopback/rest +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { + ApplicationConfig, + bind, + BindingScope, + CoreBindings, + inject, +} from '@loopback/core'; +import { + asSpecEnhancer, ISpecificationExtension, isSchemaObject, + OASEnhancer, OpenApiSpec, ReferenceObject, SchemaObject, -} from '../../types'; -import {asSpecEnhancer, OASEnhancer} from '../types'; +} from '@loopback/openapi-v3'; +import debugFactory from 'debug'; +import compare from 'json-schema-compare'; +import _ from 'lodash'; + +const debug = debugFactory('loopback:openapi:spec-enhancer:consolidate'); /** * This enhancer consolidates schemas into `/components/schemas` and replaces @@ -43,12 +58,21 @@ import {asSpecEnhancer, OASEnhancer} from '../types'; * When comparing schemas to avoid naming collisions, the description field * is ignored. */ -@bind(asSpecEnhancer) +@bind(asSpecEnhancer, {scope: BindingScope.SINGLETON}) export class ConsolidationEnhancer implements OASEnhancer { - name = 'loopback.consolidate.schemas'; + name = 'consolidate'; + isDisabled = false; + + constructor( + @inject(CoreBindings.APPLICATION_CONFIG, {optional: true}) + readonly config?: ApplicationConfig, + ) { + this.isDisabled = + this.config?.rest?.openApiSpec?.disableOASConsolidator || false; + } modifySpec(spec: OpenApiSpec): OpenApiSpec { - return this.consolidateSchemaObjects(spec); + return !this.isDisabled ? this.consolidateSchemaObjects(spec) : spec; } /** @@ -112,8 +136,10 @@ export class ConsolidationEnhancer implements OASEnhancer { refSchema = this.getRefSchema(title, spec); } if (!refSchema) { + debug('Creating new component $ref with schema %j', schema); this.patchRef(title, schema, spec); } + debug('Creating link to $ref %j', title); this.patchPath(title, parentPath, spec); } }