diff --git a/README.md b/README.md index 91902f2..96f4418 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ Options: | tags[].externalDocs.url | `custom.documentation.tags.externalDocumentation.url` | | tags[].externalDocs.description | `custom.documentation.tags.externalDocumentation.description` | | tags[].externalDocs.x- | `custom.documentation.tags.externalDocumentation.x-` if extended specifications provided | +| basePath | `custom.documentation.basePath`Specifies the base path to prepend to all Lambda HTTP function paths in the generated OpenAPI specification | | paths | `custom.documentation.paths` OpenAPI paths that are not backed by Lambda functions | | path[path] | functions.functions.events.[http OR httpApi].path | | path[path].servers[].description | functions.functions.servers.description | diff --git a/src/definitionGenerator.js b/src/definitionGenerator.js index 4510900..980fc51 100644 --- a/src/definitionGenerator.js +++ b/src/definitionGenerator.js @@ -281,6 +281,18 @@ class DefinitionGenerator { const paths = this.serverless.service.custom.documentation.paths; if (paths) { + const basePath = this.getBasePath(); + + if (basePath) { + // check is paths's keys are not starting with `basePath` prefix and if set append it + for (const key of Object.keys(paths)) { + if (!key.startsWith(`/${basePath}`)) { + paths[`/${basePath}${key}`] = paths[key]; + delete paths[key]; + } + } + } + const origPaths = this.openAPI.paths || {}; Object.assign(this.openAPI, { paths: { ...origPaths, ...paths } }); @@ -288,19 +300,13 @@ class DefinitionGenerator { } /** - * @description Retrieves the basePath value if the domain manager plugin is used, allowing the server URL to be just the plain domain. The `basePath` will be prepended to each Lambda HTTP path. + * @description Retrieves the basePath value if set, allowing the server URL to be just the plain domain. The `basePath` will be prepended to each Lambda HTTP path. If `basePath` starts with a slash (/) returns the basePath without the leading slash. * @returns {string} */ getBasePath() { - const plugins = this.serverless.service.plugins || []; - - let basePath = ""; - - if (plugins.includes("serverless-domain-manager")) { - basePath = this.serverless.service.custom.basePath || ""; - } - - return basePath; + const basePath = + this.serverless.service.custom.documentation.basePath || ""; + return basePath.replace(/^\//, ""); } createServers(servers) { diff --git a/test/helpers/serverless.js b/test/helpers/serverless.js index fec3545..2417d82 100644 --- a/test/helpers/serverless.js +++ b/test/helpers/serverless.js @@ -1,19 +1,43 @@ -'use strict' +"use strict"; module.exports = { - processedInput: { - options: { - openApiVersion: '3.0.1' - } + processedInput: { + options: { + openApiVersion: "3.0.1", }, - service: { - service: 'myAPI', - custom: { - documentation: { - title: 'My new API', - description: 'This API does things', - version: '0.0.1' - } - } - } -} + }, + service: { + service: "myAPI", + custom: { + documentation: { + title: "My new API", + description: "This API does things", + version: "0.0.1", + basePath: "v1", + paths: { + "/test/path": { + post: { + tags: ["Ttest Api"], + summary: "Test Api", + parameters: [ + { + in: "path", + name: "id", + required: true, + schema: { + type: "string", + }, + }, + ], + responses: { + 204: { + description: "No content", + }, + }, + }, + }, + }, + }, + }, + }, +}; diff --git a/test/unit/definitionGenerator.spec.js b/test/unit/definitionGenerator.spec.js index 8f94c0a..27badd6 100644 --- a/test/unit/definitionGenerator.spec.js +++ b/test/unit/definitionGenerator.spec.js @@ -105,6 +105,44 @@ describe("DefinitionGenerator", () => { expect(expected.version).to.be.equal("3.0.0"); }); + it("should use the basePath when supplied", async function () { + const serverlessWithOpenAPIVersion = structuredClone(mockServerless); + const expected = new DefinitionGenerator( + serverlessWithOpenAPIVersion, + logger + ); + const serverlessWithOpenAPIVersion2 = structuredClone(mockServerless); + delete serverlessWithOpenAPIVersion2.service.custom.documentation + .basePath; + const expectedWithoutBasePath = new DefinitionGenerator( + serverlessWithOpenAPIVersion2, + logger + ); + + expected.mergeExistingPaths(); + expectedWithoutBasePath.mergeExistingPaths(); + + expect(expected.getBasePath()).to.be.equal( + serverlessWithOpenAPIVersion.service.custom.documentation.basePath + ); + expect( + Object.keys(expected.openAPI.paths).every((key) => + key.startsWith(`/${expected.getBasePath()}`) + ) + ).to.be.equal(true); + + expect(expectedWithoutBasePath.getBasePath()).to.be.equal(""); + expect( + Object.keys(expectedWithoutBasePath.openAPI.paths).every((key, idx) => + key.startsWith( + Object.keys( + serverlessWithOpenAPIVersion2.service.custom.documentation.paths + )[idx] + ) + ) + ).to.be.equal(true); + }); + it("should respect the version of openAPI when passed in", function () { const serverlessWithOpenAPIVersion = structuredClone(mockServerless); serverlessWithOpenAPIVersion.processedInput.options.openApiVersion =