From e41794f885e380adea6c5ae4ea187a93e13835ff Mon Sep 17 00:00:00 2001 From: Bruno de Queiroz Date: Thu, 19 Sep 2024 15:33:13 +0200 Subject: [PATCH] feat: exposing openApi 3 servers to allow multiple hosts --- packages/cli/src/swagger/specGenerator3.ts | 10 +++------- packages/runtime/src/config.ts | 7 +++++++ tests/unit/swagger/schemaDetails3.spec.ts | 14 +++++++++++++- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/swagger/specGenerator3.ts b/packages/cli/src/swagger/specGenerator3.ts index cc17ee02d..f23c9bce7 100644 --- a/packages/cli/src/swagger/specGenerator3.ts +++ b/packages/cli/src/swagger/specGenerator3.ts @@ -67,7 +67,6 @@ export class SpecGenerator3 extends SpecGenerator { if (this.config.license) { info.license = { name: this.config.license }; } - if (this.config.contact) { info.contact = this.config.contact; } @@ -144,12 +143,9 @@ export class SpecGenerator3 extends SpecGenerator { private buildServers() { const basePath = normalisePath(this.config.basePath as string, '/', undefined, false); const scheme = this.config.schemes ? this.config.schemes[0] : 'https'; - const url = this.config.host ? `${scheme}://${this.config.host}${basePath}` : basePath; - return [ - { - url, - } as Swagger.Server, - ]; + const hosts = this.config.servers ? this.config.servers : this.config.host ? [this.config.host!] : undefined; + const convertHost = (host: string) => ({ url: `${scheme}://${host}${basePath}` }); + return (hosts?.map(convertHost) || [{ url: basePath }]) as Swagger.Server[]; } private buildSchema() { diff --git a/packages/runtime/src/config.ts b/packages/runtime/src/config.ts index eeac0f12c..9e908fea9 100644 --- a/packages/runtime/src/config.ts +++ b/packages/runtime/src/config.ts @@ -73,6 +73,13 @@ export interface SpecConfig { */ host?: string; + /** + * API servers, expressTemplate.g. [production.api.com, staging.api.com] + * + * Only available with the specVersion 3 + */ + servers?: string[]; + /** * Base-name of swagger.json or swagger.yaml. * diff --git a/tests/unit/swagger/schemaDetails3.spec.ts b/tests/unit/swagger/schemaDetails3.spec.ts index f954e6187..21dc43f3b 100644 --- a/tests/unit/swagger/schemaDetails3.spec.ts +++ b/tests/unit/swagger/schemaDetails3.spec.ts @@ -14,6 +14,9 @@ describe('Definition generation for OpenAPI 3.0.0', () => { const metadataPost = new MetadataGenerator('./fixtures/controllers/postController.ts').Generate(); const defaultOptions: ExtendedSpecConfig = getDefaultExtendedOptions(); + const optionsWithServers = Object.assign>({}, defaultOptions, { + servers: ['localhost:3000', 'staging.api.com'], + }); const optionsWithNoAdditional = Object.assign>({}, defaultOptions, { noImplicitAdditionalProperties: 'silently-remove-extras', }); @@ -29,13 +32,17 @@ describe('Definition generation for OpenAPI 3.0.0', () => { /** * If you want to add another spec here go for it. The reason why we use a string literal is so that tests below won't have "magic string" errors when expected test results differ based on the name of the spec you're testing. */ - specName: 'specDefault' | 'specWithNoImplicitExtras' | 'specWithXEnumVarnames' | 'specWithOperationIdTemplate'; + specName: 'specDefault' | 'specWithServers' | 'specWithNoImplicitExtras' | 'specWithXEnumVarnames' | 'specWithOperationIdTemplate'; } const specDefault: SpecAndName = { spec: new SpecGenerator3(metadataGet, defaultOptions).GetSpec(), specName: 'specDefault', }; + const specWithServers: SpecAndName = { + spec: new SpecGenerator3(metadataGet, optionsWithServers).GetSpec(), + specName: 'specWithServers', + }; const specWithNoImplicitExtras: SpecAndName = { spec: new SpecGenerator3(metadataGet, optionsWithNoAdditional).GetSpec(), specName: 'specWithNoImplicitExtras', @@ -85,6 +92,11 @@ describe('Definition generation for OpenAPI 3.0.0', () => { expect(specDefault.spec.servers[0].url).to.match(/localhost:3000/); }); + it('should replace the parent hosts element', () => { + expect(specWithServers.spec.servers[0].url).to.match(/localhost:3000/); + expect(specWithServers.spec.servers[1].url).to.match(/staging\.api\.com/); + }); + it('should replace the parent basePath element', () => { expect(specDefault.spec).to.not.have.property('basePath'); expect(specDefault.spec.servers[0].url).to.match(/\/v1/);