diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index faa0611..ade5426 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,7 +5,8 @@ name: Publish on: push: - branches: [ main ] + branches: + - main jobs: publish: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4bc37d0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +name: Create GitHub Release + +on: + push: + tags: + - '*' + +jobs: + create-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - run: npm run build + - run: npm test + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/package-lock.json b/package-lock.json index 3d1e8d6..a74e00e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "serverless-openapi-typescript", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "serverless-openapi-typescript", - "version": "2.0.0", + "version": "2.1.0", "license": "MIT", "dependencies": { "deepdash": "^5.3.9", "js-yaml": "^4.1.0", "lodash": "^4.17.21", - "ts-json-schema-generator": "^1.1.2" + "ts-json-schema-generator": "^1.3.0" }, "devDependencies": { "@types/jest": "^27.0.1", @@ -2436,9 +2436,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==" }, "node_modules/@types/keyv": { "version": "3.1.4", @@ -3933,11 +3933,11 @@ } }, "node_modules/commander": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", - "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", "engines": { - "node": "^12.20.0 || >=14" + "node": ">=16" } }, "node_modules/component-bind": { @@ -8561,9 +8561,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" }, @@ -11338,9 +11338,9 @@ "dev": true }, "node_modules/safe-stable-stringify": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.0.tgz", - "integrity": "sha512-eehKHKpab6E741ud7ZIMcXhKcP6TSIezPkNZhy5U8xC6+VvrRdUA2tMgxGxaGl4cz7c2Ew5+mg5+wNB16KQqrA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", "engines": { "node": ">=10" } @@ -12996,17 +12996,17 @@ } }, "node_modules/ts-json-schema-generator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-1.1.2.tgz", - "integrity": "sha512-XMnxvndJFJEYv3NBmW7Po5bGajKdK2qH8Q078eDy60srK9+nEvbT9nLCRKd2IV/RQ7a+oc5FNylvZWveqh7jeQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-1.3.0.tgz", + "integrity": "sha512-Y2smEgpxtWat8ICaLbUENXZ/o/SqvVy85X48V/7qOarOTu6XgVs+lr6k0OPFljVhZX5gEMrGPT3q7Ql7JKnexw==", "dependencies": { - "@types/json-schema": "^7.0.11", - "commander": "^9.4.0", + "@types/json-schema": "^7.0.12", + "commander": "^11.0.0", "glob": "^8.0.3", - "json5": "^2.2.1", + "json5": "^2.2.3", "normalize-path": "^3.0.0", - "safe-stable-stringify": "^2.4.0", - "typescript": "~4.8.3" + "safe-stable-stringify": "^2.4.3", + "typescript": "~5.1.6" }, "bin": { "ts-json-schema-generator": "bin/ts-json-schema-generator" @@ -13052,6 +13052,18 @@ "node": ">=10" } }, + "node_modules/ts-json-schema-generator/node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/tslib": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", @@ -13137,6 +13149,7 @@ "version": "4.8.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15709,9 +15722,9 @@ } }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==" }, "@types/keyv": { "version": "3.1.4", @@ -16898,9 +16911,9 @@ } }, "commander": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", - "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==" + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==" }, "component-bind": { "version": "1.0.0", @@ -20473,9 +20486,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonfile": { "version": "6.1.0", @@ -22638,9 +22651,9 @@ "dev": true }, "safe-stable-stringify": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.0.tgz", - "integrity": "sha512-eehKHKpab6E741ud7ZIMcXhKcP6TSIezPkNZhy5U8xC6+VvrRdUA2tMgxGxaGl4cz7c2Ew5+mg5+wNB16KQqrA==" + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==" }, "safer-buffer": { "version": "2.1.2", @@ -23972,17 +23985,17 @@ } }, "ts-json-schema-generator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-1.1.2.tgz", - "integrity": "sha512-XMnxvndJFJEYv3NBmW7Po5bGajKdK2qH8Q078eDy60srK9+nEvbT9nLCRKd2IV/RQ7a+oc5FNylvZWveqh7jeQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-1.3.0.tgz", + "integrity": "sha512-Y2smEgpxtWat8ICaLbUENXZ/o/SqvVy85X48V/7qOarOTu6XgVs+lr6k0OPFljVhZX5gEMrGPT3q7Ql7JKnexw==", "requires": { - "@types/json-schema": "^7.0.11", - "commander": "^9.4.0", + "@types/json-schema": "^7.0.12", + "commander": "^11.0.0", "glob": "^8.0.3", - "json5": "^2.2.1", + "json5": "^2.2.3", "normalize-path": "^3.0.0", - "safe-stable-stringify": "^2.4.0", - "typescript": "~4.8.3" + "safe-stable-stringify": "^2.4.3", + "typescript": "~5.1.6" }, "dependencies": { "brace-expansion": { @@ -24012,6 +24025,11 @@ "requires": { "brace-expansion": "^2.0.1" } + }, + "typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==" } } }, @@ -24081,7 +24099,8 @@ "typescript": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==" + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true }, "unbzip2-stream": { "version": "1.4.3", diff --git a/package.json b/package.json index 23fe7e3..fa159a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless-openapi-typescript", - "version": "2.0.0", + "version": "2.1.0", "description": "An extension of serverless-openapi-documenter that also generates your OpenAPI models from TypeScript", "main": "dist/index.js", "scripts": { @@ -11,7 +11,7 @@ "deepdash": "^5.3.9", "js-yaml": "^4.1.0", "lodash": "^4.17.21", - "ts-json-schema-generator": "^1.1.2" + "ts-json-schema-generator": "^1.3.0" }, "devDependencies": { "@types/jest": "^27.0.1", @@ -28,6 +28,6 @@ }, "keywords": [], "author": "env0", - "repository": "https://github.com/env0/serverless-openapi-typescript", + "repository": "https://github.com/gdlin/serverless-openapi-typescript", "license": "MIT" } diff --git a/src/serverless-openapi-typescript.ts b/src/serverless-openapi-typescript.ts index 521d094..a7ee0d3 100644 --- a/src/serverless-openapi-typescript.ts +++ b/src/serverless-openapi-typescript.ts @@ -1,10 +1,15 @@ import type Serverless from "serverless"; import fs from "fs"; import yaml from "js-yaml"; -import {SchemaGenerator, createGenerator} from "ts-json-schema-generator"; -import {upperFirst, camelCase, mergeWith, set, isArray, get, isEmpty, kebabCase, find} from "lodash" ; +import {createGenerator, SchemaGenerator} from "ts-json-schema-generator"; +import {camelCase, find, get, isArray, isEmpty, kebabCase, mergeWith, set, upperFirst} from "lodash"; import {ApiGatewayEvent} from "serverless/plugins/aws/package/compile/events/apiGateway/lib/validate"; -import { mapKeysDeep, mapValuesDeep} from 'deepdash/standalone' +import {mapKeysDeep, mapValuesDeep} from 'deepdash/standalone' +import path from "path"; +import {promisify} from "util"; +import {exec} from 'child_process'; + +const execAsync = promisify(exec); interface Options { typescriptApiPath?: string; @@ -19,7 +24,7 @@ type HttpEvent = ApiGatewayEvent['http'] & { export default class ServerlessOpenapiTypeScript { private readonly functionsMissingDocumentation: string[]; private readonly disable: boolean; - private hooks: { [hook: string]: () => {}}; + private hooks: { [hook: string]: () => {} }; private typescriptApiModelPath: string; private tsconfigPath: string; private schemaGenerator: SchemaGenerator; @@ -40,6 +45,7 @@ export default class ServerlessOpenapiTypeScript { if (!this.disable) { this.hooks = { + 'before:package:createDeploymentArtifacts': this.callOpenApiGenerate.bind(this), 'before:openapi:generate:serverless': this.populateServerlessWithModels.bind(this), 'after:openapi:generate:serverless': this.postProcessOpenApi.bind(this) }; @@ -48,8 +54,8 @@ export default class ServerlessOpenapiTypeScript { initOptions(options) { this.options = options || {}; - this.typescriptApiModelPath = this.options.typescriptApiPath || 'api.d.ts'; - this.tsconfigPath = this.options.tsconfigPath || 'tsconfig.json'; + this.typescriptApiModelPath = this.options.typescriptApiPath || this.serverless.service.custom?.documentation?.typescriptApiPath || 'api.d.ts'; + this.tsconfigPath = this.options.tsconfigPath || this.serverless.service.custom?.documentation?.tsconfigPath || 'tsconfig.json'; } assertPluginOrder() { @@ -82,9 +88,9 @@ export default class ServerlessOpenapiTypeScript { const paths = get(httpEvent, 'request.parameters.paths', []); const querystrings = get(httpEvent, 'request.parameters.querystrings', {}); [ - { params: paths, documentationKey: 'pathParams' }, - { params: querystrings, documentationKey: 'queryParams' } - ].forEach(({ params, documentationKey }) => { + {params: paths, documentationKey: 'pathParams'}, + {params: querystrings, documentationKey: 'queryParams'} + ].forEach(({params, documentationKey}) => { this.setDefaultParamsDocumentation(params, httpEvent, documentationKey); }); } else if (httpEvent.documentation !== null && !httpEvent.private) { @@ -97,6 +103,10 @@ export default class ServerlessOpenapiTypeScript { this.assertAllFunctionsDocumented(); } + private callOpenApiGenerate() { + this.serverless.pluginManager.spawn('openapi:generate'); + } + assertAllFunctionsDocumented() { if (!isEmpty(this.functionsMissingDocumentation)) { throw new Error( @@ -126,7 +136,7 @@ export default class ServerlessOpenapiTypeScript { const paramDocumentationFromSls = { name, required, - schema: { type: 'string' } + schema: {type: 'string'} }; if (!existingDocumentedParam) { @@ -139,12 +149,15 @@ export default class ServerlessOpenapiTypeScript { } setModels(httpEvent, functionName) { + const formatName = (model: string): string => upperFirst(camelCase(model.replace(/\W+/g, ''))); + const definitionPrefix = `${this.serverless.service.custom.documentation.apiNamespace}.${upperFirst(camelCase(functionName))}`; const method = httpEvent.method.toLowerCase(); switch (method) { case 'delete': - set(httpEvent, 'documentation.methodResponses', [{ statusCode: 204, - responseBody: { description: "Mocked response for the delete endpoint."}, + set(httpEvent, 'documentation.methodResponses', [{ + statusCode: 204, + responseBody: {description: "Mocked response for the delete endpoint."}, responseModels: { 'application/json': { @@ -161,18 +174,27 @@ export default class ServerlessOpenapiTypeScript { case 'put': case 'post': const requestModelName = `${definitionPrefix}.Request.Body`; - this.setModel(`${definitionPrefix}.Request.Body`); - set(httpEvent, 'documentation.requestModels', { 'application/json': requestModelName }); - set(httpEvent, 'documentation.requestBody', { description: '' }); + this.setModel(requestModelName); + set(httpEvent, 'documentation.requestModels', {'application/json': requestModelName}); + set(httpEvent, 'documentation.requestBody', {description: ''}); + + // Set Request schema validators + set(httpEvent, 'request.schemas', { + 'application/json': { + name: formatName(requestModelName), + schema: this.generateSchema(requestModelName), + description: `Generated schema for ${requestModelName}` + } + }); // no-break; case 'get': const responseModelName = `${definitionPrefix}.Response`; - this.setModel(`${definitionPrefix}.Response`); + this.setModel(responseModelName); set(httpEvent, 'documentation.methodResponses', [ { statusCode: 200, - responseBody: { description: '' }, - responseModels: { 'application/json': responseModelName } + responseBody: {description: ''}, + responseModels: {'application/json': responseModelName} } ]); } @@ -191,14 +213,29 @@ export default class ServerlessOpenapiTypeScript { } } - postProcessOpenApi() { + async postProcessOpenApi() { // @ts-ignore - const outputFile = this.serverless.processedInput.options.output; + const outputFile = this.serverless.processedInput.options.output || 'openapi.json'; const openApi = yaml.load(fs.readFileSync(outputFile)); this.patchOpenApiVersion(openApi); this.enrichMethodsInfo(openApi); const encodedOpenAPI = this.encodeOpenApiToStandard(openApi); fs.writeFileSync(outputFile, outputFile.endsWith('json') ? JSON.stringify(encodedOpenAPI, null, 2) : yaml.dump(encodedOpenAPI)); + + const s3Bucket = this.serverless.service.custom.documentation?.s3Bucket; + if (s3Bucket) { + await this.uploadFileToS3UsingCLI(outputFile, s3Bucket); + } + } + + async uploadFileToS3UsingCLI(filePath: string, bucketName: string) { + const s3Path = `s3://${bucketName}/${path.basename(filePath)}`; + try { + await execAsync(`aws s3 cp "${filePath}" "${s3Path}"`); + this.log(`File uploaded successfully to ${s3Path}`); + } catch (error) { + this.log(`Error uploading file: ${error.message}`); + } } // OpenApi spec define ^[a-zA-Z0-9\.\-_]+$ for legal fields - https://spec.openapis.org/oas/v3.1.0#components-object @@ -259,7 +296,7 @@ export default class ServerlessOpenapiTypeScript { this.serverless.service.custom, { documentation: { - models: [{ name: modelName, contentType: 'application/json', schema: this.generateSchema(modelName) }] + models: [{name: modelName, contentType: 'application/json', schema: this.generateSchema(modelName)}] } }, (objValue, srcValue) => { @@ -270,6 +307,24 @@ export default class ServerlessOpenapiTypeScript { ); } + // Post-process the schema to replace const with enum + constToEnum(schema) { + if (typeof schema === "object" && schema !== null) { + const newSchema = JSON.parse(JSON.stringify(schema)); + if (newSchema.hasOwnProperty("const")) { + const {const: _, ...rest} = newSchema; + return {...rest, enum: [newSchema.const]}; + } + + for (const key of Object.keys(newSchema)) { + newSchema[key] = this.constToEnum(newSchema[key]); + } + + return newSchema; + } + return schema; + } + generateSchema(modelName) { this.log(`Generating schema for ${modelName}`); @@ -284,6 +339,8 @@ export default class ServerlessOpenapiTypeScript { topRef: false }); - return this.schemaGenerator.createSchema(modelName); + const generatedSchema = this.schemaGenerator.createSchema(modelName); + + return this.constToEnum(generatedSchema); } } diff --git a/test/fixtures/expect-openapi-const-replacement.yml b/test/fixtures/expect-openapi-const-replacement.yml new file mode 100644 index 0000000..b879612 --- /dev/null +++ b/test/fixtures/expect-openapi-const-replacement.yml @@ -0,0 +1,134 @@ +openapi: 3.1.0 +components: + schemas: + ProjectApi.CreateFunc.Request.Body: + type: object + properties: + data: + type: string + statusCode: + type: number + enable: + type: boolean + object: + type: object + properties: + types: + type: array + items: + type: string + children: + type: array + items: + type: string + additionalProperties: false + replace: + type: string + enum: + - TEST + required: + - data + - enable + - replace + additionalProperties: false + ProjectApi.CreateFunc.Response: + type: object + properties: + id: + type: string + uuid: + type: string + generic: + type: array + items: + type: object + properties: + key: + type: string + name: + type: number + required: + - key + - name + additionalProperties: false + required: + - id + - uuid + - generic + additionalProperties: false + +info: + title: Project + description: > + It is a long established fact that a reader will be distracted by the + readable content of a + + page when looking at its layout. The point of using Lorem Ipsum is that + + it has a more-or-less normal distribution of letters, as opposed to using + + 'Content here, content here', making it look like readable English. Many + desktop publishing + + packages and web page editors now use Lorem Ipsum as their default model + text, and a search + + for 'lorem ipsum' will uncover many web sites still in their infancy. + Various versions have evolved over the years, + + sometimes by accident, sometimes on purpose (injected humour and the like). + + + More on https://google.com +paths: + /create: + post: + summary: Create Function + description: | + Create Function1 + Create Function2 + Create Function3 + operationId: serverless-openapi-typescript-demo-dev-create-func + parameters: [] + tags: + - Project + requestBody: + description: '' + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectApi.CreateFunc.Request.Body' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectApi.CreateFunc.Response' + headers: {} + +tags: + - name: Project + description: > + It is a long established fact that a reader will be distracted by the + readable content of a + + page when looking at its layout. The point of using Lorem Ipsum is that + + it has a more-or-less normal distribution of letters, as opposed to using + + 'Content here, content here', making it look like readable English. Many + desktop publishing + + packages and web page editors now use Lorem Ipsum as their default model + text, and a search + + for 'lorem ipsum' will uncover many web sites still in their infancy. + Various versions have evolved over the years, + + sometimes by accident, sometimes on purpose (injected humour and the + like). + + + More on https://google.com diff --git a/test/serverless-const-replacement/api.d.ts b/test/serverless-const-replacement/api.d.ts new file mode 100644 index 0000000..d319bd9 --- /dev/null +++ b/test/serverless-const-replacement/api.d.ts @@ -0,0 +1,26 @@ +interface ObjectType { + types?: string[]; + children?: string[]; +} + +export namespace ProjectApi { + export type GenericType = T[]; + + export namespace CreateFunc { + export namespace Request { + export type Body = { + data: string; + statusCode?: number; + enable: boolean; + object?: ObjectType; + replace: 'TEST' + }; + } + + export type Response = { + id: string; + uuid: string; + generic: GenericType<{ key: string, name: number}>; + }; + } +} diff --git a/test/serverless-const-replacement/resources/serverless.yml b/test/serverless-const-replacement/resources/serverless.yml new file mode 100644 index 0000000..093534a --- /dev/null +++ b/test/serverless-const-replacement/resources/serverless.yml @@ -0,0 +1,44 @@ +service: serverless-openapi-typescript-demo +provider: + name: aws + +plugins: + - ../node_modules/serverless-openapi-documenter + - ../src/index + +custom: + documentation: + title: 'Project' + description: | + It is a long established fact that a reader will be distracted by the readable content of a + page when looking at its layout. The point of using Lorem Ipsum is that + it has a more-or-less normal distribution of letters, as opposed to using + 'Content here, content here', making it look like readable English. Many desktop publishing + packages and web page editors now use Lorem Ipsum as their default model text, and a search + for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, + sometimes by accident, sometimes on purpose (injected humour and the like). + + More on https://google.com + apiNamespace: ProjectApi + +functions: + createFunc: + handler: handler.create + events: + - http: + documentation: + summary: "Create Function" + description: | + Create Function1 + Create Function2 + Create Function3 + path: create + method: post + + internalFunc: + handler: handler.internal + events: + - http: + documentation: ~ + path: internal + method: get diff --git a/test/serverless-openapi-typescript.spec.ts b/test/serverless-openapi-typescript.spec.ts index 6ea0e79..a855f19 100644 --- a/test/serverless-openapi-typescript.spec.ts +++ b/test/serverless-openapi-typescript.spec.ts @@ -15,6 +15,7 @@ describe('ServerlessOpenapiTypeScript', () => { testCase | projectName ${'Custom Tags'} | ${'custom-tags'} ${'Hyphenated Functions'} | ${'hyphenated-functions'} + ${'Const replacement post processor'} | ${'const-replacement'} ${'Full Project'} | ${'full'} `('when using $testCase', ({projectName}) => {