diff --git a/README.md b/README.md index c05c13b..669be12 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Options: #### Model Details * [Models](#models) * [Notes on Schemas](#notes-on-schemas) +* [Request Schema Validators](#serverless-request-schema-validators) #### Response Headers * [CORS](#cors) * [OWASP Secure Headers](#owasp) @@ -409,6 +410,76 @@ custom: type: string ``` +##### Serverless Request Schema Validators + +As of 0.0.64, you can now make use of [Request Schema Validators](https://www.serverless.com/framework/docs/providers/aws/events/apigateway#request-schema-validators). This allows you to define Request models via the `apiGateway` settings: + +```yml +provider: + ... + apiGateway: + request: + schemas: + post-create-model: + name: PostCreateModel + schema: ${file(api_schema/post_add_schema.json)} + description: "A Model validation for adding posts" +``` + +which are then used like: + +```yml +functions: + create: + handler: posts.create + events: + - http: + path: posts/create + method: post + request: + schemas: + application/json: post-create-model + documentation: + ... +``` + +The generator will match to the model within the `apiGateway` settings model list. If you are using the `apiGateway` to define models, please do not re-use any names that you might define in the [`models`](#models) list. + +You can also skip writing a `requestBody` and `requestModels` if you have defined a `request` property in your event. + +If you're not using `apiGateway`, you can still make use of `request` by writing in the other styles that serverless accepts for Request Schema Validators: + +```yml +functions: + create: + handler: posts.create + events: + - http: + path: posts/create + method: post + request: + schemas: + application/json: + schema: ${file(create_request.json)} + name: PostCreateModel + description: 'Validation model for Creating Posts' + +``` + +or + +```yml +functions: + create: + handler: posts.create + events: + - http: + path: posts/create + method: post + request: + schemas: + application/json: ${file(create_request.json)} +``` #### Functions diff --git a/package-lock.json b/package-lock.json index bc0afd5..444c860 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "serverless-openapi-documenter", - "version": "0.0.63", + "version": "0.0.64", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "serverless-openapi-documenter", - "version": "0.0.63", + "version": "0.0.64", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.1.0", diff --git a/package.json b/package.json index 3473c29..5b94c50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless-openapi-documenter", - "version": "0.0.63", + "version": "0.0.64", "description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config", "main": "index.js", "keywords": [ diff --git a/src/definitionGenerator.js b/src/definitionGenerator.js index 6c96934..cea0e1f 100644 --- a/src/definitionGenerator.js +++ b/src/definitionGenerator.js @@ -341,11 +341,31 @@ class DefinitionGenerator { if (Object.keys(documentation).includes('deprecated')) obj.deprecated = documentation.deprecated - if (documentation.requestBody) - obj.requestBody = await this.createRequestBody(documentation) + if (documentation.requestBody || this.currentEvent?.request?.schemas) { + const requestModel = {} + if (documentation.requestBody) { + Object.assign( + requestModel, + { + description: documentation.requestBody.description, + models: documentation.requestModels + } + ) + } else { + Object.assign( + requestModel, + { + description: '', + models: this.currentEvent?.request?.schemas + } + ) + } + + obj.requestBody = await this.createRequestBody(requestModel) .catch(err => { throw err }) + } if (documentation.methodResponses) obj.responses = await this.createResponses(documentation) @@ -488,13 +508,13 @@ class DefinitionGenerator { return obj } - async createRequestBody(documentation) { + async createRequestBody(requestBodyDetails) { const obj = { - description: documentation.requestBody.description, - required: documentation.requestBody.required || false, + description: requestBodyDetails.description, + required: false } - obj.content = await this.createMediaTypeObject(documentation.requestModels, 'requestBody') + obj.content = await this.createMediaTypeObject(requestBodyDetails.models) .catch(err => { throw err }) @@ -548,6 +568,25 @@ class DefinitionGenerator { Object.assign(mediaTypeObj, { [contentKey]: obj }) } } + + if (Object.keys(mediaTypeObj).length === 0) { + for (const contentKey of Object.keys(models)) { + const obj = {} + const schema = (models[contentKey]?.schema) ? models[contentKey].schema : models[contentKey] + const name = (models[contentKey]?.name) ? models[contentKey].name : uuid() + const schemaRef = await this.schemaHandler.createSchema(name, schema) + .catch(err => { + throw err + }) + + obj.schema = { + $ref: schemaRef + } + + Object.assign(mediaTypeObj, { [contentKey]: obj }) + } + } + return mediaTypeObj } diff --git a/src/schemaHandler.js b/src/schemaHandler.js index 6174ab7..5492eb2 100644 --- a/src/schemaHandler.js +++ b/src/schemaHandler.js @@ -9,6 +9,7 @@ const { v4: uuid } = require('uuid') class SchemaHandler { constructor(serverless, openAPI) { + this.apiGatewayModels = serverless.service?.provider?.apiGateway?.request?.schemas || {} this.documentation = serverless.service.custom.documentation this.openAPI = openAPI @@ -42,7 +43,12 @@ class SchemaHandler { const standardisedModels = this.documentation?.models?.map(standardModel) || [] const standardisedModelsList = this.documentation?.modelsList?.map(standardModel) || [] - this.models = standardisedModels.length ? standardisedModels.concat(standardisedModelsList) : standardisedModelsList + const standardisedGatewayModels = Object.keys(this.apiGatewayModels).flatMap(key => { + const gatewayModel = this.apiGatewayModels[key] + return standardModel(gatewayModel) + }) || [] + + this.models = standardisedModels.concat(standardisedModelsList, standardisedGatewayModels) } async addModelsToOpenAPI() { diff --git a/test/serverless-tests/schemas/serverless.yml b/test/serverless-tests/schemas/serverless.yml index 85513a9..870fcc0 100644 --- a/test/serverless-tests/schemas/serverless.yml +++ b/test/serverless-tests/schemas/serverless.yml @@ -3,6 +3,13 @@ frameworkVersion: ">=3.0.0 < 4.0.0" provider: name: aws runtime: nodejs14.x + apiGateway: + request: + schemas: + post-model: + name: PostModel + description: The POST Model + schema: https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/BizTalkServerApplicationSchema.json plugins: - ../../../index.js @@ -16,7 +23,7 @@ custom: - name: DeleteResponse description: The Delete response contentType: application/json - schema: https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/BizTalkServerApplicationSchema.json + schema: https://raw.githubusercontent.com/JaredCE/serverless-openapi-documenter/main/test/json/complex.json - name: GetResponse description: The Get Response contentType: application/json @@ -138,3 +145,13 @@ functions: description: The response from the delete endpoint responseModels: application/json: GetResponse + + getUserAtOrg: + handler: handler.getUserAtOrg + events: + - http: + path: getUserAtOrg/ + method: get + request: + schemas: + application/json: post-model