diff --git a/src/definitionGenerator.js b/src/definitionGenerator.js index bfbabaf..7c6429c 100644 --- a/src/definitionGenerator.js +++ b/src/definitionGenerator.js @@ -98,14 +98,14 @@ class DefinitionGenerator { throw err; }); - if (this.serverless.service.custom.documentation.securitySchemes) { + if (this.serverless.service.custom?.documentation?.securitySchemes) { this.createSecuritySchemes( - this.serverless.service.custom.documentation.securitySchemes + this.serverless.service.custom?.documentation?.securitySchemes ); - if (this.serverless.service.custom.documentation.security) { + if (this.serverless.service.custom?.documentation?.security) { this.openAPI.security = - this.serverless.service.custom.documentation.security; + this.serverless.service.custom?.documentation?.security; } } @@ -115,20 +115,20 @@ class DefinitionGenerator { this.cleanupLinks(); - if (this.serverless.service.custom.documentation.servers) { + if (this.serverless.service.custom?.documentation?.servers) { const servers = this.createServers( - this.serverless.service.custom.documentation.servers + this.serverless.service.custom?.documentation?.servers ); Object.assign(this.openAPI, { servers: servers }); } - if (this.serverless.service.custom.documentation.tags) { + if (this.serverless.service.custom?.documentation?.tags) { this.createTags(); } - if (this.serverless.service.custom.documentation.externalDocumentation) { + if (this.serverless.service.custom?.documentation?.externalDocumentation) { const extDoc = this.createExternalDocumentation( - this.serverless.service.custom.documentation.externalDocumentation + this.serverless.service.custom?.documentation?.externalDocumentation ); Object.assign(this.openAPI, { externalDocs: extDoc }); } @@ -136,7 +136,7 @@ class DefinitionGenerator { createInfo() { const service = this.serverless.service; - const documentation = this.serverless.service.custom.documentation; + const documentation = this.serverless.service.custom?.documentation || {}; const info = { title: documentation?.title || service.service, @@ -200,19 +200,20 @@ class DefinitionGenerator { async createPaths() { const paths = {}; const httpFunctions = this.getHTTPFunctions(); - + for (const httpFunction of httpFunctions) { for (const event of httpFunction.event) { - if (event?.http?.documentation || event?.httpApi?.documentation) { + if (event?.http || event?.httpApi) { this.currentEvent = event?.http || event?.httpApi; const documentation = - event?.http?.documentation || event?.httpApi?.documentation; + (event?.http?.documentation || event?.httpApi?.documentation) || {}; + documentation.description = documentation.description || httpFunction.functionInfo.description; this.currentFunctionName = httpFunction.functionInfo.name; this.operationName = httpFunction.operationName; const path = await this.createOperationObject( - event?.http?.method || event?.httpApi?.method, + event?.http || event?.httpApi, documentation ).catch((err) => { throw err; @@ -324,7 +325,7 @@ class DefinitionGenerator { createExternalDocumentation(docs) { return { ...docs }; - // const documentation = this.serverless.service.custom.documentation + // const documentation = this.serverless.service.custom?.documentation // if (documentation.externalDocumentation) { // // Object.assign(this.openAPI, {externalDocs: {...documentation.externalDocumentation}}) // return @@ -333,7 +334,7 @@ class DefinitionGenerator { createTags() { const tags = []; - for (const tag of this.serverless.service.custom.documentation.tags) { + for (const tag of this.serverless.service.custom?.documentation?.tags) { const obj = { name: tag.name, }; @@ -359,7 +360,7 @@ class DefinitionGenerator { Object.assign(this.openAPI, { tags: tags }); } - async createOperationObject(method, documentation) { + async createOperationObject(http, documentation) { let operationId = documentation?.operationId || this.operationName; if (this.operationIds.includes(operationId)) { operationId += `-${uuid()}`; @@ -390,6 +391,29 @@ class DefinitionGenerator { throw err; }); obj.parameters = obj.parameters.concat(paramObject); + } else { + const params = http.path.match(/{(.*?)}/g); + const schema = { + type: "string", + pattern: "^[-a-z0-9_]+$" + }; + const requiredParams = http?.request?.parameters?.paths || {}; + const pathParams = params.map((param) => { + const name = param.replace(/{|}/g, ""); + const required = requiredParams[name] === undefined ? true : requiredParams[name]; + return { + name, + schema, + required, + }; + }); + const paramObject = await this.createParamObject( + "path", + { pathParams } + ).catch((err) => { + throw err; + }); + obj.parameters = obj.parameters.concat(paramObject); } if (documentation.queryParams) { @@ -485,10 +509,31 @@ class DefinitionGenerator { ); } - if (documentation.methodResponses) + if (documentation.methodResponses) { obj.responses = await this.createResponses(documentation).catch((err) => { throw err; }); + } + else { + obj.responses = await this.createResponses({ + methodResponses: [ + { + statusCode: 200, + responseBody: { + description: "Successful response", + }, + }, + { + statusCode: 400, + responseBody: { + description: "Error response", + }, + }, + ], + }).catch((err) => { + throw err; + }); + } if (documentation.servers) { const servers = this.createServers(documentation.servers); @@ -501,7 +546,7 @@ class DefinitionGenerator { Object.assign(obj, extendedSpec); } - return { [method.toLowerCase()]: obj }; + return { [http.method.toLowerCase()]: obj }; } async createResponses(documentation) { @@ -754,7 +799,7 @@ class DefinitionGenerator { name: param.name, in: paramIn, description: param.description || "", - required: paramIn === "path" ? true : param.required || false, + required: param.required === undefined ? (paramIn === "path" ? true : false) : param.required, }; if (Object.keys(param).includes("deprecated")) { @@ -1004,7 +1049,7 @@ class DefinitionGenerator { RegExp(/(get|put|post|delete|options|head|patch|trace)/i).test(name) ) { for (const [statusCode, responseObj] of Object.entries( - value?.responses + value?.responses || {} )) { if (responseObj.links) { for (const [linkName, linkObj] of Object.entries( @@ -1044,7 +1089,7 @@ class DefinitionGenerator { }; const functionNames = this.serverless.service.getAllFunctions(); - + return functionNames .map((functionName) => { return this.serverless.service.getFunction(functionName); diff --git a/src/schemaHandler.js b/src/schemaHandler.js index 4c42a75..71137a7 100644 --- a/src/schemaHandler.js +++ b/src/schemaHandler.js @@ -11,7 +11,7 @@ class SchemaHandler { constructor(serverless, openAPI) { this.apiGatewayModels = serverless.service?.provider?.apiGateway?.request?.schemas || {}; - this.documentation = serverless.service.custom.documentation; + this.documentation = serverless.service.custom?.documentation; this.openAPI = openAPI; this.modelReferences = {}; @@ -62,7 +62,7 @@ class SchemaHandler { for (const model of this.models) { const modelName = model.name; const modelSchema = model.schema; - + const dereferencedSchema = await this.__dereferenceSchema( modelSchema ).catch((err) => { diff --git a/test/serverless-tests/inferred/package.json b/test/serverless-tests/inferred/package.json index 4cbdb75..007004e 100644 --- a/test/serverless-tests/inferred/package.json +++ b/test/serverless-tests/inferred/package.json @@ -4,7 +4,8 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "generate": "npx serverless openapi generate -o openapi.json -f json -a 3.0.3" }, "keywords": [], "author": "", diff --git a/test/serverless-tests/inferred/serverless.yml b/test/serverless-tests/inferred/serverless.yml index f1afe60..30ecc27 100644 --- a/test/serverless-tests/inferred/serverless.yml +++ b/test/serverless-tests/inferred/serverless.yml @@ -12,26 +12,19 @@ functions: handler: handler.update events: - httpApi: - path: 'PUT /update' + method: 'PUT' + path: '/update/{id}' cors: true createUser: handler: handler.create + description: Creates a user events: - - httpApi: - path: 'POST /create/{id}' + - http: + method: 'POST' + path: '/create/{id}' + request: + parameters: + paths: + id: false cors: true - - # createUser: - # handler: handler.create - # events: - # - httpApi: - # path: /create - # method: '*' - # cors: true - - # createUser: - # handler: handler.create - # events: - # - httpApi: '*' - diff --git a/test/serverless-tests/serverless httpApi/package.json b/test/serverless-tests/serverless httpApi/package.json index 8d62f58..6dca2e4 100644 --- a/test/serverless-tests/serverless httpApi/package.json +++ b/test/serverless-tests/serverless httpApi/package.json @@ -4,7 +4,8 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "generate": "npx serverless openapi generate -o openapi.json -f json -a 3.0.3" }, "keywords": [], "author": "",