diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 0fcd3be..89cb101 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -7,7 +7,7 @@ on: push: branches: ["main"] pull_request: - branches: ["main"] + branches: ["main", "beta"] jobs: build: @@ -15,7 +15,7 @@ jobs: strategy: matrix: - node-version: [20.x, 22.x] + node-version: [20.x, 22.x, 24.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: diff --git a/.github/workflows/npm-publish-beta.yml b/.github/workflows/npm-publish-beta.yml new file mode 100644 index 0000000..d90e996 --- /dev/null +++ b/.github/workflows/npm-publish-beta.yml @@ -0,0 +1,35 @@ +# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created +# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages + +name: Node.js Beta Package + +on: + push: + branches: + - beta # Or your beta branch name + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + - run: npm ci + # - run: npm test + + publish-beta-npm: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - name: Bump version and publish + run: npm publish --tag beta + env: + NODE_AUTH_TOKEN: ${{secrets.npm_token}} diff --git a/README.md b/README.md index a00d233..e0e03d1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@

-This will generate an OpenAPI V3 (up to v3.0.4) file for you from your serverless file. It can optionally generate a [Postman Collection V2](https://github.com/postmanlabs/openapi-to-postman) from the OpenAPI file for you too. This currently works for `http` and `httpApi` configurations. +This will generate an [OpenAPI V3](https://spec.openapis.org/oas/v3.0.0.html) (up to v3.0.4) specification file for you from your serverless file. It can optionally generate a [Postman Collection V2](https://github.com/postmanlabs/openapi-to-postman) from the OpenAPI file for you too. This currently works for `http` and `httpApi` configurations. + +If you are using the beta of 0.0.115, it will now try and create [OpenAPI V3.1 (3.1.x)](https://spec.openapis.org/oas/v3.1.0.html) specification file for you, should you run the command `serverless openapi generate -o openapi.json -f json -a 3.1.1 -p postman.json`. Please see this [guide on migrating to V3.1](https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0). Whilst I perosnally use this plugin all the time, please do open and report bugs, and I will do my best to fix them. Originally based off of: https://github.com/temando/serverless-openapi-documentation diff --git a/package-lock.json b/package-lock.json index d9581ed..0de77cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "serverless-openapi-documenter", - "version": "0.0.118", + "version": "0.0.120-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "serverless-openapi-documenter", - "version": "0.0.118", + "version": "0.0.120-beta.1", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.1.0", - "@redocly/openapi-core": "^1.2.0", + "@redocly/openapi-core": "^1.34.5", "chalk": "^4.1.2", "js-yaml": "^4.1.1", "json-schema-for-openapi": "^0.5.0", - "openapi-to-postmanv2": "^5.4.1", + "openapi-to-postmanv2": "^5.6.0", "uuid": "^11.1.0" }, "devDependencies": { @@ -128,38 +128,46 @@ } }, "node_modules/@redocly/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.3.tgz", + "integrity": "sha512-4P3iZse91TkBiY+Dx5DUgxQ9GXkVJf++cmI0MOyLDxV9b5MUBI4II6ES8zA5JCbO72nKAJxWrw4PUPW+YP3ZDQ==", + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js-replace": "^1.0.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@redocly/config": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", + "integrity": "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==", + "license": "MIT" + }, "node_modules/@redocly/openapi-core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.2.0.tgz", - "integrity": "sha512-Ccft2n/JiF4u2crmj1cdDzPq6C40U7NgLZ+p/BxzAFXbfrddr/5FN0HMJPHT/op329qqv2P2jUrXsV2Bp+rzEQ==", + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.5.tgz", + "integrity": "sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==", + "license": "MIT", "dependencies": { - "@redocly/ajv": "^8.11.0", - "@types/node": "^14.11.8", + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.22.0", "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.5", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", - "lodash.isequal": "^4.5.0", "minimatch": "^5.0.1", - "node-fetch": "^2.6.1", "pluralize": "^8.0.0", "yaml-ast-parser": "0.0.43" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.17.0", + "npm": ">=9.5.0" } }, "node_modules/@sinonjs/commons": { @@ -205,10 +213,14 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, - "node_modules/@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } }, "node_modules/ajv": { "version": "8.11.0", @@ -554,7 +566,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -796,6 +807,19 @@ "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -990,11 +1014,6 @@ "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", "dev": true }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" - }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -1146,8 +1165,7 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/neotraverse": { "version": "0.6.15", @@ -1303,9 +1321,9 @@ } }, "node_modules/openapi-to-postmanv2": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/openapi-to-postmanv2/-/openapi-to-postmanv2-5.4.1.tgz", - "integrity": "sha512-cuPnRZphMS6QsjKa+ktJ9wYXYvAHtl5WyTCodtraASOp4pe6wVcoA9mEs8VHDFZvzYeZ8t7wAQ2faYiw9PsWhQ==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/openapi-to-postmanv2/-/openapi-to-postmanv2-5.6.0.tgz", + "integrity": "sha512-3J1vqmScB3Xgw9PYfpJrjzND0IOUJLemj7tYF9cC2X6xQ0boxaCZYgDljG2A7H43QNINHUAoHo+Y+Dnp4wr+Ew==", "license": "Apache-2.0", "dependencies": { "ajv": "^8.11.0", @@ -1892,6 +1910,12 @@ "punycode": "^2.1.0" } }, + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "license": "MIT" + }, "node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", @@ -2271,29 +2295,33 @@ "optional": true }, "@redocly/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.3.tgz", + "integrity": "sha512-4P3iZse91TkBiY+Dx5DUgxQ9GXkVJf++cmI0MOyLDxV9b5MUBI4II6ES8zA5JCbO72nKAJxWrw4PUPW+YP3ZDQ==", "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js-replace": "^1.0.1" } }, + "@redocly/config": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", + "integrity": "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==" + }, "@redocly/openapi-core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.2.0.tgz", - "integrity": "sha512-Ccft2n/JiF4u2crmj1cdDzPq6C40U7NgLZ+p/BxzAFXbfrddr/5FN0HMJPHT/op329qqv2P2jUrXsV2Bp+rzEQ==", + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.5.tgz", + "integrity": "sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==", "requires": { - "@redocly/ajv": "^8.11.0", - "@types/node": "^14.11.8", + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.22.0", "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.5", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", - "lodash.isequal": "^4.5.0", "minimatch": "^5.0.1", - "node-fetch": "^2.6.1", "pluralize": "^8.0.0", "yaml-ast-parser": "0.0.43" } @@ -2340,10 +2368,10 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, - "@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" + "agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==" }, "ajv": { "version": "8.11.0", @@ -2594,7 +2622,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "requires": { "ms": "^2.1.3" } @@ -2759,6 +2786,15 @@ "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" }, + "https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "requires": { + "agent-base": "^7.1.2", + "debug": "4" + } + }, "iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -2903,11 +2939,6 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" - }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -3019,8 +3050,7 @@ "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "neotraverse": { "version": "0.6.15", @@ -3131,9 +3161,9 @@ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" }, "openapi-to-postmanv2": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/openapi-to-postmanv2/-/openapi-to-postmanv2-5.4.1.tgz", - "integrity": "sha512-cuPnRZphMS6QsjKa+ktJ9wYXYvAHtl5WyTCodtraASOp4pe6wVcoA9mEs8VHDFZvzYeZ8t7wAQ2faYiw9PsWhQ==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/openapi-to-postmanv2/-/openapi-to-postmanv2-5.6.0.tgz", + "integrity": "sha512-3J1vqmScB3Xgw9PYfpJrjzND0IOUJLemj7tYF9cC2X6xQ0boxaCZYgDljG2A7H43QNINHUAoHo+Y+Dnp4wr+Ew==", "requires": { "ajv": "^8.11.0", "ajv-draft-04": "1.0.0", @@ -3547,6 +3577,11 @@ "punycode": "^2.1.0" } }, + "uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==" + }, "uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", diff --git a/package.json b/package.json index 136cc0c..b79a36b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless-openapi-documenter", - "version": "0.0.118", + "version": "0.0.120-beta.1", "description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config", "main": "index.js", "keywords": [ @@ -46,11 +46,11 @@ "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.1.0", - "@redocly/openapi-core": "^1.2.0", + "@redocly/openapi-core": "^1.34.5", "chalk": "^4.1.2", "js-yaml": "^4.1.1", "json-schema-for-openapi": "^0.5.0", - "openapi-to-postmanv2": "^5.4.1", + "openapi-to-postmanv2": "^5.6.0", "uuid": "^11.1.0" }, "engines": { diff --git a/src/schemaHandler.js b/src/schemaHandler.js index d66bc61..b50af2f 100644 --- a/src/schemaHandler.js +++ b/src/schemaHandler.js @@ -16,6 +16,12 @@ class SchemaHandler { this.documentation = serverless.service.custom.documentation; this.openAPI = openAPI; + this.shouldConvert = true; + if (/(3\.1\.\d)/g.test(this.openAPI.openapi)) this.shouldConvert = false; + + this.logger.verbose(`OpenAPI version: ${this.openAPI.openapi}`); + this.logger.verbose(`Convert Schemas: ${this.shouldConvert}`); + this.modelReferences = {}; this.__standardiseModels(); @@ -157,18 +163,30 @@ class SchemaHandler { } ); - this.logger.verbose( - `dereferenced model: ${JSON.stringify(dereferencedSchema)}` - ); + if (this.shouldConvert) { + this.logger.verbose( + `dereferenced model: ${JSON.stringify(dereferencedSchema)}` + ); - this.logger.verbose(`converting model: ${name}`); - const convertedSchemas = SchemaConvertor.convert(dereferencedSchema, name); + this.logger.verbose(`converting model: ${name}`); + const convertedSchemas = SchemaConvertor.convert( + dereferencedSchema, + name + ); + + this.logger.verbose( + `converted schemas: ${JSON.stringify(convertedSchemas)}` + ); + return convertedSchemas; + } this.logger.verbose( - `converted schemas: ${JSON.stringify(convertedSchemas)}` + `dereferenced model: ${JSON.stringify({ + schemas: { [name]: dereferencedSchema }, + })}` ); - return convertedSchemas; + return { schemas: { [name]: dereferencedSchema } }; } async __dereferenceSchema(schema) { diff --git a/test/unit/definitionGenerator.spec.js b/test/unit/definitionGenerator.spec.js index 65b76f9..950d024 100644 --- a/test/unit/definitionGenerator.spec.js +++ b/test/unit/definitionGenerator.spec.js @@ -38,7 +38,7 @@ describe("DefinitionGenerator", () => { expect(expected).to.be.an.instanceOf(DefinitionGenerator); }); - it("should default to version 3.0.0 of openAPI when openAPI version is not passed in", function () { + it("should default to version 3.0.0 of OpenAPI when OpenAPI version is not passed in", function () { const serverlessWithoutOpenAPIVersion = structuredClone(mockServerless); delete serverlessWithoutOpenAPIVersion.processedInput; let expected = new DefinitionGenerator( @@ -105,7 +105,7 @@ describe("DefinitionGenerator", () => { expect(expected.version).to.be.equal("3.0.0"); }); - it("should respect the version of openAPI when passed in", function () { + it("should respect the version of OpenAPI when passed in", function () { const serverlessWithOpenAPIVersion = structuredClone(mockServerless); serverlessWithOpenAPIVersion.processedInput.options.openApiVersion = "3.0.2"; @@ -157,7 +157,7 @@ describe("DefinitionGenerator", () => { }); describe("createInfo", () => { - it("should create openAPI info object correctly", function () { + it("should create OpenAPI info object correctly", function () { const definitionGenerator = new DefinitionGenerator( mockServerless, logger @@ -918,14 +918,15 @@ describe("DefinitionGenerator", () => { definitionGenerator.openAPI.components.securitySchemes ).to.have.property("x_amazon_api_key"); expect( - definitionGenerator.openAPI.components.securitySchemes.x_amazon_api_key + definitionGenerator.openAPI.components.securitySchemes + .x_amazon_api_key ).to.have.property("x-amazon-apigateway-authtype"); }); }); }); describe("createTags", () => { - it("should add tags to the openAPI object correctly", function () { + it("should add tags to the OpenAPI object correctly", function () { mockServerless.service.custom.documentation.tags = [{ name: "tag1" }]; const definitionGenerator = new DefinitionGenerator( diff --git a/test/unit/openAPIGenerator.spec.js b/test/unit/openAPIGenerator.spec.js index 4436083..cbffbcc 100644 --- a/test/unit/openAPIGenerator.spec.js +++ b/test/unit/openAPIGenerator.spec.js @@ -68,7 +68,7 @@ describe("OpenAPIGenerator", () => { }); describe("generationAndValidation", () => { - it("should correctly generate a valid openAPI document", async function () { + it("should correctly generate a valid OpenAPI document", async function () { const succSpy = sinon.spy(logOutput.log, "success"); const errSpy = sinon.spy(logOutput.log, "error"); @@ -99,7 +99,7 @@ describe("OpenAPIGenerator", () => { getFuncStub.reset(); }); - xit("should throw an error when trying to generate an invalid openAPI document", async function () { + xit("should throw an error when trying to generate an invalid OpenAPI document", async function () { const succSpy = sinon.spy(logOutput.log, "success"); const errSpy = sinon.spy(logOutput.log, "error"); @@ -135,7 +135,7 @@ describe("OpenAPIGenerator", () => { getFuncStub.reset(); }); - it("should correctly validate a valid openAPI document", async function () { + it("should correctly validate a valid OpenAPI document", async function () { const succSpy = sinon.spy(logOutput.log, "success"); const errSpy = sinon.spy(logOutput.log, "error"); @@ -168,7 +168,7 @@ describe("OpenAPIGenerator", () => { getFuncStub.reset(); }); - it("should throw an error when trying to validate an invalid openAPI document", async function () { + it("should throw an error when trying to validate an invalid OpenAPI document", async function () { const succSpy = sinon.spy(logOutput.log, "success"); const errSpy = sinon.spy(logOutput.log, "error"); @@ -212,7 +212,7 @@ describe("OpenAPIGenerator", () => { }); describe("createPostman", () => { - it("should generate a postman collection when a valid openAPI file is generated", function () { + it("should generate a postman collection when a valid OpenAPI file is generated", function () { const fsStub = sinon.stub(fs, "writeFileSync").returns(true); const succSpy = sinon.spy(logOutput.log, "success"); const errSpy = sinon.spy(logOutput.log, "error"); diff --git a/test/unit/schemaHandler.spec.js b/test/unit/schemaHandler.spec.js index d5a1cbc..4d08732 100644 --- a/test/unit/schemaHandler.spec.js +++ b/test/unit/schemaHandler.spec.js @@ -4,7 +4,9 @@ const fs = require("fs").promises; const path = require("path"); const expect = require("chai").expect; +const SchemaConvertor = require("json-schema-for-openapi"); const nock = require("nock"); +const sinon = require("sinon"); const modelsDocumentOG = require("../models/models/models.json"); const modelsAltDocumentOG = require("../models/models/models-alt.json"); @@ -36,7 +38,7 @@ describe(`SchemaHandler`, function () { ); const openAPISchema = { - version: "3.0.3", + openapi: "3.0.3", components: { schemas: {}, }, @@ -277,7 +279,7 @@ describe(`SchemaHandler`, function () { describe(`addModelsToOpenAPI`, function () { describe(`embedded simple schemas`, function () { - it(`should add the model to the openAPI schema`, async function () { + it(`should add the model to the OpenAPI schema`, async function () { Object.assign( mockServerless.service.custom.documentation, modelsDocument @@ -306,7 +308,7 @@ describe(`SchemaHandler`, function () { }); }); - it(`should add a model with references to the openAPI schema`, async function () { + it(`should add a model with references to the OpenAPI schema`, async function () { Object.assign( mockServerless.service.custom.documentation, modelsDocument @@ -379,7 +381,7 @@ describe(`SchemaHandler`, function () { }); }); - it(`should add a model with poorly dereferenced references to the openAPI schema`, async function () { + it(`should add a model with poorly dereferenced references to the OpenAPI schema`, async function () { Object.assign( mockServerless.service.custom.documentation, modelsDocument @@ -446,7 +448,7 @@ describe(`SchemaHandler`, function () { }); describe(`component references`, function () { - it(`should add schemas with component references to the openAPI schema`, async function () { + it(`should add schemas with component references to the OpenAPI schema`, async function () { Object.assign( mockServerless.service.custom.documentation, modelsDocument @@ -517,10 +519,86 @@ describe(`SchemaHandler`, function () { type: "string", }); }); + + it(`should add schemas with component references to the OpenAPI schema using OpenAPI 3.1`, async function () { + Object.assign( + mockServerless.service.custom.documentation, + modelsDocument + ); + + mockServerless.service.custom.documentation.models.push({ + name: "SuccessResponse", + contentType: "application/json", + schema: { + type: "array", + items: { + $ref: "#/components/schemas/Agency", + }, + }, + }); + + mockServerless.service.custom.documentation.models.push({ + name: "Agency", + contentType: "application/json", + schema: { + type: "string", + }, + }); + + openAPI.openapi = "3.1.0"; + + const schemaHandler = new SchemaHandler( + mockServerless, + openAPI, + logger + ); + + await schemaHandler.addModelsToOpenAPI(); + + expect(schemaHandler.openAPI).to.have.property("components"); + expect(schemaHandler.openAPI.components).to.have.property("schemas"); + expect(schemaHandler.openAPI.components.schemas).to.have.property( + "ErrorResponse" + ); + expect( + schemaHandler.openAPI.components.schemas.ErrorResponse + ).to.be.an("object"); + expect( + schemaHandler.openAPI.components.schemas.ErrorResponse + ).to.be.eql({ + type: "object", + properties: { error: { type: "string" } }, + }); + + expect(schemaHandler.openAPI.components.schemas).to.have.property( + "SuccessResponse" + ); + expect( + schemaHandler.openAPI.components.schemas.SuccessResponse + ).to.be.an("object"); + expect( + schemaHandler.openAPI.components.schemas.SuccessResponse + ).to.be.eql({ + type: "array", + items: { + $ref: "#/components/schemas/Agency", + }, + }); + + expect(schemaHandler.openAPI.components.schemas).to.have.property( + "Agency" + ); + expect(schemaHandler.openAPI.components.schemas.Agency).to.be.an( + "object" + ); + expect(schemaHandler.openAPI.components.schemas.Agency).to.be.eql({ + type: "string", + }); + }); }); describe(`other references`, function () { - it(`should add a model that is a webUrl to the openAPI schema`, async function () { + it(`should add a model that is a webUrl to the OpenAPI schema`, async function () { Object.assign( mockServerless.service.custom.documentation, modelsDocument @@ -589,7 +667,7 @@ describe(`SchemaHandler`, function () { }); }); - it(`should add a complex model that is a webUrl to the openAPI schema`, async function () { + it(`should add a complex model that is a webUrl to the OpenAPI schema`, async function () { Object.assign( mockServerless.service.custom.documentation, modelsDocument @@ -710,6 +788,28 @@ describe(`SchemaHandler`, function () { }); describe(`createSchema`, function () { + it(`does not convert schemas when using OpenAPI 3.1.x`, async function () { + Object.assign( + mockServerless.service.custom.documentation, + modelsDocument + ); + + openAPI.openapi = "3.1.0"; + + const schemaHandler = new SchemaHandler(mockServerless, openAPI, logger); + + const spy = sinon.spy(SchemaConvertor, "convert"); + + await schemaHandler.addModelsToOpenAPI(); + + const expected = await schemaHandler.createSchema("ErrorResponse"); + + expect(expected).to.be.equal("#/components/schemas/ErrorResponse"); + expect(spy.calledOnce).to.be.false; + + spy.restore(); + }); + it(`returns a reference to the schema when the schema already exists in components and we don't pass through a schema`, async function () { Object.assign( mockServerless.service.custom.documentation,