├── .all-contributorsrc ├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── _config.yml ├── assets ├── auth-example.gif ├── changelog_assets │ ├── 1.2.5_route_naming.jpg │ ├── additional-properties-types.jpg │ ├── api-module-description.jpg │ ├── bug-with-no-path-args.jpg │ ├── bug-with-no-path-args2.jpg │ ├── changes_procedure_call_1.jpg │ ├── extractRequestParams.jpg │ ├── fixComplexTypeAny.jpg │ ├── http-client-class1.jpg │ ├── http-client-class2.jpg │ ├── http-client-template-diff-4.3.0.jpg │ ├── onCreateRequestParamsHook.jpg │ ├── one-line-comments.jpg │ ├── removed-title-version-props.jpg │ ├── responses-catch-types.jpg │ ├── responses-comments.jpg │ ├── right-comments-space.jpg │ └── route-types.jpg ├── components-converter-example.jpg ├── npx.gif ├── swagger-typescript-api-logo.png └── typings1.gif ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json ├── scriptsRunner.js ├── src ├── apiConfig.js ├── common.js ├── components.js ├── config.js ├── constants.js ├── filePrefix.js ├── files.js ├── formatFileContent.js ├── index.js ├── logger.js ├── modelNames.js ├── modelTypes.js ├── output.js ├── prettierOptions.js ├── render │ └── utils │ │ ├── fmtToJSDocLine.js │ │ ├── index.js │ │ └── templateRequire.js ├── routeNames.js ├── routes.js ├── schema.js ├── swagger.js ├── templates.js ├── translators │ └── JavaScript.js ├── typeFormatters.js └── utils │ ├── random.js │ └── resolveName.js ├── templates ├── README.md ├── base │ ├── README.md │ ├── data-contracts.eta │ ├── http-client.eta │ ├── http-clients │ │ ├── axios-http-client.eta │ │ └── fetch-http-client.eta │ ├── route-docs.eta │ ├── route-name.eta │ └── route-type.eta ├── default │ ├── README.md │ ├── api.eta │ ├── procedure-call.eta │ └── route-types.eta └── modular │ ├── README.md │ ├── api.eta │ ├── procedure-call.eta │ └── route-types.eta ├── tests ├── README.md ├── allSchemas.js ├── generate-extended.js ├── generate.js ├── generated │ ├── v2.0 │ │ ├── adafruit.ts │ │ ├── another-example.ts │ │ ├── another-schema.ts │ │ ├── api-with-examples.ts │ │ ├── authentiq.ts │ │ ├── enums.ts │ │ ├── example1.ts │ │ ├── file-formdata-example.ts │ │ ├── furkot-example.ts │ │ ├── giphy.ts │ │ ├── github-swagger.ts │ │ ├── path-args.ts │ │ ├── petstore-expanded.ts │ │ ├── petstore-minimal.ts │ │ ├── petstore-simple.ts │ │ ├── petstore-swagger-io.ts │ │ ├── petstore-with-external-docs.ts │ │ ├── petstore.ts │ │ ├── query-path-param.ts │ │ └── uber.ts │ └── v3.0 │ │ ├── additional-properties.ts │ │ ├── additional-properties2.ts │ │ ├── allof-example.ts │ │ ├── anyof-example.ts │ │ ├── api-with-examples.ts │ │ ├── callback-example.ts │ │ ├── components-responses.ts │ │ ├── explode-param-3.0.1.ts │ │ ├── full-swagger-scheme.ts │ │ ├── link-example.ts │ │ ├── no-definitions-schema.ts │ │ ├── nullable-refs.ts │ │ ├── oneof-example.ts │ │ ├── personal-api-example.ts │ │ ├── petstore-expanded.ts │ │ ├── petstore.ts │ │ ├── recursive-schema.ts │ │ ├── responses.ts │ │ ├── swaggerhub-template.ts │ │ ├── tsoa-odd-types-3.0.2.ts │ │ ├── up-banking.ts │ │ ├── uspto.ts │ │ ├── wrong-enum-subtypes.ts │ │ └── wrong-schema-names.ts ├── helpers │ ├── createGeneratedApiInfos.js │ ├── createSchemaInfos.js │ ├── generateApiForTest.js │ ├── specGenerateOptions.js │ └── validateGeneratedModule.js ├── schemas │ ├── v2.0 │ │ ├── adafruit.yaml │ │ ├── another-example.json │ │ ├── another-schema.json │ │ ├── api-with-examples.yaml │ │ ├── authentiq.json │ │ ├── enums.json │ │ ├── example1.json │ │ ├── file-formdata-example.json │ │ ├── furkot-example.yaml │ │ ├── giphy.yaml │ │ ├── github-swagger.json │ │ ├── path-args.yaml │ │ ├── petstore-expanded.json │ │ ├── petstore-minimal.json │ │ ├── petstore-simple.yaml │ │ ├── petstore-swagger-io.json │ │ ├── petstore-with-external-docs.yaml │ │ ├── petstore.yaml │ │ ├── query-path-param.yaml │ │ └── uber.yaml │ └── v3.0 │ │ ├── additional-properties.yaml │ │ ├── additional-properties2.json │ │ ├── allof-example.yaml │ │ ├── anyof-example.yaml │ │ ├── api-with-examples.yaml │ │ ├── callback-example.yaml │ │ ├── components-responses.yaml │ │ ├── explode-param-3.0.1.yaml │ │ ├── full-swagger-scheme.json │ │ ├── link-example.yaml │ │ ├── no-definitions-schema.yaml │ │ ├── nullable-refs.yaml │ │ ├── oneof-example.yaml │ │ ├── personal-api-example.json │ │ ├── petstore-expanded.yaml │ │ ├── petstore.yaml │ │ ├── recursive-schema.json │ │ ├── responses.yaml │ │ ├── swaggerhub-template.yaml │ │ ├── tsoa-odd-types-3.0.2.json │ │ ├── up-banking.json │ │ ├── uspto.yaml │ │ ├── wrong-enum-subtypes.yaml │ │ └── wrong-schema-names.yaml ├── spec │ ├── axios │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── axiosSingleHttpClient │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── defaultAsSuccess │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── defaultResponse │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── enumNamesAsValues │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── extractRequestBody │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── extractRequestParams │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── js │ │ ├── schema.d.ts │ │ ├── schema.js │ │ ├── schema.json │ │ └── test.js │ ├── jsAxios │ │ ├── schema.d.ts │ │ ├── schema.js │ │ ├── schema.json │ │ └── test.js │ ├── modular │ │ ├── Key.ts │ │ ├── KeyRoute.ts │ │ ├── Login.ts │ │ ├── LoginRoute.ts │ │ ├── Scope.ts │ │ ├── ScopeRoute.ts │ │ ├── data-contracts.ts │ │ ├── http-client.ts │ │ ├── route-types.ts │ │ ├── schema.json │ │ └── test.js │ ├── moduleNameFirstTag │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── moduleNameIndex │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── noClient │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── partialBaseTemplate │ │ ├── schema.json │ │ ├── schema.ts │ │ ├── spec_templates │ │ │ └── data-contracts.eta │ │ └── test.js │ ├── partialDefaultTemplate │ │ ├── schema.json │ │ ├── schema.ts │ │ ├── spec_templates │ │ │ └── api.eta │ │ └── test.js │ ├── patch │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── responses │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── routeTypes │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── singleHttpClient │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── specProperty │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ ├── templates │ │ ├── schema.json │ │ ├── schema.ts │ │ ├── spec_templates │ │ │ ├── api.eta │ │ │ ├── data-contracts.eta │ │ │ ├── http-client.eta │ │ │ ├── procedure-call.eta │ │ │ ├── route-docs.eta │ │ │ ├── route-name.eta │ │ │ ├── route-type.eta │ │ │ └── route-types.eta │ │ └── test.js │ ├── typeSuffixPrefix │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js │ └── unionEnums │ │ ├── schema.json │ │ ├── schema.ts │ │ └── test.js └── validate.js └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: js2me 2 | custom: ["https://paypal.me/acacode"] 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Run tests 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | pull_request: 9 | branches: [ master, next ] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "build" 17 | build: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-latest 20 | strategy: 21 | matrix: 22 | node-version: [11.x, 13.x, 15.x] 23 | 24 | name: Node.js (test-all) ${{ matrix.node-version }} 25 | 26 | # Steps represent a sequence of tasks that will be executed as part of the job 27 | steps: 28 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 29 | - uses: actions/checkout@v2 30 | 31 | # Runs a single command using the runners shell 32 | - name: install deps 33 | run: npm i 34 | 35 | # Runs a set of commands using the runners shell 36 | - name: test 37 | run: npm run test-all-extended 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | .idea 4 | swagger-test-cli.* 5 | swagger-test-cli 6 | dist -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | tests/**/*.ts 2 | tests/**/schema.js 3 | tests/**/*.d.js 4 | swagger-test-cli 5 | swagger-test-cli.* 6 | templates 7 | *.md 8 | .github 9 | node_modules 10 | .openapi-generator 11 | .vscode 12 | assets 13 | templates -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug generate", 9 | "type": "node", 10 | "request": "launch", 11 | "cwd": "${workspaceFolder}", 12 | "runtimeExecutable": "npm", 13 | "runtimeArgs": ["run-script", "generate:debug"] 14 | }, 15 | { 16 | "name": "Debug JSON CLI", 17 | "type": "node", 18 | "request": "launch", 19 | "cwd": "${workspaceFolder}", 20 | "runtimeExecutable": "npm", 21 | "runtimeArgs": ["run-script", "cli:json"] 22 | }, 23 | { 24 | "name": "Debug YAML CLI", 25 | "type": "node", 26 | "request": "launch", 27 | "cwd": "${workspaceFolder}", 28 | "runtimeExecutable": "npm", 29 | "runtimeArgs": ["run-script", "cli:yaml"] 30 | }, 31 | { 32 | "name": "Debug Node", 33 | "type": "node", 34 | "request": "launch", 35 | "cwd": "${workspaceFolder}", 36 | "runtimeExecutable": "npm", 37 | "runtimeArgs": ["run-script", "node:debug"] 38 | }, 39 | { 40 | "name": "Debug partialTemplates test", 41 | "type": "node", 42 | "request": "launch", 43 | "cwd": "${workspaceFolder}", 44 | "runtimeExecutable": "npm", 45 | "runtimeArgs": ["run-script", "test:partialBaseTemplate"] 46 | }, 47 | { 48 | "name": "Debug test:--templates", 49 | "type": "node", 50 | "request": "launch", 51 | "cwd": "${workspaceFolder}", 52 | "runtimeExecutable": "npm", 53 | "runtimeArgs": ["run-script", "test:--templates"] 54 | }, 55 | { 56 | "name": "Debug test-all", 57 | "type": "node", 58 | "request": "launch", 59 | "cwd": "${workspaceFolder}", 60 | "runtimeExecutable": "npm", 61 | "runtimeArgs": ["run-script", "test-all"] 62 | }, 63 | { 64 | "name": "Debug test-all-extended", 65 | "type": "node", 66 | "request": "launch", 67 | "cwd": "${workspaceFolder}", 68 | "runtimeExecutable": "npm", 69 | "runtimeArgs": ["run-script", "test-all-extended"] 70 | }, 71 | { 72 | "name": "Debug test:--extract-request-body", 73 | "type": "node", 74 | "request": "launch", 75 | "cwd": "${workspaceFolder}", 76 | "runtimeExecutable": "npm", 77 | "runtimeArgs": ["run-script", "test:--extract-request-body"] 78 | }, 79 | { 80 | "name": "Debug test:--type-suffix--type-prefix", 81 | "type": "node", 82 | "request": "launch", 83 | "cwd": "${workspaceFolder}", 84 | "runtimeExecutable": "npm", 85 | "runtimeArgs": ["run-script", "test:--type-suffix--type-prefix"] 86 | } 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-present acacode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal 2 | -------------------------------------------------------------------------------- /assets/auth-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/auth-example.gif -------------------------------------------------------------------------------- /assets/changelog_assets/1.2.5_route_naming.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/1.2.5_route_naming.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/additional-properties-types.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/additional-properties-types.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/api-module-description.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/api-module-description.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/bug-with-no-path-args.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/bug-with-no-path-args.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/bug-with-no-path-args2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/bug-with-no-path-args2.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/changes_procedure_call_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/changes_procedure_call_1.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/extractRequestParams.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/extractRequestParams.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/fixComplexTypeAny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/fixComplexTypeAny.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/http-client-class1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/http-client-class1.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/http-client-class2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/http-client-class2.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/http-client-template-diff-4.3.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/http-client-template-diff-4.3.0.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/onCreateRequestParamsHook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/onCreateRequestParamsHook.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/one-line-comments.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/one-line-comments.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/removed-title-version-props.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/removed-title-version-props.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/responses-catch-types.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/responses-catch-types.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/responses-comments.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/responses-comments.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/right-comments-space.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/right-comments-space.jpg -------------------------------------------------------------------------------- /assets/changelog_assets/route-types.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/changelog_assets/route-types.jpg -------------------------------------------------------------------------------- /assets/components-converter-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/components-converter-example.jpg -------------------------------------------------------------------------------- /assets/npx.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/npx.gif -------------------------------------------------------------------------------- /assets/swagger-typescript-api-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/swagger-typescript-api-logo.png -------------------------------------------------------------------------------- /assets/typings1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grandsilence/swagger-typescript-api-nextgen/cf2bbc01dffc7dead8fae74e98ee7794a988c38c/assets/typings1.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-typescript-api-nextgen", 3 | "version": "10.0.1", 4 | "description": "Nextgen TypeScript/JavaScript API generator from Swagger schema", 5 | "scripts": { 6 | "cli:json": "node index.js -r -d -p ./swagger-test-cli.json -n swagger-test-cli.ts", 7 | "cli:yaml": "node index.js -r -d -p ./swagger-test-cli.yaml -n swagger-test-cli.ts", 8 | "node": "node swagger-test-cli/generate.js", 9 | "node:debug": "node --nolazy swagger-test-cli/generate.js", 10 | "contributors": "all-contributors generate", 11 | "cli:help": "node index.js -h", 12 | "test-all": "node --unhandled-rejections=strict ./scriptsRunner.js generate validate test:*", 13 | "test-all-extended": "node --unhandled-rejections=strict ./scriptsRunner.js generate-extended validate generate validate test:*", 14 | "test-specific": "node ./scriptsRunner.js generate validate test:*", 15 | "prepare": "npm run test-all-extended", 16 | "generate": "node tests/generate.js", 17 | "generate-extended": "node tests/generate-extended.js", 18 | "generate:debug": "node --nolazy tests/generate.js", 19 | "validate": "node tests/validate.js", 20 | "validate:debug": "node --nolazy tests/validate.js", 21 | "test:--route-types": "node tests/spec/routeTypes/test.js", 22 | "test:--no-client": "node tests/spec/noClient/test.js", 23 | "test:--default-as-success": "node tests/spec/defaultAsSuccess/test.js", 24 | "test:--templates": "node tests/spec/templates/test.js", 25 | "test:--union-enums": "node tests/spec/unionEnums/test.js", 26 | "test:--responses": "node tests/spec/responses/test.js", 27 | "test:specProperty": "node tests/spec/specProperty/test.js", 28 | "test:--module-name-index": "node tests/spec/moduleNameIndex/test.js", 29 | "test:--module-name-first-tag": "node tests/spec/moduleNameFirstTag/test.js", 30 | "test:--modular": "node tests/spec/modular/test.js", 31 | "test:--single-http-client": "node tests/spec/singleHttpClient/test.js", 32 | "test:--extract-request-params": "node tests/spec/extractRequestParams/test.js", 33 | "test:--extract-request-body": "node tests/spec/extractRequestBody/test.js", 34 | "test:--enum-names-as-values": "node tests/spec/enumNamesAsValues/test.js", 35 | "test:--default-response": "node tests/spec/defaultResponse/test.js", 36 | "test:--js": "node tests/spec/js/test.js", 37 | "test:--js--axios": "node tests/spec/jsAxios/test.js", 38 | "test:--axios": "node tests/spec/axios/test.js", 39 | "test:--axios--single-http-client": "node tests/spec/axiosSingleHttpClient/test.js", 40 | "test:--type-suffix--type-prefix": "node tests/spec/typeSuffixPrefix/test.js", 41 | "test:partialBaseTemplate": "node tests/spec/partialBaseTemplate/test.js", 42 | "test:partialDefaultTemplate": "node tests/spec/partialDefaultTemplate/test.js", 43 | "test:--patch": "node tests/spec/patch/test.js" 44 | }, 45 | "author": "grandsilence", 46 | "license": "MIT", 47 | "typings": "./index.d.ts", 48 | "main": "src/index.js", 49 | "devDependencies": { 50 | "@types/lodash": "^4.14.166", 51 | "@types/node": "^15.0.2", 52 | "@types/prettier": "^2.1.6", 53 | "all-contributors-cli": "^6.19.0", 54 | "axios": "^0.27.2", 55 | "husky": "^4.3.6", 56 | "pretty-quick": "^3.1.0" 57 | }, 58 | "dependencies": { 59 | "@types/swagger-schema-official": "2.0.21", 60 | "commander": "^6.2.1", 61 | "cosmiconfig": "^7.0.0", 62 | "eta": "^1.12.1", 63 | "js-yaml": "^4.0.0", 64 | "lodash": "^4.17.21", 65 | "make-dir": "^3.1.0", 66 | "nanoid": "^3.1.22", 67 | "node-emoji": "^1.10.0", 68 | "prettier": "^2.2.1", 69 | "swagger-schema-official": "2.0.0-bab6bed", 70 | "swagger2openapi": "^7.0.5", 71 | "typescript": "^4.2.4" 72 | }, 73 | "bin": { 74 | "swagger-typescript-api-nextgen": "index.js", 75 | "sta": "index.js" 76 | }, 77 | "husky": { 78 | "hooks": { 79 | "pre-commit": "pretty-quick --staged", 80 | "post-commit": "git update-index -g" 81 | } 82 | }, 83 | "keywords": [ 84 | "openapi", 85 | "swagger", 86 | "typescript", 87 | "api", 88 | "javascript", 89 | "rest", 90 | "codegen", 91 | "generation", 92 | "http" 93 | ], 94 | "files": [ 95 | "src", 96 | "index.js", 97 | "index.d.ts", 98 | "templates", 99 | "LICENSE" 100 | ], 101 | "bugs": { 102 | "url": "https://github.com/grandsilence/swagger-typescript-api-nextgen/issues" 103 | }, 104 | "homepage": "https://github.com/grandsilence/swagger-typescript-api-nextgen", 105 | "repository": { 106 | "type": "git", 107 | "url": "git://github.com/grandsilence/swagger-typescript-api-nextgen" 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /scriptsRunner.js: -------------------------------------------------------------------------------- 1 | const packageJson = require("./package.json"); 2 | const { exec, spawn } = require("child_process"); 3 | 4 | const commands = process.argv.slice(2); 5 | 6 | const packageScripts = Object.keys(packageJson.scripts); 7 | 8 | const execute = (scriptName) => 9 | new Promise((resolve, reject) => { 10 | console.log(`npm run ${scriptName}`); 11 | const spawned = spawn(/^win/.test(process.platform) ? "npm.cmd" : "npm", ["run", scriptName]); 12 | 13 | spawned.stdout.on("data", (data) => { 14 | process.stdout.write(data); 15 | }); 16 | 17 | spawned.stderr.on("data", (data) => { 18 | process.stderr.write(data); 19 | }); 20 | 21 | spawned.on("error", (error) => { 22 | console.error(error); 23 | }); 24 | 25 | spawned.on("message", (message) => { 26 | console.log(message); 27 | }); 28 | 29 | spawned.on("close", (code) => { 30 | if (code) { 31 | reject(code); 32 | } else { 33 | resolve(code); 34 | } 35 | }); 36 | }); 37 | 38 | const run = async () => { 39 | for (const command of commands) { 40 | for (const scriptName of packageScripts) { 41 | try { 42 | if (scriptName === command) { 43 | await execute(scriptName); 44 | } 45 | 46 | if (command.includes("*")) { 47 | const commandPart = command.replace("*", ""); 48 | // TODO: refactor 49 | if (scriptName.startsWith(commandPart) || scriptName.endsWith(commandPart)) { 50 | await execute(scriptName); 51 | } 52 | } 53 | } catch (e) { 54 | process.exit(1); 55 | } 56 | } 57 | } 58 | }; 59 | 60 | run(); 61 | -------------------------------------------------------------------------------- /src/apiConfig.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | 3 | const createApiConfig = (swaggerSchema) => { 4 | const { info, servers, host, basePath, externalDocs, tags } = swaggerSchema; 5 | const server = (servers && servers[0]) || { url: "" }; 6 | const { title = "No title", version, description: schemaDescription = "" } = info || {}; 7 | const { url: serverUrl } = server; 8 | 9 | return { 10 | info: info || {}, 11 | servers: servers || [], 12 | basePath, 13 | host, 14 | externalDocs: _.merge( 15 | { 16 | url: "", 17 | description: "", 18 | }, 19 | externalDocs, 20 | ), 21 | tags: _.compact(tags), 22 | baseUrl: serverUrl, 23 | title, 24 | version, 25 | }; 26 | }; 27 | 28 | module.exports = { 29 | createApiConfig, 30 | }; 31 | -------------------------------------------------------------------------------- /src/common.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | 3 | module.exports = { 4 | formatDescription: (description, inline) => { 5 | if (!description) return ""; 6 | 7 | let prettified = description; 8 | 9 | prettified = _.replace(prettified, /\*\//g, "*/"); 10 | 11 | const hasMultipleLines = _.includes(prettified, "\n"); 12 | 13 | if (!hasMultipleLines) return prettified; 14 | 15 | if (inline) { 16 | return _(prettified) 17 | .split(/\n/g) 18 | .map((part) => _.trim(part)) 19 | .compact() 20 | .join(" ") 21 | .valueOf(); 22 | } 23 | 24 | return _.replace(prettified, /\n$/g, ""); 25 | }, 26 | internalCase: (value) => _.camelCase(_.lowerCase(value)), 27 | classNameCase: (value) => _.upperFirst(_.camelCase(value)), 28 | }; 29 | -------------------------------------------------------------------------------- /src/components.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const { parseSchema } = require("./schema"); 3 | const { config } = require("./config"); 4 | 5 | /** 6 | * @typedef {"schemas" | "examples" | "headers" | "parameters" | "requestBodies" | "responses" | "securitySchemes"} ComponentName 7 | * @typedef {object} RawTypeData 8 | * @typedef {{ type, typeIdentifier, name, description, content }} TypeData 9 | * 10 | * @typedef {{ 11 | * typeName: string; 12 | * componentName: ComponentName; 13 | * rawTypeData: RawTypeData; 14 | * typeData: TypeData | null; 15 | * }} TypeInfo 16 | */ 17 | 18 | /** 19 | * 20 | * @param {ComponentName} componentName 21 | * @param {string} typeName 22 | * @param {RawTypeData} rawTypeData 23 | * @returns {TypeInfo} 24 | */ 25 | const createComponent = (componentName, typeName, rawTypeData) => { 26 | const $ref = `#/components/${componentName}/${typeName}`; 27 | 28 | const componentSchema = { 29 | $ref, 30 | typeName, 31 | rawTypeData, 32 | componentName, 33 | typeData: null, 34 | }; 35 | 36 | const usageComponent = config.hooks.onCreateComponent(componentSchema) || componentSchema; 37 | 38 | config.componentsMap[$ref] = usageComponent; 39 | 40 | return usageComponent; 41 | }; 42 | 43 | /** 44 | * @returns {{ [key: string]: TypeInfo }} 45 | */ 46 | const createComponentsMap = (components) => { 47 | config.componentsMap = {}; 48 | 49 | _.each(components, (component, componentName) => 50 | _.each(component, (rawTypeData, typeName) => 51 | createComponent(componentName, typeName, rawTypeData), 52 | ), 53 | ); 54 | 55 | return config.componentsMap; 56 | }; 57 | 58 | /** 59 | * 60 | * @param {{ [key: string]: TypeInfo }} componentsMap 61 | * @param {ComponentName} componentName 62 | * @returns {TypeInfo[]} 63 | */ 64 | const filterComponentsMap = (componentsMap, componentName) => 65 | _.filter(componentsMap, (v, ref) => _.startsWith(ref, `#/components/${componentName}`)); 66 | 67 | /** 68 | * 69 | * @param {TypeInfo} typeInfo 70 | * @returns {TypeData} 71 | */ 72 | const getTypeData = (typeInfo) => { 73 | if (!typeInfo.typeData) { 74 | typeInfo.typeData = parseSchema(typeInfo.rawTypeData, typeInfo.typeName); 75 | } 76 | 77 | return typeInfo.typeData; 78 | }; 79 | 80 | /** 81 | * 82 | * @param {string} ref 83 | * @returns {TypeInfo | undefined} 84 | */ 85 | const getComponentByRef = (ref) => config.componentsMap[ref]; 86 | 87 | module.exports = { 88 | getTypeData, 89 | createComponent, 90 | createComponentsMap, 91 | filterComponentsMap, 92 | getComponentByRef, 93 | }; 94 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | const { HTTP_CLIENT, TS_KEYWORDS, PRETTIER_OPTIONS } = require("./constants"); 2 | const { NameResolver } = require("./utils/resolveName"); 3 | 4 | const config = { 5 | /** CLI flag */ 6 | templates: "../templates/default", 7 | /** CLI flag */ 8 | generateResponses: false, 9 | /** CLI flag */ 10 | defaultResponseAsSuccess: false, 11 | /** CLI flag */ 12 | generateRouteTypes: false, 13 | /** CLI flag */ 14 | generateClient: true, 15 | /** CLI flag */ 16 | generateUnionEnums: false, 17 | enumNamesAsValues: false, 18 | /** parsed swagger schema from getSwaggerObject() */ 19 | 20 | /** parsed swagger schema ref */ 21 | swaggerSchema: null, 22 | /** original (converted to json) swagger schema ref */ 23 | originalSchema: null, 24 | 25 | /** { "#/components/schemas/Foo": @TypeInfo, ... } */ 26 | componentsMap: {}, 27 | /** flag for catching convertion from swagger 2.0 */ 28 | convertedFromSwagger2: false, 29 | 30 | /** url index from paths used for merging into modules */ 31 | moduleNameIndex: 0, 32 | 33 | /** use the first tag for the module name */ 34 | moduleNameFirstTag: false, 35 | disableStrictSSL: false, 36 | disableProxy: false, 37 | extractRequestParams: false, 38 | extractRequestBody: false, 39 | fileNames: { 40 | dataContracts: "data-contracts", 41 | routeTypes: "route-types", 42 | httpClient: "http-client", 43 | outOfModuleApi: "Common", 44 | }, 45 | routeNameDuplicatesMap: new Map(), 46 | prettierOptions: PRETTIER_OPTIONS, 47 | hooks: { 48 | onCreateComponent: (schema) => schema, 49 | onParseSchema: (originalSchema, parsedSchema) => parsedSchema, 50 | onCreateRoute: (routeData) => routeData, 51 | onInit: (config) => config, 52 | onPrepareConfig: (apiConfig) => apiConfig, 53 | onCreateRequestParams: (rawType) => {}, 54 | onCreateRouteName: () => {}, 55 | onFormatTypeName: (typeName, rawTypeName) => {}, 56 | onFormatRouteName: (routeInfo, templateRouteName) => {}, 57 | }, 58 | defaultResponseType: TS_KEYWORDS.VOID, 59 | singleHttpClient: false, 60 | httpClientType: HTTP_CLIENT.FETCH, 61 | unwrapResponseData: false, 62 | disableThrowOnError: false, 63 | sortTypes: false, 64 | templatePaths: { 65 | /** `templates/base` */ 66 | base: "", 67 | /** `templates/default` */ 68 | default: "", 69 | /** `templates/modular` */ 70 | modular: "", 71 | /** usage path if `--templates` option is not set */ 72 | original: "", 73 | /** custom path to templates (`--templates`) */ 74 | custom: "", 75 | }, 76 | /** Record */ 77 | templatesToRender: { 78 | api: "", 79 | dataContracts: "", 80 | httpClient: "", 81 | routeTypes: "", 82 | routeName: "", 83 | }, 84 | toJS: false, 85 | silent: false, 86 | typePrefix: "", 87 | typeSuffix: "", 88 | patch: false, 89 | componentTypeNameResolver: new NameResolver([]), 90 | }; 91 | 92 | /** needs to use data everywhere in project */ 93 | module.exports = { 94 | addToConfig: (configParts) => Object.assign(config, configParts), 95 | config, 96 | }; 97 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | const TS_KEYWORDS = { 2 | NUMBER: "number", 3 | STRING: "string", 4 | BOOLEAN: "boolean", 5 | ANY: "any", 6 | VOID: "void", 7 | UNKNOWN: "unknown", 8 | NULL: "null", 9 | UNDEFINED: "undefined", 10 | OBJECT: "object", 11 | FILE: "File", 12 | DATE: "Date", 13 | TYPE: "type", 14 | ENUM: "enum", 15 | INTERFACE: "interface", 16 | }; 17 | 18 | const JS_PRIMITIVE_TYPES = [TS_KEYWORDS.NUMBER, TS_KEYWORDS.STRING, TS_KEYWORDS.BOOLEAN]; 19 | const JS_EMPTY_TYPES = [TS_KEYWORDS.NULL, TS_KEYWORDS.UNDEFINED]; 20 | 21 | const RESERVED_QUERY_ARG_NAMES = ["query", "queryParams", "queryArg"]; 22 | const RESERVED_BODY_ARG_NAMES = ["data", "body", "reqBody"]; 23 | const RESERVED_REQ_PARAMS_ARG_NAMES = ["params", "requestParams", "reqParams", "httpParams"]; 24 | const RESERVED_PATH_ARG_NAMES = ["path", "pathParams"]; 25 | const RESERVED_HEADER_ARG_NAMES = ["headers", "headersParams"]; 26 | 27 | const SCHEMA_TYPES = { 28 | ARRAY: "array", 29 | OBJECT: "object", 30 | ENUM: "enum", 31 | REF: "$ref", 32 | PRIMITIVE: "primitive", 33 | COMPLEX: "complex", 34 | COMPLEX_ONE_OF: "oneOf", 35 | COMPLEX_ANY_OF: "anyOf", 36 | COMPLEX_ALL_OF: "allOf", 37 | COMPLEX_NOT: "not", 38 | COMPLEX_UNKNOWN: "__unknown", 39 | }; 40 | 41 | const HTTP_CLIENT = { 42 | FETCH: "fetch", 43 | AXIOS: "axios", 44 | }; 45 | 46 | module.exports = { 47 | DEFAULT_BODY_ARG_NAME: "data", 48 | SUCCESS_RESPONSE_STATUS_RANGE: [200, 300], 49 | JS_PRIMITIVE_TYPES, 50 | JS_EMPTY_TYPES, 51 | TS_KEYWORDS, 52 | SCHEMA_TYPES, 53 | HTTP_CLIENT, 54 | RESERVED_QUERY_ARG_NAMES, 55 | RESERVED_BODY_ARG_NAMES, 56 | RESERVED_REQ_PARAMS_ARG_NAMES, 57 | RESERVED_PATH_ARG_NAMES, 58 | RESERVED_HEADER_ARG_NAMES, 59 | PRETTIER_OPTIONS: { 60 | printWidth: 120, 61 | tabWidth: 2, 62 | trailingComma: "all", 63 | parser: "typescript", 64 | }, 65 | }; 66 | -------------------------------------------------------------------------------- /src/filePrefix.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | filePrefix: `/* eslint-disable */ 3 | /* tslint:disable */ 4 | /* 5 | * ------------------------------------------------------------------ 6 | * # THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API-NEXTGEN # 7 | * # AUTHORS: acacode & grandsilence # 8 | * # https://github.com/grandsilence/swagger-typescript-api-nextgen # 9 | * ------------------------------------------------------------------ 10 | */ 11 | 12 | `, 13 | }; 14 | -------------------------------------------------------------------------------- /src/files.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const fs = require("fs"); 3 | const { resolve } = require("path"); 4 | const { filePrefix } = require("./filePrefix"); 5 | const makeDir = require("make-dir"); 6 | 7 | const getFileContent = (path) => { 8 | return fs.readFileSync(path, { encoding: "UTF-8" }); 9 | }; 10 | 11 | const pathIsDir = (path) => { 12 | if (!path) return false; 13 | 14 | try { 15 | const stat = fs.statSync(path); 16 | return stat.isDirectory(); 17 | } catch (e) { 18 | return false; 19 | } 20 | }; 21 | 22 | const removeDir = (path) => { 23 | try { 24 | fs.rmdirSync(path, { recursive: true }); 25 | } catch (e) {} 26 | }; 27 | 28 | const createDir = (path) => { 29 | try { 30 | makeDir.sync(path); 31 | } catch (e) {} 32 | }; 33 | 34 | const cleanDir = (path) => { 35 | removeDir(path); 36 | createDir(path); 37 | }; 38 | 39 | const pathIsExist = (path) => path && fs.existsSync(path); 40 | 41 | const createFile = ({ path, fileName, content, withPrefix }) => 42 | fs.writeFileSync( 43 | resolve(__dirname, path, `./${fileName}`), 44 | `${withPrefix ? filePrefix : ""}${content}`, 45 | _.noop, 46 | ); 47 | 48 | module.exports = { 49 | createFile, 50 | pathIsDir, 51 | cleanDir, 52 | pathIsExist, 53 | createDir, 54 | removeDir, 55 | getFileContent, 56 | }; 57 | -------------------------------------------------------------------------------- /src/formatFileContent.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const prettier = require("prettier"); 3 | const { config } = require("./config"); 4 | const ts = require("typescript"); 5 | 6 | class LanguageServiceHost { 7 | constructor(fileName, content) { 8 | const tsconfig = ts.findConfigFile(fileName, ts.sys.fileExists); 9 | 10 | Object.assign(this, { 11 | fileName, 12 | content, 13 | compilerOptions: tsconfig 14 | ? ts.convertCompilerOptionsFromJson( 15 | ts.readConfigFile(tsconfig, ts.sys.readFile).config.compilerOptions, 16 | ).options 17 | : ts.getDefaultCompilerOptions(), 18 | }); 19 | } 20 | 21 | getNewLine() { 22 | return "\n"; 23 | } 24 | getScriptFileNames() { 25 | return [this.fileName]; 26 | } 27 | getCompilationSettings() { 28 | return this.compilerOptions; 29 | } 30 | getDefaultLibFileName() { 31 | return ts.getDefaultLibFileName(this.getCompilationSettings()); 32 | } 33 | getCurrentDirectory() { 34 | return process.cwd(); 35 | } 36 | getScriptVersion() { 37 | return ts.version; 38 | } 39 | getScriptSnapshot() { 40 | return ts.ScriptSnapshot.fromString(this.content); 41 | } 42 | } 43 | 44 | const removeUnusedImports = (content) => { 45 | const tempFileName = "file.ts"; 46 | 47 | const host = new LanguageServiceHost(tempFileName, content); 48 | const languageService = ts.createLanguageService(host); 49 | 50 | const fileTextChanges = languageService.organizeImports( 51 | { type: "file", fileName: tempFileName }, 52 | { newLineCharacter: ts.sys.newLine }, 53 | )[0]; 54 | 55 | if (fileTextChanges && fileTextChanges.textChanges.length) { 56 | return _.reduceRight( 57 | fileTextChanges.textChanges, 58 | (content, { span, newText }) => 59 | `${content.slice(0, span.start)}${newText}${content.slice(span.start + span.length)}`, 60 | content, 61 | ); 62 | } 63 | 64 | return content; 65 | }; 66 | 67 | const prettierFormat = (content) => { 68 | return prettier.format(content, config.prettierOptions); 69 | }; 70 | 71 | const formatters = [removeUnusedImports, prettierFormat]; 72 | 73 | module.exports = (content) => 74 | formatters.reduce((fixedContent, formatter) => formatter(fixedContent), content); 75 | -------------------------------------------------------------------------------- /src/logger.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const { config } = require("./config"); 3 | const { emojify, emoji } = require("node-emoji"); 4 | 5 | /** 6 | * 7 | * @param {{ type: "warn" | "log" | "error", emojiName: keyof emoji, messages: unknown[] }} payload 8 | * @returns {void} 9 | */ 10 | const createLogMessage = ({ type, emojiName, messages }) => { 11 | if (config.silent) return; 12 | 13 | const emoji = emojify(emojiName); 14 | 15 | console[type]( 16 | emoji, 17 | " ", 18 | ..._.map(messages, (message) => 19 | _.startsWith(message, "\n") ? `\n${emoji} ${message.replace(/\n/, "")}` : message, 20 | ), 21 | ); 22 | }; 23 | 24 | const logger = { 25 | log: (...messages) => 26 | createLogMessage({ 27 | type: "log", 28 | emojiName: ":sparkles:", 29 | messages, 30 | }), 31 | event: (...messages) => 32 | createLogMessage({ 33 | type: "log", 34 | emojiName: ":comet: ", 35 | messages, 36 | }), 37 | success: (...messages) => 38 | createLogMessage({ 39 | type: "log", 40 | emojiName: ":white_check_mark:", 41 | messages, 42 | }), 43 | warn: (...messages) => 44 | createLogMessage({ 45 | type: "warn", 46 | emojiName: ":warning: ", 47 | messages, 48 | }), 49 | error: (...messages) => 50 | createLogMessage({ 51 | type: "error", 52 | emojiName: ":exclamation:", 53 | messages, 54 | }), 55 | }; 56 | 57 | module.exports = { 58 | logger, 59 | }; 60 | -------------------------------------------------------------------------------- /src/modelNames.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const { config } = require("./config"); 3 | const { logger } = require("./logger"); 4 | 5 | const isValidName = (name) => /^([A-Za-z$_]{1,})$/g.test(name); 6 | 7 | const formattedModelNamesMap = new Map(); 8 | 9 | const fixModelName = (name) => { 10 | if (!isValidName(name)) { 11 | if (!/^[a-zA-Z_$]/g.test(name)) { 12 | name = `Type ${name}`; 13 | } 14 | 15 | // specific replaces for TSOA 3.x 16 | if (name.includes(".")) 17 | name = name 18 | .replace(/Exclude_keyof[A-Za-z]{1,}/g, (match) => "ExcludeKeys") 19 | .replace(/%22\~AND\~%22/g, "And") 20 | .replace(/%22\~OR\~%22/g, "Or") 21 | .replace(/(\.?%22)|\./g, "_") 22 | .replace(/__+$/, ""); 23 | 24 | if (name.includes("-")) name = _.startCase(name).replace(/ /g, ""); 25 | } 26 | 27 | return name; 28 | }; 29 | 30 | /** 31 | * 32 | * @param {string} name 33 | * @param {{ ignorePrefix?: boolean; ignoreSuffix?: boolean }} options 34 | * @returns 35 | */ 36 | const formatModelName = (name, options) => { 37 | const typePrefix = options && options.ignorePrefix ? "" : config.typePrefix; 38 | const typeSuffix = options && options.ignoreSuffix ? "" : config.typeSuffix; 39 | const hashKey = `${typePrefix}_${name}_${typeSuffix}`; 40 | 41 | if (typeof name !== "string") { 42 | logger.warn("wrong name of the model name", name); 43 | return name; 44 | } 45 | 46 | if (/^([A-Z_]{1,})$/g.test(name)) { 47 | return _.compact([typePrefix, name, typeSuffix]).join("_"); 48 | } 49 | 50 | if (formattedModelNamesMap.has(hashKey)) { 51 | return formattedModelNamesMap.get(hashKey); 52 | } 53 | 54 | const fixedModelName = fixModelName(name); 55 | 56 | const formattedModelName = _.replace( 57 | _.startCase(`${typePrefix}_${fixedModelName}_${typeSuffix}`), 58 | /\s/g, 59 | "", 60 | ); 61 | const modelName = config.hooks.onFormatTypeName(formattedModelName, name) || formattedModelName; 62 | 63 | formattedModelNamesMap.set(hashKey, modelName); 64 | 65 | return modelName; 66 | }; 67 | 68 | const formatEnumKey = (key) => 69 | formatModelName(key, { 70 | ignorePrefix: true, 71 | ignoreSuffix: true, 72 | }); 73 | 74 | module.exports = { 75 | formatModelName: formatModelName, 76 | formatEnumKey: formatEnumKey, 77 | isValidName, 78 | }; 79 | -------------------------------------------------------------------------------- /src/modelTypes.js: -------------------------------------------------------------------------------- 1 | const { formatters } = require("./typeFormatters"); 2 | const { formatModelName } = require("./modelNames"); 3 | const { config } = require("./config"); 4 | const { getTypeData } = require("./components"); 5 | 6 | /** 7 | * 8 | * @param {import("./components").TypeInfo} typeInfo 9 | * @returns 10 | */ 11 | const prepareModelType = (typeInfo) => { 12 | const typeData = getTypeData(typeInfo); 13 | let { typeIdentifier, name: originalName, content, type, description } = typeData; 14 | 15 | const resultContent = formatters[type] ? formatters[type](content) : content; 16 | const name = formatModelName(originalName); 17 | 18 | return { 19 | typeIdentifier, 20 | name, 21 | description, 22 | rawContent: content, 23 | content: resultContent, 24 | typeData, 25 | }; 26 | }; 27 | 28 | module.exports = { 29 | prepareModelType, 30 | }; 31 | -------------------------------------------------------------------------------- /src/prettierOptions.js: -------------------------------------------------------------------------------- 1 | const { cosmiconfigSync } = require("cosmiconfig"); 2 | const constants = require("./constants"); 3 | 4 | /** 5 | * Get prettier options from user's project or return the default one 6 | * @return {import('prettier').Options} Prettier options 7 | */ 8 | function getPrettierOptions() { 9 | const prettier = cosmiconfigSync("prettier").search(); 10 | 11 | if (prettier) { 12 | return { 13 | ...prettier.config, 14 | parser: "typescript", 15 | }; 16 | } 17 | 18 | return constants.PRETTIER_OPTIONS; 19 | } 20 | 21 | module.exports = { 22 | getPrettierOptions, 23 | }; 24 | -------------------------------------------------------------------------------- /src/render/utils/fmtToJSDocLine.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Format a line in JSDOC line by adding ' *' at the beginning and \n character at the end 3 | * 4 | * @example 'abc' -> ' * abc\n' 5 | * @param line 6 | * @return {string} 7 | */ 8 | module.exports = function fmtToJSDocLine(line, { eol = true }) { 9 | return ` * ${line}${eol ? "\n" : ""}`; 10 | }; 11 | -------------------------------------------------------------------------------- /src/render/utils/index.js: -------------------------------------------------------------------------------- 1 | const { classNameCase, formatDescription, internalCase } = require("../../common"); 2 | const { getComponentByRef } = require("../../components"); 3 | const { formatModelName } = require("../../modelNames"); 4 | const { getInlineParseContent, getParseContent, parseSchema } = require("../../schema"); 5 | const { formatters, inlineExtraFormatters } = require("../../typeFormatters"); 6 | const { NameResolver } = require("../../utils/resolveName"); 7 | 8 | module.exports = { 9 | formatDescription, 10 | internalCase, 11 | classNameCase, 12 | getInlineParseContent, 13 | getParseContent, 14 | getComponentByRef, 15 | parseSchema, 16 | formatters, 17 | inlineExtraFormatters, 18 | formatModelName, 19 | fmtToJSDocLine: require("./fmtToJSDocLine"), 20 | NameResolver: NameResolver, 21 | _: require("lodash"), 22 | require: require("./templateRequire").templateRequire, 23 | }; 24 | -------------------------------------------------------------------------------- /src/render/utils/templateRequire.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const path = require("path"); 3 | const { config } = require("../../config"); 4 | 5 | const templateRequire = (packageOrPath) => { 6 | const isPath = _.startsWith(packageOrPath, "./") || _.startsWith(packageOrPath, "../"); 7 | 8 | if (isPath) { 9 | return require(path.resolve(config.templates, packageOrPath)); 10 | } 11 | 12 | return require(packageOrPath); 13 | }; 14 | 15 | module.exports = { 16 | templateRequire, 17 | }; 18 | -------------------------------------------------------------------------------- /src/routeNames.js: -------------------------------------------------------------------------------- 1 | const { config } = require("./config"); 2 | const { logger } = require("./logger"); 3 | const { renderTemplate } = require("./templates"); 4 | 5 | const getRouteName = (routeInfo) => { 6 | const { moduleName } = routeInfo; 7 | const { routeNameDuplicatesMap, templatesToRender } = config; 8 | const routeNameTemplate = templatesToRender.routeName; 9 | 10 | const routeNameFromTemplate = renderTemplate(routeNameTemplate, { 11 | routeInfo: routeInfo, 12 | utils: require("./render/utils"), 13 | config, 14 | }); 15 | 16 | const routeName = 17 | config.hooks.onFormatRouteName(routeInfo, routeNameFromTemplate) || routeNameFromTemplate; 18 | 19 | const duplicateIdentifier = `${moduleName}|${routeName}`; 20 | 21 | if (routeNameDuplicatesMap.has(duplicateIdentifier)) { 22 | routeNameDuplicatesMap.set( 23 | duplicateIdentifier, 24 | routeNameDuplicatesMap.get(duplicateIdentifier) + 1, 25 | ); 26 | 27 | logger.warn( 28 | `Module "${moduleName}" already have method "${routeName}()"`, 29 | `\nThis method has been renamed to "${ 30 | routeName + routeNameDuplicatesMap.get(duplicateIdentifier) 31 | }()" to solve conflict names.`, 32 | ); 33 | } else { 34 | routeNameDuplicatesMap.set(duplicateIdentifier, 1); 35 | } 36 | 37 | const duplicates = routeNameDuplicatesMap.get(duplicateIdentifier); 38 | 39 | const routeNameInfo = { 40 | usage: routeName + (duplicates > 1 ? duplicates : ""), 41 | original: routeName, 42 | duplicate: duplicates > 1, 43 | }; 44 | 45 | return config.hooks.onCreateRouteName(routeNameInfo, routeInfo) || routeNameInfo; 46 | }; 47 | 48 | module.exports = { 49 | getRouteName, 50 | }; 51 | -------------------------------------------------------------------------------- /src/swagger.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const yaml = require("js-yaml"); 3 | const axios = require("axios"); 4 | const converter = require("swagger2openapi"); 5 | const https = require("https"); 6 | const { addToConfig, config } = require("./config"); 7 | const { pathIsExist, getFileContent } = require("./files"); 8 | const { logger } = require("./logger"); 9 | 10 | const parseSwaggerFile = (file) => { 11 | if (typeof file !== "string") return file; 12 | 13 | try { 14 | return JSON.parse(file); 15 | } catch (e) { 16 | return yaml.load(file); 17 | } 18 | }; 19 | 20 | const getSwaggerFile = (pathToSwagger, urlToSwagger, disableStrictSSL, disableProxy) => 21 | new Promise((resolve, reject) => { 22 | if (pathIsExist(pathToSwagger)) { 23 | logger.log(`try to get swagger by path "${pathToSwagger}"`); 24 | resolve(getFileContent(pathToSwagger)); 25 | } else { 26 | logger.log(`try to get swagger by URL "${urlToSwagger}"`); 27 | // setup options for Axios 28 | const axiosOptions = {}; 29 | // 30 | if (disableStrictSSL) { 31 | axiosOptions.httpsAgent = new https.Agent({ 32 | rejectUnauthorized: false, 33 | }); 34 | } 35 | // 36 | if (disableProxy) axiosOptions.proxy = false; 37 | // 38 | axios 39 | .get(urlToSwagger, axiosOptions) 40 | .then((res) => resolve(res.data)) 41 | .catch(() => { 42 | const message = `error while getting swagger by URL ${urlToSwagger}`; 43 | 44 | logger.error(message); 45 | 46 | reject(message); 47 | }); 48 | } 49 | }); 50 | 51 | const getSwaggerObject = ( 52 | pathToSwagger, 53 | urlToSwagger, 54 | disableStrictSSL, 55 | disableProxy, 56 | converterOptions, 57 | ) => 58 | getSwaggerFile(pathToSwagger, urlToSwagger, disableStrictSSL, disableProxy).then((file) => 59 | convertSwaggerObject(parseSwaggerFile(file), converterOptions), 60 | ); 61 | 62 | const convertSwaggerObject = (swaggerSchema, converterOptions) => { 63 | return new Promise((resolve) => { 64 | swaggerSchema.info = _.merge( 65 | { 66 | title: "No title", 67 | version: "", 68 | }, 69 | swaggerSchema.info, 70 | ); 71 | 72 | if (!swaggerSchema.openapi) { 73 | swaggerSchema.paths = _.merge({}, swaggerSchema.paths); 74 | 75 | converter.convertObj( 76 | swaggerSchema, 77 | { 78 | ...converterOptions, 79 | warnOnly: true, 80 | refSiblings: "preserve", 81 | rbname: "requestBodyName", 82 | }, 83 | function (err, options) { 84 | const parsedSwaggerSchema = _.get(err, "options.openapi", _.get(options, "openapi")); 85 | if (!parsedSwaggerSchema && err) { 86 | throw new Error(err); 87 | } 88 | addToConfig({ convertedFromSwagger2: true }); 89 | resolve({ 90 | usageSchema: parsedSwaggerSchema, 91 | originalSchema: swaggerSchema, 92 | }); 93 | }, 94 | ); 95 | } else { 96 | resolve({ 97 | usageSchema: swaggerSchema, 98 | originalSchema: swaggerSchema, 99 | }); 100 | } 101 | }); 102 | }; 103 | 104 | const fixSwaggerScheme = (usage, original) => { 105 | const usagePaths = _.get(usage, "paths"); 106 | const originalPaths = _.get(original, "paths"); 107 | 108 | // walk by routes 109 | _.each(usagePaths, (usagePathObject, route) => { 110 | const originalPathObject = _.get(originalPaths, route); 111 | 112 | // walk by methods 113 | _.each(usagePathObject, (usageRouteInfo, methodName) => { 114 | const originalRouteInfo = _.get(originalPathObject, methodName); 115 | const usageRouteParams = _.get(usageRouteInfo, "parameters", []); 116 | const originalRouteParams = _.get(originalRouteInfo, "parameters", []); 117 | 118 | usageRouteInfo.consumes = _.uniq( 119 | _.compact([...(usageRouteInfo.consumes || []), ...(originalRouteInfo.consumes || [])]), 120 | ); 121 | usageRouteInfo.produces = _.uniq( 122 | _.compact([...(usageRouteInfo.produces || []), ...(originalRouteInfo.produces || [])]), 123 | ); 124 | 125 | _.each(originalRouteParams, (originalRouteParam) => { 126 | const existUsageParam = _.find( 127 | usageRouteParams, 128 | (param) => originalRouteParam.in === param.in && originalRouteParam.name === param.name, 129 | ); 130 | if (!existUsageParam) { 131 | usageRouteParams.push(originalRouteParam); 132 | } 133 | }); 134 | }); 135 | }); 136 | }; 137 | 138 | module.exports = { 139 | getSwaggerObject, 140 | fixSwaggerScheme, 141 | convertSwaggerObject, 142 | }; 143 | -------------------------------------------------------------------------------- /src/templates.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const Eta = require("eta"); 3 | const { getFileContent, pathIsExist } = require("./files"); 4 | const { config } = require("./config"); 5 | const { resolve } = require("path"); 6 | const { logger } = require("./logger"); 7 | 8 | /** 9 | * name - project template name, 10 | * fileName - template file name, 11 | */ 12 | const TEMPLATE_INFOS = [ 13 | { name: "api", fileName: "api.eta" }, 14 | { name: "dataContracts", fileName: "data-contracts.eta" }, 15 | { name: "httpClient", fileName: "http-client.eta" }, 16 | { name: "routeTypes", fileName: "route-types.eta" }, 17 | { name: "routeName", fileName: "route-name.eta" }, 18 | ]; 19 | 20 | const getTemplatePaths = ({ templates, modular }) => { 21 | const baseTemplatesPath = resolve(__dirname, "../templates/base"); 22 | const defaultTemplatesPath = resolve(__dirname, "../templates/default"); 23 | const modularTemplatesPath = resolve(__dirname, "../templates/modular"); 24 | const originalTemplatesPath = modular ? modularTemplatesPath : defaultTemplatesPath; 25 | const customTemplatesPath = templates ? resolve(process.cwd(), templates) : originalTemplatesPath; 26 | 27 | return { 28 | /** `templates/base` */ 29 | base: baseTemplatesPath, 30 | /** `templates/default` */ 31 | default: defaultTemplatesPath, 32 | /** `templates/modular` */ 33 | modular: modularTemplatesPath, 34 | /** usage path if `--templates` option is not set */ 35 | original: originalTemplatesPath, 36 | /** custom path to templates (`--templates`) */ 37 | custom: customTemplatesPath, 38 | }; 39 | }; 40 | 41 | const getTemplate = ({ fileName, name, path }) => { 42 | const { templatePaths } = config; 43 | 44 | if (path) { 45 | return getFileContent(path); 46 | } 47 | 48 | if (!fileName) return ""; 49 | 50 | const customFullPath = resolve(templatePaths.custom, "./", fileName); 51 | let fileContent = pathIsExist(customFullPath) && getFileContent(customFullPath); 52 | 53 | if (!fileContent) { 54 | const baseFullPath = resolve(templatePaths.base, "./", fileName); 55 | const originalFullPath = resolve(templatePaths.original, "./", fileName); 56 | 57 | if (pathIsExist(baseFullPath)) { 58 | fileContent = getFileContent(baseFullPath); 59 | } else { 60 | logger.warn( 61 | `${_.lowerCase(name)} template not found in ${customFullPath}`, 62 | `\nCode generator will use the default template`, 63 | ); 64 | } 65 | 66 | if (pathIsExist(originalFullPath)) { 67 | fileContent = getFileContent(originalFullPath); 68 | } 69 | } 70 | 71 | return fileContent; 72 | }; 73 | 74 | const getTemplates = ({ templatePaths }) => { 75 | logger.log(`try to read templates from directory "${templatePaths.custom}"`); 76 | 77 | const templatesMap = _.reduce( 78 | TEMPLATE_INFOS, 79 | (acc, { fileName, name }) => ({ 80 | ...acc, 81 | [name]: getTemplate({ fileName, name }), 82 | }), 83 | {}, 84 | ); 85 | 86 | return templatesMap; 87 | }; 88 | 89 | const getTemplateContent = (path) => { 90 | let fixedPath = _.endsWith(path, ".eta") ? path : `${path}.eta`; 91 | 92 | _.keys(config.templatePaths).forEach((key) => { 93 | if (_.startsWith(fixedPath, `@${key}`)) { 94 | fixedPath = resolve(_.replace(fixedPath, `@${key}`, config.templatePaths[key])); 95 | } 96 | }); 97 | 98 | if (pathIsExist(fixedPath)) { 99 | return getFileContent(fixedPath); 100 | } 101 | 102 | const customPath = resolve(config.templatePaths.custom, fixedPath); 103 | 104 | if (pathIsExist(customPath)) { 105 | return getFileContent(customPath); 106 | } 107 | 108 | const originalPath = resolve(config.templatePaths.original, fixedPath); 109 | 110 | if (pathIsExist(originalPath)) { 111 | return getFileContent(originalPath); 112 | } 113 | 114 | return ""; 115 | }; 116 | 117 | const renderTemplate = (template, configuration, options) => { 118 | if (!template) return ""; 119 | 120 | return Eta.render(template, configuration, { 121 | async: false, 122 | ...(options || {}), 123 | includeFile: (path, payload) => renderTemplate(getTemplateContent(path), payload), 124 | }); 125 | }; 126 | 127 | module.exports = { 128 | getTemplate, 129 | getTemplates, 130 | getTemplatePaths, 131 | renderTemplate, 132 | }; 133 | -------------------------------------------------------------------------------- /src/translators/JavaScript.js: -------------------------------------------------------------------------------- 1 | const ts = require("typescript"); 2 | 3 | function translate(fileName, content, options) { 4 | const output = {}; 5 | const host = ts.createCompilerHost(options, true); 6 | const fileNames = [fileName]; 7 | const originalSourceFileGet = host.getSourceFile.bind(host); 8 | host.getSourceFile = (sourceFileName, languageVersion, onError, shouldCreateNewSourceFile) => { 9 | if (sourceFileName !== fileName) 10 | return originalSourceFileGet( 11 | sourceFileName, 12 | languageVersion, 13 | onError, 14 | shouldCreateNewSourceFile, 15 | ); 16 | 17 | return ts.createSourceFile( 18 | sourceFileName, 19 | content, 20 | languageVersion, 21 | true, 22 | ts.ScriptKind.External, 23 | ); 24 | }; 25 | 26 | host.writeFile = (fileName, contents) => { 27 | output[fileName] = contents; 28 | }; 29 | 30 | ts.createProgram(fileNames, options, host).emit(); 31 | 32 | return output; 33 | } 34 | 35 | module.exports = { 36 | translate: (fileName, sourceTypeScript) => { 37 | const translated = translate(fileName, sourceTypeScript, { 38 | module: "ESNext", 39 | noImplicitReturns: true, 40 | alwaysStrict: true, 41 | target: ts.ScriptTarget.ESNext, 42 | declaration: true, 43 | noImplicitAny: false, 44 | sourceMap: false, 45 | removeComments: false, 46 | disableSizeLimit: true, 47 | esModuleInterop: true, 48 | emitDecoratorMetadata: true, 49 | skipLibCheck: true, 50 | }); 51 | 52 | const sourceFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Js); 53 | const declarationFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Dts); 54 | 55 | return { 56 | sourceContent: translated[sourceFileName], 57 | declarationContent: translated[declarationFileName], 58 | }; 59 | }, 60 | }; 61 | -------------------------------------------------------------------------------- /src/typeFormatters.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const { config } = require("./config"); 3 | const { TS_KEYWORDS, SCHEMA_TYPES } = require("./constants"); 4 | 5 | const formatters = { 6 | [SCHEMA_TYPES.ENUM]: (content) => { 7 | const isNumberEnum = _.some(content, (content) => typeof content.key === "number"); 8 | const formatAsUnionType = !!(isNumberEnum || config.generateUnionEnums); 9 | 10 | if (formatAsUnionType) { 11 | return _.map(content, ({ value }) => value).join(" | "); 12 | } 13 | 14 | return _.map(content, ({ key, value }) => ` ${key} = ${value}`).join(",\n"); 15 | }, 16 | [SCHEMA_TYPES.OBJECT]: (content) => 17 | _.map(content, (part) => { 18 | const extraSpace = " "; 19 | const result = `${extraSpace}${part.field};\n`; 20 | 21 | const comments = _.uniq(_.compact([part.title, part.description]).reduce( 22 | (acc, comment) => [...acc, ...comment.split(/\n/g)], 23 | [], 24 | )); 25 | 26 | const commonText = comments.length 27 | ? [ 28 | "", 29 | ...(comments.length === 1 30 | ? [`/** ${comments[0]} */`] 31 | : ["/**", ...comments.map((commentPart) => ` * ${commentPart}`), " */"]), 32 | ] 33 | .map((part) => `${extraSpace}${part}\n`) 34 | .join("") 35 | : ""; 36 | 37 | return `${commonText}${result}`; 38 | }).join(""), 39 | [SCHEMA_TYPES.PRIMITIVE]: (content) => { 40 | return content; 41 | }, 42 | }; 43 | 44 | /** transform content of parsed schema to string or compact size */ 45 | const inlineExtraFormatters = { 46 | [SCHEMA_TYPES.OBJECT]: (parsedSchema) => { 47 | return { 48 | ...parsedSchema, 49 | typeIdentifier: TS_KEYWORDS.TYPE, 50 | content: _.isString(parsedSchema.content) 51 | ? parsedSchema.content 52 | : parsedSchema.content.length 53 | ? `{ ${parsedSchema.content.map((part) => part.field).join(", ")} }` 54 | : TS_KEYWORDS.OBJECT, 55 | }; 56 | }, 57 | [SCHEMA_TYPES.ENUM]: (parsedSchema) => { 58 | return { 59 | ...parsedSchema, 60 | content: parsedSchema.$ref 61 | ? parsedSchema.typeName 62 | : _.uniq( 63 | _.compact([ 64 | ..._.map(parsedSchema.content, ({ value }) => `${value}`), 65 | parsedSchema.nullable && TS_KEYWORDS.NULL, 66 | ]), 67 | ).join(" | "), 68 | }; 69 | }, 70 | }; 71 | 72 | module.exports = { 73 | formatters, 74 | inlineExtraFormatters, 75 | }; 76 | -------------------------------------------------------------------------------- /src/utils/random.js: -------------------------------------------------------------------------------- 1 | const getRandomFloat = (min = 0, max = 1) => { 2 | return Math.random() * (max - min) + min; 3 | }; 4 | 5 | const getRandomInt = (min = 0, max = 1) => { 6 | if (min === max) return min; 7 | 8 | return Math.round(getRandomFloat(min, max)); 9 | }; 10 | 11 | module.exports = { 12 | getRandomFloat, 13 | getRandomInt, 14 | }; 15 | -------------------------------------------------------------------------------- /src/utils/resolveName.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const { getRandomInt } = require("./random"); 3 | 4 | class NameResolver { 5 | reservedNames = []; 6 | getDefaultName = null; 7 | 8 | /** 9 | * 10 | * @param {string[]} reservedNames 11 | */ 12 | constructor(reservedNames, getDefaultName) { 13 | this.getDefaultName = getDefaultName; 14 | this.reserve(reservedNames); 15 | } 16 | 17 | /** 18 | * 19 | * @param {string[]} names 20 | */ 21 | reserve(names) { 22 | this.reservedNames.push(..._.uniq(_.compact(names))); 23 | } 24 | 25 | unreserve(names) { 26 | this.reservedNames.filter((reservedName) => !names.some((name) => name === reservedName)); 27 | } 28 | 29 | isReserved(name) { 30 | return _.some(this.reservedNames, (reservedName) => reservedName === name); 31 | } 32 | 33 | /** 34 | * 35 | * @param {string[]} variants 36 | * @param {((variant: string) => string) | undefined} onSelectMutation 37 | * @returns {string | null} 38 | */ 39 | resolve(variants, onSelectMutation) { 40 | let usageName = null; 41 | const uniqVariants = _.uniq(_.compact(variants)); 42 | 43 | _.forEach(uniqVariants, (variant) => { 44 | const mutatedVariant = onSelectMutation ? onSelectMutation(variant) : variant; 45 | if (!usageName && !this.isReserved(mutatedVariant)) { 46 | usageName = mutatedVariant; 47 | } 48 | }); 49 | 50 | if (usageName) { 51 | this.reserve([usageName]); 52 | return usageName; 53 | } 54 | 55 | const defaultName = this.getDefaultName && this.getDefaultName(variants); 56 | 57 | if (defaultName) { 58 | this.reserve([onSelectMutation ? onSelectMutation(defaultName) : defaultName]); 59 | return defaultName; 60 | } 61 | 62 | return null; 63 | } 64 | } 65 | 66 | class SpecificArgNameResolver extends NameResolver { 67 | /** 68 | * 69 | * @param {string[]} reservedNames 70 | */ 71 | constructor(reservedNames) { 72 | super(reservedNames, (variants) => { 73 | return (variants[0] && `${variants[0]}${getRandomInt(1, 10)}`) || `arg${getRandomInt(1, 10)}`; 74 | }); 75 | } 76 | } 77 | 78 | class ComponentTypeNameResolver extends NameResolver { 79 | /** 80 | * 81 | * @param {string[]} reservedNames 82 | */ 83 | constructor(reservedNames) { 84 | super(reservedNames, (variants) => { 85 | return ( 86 | (variants[0] && `${variants[0]}${getRandomInt(1, 10)}`) || 87 | `ComponentType${getRandomInt(1, 10)}` 88 | ); 89 | }); 90 | } 91 | } 92 | 93 | module.exports = { 94 | NameResolver, 95 | SpecificArgNameResolver, 96 | ComponentTypeNameResolver, 97 | }; 98 | -------------------------------------------------------------------------------- /templates/README.md: -------------------------------------------------------------------------------- 1 | # swagger-typescript-api-nextgen 2 | 3 | # templates 4 | 5 | Templates: 6 | - `api.eta` - Api class module (locations: [default](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/default/api.eta), [modular](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/modular/api.eta)) 7 | - `data-contracts.eta` - all types (data contracts) from swagger schema (locations: [base](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/base/data-contracts.eta)) 8 | - `http-client.eta` - HttpClient class module (locations: [base](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/base/http-client.eta)) 9 | - `procedure-call.eta` - route in Api class (locations: [default](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/default/procedure-call.eta), [modular](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/modular/procedure-call.eta)) 10 | - `route-docs.eta` - documentation for route in Api class (locations: [base](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/base/route-docs.eta)) 11 | - `route-name.eta` - route name for route in Api class (locations: [base](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/base/route-name.eta)) 12 | - `route-type.eta` - *(`--route-types` option)* (locations: [base](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/base/route-type.eta)) 13 | - `route-types.eta` - *(`--route-types` option)* (locations: [base](https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/base/route-types.eta)) 14 | -------------------------------------------------------------------------------- /templates/base/README.md: -------------------------------------------------------------------------------- 1 | # swagger-typescript-api 2 | 3 | # templates/base 4 | 5 | This templates use both for multiple api files and single api file 6 | 7 | 8 | path prefix `@base` -------------------------------------------------------------------------------- /templates/base/data-contracts.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { modelTypes, utils } = it; 3 | const { formatDescription, require, _ } = utils; 4 | 5 | 6 | const dataContractTemplates = { 7 | enum: (contract) => { 8 | return `enum ${contract.name} {\r\n${contract.content} \r\n }`; 9 | }, 10 | interface: (contract) => { 11 | return `interface ${contract.name} {\r\n${contract.content}}`; 12 | }, 13 | type: (contract) => { 14 | return `type ${contract.name} = ${contract.content}`; 15 | }, 16 | } 17 | 18 | const createDescription = (contract) => { 19 | if (!contract.typeData) return _.compact([contract.description]); 20 | 21 | return _.compact([ 22 | contract.description && formatDescription(contract.description), 23 | !_.isUndefined(contract.typeData.format) && `@format ${contract.typeData.format}`, 24 | !_.isUndefined(contract.typeData.minimum) && `@min ${contract.typeData.minimum}`, 25 | !_.isUndefined(contract.typeData.maximum) && `@max ${contract.typeData.maximum}`, 26 | !_.isUndefined(contract.typeData.pattern) && `@pattern ${contract.typeData.pattern}`, 27 | !_.isUndefined(contract.typeData.example) && `@example ${ 28 | _.isObject(contract.typeData.example) ? JSON.stringify(contract.typeData.example) : contract.typeData.example 29 | }`, 30 | ]); 31 | } 32 | %> 33 | <% modelTypes.forEach((contract) => { %> 34 | <% const description = createDescription(contract); %> 35 | <% if (description.length) { %> 36 | /** 37 | <%~ description.map(part => `* ${part}`).join("\n") %> 38 | 39 | */ 40 | <% } %> 41 | export <%~ (dataContractTemplates[contract.typeIdentifier] || dataContractTemplates.type)(contract) %> 42 | 43 | 44 | <% }) %> 45 | -------------------------------------------------------------------------------- /templates/base/http-client.eta: -------------------------------------------------------------------------------- 1 | <% const { config } = it; %> 2 | <% /* https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/base/http-clients/ */ %> 3 | <%~ includeFile(`@base/http-clients/${config.httpClientType}-http-client`, it) %> -------------------------------------------------------------------------------- /templates/base/http-clients/axios-http-client.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { apiConfig, generateResponses, config } = it; 3 | %> 4 | 5 | import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, ResponseType } from "axios"; 6 | import axios from "axios"; 7 | 8 | export type QueryParamsType = Record; 9 | 10 | export interface FullRequestParams extends Omit { 11 | /** set parameter to `true` for call `securityWorker` for this request */ 12 | secure?: boolean; 13 | /** request path */ 14 | path: string; 15 | /** content type of request body */ 16 | type?: ContentType; 17 | /** query params */ 18 | query?: QueryParamsType; 19 | /** format of response (i.e. response.json() -> format: "json") */ 20 | format?: ResponseType; 21 | /** request body */ 22 | body?: unknown; 23 | } 24 | 25 | export type RequestParams = Omit; 26 | 27 | export interface ApiConfig extends Omit { 28 | securityWorker?: (securityData: SecurityDataType | null) => Promise | AxiosRequestConfig | void; 29 | secure?: boolean; 30 | format?: ResponseType; 31 | } 32 | 33 | export enum ContentType { 34 | Json = "application/json", 35 | FormData = "multipart/form-data", 36 | UrlEncoded = "application/x-www-form-urlencoded", 37 | } 38 | 39 | export class HttpClient { 40 | public instance: AxiosInstance; 41 | private securityData: SecurityDataType | null = null; 42 | private securityWorker?: ApiConfig["securityWorker"]; 43 | private secure?: boolean; 44 | private format?: ResponseType; 45 | 46 | constructor({ securityWorker, secure, format, ...axiosConfig }: ApiConfig = {}) { 47 | this.instance = axios.create({ ...axiosConfig, baseURL: axiosConfig.baseURL || "<%~ apiConfig.baseUrl %>" }) 48 | this.secure = secure; 49 | this.format = format; 50 | this.securityWorker = securityWorker; 51 | } 52 | 53 | public setSecurityData = (data: SecurityDataType | null) => { 54 | this.securityData = data 55 | } 56 | 57 | private mergeRequestParams(params1: AxiosRequestConfig, params2?: AxiosRequestConfig): AxiosRequestConfig { 58 | return { 59 | ...params1, 60 | ...(params2 || {}), 61 | headers: { 62 | ...(params1.headers || {}), 63 | ...((params2 && params2.headers) || {}), 64 | }, 65 | }; 66 | } 67 | 68 | private createFormData(input: Record): FormData { 69 | return Object.keys(input || {}).reduce((formData, key) => { 70 | const property = input[key]; 71 | formData.append( 72 | key, 73 | property instanceof Blob ? 74 | property : 75 | typeof property === "object" && property !== null ? 76 | JSON.stringify(property) : 77 | `${property}` 78 | ); 79 | return formData; 80 | }, new FormData()) 81 | } 82 | 83 | public request = async ({ 84 | secure, 85 | path, 86 | type, 87 | query, 88 | format, 89 | body, 90 | ...params 91 | <% if (config.unwrapResponseData) { %> 92 | }: FullRequestParams): Promise => { 93 | <% } else { %> 94 | }: FullRequestParams): Promise> => { 95 | <% } %> 96 | const secureParams = ((typeof secure === 'boolean' ? secure : this.secure) && this.securityWorker && (await this.securityWorker(this.securityData))) || {}; 97 | const requestParams = this.mergeRequestParams(params, secureParams); 98 | const responseFormat = (format && this.format) || void 0; 99 | 100 | if (type === ContentType.FormData && body && typeof body === "object") { 101 | body = this.createFormData(body as Record); 102 | } 103 | 104 | return this.instance.request({ 105 | ...requestParams, 106 | headers: { 107 | ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}), 108 | ...(requestParams.headers || {}), 109 | }, 110 | params: query, 111 | responseType: responseFormat, 112 | data: body, 113 | url: path, 114 | <% if (config.unwrapResponseData) { %> 115 | }).then(response => response.data); 116 | <% } else { %> 117 | }); 118 | <% } %> 119 | }; 120 | } 121 | -------------------------------------------------------------------------------- /templates/base/route-docs.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { config, route, utils } = it; 3 | const { _, formatDescription, fmtToJSDocLine, classNameCase, require } = utils; 4 | const { raw, request, routeName } = route; 5 | 6 | const jsDocDescription = raw.description ? 7 | ` * @description ${formatDescription(raw.description, true)}` : 8 | fmtToJSDocLine('No description', { eol: false }); 9 | const jsDocLines = _.compact([ 10 | _.size(raw.tags) && ` * @tags ${raw.tags.join(", ")}`, 11 | ` * @name ${classNameCase(routeName.usage)}`, 12 | raw.summary && ` * @summary ${raw.summary}`, 13 | ` * @request ${_.upperCase(request.method)}:${raw.route}`, 14 | raw.deprecated && ` * @deprecated`, 15 | routeName.duplicate && ` * @originalName ${routeName.original}`, 16 | routeName.duplicate && ` * @duplicate`, 17 | request.security && ` * @secure`, 18 | ...(config.generateResponses && raw.responsesTypes.length 19 | ? raw.responsesTypes.map( 20 | ({ type, status, description, isSuccess }) => 21 | ` * @response \`${status}\` \`${type}\` ${description}`, 22 | ) 23 | : []), 24 | ]).join("\n"); 25 | 26 | 27 | return { 28 | description: jsDocDescription, 29 | lines: jsDocLines, 30 | } 31 | %> 32 | -------------------------------------------------------------------------------- /templates/base/route-name.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { routeInfo, utils } = it; 3 | const { 4 | operationId, 5 | method, 6 | route, 7 | moduleName, 8 | responsesTypes, 9 | description, 10 | tags, 11 | summary, 12 | pathArgs, 13 | } = routeInfo; 14 | const { _, fmtToJSDocLine, require } = utils; 15 | 16 | const methodAliases = { 17 | get: (pathName, hasPathInserts) => 18 | _.camelCase(`${pathName}_${hasPathInserts ? "detail" : "list"}`), 19 | post: (pathName, hasPathInserts) => _.camelCase(`${pathName}_create`), 20 | put: (pathName, hasPathInserts) => _.camelCase(`${pathName}_update`), 21 | patch: (pathName, hasPathInserts) => _.camelCase(`${pathName}_partial_update`), 22 | delete: (pathName, hasPathInserts) => _.camelCase(`${pathName}_delete`), 23 | }; 24 | 25 | const createCustomOperationId = (method, route, moduleName) => { 26 | const hasPathInserts = /\{(\w){1,}\}/g.test(route); 27 | const splitedRouteBySlash = _.compact(_.replace(route, /\{(\w){1,}\}/g, "").split("/")); 28 | const routeParts = (splitedRouteBySlash.length > 1 29 | ? splitedRouteBySlash.splice(1) 30 | : splitedRouteBySlash 31 | ).join("_"); 32 | return routeParts.length > 3 && methodAliases[method] 33 | ? methodAliases[method](routeParts, hasPathInserts) 34 | : _.camelCase(_.lowerCase(method) + "_" + [moduleName].join("_")) || "index"; 35 | }; 36 | 37 | if (operationId) 38 | return _.camelCase(operationId); 39 | if (route === "/") 40 | return _.camelCase(`${_.lowerCase(method)}Root`); 41 | 42 | return createCustomOperationId(method, route, moduleName); 43 | %> -------------------------------------------------------------------------------- /templates/base/route-type.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { route, utils, config } = it; 3 | const { _, classNameCase, require } = utils; 4 | const { query, payload, pathParams, headers } = route.request; 5 | 6 | const routeDocs = includeFile("@base/route-docs", { config, route, utils }); 7 | const routeNamespace = classNameCase(route.routeName.usage); 8 | 9 | %> 10 | /** 11 | <%~ routeDocs.description %> 12 | 13 | <%~ routeDocs.lines %> 14 | 15 | */ 16 | export namespace <%~ routeNamespace %> { 17 | export type RequestParams = <%~ (pathParams && pathParams.type) || '{}' %>; 18 | export type RequestQuery = <%~ (query && query.type) || '{}' %>; 19 | export type RequestBody = <%~ (payload && payload.type) || 'never' %>; 20 | export type RequestHeaders = <%~ (headers && headers.type) || '{}' %>; 21 | export type ResponseBody = <%~ route.response.type %>; 22 | } -------------------------------------------------------------------------------- /templates/default/README.md: -------------------------------------------------------------------------------- 1 | # swagger-typescript-api 2 | 3 | # templates/default 4 | 5 | This templates use for single api file (without `--modular` option) 6 | 7 | path prefix `@default` -------------------------------------------------------------------------------- /templates/default/api.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { apiConfig, routes, utils, config } = it; 3 | const { info, servers, externalDocs } = apiConfig; 4 | const { _, require, formatDescription } = utils; 5 | 6 | const server = (servers && servers[0]) || { url: "" }; 7 | 8 | const descriptionLines = _.compact([ 9 | `@title ${info.title || "No title"}`, 10 | info.version && `@version ${info.version}`, 11 | info.license && `@license ${_.compact([ 12 | info.license.name, 13 | info.license.url && `(${info.license.url})`, 14 | ]).join(" ")}`, 15 | info.termsOfService && `@termsOfService ${info.termsOfService}`, 16 | server.url && `@baseUrl ${server.url}`, 17 | externalDocs.url && `@externalDocs ${externalDocs.url}`, 18 | info.contact && `@contact ${_.compact([ 19 | info.contact.name, 20 | info.contact.email && `<${info.contact.email}>`, 21 | info.contact.url && `(${info.contact.url})`, 22 | ]).join(" ")}`, 23 | info.description && " ", 24 | info.description && _.replace(formatDescription(info.description), /\n/g, "\n * "), 25 | ]); 26 | 27 | %> 28 | 29 | <% if (config.httpClientType === config.constants.HTTP_CLIENT.AXIOS) { %> import { AxiosRequestConfig, AxiosResponse } from "axios"; <% } %> 30 | 31 | <% if (descriptionLines.length) { %> 32 | /** 33 | <% descriptionLines.forEach((descriptionLine) => { %> 34 | * <%~ descriptionLine %> 35 | 36 | <% }) %> 37 | */ 38 | <% } %> 39 | export class Api<% if (!config.singleHttpClient) { %> extends HttpClient <% } %> { 40 | 41 | <% if(config.singleHttpClient) { %> 42 | http: HttpClient; 43 | 44 | constructor (http: HttpClient) { 45 | this.http = http; 46 | } 47 | <% } %> 48 | 49 | 50 | <% routes.outOfModule && routes.outOfModule.forEach((route) => { %> 51 | 52 | <%~ includeFile('./procedure-call.eta', { ...it, route }) %> 53 | 54 | <% }) %> 55 | 56 | <% routes.combined && routes.combined.forEach(({ routes = [], moduleName }) => { %> 57 | <%~ moduleName %> = { 58 | <% routes.forEach((route) => { %> 59 | 60 | <%~ includeFile('./procedure-call.eta', { ...it, route }) %> 61 | 62 | <% }) %> 63 | } 64 | <% }) %> 65 | } 66 | -------------------------------------------------------------------------------- /templates/default/procedure-call.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { utils, route, config } = it; 3 | const { requestBodyInfo, responseBodyInfo, specificArgNameResolver } = route; 4 | const { _, getInlineParseContent, getParseContent, parseSchema, getComponentByRef, require } = utils; 5 | const { parameters, path, method, payload, query, formData, security, requestParams } = route.request; 6 | const { type, errorType, contentTypes } = route.response; 7 | const { HTTP_CLIENT, RESERVED_REQ_PARAMS_ARG_NAMES } = config.constants; 8 | const routeDocs = includeFile("@base/route-docs", { config, route, utils }); 9 | const queryName = (query && query.name) || "query"; 10 | const pathParams = _.values(parameters); 11 | const pathParamsNames = _.map(pathParams, "name"); 12 | 13 | const isFetchTemplate = config.httpClientType === HTTP_CLIENT.FETCH; 14 | 15 | const requestConfigParam = { 16 | name: specificArgNameResolver.resolve(RESERVED_REQ_PARAMS_ARG_NAMES), 17 | optional: true, 18 | type: "RequestParams", 19 | defaultValue: "{}", 20 | } 21 | 22 | const argToTmpl = ({ name, optional, type, defaultValue }) => `${name}${!defaultValue && optional ? '?' : ''}: ${type}${defaultValue ? ` = ${defaultValue}` : ''}`; 23 | 24 | const rawWrapperArgs = config.extractRequestParams ? 25 | _.compact([ 26 | requestParams && { 27 | name: pathParams.length ? `{ ${_.join(pathParamsNames, ", ")}, ...${queryName} }` : queryName, 28 | optional: false, 29 | type: getInlineParseContent(requestParams), 30 | }, 31 | ...(!requestParams ? pathParams : []), 32 | payload, 33 | requestConfigParam, 34 | ]) : 35 | _.compact([ 36 | ...pathParams, 37 | query, 38 | payload, 39 | requestConfigParam, 40 | ]) 41 | 42 | const wrapperArgs = _ 43 | // Sort by optionality 44 | .sortBy(rawWrapperArgs, [o => o.optional]) 45 | .map(argToTmpl) 46 | .join(', ') 47 | 48 | // RequestParams["type"] 49 | const requestContentKind = { 50 | "JSON": "ContentType.Json", 51 | "URL_ENCODED": "ContentType.UrlEncoded", 52 | "FORM_DATA": "ContentType.FormData", 53 | } 54 | // RequestParams["format"] 55 | const responseContentKind = { 56 | "JSON": '"json"', 57 | "IMAGE": '"blob"', 58 | "FORM_DATA": isFetchTemplate ? '"formData"' : '"document"' 59 | } 60 | 61 | const bodyTmpl = _.get(payload, "name") || null; 62 | const queryTmpl = (query != null && queryName) || null; 63 | const bodyContentKindTmpl = requestContentKind[requestBodyInfo.contentKind] || null; 64 | const responseFormatTmpl = responseContentKind[responseBodyInfo.success && responseBodyInfo.success.schema && responseBodyInfo.success.schema.contentKind] || null; 65 | const securityTmpl = security ? 'true' : null; 66 | 67 | const describeReturnType = () => { 68 | if (!config.toJS) return ""; 69 | 70 | switch(config.httpClientType) { 71 | case HTTP_CLIENT.AXIOS: { 72 | return `Promise>` 73 | } 74 | default: { 75 | return `Promise` 76 | } 77 | } 78 | } 79 | 80 | %> 81 | /** 82 | <%~ routeDocs.description %> 83 | 84 | * <% /* Here you can add some other JSDoc tags */ %> 85 | 86 | <%~ routeDocs.lines %> 87 | 88 | */ 89 | <%~ route.routeName.usage %><%~ route.namespace ? ': ' : ' = ' %>(<%~ wrapperArgs %>)<%~ config.toJS ? `: ${describeReturnType()}` : "" %> => 90 | <%~ config.singleHttpClient ? 'this.http.request' : 'this.request' %><<%~ type %>, <%~ errorType %>>({ 91 | path: `<%~ path %>`, 92 | method: '<%~ _.upperCase(method) %>', 93 | <%~ queryTmpl ? `query: ${queryTmpl},` : '' %> 94 | <%~ bodyTmpl ? `body: ${bodyTmpl},` : '' %> 95 | <%~ securityTmpl ? `secure: ${securityTmpl},` : '' %> 96 | <%~ bodyContentKindTmpl ? `type: ${bodyContentKindTmpl},` : '' %> 97 | <%~ responseFormatTmpl ? `format: ${responseFormatTmpl},` : '' %> 98 | ...<%~ _.get(requestConfigParam, "name") %>, 99 | })<%~ route.namespace ? ',' : '' %> -------------------------------------------------------------------------------- /templates/default/route-types.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { utils, config, routes, modelTypes } = it; 3 | const { _, classNameCase } = utils; 4 | const dataContracts = config.modular ? _.map(modelTypes, "name") : []; 5 | %> 6 | 7 | <% if (dataContracts.length) { %> 8 | import { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>" 9 | <% } %> 10 | 11 | <% 12 | /* TODO: outOfModule, combined should be attributes of route, which will allow to avoid duplication of code */ 13 | %> 14 | 15 | <% routes.outOfModule && routes.outOfModule.forEach(({ routes = [] }) => { %> 16 | <% routes.forEach((route) => { %> 17 | <%~ includeFile('@base/route-type.eta', { ...it, route }) %> 18 | <% }) %> 19 | <% }) %> 20 | 21 | <% routes.combined && routes.combined.forEach(({ routes = [], moduleName }) => { %> 22 | export namespace <%~ classNameCase(moduleName) %> { 23 | <% routes.forEach((route) => { %> 24 | <%~ includeFile('@base/route-type.eta', { ...it, route }) %> 25 | <% }) %> 26 | } 27 | 28 | <% }) %> 29 | -------------------------------------------------------------------------------- /templates/modular/README.md: -------------------------------------------------------------------------------- 1 | # swagger-typescript-api 2 | 3 | # templates/modular 4 | 5 | This templates use for multiple api files (`--modular` option) 6 | 7 | path prefix `@modular` -------------------------------------------------------------------------------- /templates/modular/api.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { utils, route, config, modelTypes } = it; 3 | const { _, classNameCase, require } = utils; 4 | const apiClassName = classNameCase(route.moduleName); 5 | const routes = route.routes; 6 | const dataContracts = _.map(modelTypes, "name"); 7 | %> 8 | 9 | <% if (config.httpClientType === config.constants.HTTP_CLIENT.AXIOS) { %> import { AxiosRequestConfig, AxiosResponse } from "axios"; <% } %> 10 | 11 | import { HttpClient, RequestParams, ContentType, HttpResponse } from "./<%~ config.fileNames.httpClient %>"; 12 | <% if (dataContracts.length) { %> 13 | import { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>" 14 | <% } %> 15 | 16 | export class <%= apiClassName %><% if (!config.singleHttpClient) { %> extends HttpClient <% } %> { 17 | <% if(config.singleHttpClient) { %> 18 | http: HttpClient; 19 | 20 | constructor (http: HttpClient) { 21 | this.http = http; 22 | } 23 | <% } %> 24 | 25 | <% routes.forEach((route) => { %> 26 | <%~ includeFile('./procedure-call.eta', { ...it, route }) %> 27 | <% }) %> 28 | } 29 | -------------------------------------------------------------------------------- /templates/modular/procedure-call.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { utils, route, config } = it; 3 | const { requestBodyInfo, responseBodyInfo, specificArgNameResolver } = route; 4 | const { _, getInlineParseContent, getParseContent, parseSchema, getComponentByRef, require } = utils; 5 | const { parameters, path, method, payload, query, formData, security, requestParams } = route.request; 6 | const { type, errorType, contentTypes } = route.response; 7 | const { HTTP_CLIENT, RESERVED_REQ_PARAMS_ARG_NAMES } = config.constants; 8 | const routeDocs = includeFile("@base/route-docs", { config, route, utils }); 9 | const queryName = (query && query.name) || "query"; 10 | const pathParams = _.values(parameters); 11 | const pathParamsNames = _.map(pathParams, "name"); 12 | 13 | const isFetchTemplate = config.httpClientType === HTTP_CLIENT.FETCH; 14 | 15 | const requestConfigParam = { 16 | name: specificArgNameResolver.resolve(RESERVED_REQ_PARAMS_ARG_NAMES), 17 | optional: true, 18 | type: "RequestParams", 19 | defaultValue: "{}", 20 | } 21 | 22 | const argToTmpl = ({ name, optional, type, defaultValue }) => `${name}${!defaultValue && optional ? '?' : ''}: ${type}${defaultValue ? ` = ${defaultValue}` : ''}`; 23 | 24 | const rawWrapperArgs = config.extractRequestParams ? 25 | _.compact([ 26 | requestParams && { 27 | name: pathParams.length ? `{ ${_.join(pathParamsNames, ", ")}, ...${queryName} }` : queryName, 28 | optional: false, 29 | type: getInlineParseContent(requestParams), 30 | }, 31 | ...(!requestParams ? pathParams : []), 32 | payload, 33 | requestConfigParam, 34 | ]) : 35 | _.compact([ 36 | ...pathParams, 37 | query, 38 | payload, 39 | requestConfigParam, 40 | ]) 41 | 42 | const wrapperArgs = _ 43 | // Sort by optionality 44 | .sortBy(rawWrapperArgs, [o => o.optional]) 45 | .map(argToTmpl) 46 | .join(', ') 47 | 48 | // RequestParams["type"] 49 | const requestContentKind = { 50 | "JSON": "ContentType.Json", 51 | "URL_ENCODED": "ContentType.UrlEncoded", 52 | "FORM_DATA": "ContentType.FormData", 53 | } 54 | // RequestParams["format"] 55 | const responseContentKind = { 56 | "JSON": '"json"', 57 | "IMAGE": '"blob"', 58 | "FORM_DATA": isFetchTemplate ? '"formData"' : '"document"' 59 | } 60 | 61 | const bodyTmpl = _.get(payload, "name") || null; 62 | const queryTmpl = (query != null && queryName) || null; 63 | const bodyContentKindTmpl = requestContentKind[requestBodyInfo.contentKind] || null; 64 | const responseFormatTmpl = responseContentKind[responseBodyInfo.success && responseBodyInfo.success.schema && responseBodyInfo.success.schema.contentKind] || null; 65 | const securityTmpl = security ? 'true' : null; 66 | 67 | const describeReturnType = () => { 68 | if (!config.toJS) return ""; 69 | 70 | switch(config.httpClientType) { 71 | case HTTP_CLIENT.AXIOS: { 72 | return `Promise>` 73 | } 74 | default: { 75 | return `Promise` 76 | } 77 | } 78 | } 79 | 80 | %> 81 | /** 82 | <%~ routeDocs.description %> 83 | 84 | * <% /* Here you can add some other JSDoc tags */ %> 85 | 86 | <%~ routeDocs.lines %> 87 | 88 | */ 89 | <%~ route.routeName.usage %> = (<%~ wrapperArgs %>)<%~ config.toJS ? `: ${describeReturnType()}` : "" %> => 90 | <%~ config.singleHttpClient ? 'this.http.request' : 'this.request' %><<%~ type %>, <%~ errorType %>>({ 91 | path: `<%~ path %>`, 92 | method: '<%~ _.upperCase(method) %>', 93 | <%~ queryTmpl ? `query: ${queryTmpl},` : '' %> 94 | <%~ bodyTmpl ? `body: ${bodyTmpl},` : '' %> 95 | <%~ securityTmpl ? `secure: ${securityTmpl},` : '' %> 96 | <%~ bodyContentKindTmpl ? `type: ${bodyContentKindTmpl},` : '' %> 97 | <%~ responseFormatTmpl ? `format: ${responseFormatTmpl},` : '' %> 98 | ...<%~ _.get(requestConfigParam, "name") %>, 99 | }) -------------------------------------------------------------------------------- /templates/modular/route-types.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { utils, config, route, modelTypes } = it; 3 | const { _, classNameCase } = utils; 4 | const { routes, moduleName } = route; 5 | const dataContracts = config.modular ? _.map(modelTypes, "name") : []; 6 | 7 | %> 8 | <% if (dataContracts.length) { %> 9 | import { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>" 10 | <% } %> 11 | 12 | export namespace <%~ classNameCase(moduleName) %> { 13 | <% _.forEach(routes, (route) => { %> 14 | 15 | <%~ includeFile('@base/route-type.eta', { ...it, route }) %> 16 | 17 | <% }) %> 18 | } -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # swagger-typescript-api 2 | 3 | ## 📃 EXAMPLES 4 | 5 | As you see above here is two folders: 6 | 1. [**`schemas`**](./schemas) - 7 | - [**`v2.0`**](./schemas/v2.0) - schemas with Swagger 2.0 8 | - [**`v3.0`**](./schemas/v3.0) - schemas with OA 3.0 9 | 1. [**`generated`**](./generated) - 10 | - [**`v2.0`**](./generated/v2.0) - generated api modules for Swagger 2.0 schemas from above folder 11 | - [**`v3.0`**](./generated/v3.0) - generated api modules for OA 3.0 schemas from above folder 12 | 13 | 14 | Most schemas taken from [apis.guru](https://apis.guru/openapi-directory/), [swagger.io github repo](https://swagger.io/), [Github api description](https://github.com/github/rest-api-description) and [up-banking](https://github.com/up-banking/api) -------------------------------------------------------------------------------- /tests/allSchemas.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require("path"); 2 | const createSchemaInfos = require("./helpers/createSchemaInfos"); 3 | 4 | module.exports = [ 5 | ...createSchemaInfos({ 6 | absolutePathToSchemas: resolve(__dirname, "./schemas/v2.0"), 7 | absoluteOutputPath: resolve(__dirname, "./generated/v2.0"), 8 | }), 9 | ...createSchemaInfos({ 10 | absolutePathToSchemas: resolve(__dirname, "./schemas/v3.0"), 11 | absoluteOutputPath: resolve(__dirname, "./generated/v3.0"), 12 | }), 13 | ]; 14 | -------------------------------------------------------------------------------- /tests/generate-extended.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const { resolve } = require("path"); 3 | const allSchemas = require("./allSchemas"); 4 | const { generateApiForTest } = require("./helpers/generateApiForTest"); 5 | 6 | class GenerateExtendedError extends Error { 7 | constructor(message, outputPath, fileName) { 8 | super(message); 9 | 10 | const stackLines = _.split(this.stack, "\n"); 11 | const realStack = stackLines.slice(1); 12 | const stackLineExtraSpace = (realStack[0] && realStack[0].split("at")[0]) || ""; 13 | 14 | this.stack = [ 15 | stackLines[0], 16 | `${stackLineExtraSpace}at ${resolve(outputPath, fileName)}`, 17 | ...realStack, 18 | ].join("\n"); 19 | } 20 | } 21 | 22 | allSchemas.forEach(async ({ absolutePath, apiFileName, outputPath }) => { 23 | const name = apiFileName; 24 | const input = absolutePath; 25 | const output = outputPath; 26 | 27 | await generateApiForTest({ 28 | name: name, 29 | input: input, 30 | output: output, 31 | generateClient: true, 32 | generateRouteTypes: false, 33 | silent: true, 34 | extractRequestBody: true, 35 | extractRequestParams: true, 36 | typePrefix: "IMySuperPrefix", 37 | typeSuffix: "MySuperSuffix", 38 | }).then((result) => { 39 | result.configuration.modelTypes.forEach((modelType) => { 40 | if (modelType.name) { 41 | if (modelType.name.startsWith("IMySuperPrefixIMySuperPrefix")) { 42 | throw new GenerateExtendedError( 43 | `modelType has prefix/suffix duplicates - ${modelType.name}`, 44 | output, 45 | name, 46 | ); 47 | } 48 | if (!modelType.name.startsWith("IMySuperPrefix")) { 49 | throw new GenerateExtendedError( 50 | `modelType has not prefix/suffix - ${modelType.name}`, 51 | output, 52 | name, 53 | ); 54 | } 55 | } 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /tests/generate.js: -------------------------------------------------------------------------------- 1 | const allSchemas = require("./allSchemas"); 2 | const { generateApiForTest } = require("./helpers/generateApiForTest"); 3 | 4 | allSchemas.forEach(({ absolutePath, apiFileName, outputPath }) => { 5 | generateApiForTest({ 6 | testName: "generate script", 7 | name: apiFileName, 8 | input: absolutePath, 9 | output: outputPath, 10 | generateClient: true, 11 | generateRouteTypes: false, 12 | silent: true, 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/helpers/createGeneratedApiInfos.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { resolve } = require("path"); 3 | 4 | module.exports = (pathToApis) => 5 | (fs.readdirSync(pathToApis) || []) 6 | .map(fileName => resolve(pathToApis, fileName)) -------------------------------------------------------------------------------- /tests/helpers/createSchemaInfos.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | 5 | /** 6 | * 7 | * @param {{ absolutePathToSchemas: string; absoluteOutputPath: string }} options 8 | * @returns {{ 9 | * absolutePath: string; 10 | * schemaFileName: string; 11 | * apiFileName: string; 12 | * outputPath: string; 13 | * Exception: typeof Error; 14 | * }[]} 15 | */ 16 | const createSchemaInfos = ({ absolutePathToSchemas, absoluteOutputPath }) => { 17 | return (fs.readdirSync(absolutePathToSchemas) || []).reduce((schemas, fileName) => { 18 | if (fileName.endsWith(".yaml") || fileName.endsWith(".json") || fileName.endsWith(".yml")) { 19 | const apiFileName = fileName.replace(/.(yaml|json|yml)/g, ".ts"); 20 | const outputPath = absoluteOutputPath || path.resolve(__dirname, "./"); 21 | 22 | schemas.push({ 23 | absolutePath: path.resolve(absolutePathToSchemas, fileName), 24 | schemaFileName: fileName, 25 | apiFileName: apiFileName, 26 | outputPath: outputPath, 27 | Exception: class TestError extends Error { 28 | constructor(message, ...datas) { 29 | super(message); 30 | 31 | const stackLines = _.split(this.stack, "\n"); 32 | const realStack = stackLines.slice(1); 33 | const stackLineExtraSpace = (realStack[0] && realStack[0].split("at")[0]) || ""; 34 | 35 | this.stack = [ 36 | stackLines[0], 37 | `${stackLineExtraSpace}at ${path.resolve(outputPath, apiFileName)}`, 38 | ...realStack, 39 | ].join("\n"); 40 | 41 | console.error(stackLines[0], ...datas); 42 | console.error(`${stackLineExtraSpace}at ${path.resolve(outputPath, apiFileName)}`); 43 | console.error(realStack.join("\n")); 44 | process.exit(1); 45 | } 46 | }, 47 | }); 48 | } 49 | 50 | return schemas; 51 | }, []); 52 | }; 53 | 54 | module.exports = createSchemaInfos; 55 | -------------------------------------------------------------------------------- /tests/helpers/generateApiForTest.js: -------------------------------------------------------------------------------- 1 | const { generateApi } = require("../../src"); 2 | 3 | /** 4 | * 5 | * @param {import("../../index").GenerateApiParams & { testName?: string }} options 6 | * @returns {Promise} 7 | */ 8 | const generateApiForTest = (options) => generateApi(options); 9 | 10 | module.exports = { 11 | generateApiForTest: generateApiForTest, 12 | }; 13 | -------------------------------------------------------------------------------- /tests/helpers/specGenerateOptions.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | 3 | module.exports = { 4 | name: "Api.ts", 5 | input: path.resolve(__dirname, "../schemas/v2.0/petstore-simple.yaml"), 6 | } -------------------------------------------------------------------------------- /tests/helpers/validateGeneratedModule.js: -------------------------------------------------------------------------------- 1 | const ts = require("typescript"); 2 | const tsconfig = require("../../tsconfig.json"); 3 | 4 | /** @type {ts.CompilerOptions} */ 5 | const compilerOptions = { 6 | allowJs: true, 7 | noEmitOnError: true, 8 | noImplicitAny: true, 9 | target: ts.ScriptTarget.ES2018, 10 | module: ts.ModuleKind.CommonJS, 11 | strict: true, 12 | alwaysStrict: true, 13 | noEmit: true, 14 | }; 15 | 16 | function compile(fileNames) { 17 | console.log(`compiling ${fileNames.join(", ")}`); 18 | 19 | let program = ts.createProgram(fileNames, compilerOptions); 20 | let emitResult = program.emit(); 21 | 22 | let allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); 23 | 24 | allDiagnostics.forEach((diagnostic) => { 25 | if (diagnostic.file) { 26 | let { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); 27 | let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); 28 | console.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); 29 | } else { 30 | console.error(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")); 31 | } 32 | }); 33 | 34 | let exitCode = allDiagnostics.length || emitResult.emitSkipped ? 1 : 0; 35 | 36 | if (exitCode) { 37 | console.error(`Process exiting with code '${exitCode}'.`); 38 | process.exit(exitCode); 39 | throw "TS validation failed"; 40 | } 41 | 42 | return { 43 | code: exitCode, 44 | diagnostics: allDiagnostics, 45 | }; 46 | } 47 | 48 | module.exports = (pathToFile) => { 49 | const { diagnostics, code } = compile([pathToFile]); 50 | // const relativePathToFile = relative("", pathToFile); 51 | 52 | // console.log(`validating ${relativePathToFile}: errors ${diagnostics.length}`); 53 | // diagnostics.forEach(({ messageText, file, start }) => { 54 | // var message = ts.flattenDiagnosticMessageText(messageText, "\n"); 55 | // if (!file) { 56 | // console.error(`${relativePathToFile}\r\n`, message); 57 | // return; 58 | // } 59 | // var { line, character } = file.getLineAndCharacterOfPosition(start); 60 | // console.error( 61 | // `${relativePathToFile}\r\n`, 62 | // `${file.fileName} (${line + 1},${character + 1}): ${message}`, 63 | // ); 64 | // }); 65 | 66 | return diagnostics; 67 | }; 68 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/another-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "x-generator": "NSwag v11.14.0.0 (NJsonSchema v9.10.24.0 (Newtonsoft.Json v9.0.0.0))", 3 | "swagger": "2.0", 4 | "info": { 5 | "title": "", 6 | "version": "" 7 | }, 8 | "schemes": [], 9 | "consumes": ["application/json"], 10 | "produces": ["application/json"], 11 | "paths": { 12 | "/api/Foo/GetBarDescriptions": { 13 | "get": { 14 | "tags": ["Foo"], 15 | "operationId": "Foo_GetBarDescriptions", 16 | "parameters": [], 17 | "responses": { 18 | "200": { 19 | "description": "", 20 | "schema": { 21 | "type": "array", 22 | "items": { 23 | "type": "string" 24 | } 25 | }, 26 | "x-nullable": true 27 | } 28 | } 29 | } 30 | }, 31 | "/api/Foo/GetBar": { 32 | "get": { 33 | "tags": ["Foo"], 34 | "operationId": "Foo_GetBar", 35 | "parameters": [ 36 | { 37 | "type": "integer", 38 | "name": "id", 39 | "in": "query", 40 | "required": true, 41 | "x-nullable": false, 42 | "format": "int32" 43 | } 44 | ], 45 | "responses": { 46 | "200": { 47 | "description": "", 48 | "schema": { 49 | "$ref": "#/definitions/Bar" 50 | }, 51 | "x-nullable": true 52 | } 53 | } 54 | } 55 | }, 56 | "/api/Foo/SetBar": { 57 | "post": { 58 | "tags": ["Foo"], 59 | "operationId": "Foo_SetBar", 60 | "parameters": [ 61 | { 62 | "name": "value", 63 | "in": "body", 64 | "required": true, 65 | "schema": { 66 | "$ref": "#/definitions/Bar" 67 | }, 68 | "x-nullable": true 69 | } 70 | ], 71 | "responses": { 72 | "204": { 73 | "description": "" 74 | } 75 | } 76 | } 77 | } 78 | }, 79 | "definitions": { 80 | "Bar": { 81 | "type": "object", 82 | "additionalProperties": false, 83 | "required": ["B", "C"], 84 | "properties": { 85 | "A": { 86 | "type": "string" 87 | }, 88 | "B": { 89 | "type": "integer", 90 | "format": "int32" 91 | }, 92 | "C": { 93 | "type": "string", 94 | "format": "date-time" 95 | }, 96 | "Baz": { 97 | "$ref": "#/definitions/Baz" 98 | } 99 | } 100 | }, 101 | "Baz": { 102 | "type": "object", 103 | "additionalProperties": false, 104 | "required": ["D", "Color"], 105 | "properties": { 106 | "D": { 107 | "type": "number", 108 | "format": "decimal" 109 | }, 110 | "Color": { 111 | "$ref": "#/definitions/Color" 112 | } 113 | } 114 | }, 115 | "Color": { 116 | "type": "integer", 117 | "description": "", 118 | "x-enumNames": ["RED", "GREEN", "BLUE"], 119 | "enum": [0, 1, 2] 120 | } 121 | }, 122 | "parameters": {}, 123 | "responses": {}, 124 | "securityDefinitions": {} 125 | } 126 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/enums.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "schemes": ["https"], 4 | "host": "ffff.com", 5 | "basePath": "/", 6 | "info": {}, 7 | "definitions": { 8 | "OnlyEnumNames": { 9 | "x-enumNames": ["Bla", "Blabla", "Boiler"] 10 | }, 11 | "StringOnlyEnumNames": { 12 | "type": "int32", 13 | "x-enumNames": ["Bla", "Blabla", "Boiler"] 14 | }, 15 | "StringEnums": { 16 | "type": "int32", 17 | "enum": ["foo", "bar"], 18 | "x-enumNames": ["Bla", "Blabla", "Boiler"] 19 | }, 20 | "StringCompleteEnums": { 21 | "type": "int32", 22 | "enum": ["foo", "bar", "baz"], 23 | "x-enumNames": ["Bla", "Blabla", "Boiler"] 24 | }, 25 | "EmptyEnum": { 26 | "format": "int32", 27 | "type": "integer", 28 | "x-enumNames": ["Bla", "Blabla", "Boiler"] 29 | }, 30 | "EnumWithMoreNames": { 31 | "format": "int32", 32 | "type": "integer", 33 | "enum": [1], 34 | "x-enumNames": ["Bla", "Blabla", "Boiler"] 35 | }, 36 | "SomeInterestEnum": { 37 | "format": "int32", 38 | "enum": [6, 2, 1, 67, 88, 122, 88, 0, 213, 12378, 123125, 32452, 1111, 66666], 39 | "type": "integer", 40 | "x-enumNames": [ 41 | "Bla", 42 | "Blabla", 43 | "Boiler", 44 | "Bbabab", 45 | "Nowadays", 46 | "FAIL", 47 | "Vvvvv", 48 | "ASdasAS", 49 | "ASDsacZX", 50 | "Zook", 51 | "EnumMm", 52 | "VCsa", 53 | "Yuuu", 54 | "ASddd", 55 | "ASdsdsa", 56 | "ASDds", 57 | "HSDFDS" 58 | ] 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/file-formdata-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "version": "v0.1", 5 | "title": "Title" 6 | }, 7 | "paths": { 8 | "/upload-file": { 9 | "post": { 10 | "tags": ["tag"], 11 | "summary": "Upload file", 12 | "operationId": "UploadFile", 13 | "consumes": ["multipart/form-data"], 14 | "produces": ["application/json", "application/vnd.api+json"], 15 | "parameters": [ 16 | { 17 | "name": "file", 18 | "in": "formData", 19 | "description": "File description", 20 | "required": false, 21 | "type": "file" 22 | }, 23 | { 24 | "name": "someFlag", 25 | "in": "formData", 26 | "description": "Boolean flag", 27 | "required": false, 28 | "type": "boolean" 29 | } 30 | ], 31 | "responses": { 32 | "200": { 33 | "description": "Success" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/furkot-example.yaml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | schemes: 3 | - https 4 | host: trips.furkot.com 5 | basePath: /pub/api 6 | info: 7 | contact: 8 | email: trips@furkot.com 9 | description: | 10 | Furkot provides Rest API to access user trip data. 11 | Using Furkot API an application can list user trips and display stops for a specific trip. 12 | Furkot API uses OAuth2 protocol to authorize applications to access data on behalf of users. 13 | title: Furkot Trips 14 | version: 1.0.0 15 | x-apisguru-categories: 16 | - location 17 | x-logo: 18 | url: 'https://api.apis.guru/v2/cache/logo/https_cdn.furkot.com_img_furkot-banner-black-4x1.svg' 19 | x-origin: 20 | - format: swagger 21 | url: 'https://help.furkot.com/widgets/furkot-api.yaml' 22 | version: '2.0' 23 | x-preferred: true 24 | x-providerName: furkot.com 25 | externalDocs: 26 | description: Furkot API description 27 | url: 'https://help.furkot.com/widgets/furkot-api.html' 28 | produces: 29 | - application/json 30 | securityDefinitions: 31 | furkot_auth_access_code: 32 | authorizationUrl: 'https://trips.furkot.com/oauth/authorize' 33 | flow: accessCode 34 | scopes: 35 | 'read:trips': list trips and stops info 36 | tokenUrl: 'https://trips.furkot.com/pub/api/access_token' 37 | type: oauth2 38 | furkot_auth_implicit: 39 | authorizationUrl: 'https://trips.furkot.com/oauth/authorize' 40 | flow: implicit 41 | scopes: 42 | 'read:trips': list users trips info 43 | type: oauth2 44 | security: 45 | - furkot_auth_access_code: 46 | - 'read:trips' 47 | - furkot_auth_implicit: 48 | - 'read:trips' 49 | paths: 50 | /trip: 51 | get: 52 | description: list user's trips 53 | responses: 54 | '200': 55 | description: Successful response 56 | schema: 57 | items: 58 | $ref: '#/definitions/Trip' 59 | type: array 60 | '/trip/{trip_id}/stop': 61 | get: 62 | description: 'list stops for a trip identified by {trip_id}' 63 | parameters: 64 | - description: id of the trip 65 | in: path 66 | name: trip_id 67 | required: true 68 | type: string 69 | responses: 70 | '200': 71 | description: Successful response 72 | schema: 73 | items: 74 | $ref: '#/definitions/Step' 75 | type: array 76 | definitions: 77 | Step: 78 | properties: 79 | address: 80 | description: address of the stop 81 | type: string 82 | arrival: 83 | description: 'arrival at the stop in its local timezone as YYYY-MM-DDThh:mm' 84 | format: date-time 85 | type: string 86 | coordinates: 87 | description: geographical coordinates of the stop 88 | properties: 89 | lat: 90 | description: latitude 91 | format: float 92 | type: number 93 | lon: 94 | description: longitude 95 | format: float 96 | type: number 97 | type: object 98 | departure: 99 | description: 'departure from the stop in its local timezone as YYYY-MM-DDThh:mm' 100 | format: date-time 101 | type: string 102 | name: 103 | description: name of the stop 104 | type: string 105 | nights: 106 | description: number of nights 107 | format: int64 108 | type: integer 109 | route: 110 | description: route leading to the stop 111 | properties: 112 | distance: 113 | description: route distance in meters 114 | format: int64 115 | type: integer 116 | duration: 117 | description: route duration in seconds 118 | format: int64 119 | type: integer 120 | mode: 121 | description: travel mode 122 | enum: 123 | - car 124 | - motorcycle 125 | - bicycle 126 | - walk 127 | - other 128 | type: string 129 | polyline: 130 | description: route path compatible with Google polyline encoding algorithm 131 | type: string 132 | type: object 133 | url: 134 | description: url of the page with more information about the stop 135 | type: string 136 | type: object 137 | Trip: 138 | properties: 139 | begin: 140 | description: 'begin of the trip in its local timezone as YYYY-MM-DDThh:mm' 141 | format: date-time 142 | type: string 143 | description: 144 | description: description of the trip (truncated to 200 characters) 145 | type: string 146 | end: 147 | description: 'end of the trip in its local timezone as YYYY-MM-DDThh:mm' 148 | format: date-time 149 | type: string 150 | id: 151 | description: Unique ID of the trip 152 | type: string 153 | name: 154 | description: name of the trip 155 | type: string 156 | type: object 157 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/path-args.yaml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | version: 1.0.0 4 | title: Path Args 5 | license: 6 | name: MIT 7 | host: unknown.io 8 | basePath: /v666 9 | schemes: 10 | - http 11 | consumes: 12 | - application/json 13 | produces: 14 | - application/json 15 | paths: 16 | /pets/{param1}/{param2}/{param3}: 17 | get: 18 | summary: List all pets 19 | operationId: listPets 20 | tags: 21 | - pets 22 | parameters: 23 | - name: queryParam 24 | in: query 25 | description: How many items to return at one time (max 100) 26 | required: false 27 | type: integer 28 | format: int32 29 | - name: param1 30 | in: path 31 | description: How many items to return at one time (max 100) 32 | required: false 33 | type: integer 34 | format: int32 35 | - name: param2 36 | in: path 37 | description: How many items to return at one time (max 100) 38 | required: false 39 | type: integer 40 | format: int32 41 | - name: param3 42 | in: path 43 | description: How many items to return at one time (max 100) 44 | required: true 45 | type: integer 46 | format: int32 47 | responses: 48 | "200": 49 | description: A paged array of pets 50 | headers: 51 | x-next: 52 | type: string 53 | description: A link to the next page of responses 54 | schema: 55 | type: object 56 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/petstore-minimal.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Swagger Petstore", 6 | "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", 7 | "termsOfService": "http://swagger.io/terms/", 8 | "contact": { 9 | "name": "Swagger API Team" 10 | }, 11 | "license": { 12 | "name": "MIT" 13 | } 14 | }, 15 | "host": "petstore.swagger.io", 16 | "basePath": "/api", 17 | "schemes": ["http"], 18 | "consumes": ["application/json"], 19 | "produces": ["application/json"], 20 | "paths": { 21 | "/pets": { 22 | "get": { 23 | "description": "Returns all pets from the system that the user has access to", 24 | "produces": ["application/json"], 25 | "responses": { 26 | "200": { 27 | "description": "A list of pets.", 28 | "schema": { 29 | "type": "array", 30 | "items": { 31 | "$ref": "#/definitions/Pet" 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | }, 39 | "definitions": { 40 | "Pet": { 41 | "type": "object", 42 | "required": ["id", "name"], 43 | "properties": { 44 | "id": { 45 | "type": "integer", 46 | "format": "int64" 47 | }, 48 | "name": { 49 | "type": "string" 50 | }, 51 | "tag": { 52 | "type": "string" 53 | }, 54 | "multiple": { 55 | "type": ["string", "number"] 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/petstore-simple.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | swagger: "2.0" 3 | info: 4 | version: "1.0.0" 5 | title: "Swagger Petstore" 6 | description: "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification" 7 | termsOfService: "http://swagger.io/terms/" 8 | contact: 9 | name: "Swagger API Team" 10 | license: 11 | name: "MIT" 12 | host: "petstore.swagger.io" 13 | basePath: "/api" 14 | schemes: 15 | - "http" 16 | consumes: 17 | - "application/json" 18 | produces: 19 | - "application/json" 20 | paths: 21 | /pets: 22 | get: 23 | description: "Returns all pets from the system that the user has access to" 24 | operationId: "findPets" 25 | produces: 26 | - "application/json" 27 | - "application/xml" 28 | - "text/xml" 29 | - "text/html" 30 | parameters: 31 | - 32 | name: "tags" 33 | in: "query" 34 | description: "tags to filter by" 35 | required: false 36 | type: "array" 37 | items: 38 | type: "string" 39 | collectionFormat: "csv" 40 | - 41 | name: "limit" 42 | in: "query" 43 | description: "maximum number of results to return" 44 | required: false 45 | type: "integer" 46 | format: "int32" 47 | responses: 48 | "200": 49 | description: "pet response" 50 | schema: 51 | type: "array" 52 | items: 53 | $ref: "#/definitions/Pet" 54 | default: 55 | description: "unexpected error" 56 | schema: 57 | $ref: "#/definitions/ErrorModel" 58 | post: 59 | description: "Creates a new pet in the store. Duplicates are allowed" 60 | operationId: "addPet" 61 | produces: 62 | - "application/json" 63 | parameters: 64 | - 65 | name: "pet" 66 | in: "body" 67 | description: "Pet to add to the store" 68 | required: true 69 | schema: 70 | $ref: "#/definitions/NewPet" 71 | responses: 72 | "200": 73 | description: "pet response" 74 | schema: 75 | $ref: "#/definitions/Pet" 76 | default: 77 | description: "unexpected error" 78 | schema: 79 | $ref: "#/definitions/ErrorModel" 80 | /pets/{id}: 81 | get: 82 | description: "Returns a user based on a single ID, if the user does not have access to the pet" 83 | operationId: "findPetById" 84 | produces: 85 | - "application/json" 86 | - "application/xml" 87 | - "text/xml" 88 | - "text/html" 89 | parameters: 90 | - 91 | name: "id" 92 | in: "path" 93 | description: "ID of pet to fetch" 94 | required: true 95 | type: "integer" 96 | format: "int64" 97 | responses: 98 | "200": 99 | description: "pet response" 100 | schema: 101 | $ref: "#/definitions/Pet" 102 | default: 103 | description: "unexpected error" 104 | schema: 105 | $ref: "#/definitions/ErrorModel" 106 | delete: 107 | description: "deletes a single pet based on the ID supplied" 108 | operationId: "deletePet" 109 | parameters: 110 | - 111 | name: "id" 112 | in: "path" 113 | description: "ID of pet to delete" 114 | required: true 115 | type: "integer" 116 | format: "int64" 117 | responses: 118 | "204": 119 | description: "pet deleted" 120 | default: 121 | description: "unexpected error" 122 | schema: 123 | $ref: "#/definitions/ErrorModel" 124 | definitions: 125 | Pet: 126 | type: "object" 127 | allOf: 128 | - 129 | $ref: "#/definitions/NewPet" 130 | - 131 | required: 132 | - "id" 133 | properties: 134 | id: 135 | type: "integer" 136 | format: "int64" 137 | Test: 138 | $ref: "#/definitions/NewPet" 139 | description: Description of Test type 140 | Test2: 141 | type: "object" 142 | properties: 143 | data: 144 | $ref: "#/definitions/NewPet" 145 | description: Field description 146 | NewPet: 147 | type: "object" 148 | required: 149 | - "name" 150 | properties: 151 | name: 152 | type: "string" 153 | tag: 154 | type: "string" 155 | ErrorModel: 156 | type: "object" 157 | required: 158 | - "code" 159 | - "message" 160 | properties: 161 | code: 162 | type: "integer" 163 | format: "int32" 164 | message: 165 | type: "string" 166 | 167 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/petstore-with-external-docs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | swagger: "2.0" 3 | info: 4 | version: "1.0.0" 5 | title: "Swagger Petstore" 6 | description: "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification" 7 | termsOfService: "http://swagger.io/terms/" 8 | contact: 9 | name: "Swagger API Team" 10 | email: "apiteam@swagger.io" 11 | url: "http://swagger.io" 12 | license: 13 | name: "Apache 2.0" 14 | url: "https://www.apache.org/licenses/LICENSE-2.0.html" 15 | externalDocs: 16 | description: "find more info here" 17 | url: "https://swagger.io/about" 18 | host: "petstore.swagger.io" 19 | basePath: "/api" 20 | schemes: 21 | - "http" 22 | consumes: 23 | - "application/json" 24 | produces: 25 | - "application/json" 26 | paths: 27 | /pets: 28 | get: 29 | description: "Returns all pets from the system that the user has access to" 30 | operationId: "findPets" 31 | externalDocs: 32 | description: "find more info here" 33 | url: "https://swagger.io/about" 34 | produces: 35 | - "application/json" 36 | - "application/xml" 37 | - "text/xml" 38 | - "text/html" 39 | parameters: 40 | - 41 | name: "tags" 42 | in: "query" 43 | description: "tags to filter by" 44 | required: false 45 | type: "array" 46 | items: 47 | type: "string" 48 | collectionFormat: "csv" 49 | - 50 | name: "limit" 51 | in: "query" 52 | description: "maximum number of results to return" 53 | required: false 54 | type: "integer" 55 | format: "int32" 56 | responses: 57 | "200": 58 | description: "pet response" 59 | schema: 60 | type: "array" 61 | items: 62 | $ref: "#/definitions/Pet" 63 | default: 64 | description: "unexpected error" 65 | schema: 66 | $ref: "#/definitions/ErrorModel" 67 | post: 68 | description: "Creates a new pet in the store. Duplicates are allowed" 69 | operationId: "addPet" 70 | produces: 71 | - "application/json" 72 | parameters: 73 | - 74 | name: "pet" 75 | in: "body" 76 | description: "Pet to add to the store" 77 | required: true 78 | schema: 79 | $ref: "#/definitions/NewPet" 80 | responses: 81 | "200": 82 | description: "pet response" 83 | schema: 84 | $ref: "#/definitions/Pet" 85 | default: 86 | description: "unexpected error" 87 | schema: 88 | $ref: "#/definitions/ErrorModel" 89 | /pets/{id}: 90 | get: 91 | description: "Returns a user based on a single ID, if the user does not have access to the pet" 92 | operationId: "findPetById" 93 | produces: 94 | - "application/json" 95 | - "application/xml" 96 | - "text/xml" 97 | - "text/html" 98 | parameters: 99 | - 100 | name: "id" 101 | in: "path" 102 | description: "ID of pet to fetch" 103 | required: true 104 | type: "integer" 105 | format: "int64" 106 | responses: 107 | "200": 108 | description: "pet response" 109 | schema: 110 | $ref: "#/definitions/Pet" 111 | default: 112 | description: "unexpected error" 113 | schema: 114 | $ref: "#/definitions/ErrorModel" 115 | delete: 116 | description: "deletes a single pet based on the ID supplied" 117 | operationId: "deletePet" 118 | parameters: 119 | - 120 | name: "id" 121 | in: "path" 122 | description: "ID of pet to delete" 123 | required: true 124 | type: "integer" 125 | format: "int64" 126 | responses: 127 | "204": 128 | description: "pet deleted" 129 | default: 130 | description: "unexpected error" 131 | schema: 132 | $ref: "#/definitions/ErrorModel" 133 | definitions: 134 | Pet: 135 | type: "object" 136 | allOf: 137 | - 138 | $ref: "#/definitions/NewPet" 139 | - 140 | required: 141 | - "id" 142 | properties: 143 | id: 144 | type: "integer" 145 | format: "int64" 146 | NewPet: 147 | type: "object" 148 | required: 149 | - "name" 150 | properties: 151 | name: 152 | type: "string" 153 | tag: 154 | type: "string" 155 | ErrorModel: 156 | type: "object" 157 | required: 158 | - "code" 159 | - "message" 160 | properties: 161 | code: 162 | type: "integer" 163 | format: "int32" 164 | message: 165 | type: "string" 166 | 167 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/petstore.yaml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | host: petstore.swagger.io 8 | basePath: /v1 9 | schemes: 10 | - http 11 | consumes: 12 | - application/json 13 | produces: 14 | - application/json 15 | paths: 16 | /pets: 17 | get: 18 | summary: List all pets 19 | operationId: listPets 20 | tags: 21 | - pets 22 | parameters: 23 | - name: limit 24 | in: query 25 | description: How many items to return at one time (max 100) 26 | required: false 27 | type: integer 28 | format: int32 29 | responses: 30 | "200": 31 | description: A paged array of pets 32 | headers: 33 | x-next: 34 | type: string 35 | description: A link to the next page of responses 36 | schema: 37 | $ref: '#/definitions/Pets' 38 | default: 39 | description: unexpected error 40 | schema: 41 | $ref: '#/definitions/Error' 42 | post: 43 | summary: Create a pet 44 | operationId: createPets 45 | tags: 46 | - pets 47 | responses: 48 | "201": 49 | description: Null response 50 | default: 51 | description: unexpected error 52 | schema: 53 | $ref: '#/definitions/Error' 54 | /pets/{petId}: 55 | get: 56 | summary: Info for a specific pet 57 | operationId: showPetById 58 | tags: 59 | - pets 60 | parameters: 61 | - name: petId 62 | in: path 63 | required: true 64 | description: The id of the pet to retrieve 65 | type: string 66 | responses: 67 | "200": 68 | description: Expected response to a valid request 69 | schema: 70 | $ref: '#/definitions/Pets' 71 | default: 72 | description: unexpected error 73 | schema: 74 | $ref: '#/definitions/Error' 75 | definitions: 76 | Pet: 77 | type: "object" 78 | required: 79 | - id 80 | - name 81 | properties: 82 | id: 83 | type: integer 84 | format: int64 85 | name: 86 | type: string 87 | tag: 88 | type: string 89 | Pets: 90 | type: array 91 | items: 92 | $ref: '#/definitions/Pet' 93 | Error: 94 | type: "object" 95 | required: 96 | - code 97 | - message 98 | properties: 99 | code: 100 | type: integer 101 | format: int32 102 | message: 103 | type: string 104 | -------------------------------------------------------------------------------- /tests/schemas/v2.0/query-path-param.yaml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | version: 1.0.0 4 | title: Query Path Param 5 | license: 6 | name: MIT 7 | host: unknown.io 8 | basePath: /v666 9 | schemes: 10 | - http 11 | consumes: 12 | - application/json 13 | produces: 14 | - application/json 15 | paths: 16 | /foobarbaz/{query}: 17 | get: 18 | summary: List all pets 19 | operationId: listPets 20 | tags: 21 | - pets 22 | parameters: 23 | - name: queryParam 24 | in: query 25 | description: How many items to return at one time (max 100) 26 | required: false 27 | type: integer 28 | format: int32 29 | - name: query 30 | in: path 31 | description: How many items to return at one time (max 100) 32 | required: false 33 | type: integer 34 | format: int32 35 | responses: 36 | "200": 37 | description: A paged array of pets 38 | headers: 39 | x-next: 40 | type: string 41 | description: A link to the next page of responses 42 | schema: 43 | type: object 44 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/additional-properties.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | title: Additional propeties Example 4 | version: 1.0.0 5 | components: 6 | schemas: 7 | Messages: # <---- dictionary 8 | type: object 9 | additionalProperties: 10 | $ref: "#/components/schemas/Message" 11 | Message: 12 | type: object 13 | properties: 14 | code: 15 | type: integer 16 | text: 17 | type: string 18 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/additional-properties2.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { "title": "" }, 3 | "openapi": "3.0.0", 4 | "components": { 5 | "schemas": { 6 | "Primitive": { 7 | "anyOf": [ 8 | { 9 | "type": "string" 10 | }, 11 | { 12 | "type": "number", 13 | "format": "double" 14 | }, 15 | { 16 | "type": "boolean" 17 | }, 18 | { 19 | "type": "number", 20 | "enum": [null], 21 | "nullable": true 22 | } 23 | ] 24 | }, 25 | "PrimitiveMap": { 26 | "properties": {}, 27 | "type": "object", 28 | "additionalProperties": { 29 | "$ref": "#/components/schemas/Primitive" 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/allof-example.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | title: Allof Example 4 | version: 1.0.0 5 | paths: 6 | /pets: 7 | patch: 8 | requestBody: 9 | content: 10 | application/json: 11 | schema: 12 | oneOf: 13 | - $ref: '#/components/schemas/Cat' 14 | - $ref: '#/components/schemas/Dog' 15 | discriminator: 16 | propertyName: pet_type 17 | responses: 18 | '200': 19 | description: Updated 20 | components: 21 | schemas: 22 | Pet: 23 | type: object 24 | required: 25 | - pet_type 26 | properties: 27 | pet_type: 28 | type: string 29 | discriminator: 30 | propertyName: pet_type 31 | Dog: # "Dog" is a value for the pet_type property (the discriminator value) 32 | allOf: # Combines the main `Pet` schema with `Dog`-specific properties 33 | - $ref: '#/components/schemas/Pet' 34 | - type: object 35 | # all other properties specific to a `Dog` 36 | properties: 37 | bark: 38 | type: boolean 39 | breed: 40 | type: string 41 | enum: [Dingo, Husky, Retriever, Shepherd] 42 | Cat: # "Cat" is a value for the pet_type property (the discriminator value) 43 | allOf: # Combines the main `Pet` schema with `Cat`-specific properties 44 | - $ref: '#/components/schemas/Pet' 45 | - type: object 46 | # all other properties specific to a `Cat` 47 | properties: 48 | hunts: 49 | type: boolean 50 | age: 51 | type: integer -------------------------------------------------------------------------------- /tests/schemas/v3.0/anyof-example.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | title: Anyof Example 4 | version: 1.0.0 5 | paths: 6 | /pets: 7 | patch: 8 | requestBody: 9 | content: 10 | application/json: 11 | schema: 12 | anyOf: 13 | - $ref: '#/components/schemas/PetByAge' 14 | - $ref: '#/components/schemas/PetByType' 15 | responses: 16 | '200': 17 | description: Updated 18 | components: 19 | schemas: 20 | PetByAge: 21 | type: object 22 | properties: 23 | age: 24 | type: integer 25 | nickname: 26 | type: string 27 | required: 28 | - age 29 | 30 | PetByType: 31 | type: object 32 | properties: 33 | pet_type: 34 | type: string 35 | enum: [Cat, Dog] 36 | hunts: 37 | type: boolean 38 | required: 39 | - pet_type -------------------------------------------------------------------------------- /tests/schemas/v3.0/callback-example.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | title: Callback Example 4 | version: 1.0.0 5 | paths: 6 | /streams: 7 | post: 8 | description: subscribes a client to receive out-of-band data 9 | parameters: 10 | - name: callbackUrl 11 | in: query 12 | required: true 13 | description: | 14 | the location where data will be sent. Must be network accessible 15 | by the source server 16 | schema: 17 | type: string 18 | format: uri 19 | example: https://tonys-server.com 20 | responses: 21 | '201': 22 | description: subscription successfully created 23 | content: 24 | application/json: 25 | schema: 26 | description: subscription information 27 | required: 28 | - subscriptionId 29 | properties: 30 | subscriptionId: 31 | description: this unique identifier allows management of the subscription 32 | type: string 33 | example: 2531329f-fb09-4ef7-887e-84e648214436 34 | callbacks: 35 | # the name `onData` is a convenience locator 36 | onData: 37 | # when data is sent, it will be sent to the `callbackUrl` provided 38 | # when making the subscription PLUS the suffix `/data` 39 | '{$request.query.callbackUrl}/data': 40 | post: 41 | requestBody: 42 | description: subscription payload 43 | content: 44 | application/json: 45 | schema: 46 | type: object 47 | properties: 48 | timestamp: 49 | type: string 50 | format: date-time 51 | userData: 52 | type: string 53 | responses: 54 | '202': 55 | description: | 56 | Your server implementation should return this HTTP status code 57 | if the data was received successfully 58 | '204': 59 | description: | 60 | Your server should return this HTTP status code if no longer interested 61 | in further updates 62 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/components-responses.yaml: -------------------------------------------------------------------------------- 1 | openapi: '3.0.0' 2 | 3 | info: 4 | description: 'Description' 5 | version: 'latest' 6 | title: 'Title' 7 | 8 | paths: 9 | '/api': 10 | get: 11 | operationId: getData 12 | responses: 13 | 200: 14 | $ref: '#/components/responses/default' 15 | 16 | components: 17 | responses: 18 | default: 19 | description: OK 20 | content: 21 | 'application/json': 22 | schema: 23 | type: object 24 | properties: 25 | data: { type: string } 26 | examples: | 27 | Lorem ipsum de foo bar 28 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/explode-param-3.0.1.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.1 2 | info: 3 | title: API 4 | description: Documentation 5 | version: "0.1" 6 | paths: 7 | "/{user}/foos": 8 | parameters: 9 | - schema: 10 | type: string 11 | user: blbalbla 12 | in: path 13 | required: true 14 | post: 15 | summary: Some summary 16 | operationId: createFile 17 | responses: 18 | "200": 19 | description: OK 20 | content: 21 | application/json: 22 | schema: 23 | $ref: "#/components/schemas/Floop" 24 | requestBody: 25 | content: 26 | application/json: 27 | schema: 28 | type: object 29 | properties: 30 | meme: 31 | type: string 32 | default: "" 33 | memeType: 34 | type: string 35 | required: 36 | - meme 37 | required: true 38 | description: "" 39 | /something/: 40 | get: 41 | operationId: gets 42 | parameters: 43 | - name: params 44 | in: query 45 | required: false 46 | explode: true 47 | schema: 48 | $ref: "#/components/schemas/QueryParams" 49 | 50 | components: 51 | schemas: 52 | Floop: 53 | type: object 54 | properties: 55 | info: 56 | type: string 57 | QueryParams: 58 | type: object 59 | properties: 60 | page: 61 | minimum: 0 62 | type: integer 63 | description: Page number 64 | format: int32 65 | nullable: true 66 | page-size: 67 | minimum: 0 68 | type: integer 69 | description: Page size 70 | format: int32 71 | nullable: true 72 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/no-definitions-schema.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | title: Empty schema example 4 | version: 1.0.0 5 | components: 6 | schemas: 7 | BasicErrorModel: 8 | type: object 9 | required: 10 | - message 11 | - code 12 | properties: 13 | message: 14 | type: string 15 | field: 16 | type: string 17 | x-nullable: true 18 | code: 19 | type: integer 20 | minimum: 100 21 | maximum: 600 22 | ExtendedErrorModel: 23 | allOf: # Combines the BasicErrorModel and the inline model 24 | - $ref: "#/components/schemas/BasicErrorModel" 25 | - type: object 26 | required: 27 | - rootCause 28 | properties: 29 | rootCause: 30 | type: string 31 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/nullable-refs.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.1" 2 | info: 3 | title: Nullable Refs Example 4 | version: 1.0.0 5 | components: 6 | schemas: 7 | TestObject: 8 | type: object 9 | properties: 10 | stringMaybeUndefined: 11 | type: string 12 | stringMaybeNullA: 13 | type: string 14 | nullable: true 15 | stringMaybeNullB: 16 | anyOf: 17 | - type: string 18 | nullable: true 19 | stringMaybeNullAndUndefined: 20 | anyOf: 21 | - type: string 22 | nullable: true 23 | otherObjectMaybeUndefined: 24 | $ref: "#/components/schemas/OtherObject" 25 | otherObjectMaybeNullA: 26 | $ref: "#/components/schemas/OtherObject" 27 | nullable: true 28 | otherObjectMaybeNullB: 29 | anyOf: 30 | - $ref: "#/components/schemas/OtherObject" 31 | nullable: true 32 | otherObjectMaybeNullC: 33 | anyOf: 34 | - $ref: "#/components/schemas/OtherObject" 35 | type: "null" 36 | otherObjectMaybeNullD: 37 | anyOf: 38 | - $ref: "#/components/schemas/OtherObject" 39 | - type: "null" 40 | required: 41 | - stringMaybeNullA 42 | - stringMaybeNullB 43 | - otherObjectMaybeNullA 44 | - otherObjectMaybeNullB 45 | - otherObjectMaybeNullC 46 | - otherObjectMaybeNullD 47 | 48 | OtherObject: 49 | type: object 50 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/oneof-example.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | title: Oneof Example 4 | version: 1.0.0 5 | paths: 6 | /pets: 7 | patch: 8 | requestBody: 9 | content: 10 | application/json: 11 | schema: 12 | oneOf: 13 | - $ref: '#/components/schemas/Cat' 14 | - $ref: '#/components/schemas/Dog' 15 | responses: 16 | '200': 17 | description: Updated 18 | components: 19 | schemas: 20 | Dog: 21 | type: object 22 | properties: 23 | bark: 24 | type: boolean 25 | breed: 26 | type: string 27 | enum: [Dingo, Husky, Retriever, Shepherd] 28 | Cat: 29 | type: object 30 | properties: 31 | hunts: 32 | type: boolean 33 | age: 34 | type: integer -------------------------------------------------------------------------------- /tests/schemas/v3.0/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pets: 11 | get: 12 | summary: List all pets 13 | operationId: listPets 14 | tags: 15 | - pets 16 | parameters: 17 | - name: limit 18 | in: query 19 | description: How many items to return at one time (max 100) 20 | required: false 21 | schema: 22 | type: integer 23 | format: int32 24 | responses: 25 | "200": 26 | description: A paged array of pets 27 | headers: 28 | x-next: 29 | description: A link to the next page of responses 30 | schema: 31 | type: string 32 | content: 33 | application/json: 34 | schema: 35 | $ref: "#/components/schemas/Pets" 36 | default: 37 | description: unexpected error 38 | content: 39 | application/json: 40 | schema: 41 | $ref: "#/components/schemas/Error" 42 | post: 43 | summary: Create a pet 44 | operationId: createPets 45 | tags: 46 | - pets 47 | responses: 48 | "201": 49 | description: Null response 50 | default: 51 | description: unexpected error 52 | content: 53 | application/json: 54 | schema: 55 | $ref: "#/components/schemas/Error" 56 | /pets/{petId}: 57 | get: 58 | summary: Info for a specific pet 59 | operationId: showPetById 60 | tags: 61 | - pets 62 | parameters: 63 | - name: petId 64 | in: path 65 | required: true 66 | description: The id of the pet to retrieve 67 | schema: 68 | type: string 69 | responses: 70 | "200": 71 | description: Expected response to a valid request 72 | content: 73 | application/json: 74 | schema: 75 | $ref: "#/components/schemas/Pet" 76 | default: 77 | description: unexpected error 78 | content: 79 | application/json: 80 | schema: 81 | $ref: "#/components/schemas/Error" 82 | components: 83 | schemas: 84 | StringNullable: 85 | type: string 86 | nullable: true 87 | Pet: 88 | type: object 89 | required: 90 | - id 91 | - name 92 | properties: 93 | id: 94 | type: integer 95 | format: int64 96 | name: 97 | type: string 98 | tag: 99 | type: string 100 | Pets: 101 | type: array 102 | items: 103 | $ref: "#/components/schemas/Pet" 104 | Error: 105 | type: object 106 | required: 107 | - code 108 | - message 109 | properties: 110 | code: 111 | type: integer 112 | format: int32 113 | message: 114 | type: string 115 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/recursive-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "examples": {}, 4 | "headers": {}, 5 | "parameters": {}, 6 | "requestBodies": {}, 7 | "responses": {}, 8 | "schemas": { 9 | "recursive-object": { 10 | "type": "object", 11 | "description": "RECURSIVE", 12 | "properties": { 13 | "id": { 14 | "description": "Unique identifier of the GitHub app", 15 | "example": 37, 16 | "type": "integer" 17 | }, 18 | "bar": { 19 | "$ref": "#/components/schemas/recursive-object" 20 | } 21 | } 22 | } 23 | }, 24 | "securitySchemes": {} 25 | }, 26 | "info": { 27 | "title": "" 28 | }, 29 | "openapi": "3.0.0", 30 | "paths": {}, 31 | "servers": [] 32 | } 33 | -------------------------------------------------------------------------------- /tests/schemas/v3.0/responses.yaml: -------------------------------------------------------------------------------- 1 | openapi: '3.0.0' 2 | 3 | info: 4 | description: 'Description' 5 | version: 'latest' 6 | title: 'Title' 7 | 8 | paths: 9 | '/api': 10 | get: 11 | operationId: getData 12 | responses: 13 | 200: 14 | description: OK 15 | content: 16 | 'application/json': 17 | schema: 18 | type: object 19 | properties: 20 | data: { type: string } -------------------------------------------------------------------------------- /tests/schemas/v3.0/swaggerhub-template.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | # Added by API Auto Mocking Plugin 3 | servers: 4 | - description: SwaggerHub API Auto Mocking 5 | url: https://virtserver.swaggerhub.com/sdfsdfsffs/sdfff/1.0.0 6 | info: 7 | version: "1.0.0" 8 | title: Sample Application Flow OAuth2 Project 9 | description: >- 10 | This is an example of using OAuth2 Application Flow in a specification to 11 | describe security to your API. 12 | security: 13 | - application: 14 | - read 15 | - write 16 | paths: 17 | /example: 18 | get: 19 | summary: Server example operation 20 | description: >- 21 | This is an example operation to show how security is applied to the 22 | call. 23 | responses: 24 | '200': 25 | description: OK 26 | /ping: 27 | get: 28 | summary: Server heartbeat operation 29 | description: >- 30 | This operation shows how to override the global security defined above, 31 | as we want to open it up for all users. 32 | security: [] 33 | responses: 34 | '200': 35 | description: OK 36 | components: 37 | schemas: {} 38 | securitySchemes: 39 | application: 40 | type: oauth2 41 | flows: 42 | clientCredentials: 43 | tokenUrl: 'http://example.com/oauth/token' 44 | scopes: 45 | write: allows modifying resources 46 | read: allows reading resources -------------------------------------------------------------------------------- /tests/schemas/v3.0/wrong-enum-subtypes.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.1 2 | info: 3 | title: Test 4 | version: test 5 | paths: {} 6 | components: 7 | schemas: 8 | Test: 9 | type: object 10 | allOf: 11 | - type: object 12 | properties: 13 | x: 14 | type: array 15 | items: 16 | type: string 17 | enum: 18 | - A-B 19 | - type: object 20 | properties: 21 | y: 22 | type: string 23 | -------------------------------------------------------------------------------- /tests/spec/axios/test.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require("path"); 2 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 3 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 4 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--axios option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | generateClient: true, 16 | httpClientType: "axios", 17 | }).then(() => { 18 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/spec/axiosSingleHttpClient/test.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require("path"); 2 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 3 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 4 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--axios --single-http-client test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | generateClient: true, 16 | singleHttpClient: true, 17 | httpClientType: "axios", 18 | }).then(() => { 19 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /tests/spec/defaultAsSuccess/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--default-as-success option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | defaultResponseAsSuccess: true, 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/spec/defaultResponse/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Swagger Petstore", 6 | "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", 7 | "termsOfService": "http://swagger.io/terms/", 8 | "contact": { 9 | "name": "Swagger API Team", 10 | "email": "apiteam@swagger.io", 11 | "url": "http://swagger.io" 12 | }, 13 | "license": { 14 | "name": "Apache 2.0", 15 | "url": "https://www.apache.org/licenses/LICENSE-2.0.html" 16 | } 17 | }, 18 | "host": "petstore.swagger.io", 19 | "basePath": "/api", 20 | "schemes": ["http"], 21 | "consumes": ["application/json"], 22 | "produces": ["application/json"], 23 | "paths": { 24 | "/pets": { 25 | "get": { 26 | "description": "Returns all pets from the system that the user has access to\nNam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia.\n\nSed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien.\n", 27 | "operationId": "findPets", 28 | "parameters": [ 29 | { 30 | "name": "tags", 31 | "in": "query", 32 | "description": "tags to filter by", 33 | "required": false, 34 | "type": "array", 35 | "collectionFormat": "csv", 36 | "items": { 37 | "type": "string" 38 | } 39 | }, 40 | { 41 | "name": "limit", 42 | "in": "query", 43 | "description": "maximum number of results to return", 44 | "required": false, 45 | "type": "integer", 46 | "format": "int32" 47 | } 48 | ], 49 | "responses": { 50 | "200": {}, 51 | "default": {} 52 | } 53 | } 54 | } 55 | }, 56 | "definitions": {} 57 | } 58 | -------------------------------------------------------------------------------- /tests/spec/defaultResponse/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--default-response option test", 11 | silent: true, 12 | name: apiFileName, 13 | spec: require(absolutePath), 14 | output: resolve(__dirname, "./"), 15 | defaultResponseType: "unknown", 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/spec/enumNamesAsValues/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--enum-names-as-values option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | enumNamesAsValues: true, 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/spec/extractRequestBody/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--extract-request-body option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | extractRequestBody: true, 16 | typeSuffix: "TTT", 17 | }).then(() => { 18 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/spec/extractRequestParams/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--extract-request-params option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | extractRequestParams: true, 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/spec/js/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--js option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | toJS: true, 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/spec/jsAxios/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--js --axios option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | toJS: true, 16 | httpClientType: "axios", 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /tests/spec/modular/Key.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | /* 4 | * ------------------------------------------------------------------ 5 | * # THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API-NEXTGEN # 6 | * # AUTHORS: acacode & grandsilence # 7 | * # https://github.com/grandsilence/swagger-typescript-api-nextgen # 8 | * ------------------------------------------------------------------ 9 | */ 10 | 11 | import { AuthentiqID, Error } from "./data-contracts"; 12 | import { HttpClient, RequestParams } from "./http-client"; 13 | 14 | export class Key extends HttpClient { 15 | /** 16 | * @description Revoke an Authentiq ID using email & phone. If called with `email` and `phone` only, a verification code will be sent by email. Do a second call adding `code` to complete the revocation. 17 | * 18 | * @tags key, delete 19 | * @name KeyRevokeNosecret 20 | * @request DELETE:/key 21 | */ 22 | keyRevokeNosecret = (query: { email: string; phone: string; code?: string }, params: RequestParams = {}) => 23 | this.request<{ status?: string }, Error>({ 24 | path: `/key`, 25 | method: "DELETE", 26 | query: query, 27 | format: "json", 28 | ...params, 29 | }); 30 | /** 31 | * @description Register a new ID `JWT(sub, devtoken)` v5: `JWT(sub, pk, devtoken, ...)` See: https://github.com/skion/authentiq/wiki/JWT-Examples 32 | * 33 | * @tags key, post 34 | * @name KeyRegister 35 | * @request POST:/key 36 | */ 37 | keyRegister = (body: AuthentiqID, params: RequestParams = {}) => 38 | this.request<{ secret?: string; status?: string }, Error>({ 39 | path: `/key`, 40 | method: "POST", 41 | body: body, 42 | format: "json", 43 | ...params, 44 | }); 45 | /** 46 | * @description Revoke an Identity (Key) with a revocation secret 47 | * 48 | * @tags key, delete 49 | * @name KeyRevoke 50 | * @request DELETE:/key/{PK} 51 | */ 52 | keyRevoke = (pk: string, query: { secret: string }, params: RequestParams = {}) => 53 | this.request<{ status?: string }, Error>({ 54 | path: `/key/${pk}`, 55 | method: "DELETE", 56 | query: query, 57 | format: "json", 58 | ...params, 59 | }); 60 | /** 61 | * @description Get public details of an Authentiq ID. 62 | * 63 | * @tags key, get 64 | * @name GetKey 65 | * @request GET:/key/{PK} 66 | */ 67 | getKey = (pk: string, params: RequestParams = {}) => 68 | this.request<{ since?: string; status?: string; sub?: string }, Error>({ 69 | path: `/key/${pk}`, 70 | method: "GET", 71 | format: "json", 72 | ...params, 73 | }); 74 | /** 75 | * @description HEAD info on Authentiq ID 76 | * 77 | * @tags key, head 78 | * @name HeadKey 79 | * @request HEAD:/key/{PK} 80 | */ 81 | headKey = (pk: string, params: RequestParams = {}) => 82 | this.request({ 83 | path: `/key/${pk}`, 84 | method: "HEAD", 85 | ...params, 86 | }); 87 | /** 88 | * @description update properties of an Authentiq ID. (not operational in v4; use PUT for now) v5: POST issuer-signed email & phone scopes in a self-signed JWT See: https://github.com/skion/authentiq/wiki/JWT-Examples 89 | * 90 | * @tags key, post 91 | * @name KeyUpdate 92 | * @request POST:/key/{PK} 93 | */ 94 | keyUpdate = (pk: string, body: AuthentiqID, params: RequestParams = {}) => 95 | this.request<{ status?: string }, Error>({ 96 | path: `/key/${pk}`, 97 | method: "POST", 98 | body: body, 99 | format: "json", 100 | ...params, 101 | }); 102 | /** 103 | * @description Update Authentiq ID by replacing the object. v4: `JWT(sub,email,phone)` to bind email/phone hash; v5: POST issuer-signed email & phone scopes and PUT to update registration `JWT(sub, pk, devtoken, ...)` See: https://github.com/skion/authentiq/wiki/JWT-Examples 104 | * 105 | * @tags key, put 106 | * @name KeyBind 107 | * @request PUT:/key/{PK} 108 | */ 109 | keyBind = (pk: string, body: AuthentiqID, params: RequestParams = {}) => 110 | this.request<{ status?: string }, Error>({ 111 | path: `/key/${pk}`, 112 | method: "PUT", 113 | body: body, 114 | format: "json", 115 | ...params, 116 | }); 117 | } 118 | -------------------------------------------------------------------------------- /tests/spec/modular/KeyRoute.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | /* 4 | * ------------------------------------------------------------------ 5 | * # THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API-NEXTGEN # 6 | * # AUTHORS: acacode & grandsilence # 7 | * # https://github.com/grandsilence/swagger-typescript-api-nextgen # 8 | * ------------------------------------------------------------------ 9 | */ 10 | 11 | import { AuthentiqID } from "./data-contracts"; 12 | 13 | export namespace Key { 14 | /** 15 | * @description Revoke an Authentiq ID using email & phone. If called with `email` and `phone` only, a verification code will be sent by email. Do a second call adding `code` to complete the revocation. 16 | * @tags key, delete 17 | * @name KeyRevokeNosecret 18 | * @request DELETE:/key 19 | */ 20 | export namespace KeyRevokeNosecret { 21 | export type RequestParams = {}; 22 | export type RequestQuery = { email: string; phone: string; code?: string }; 23 | export type RequestBody = never; 24 | export type RequestHeaders = {}; 25 | export type ResponseBody = { status?: string }; 26 | } 27 | 28 | /** 29 | * @description Register a new ID `JWT(sub, devtoken)` v5: `JWT(sub, pk, devtoken, ...)` See: https://github.com/skion/authentiq/wiki/JWT-Examples 30 | * @tags key, post 31 | * @name KeyRegister 32 | * @request POST:/key 33 | */ 34 | export namespace KeyRegister { 35 | export type RequestParams = {}; 36 | export type RequestQuery = {}; 37 | export type RequestBody = AuthentiqID; 38 | export type RequestHeaders = {}; 39 | export type ResponseBody = { secret?: string; status?: string }; 40 | } 41 | 42 | /** 43 | * @description Revoke an Identity (Key) with a revocation secret 44 | * @tags key, delete 45 | * @name KeyRevoke 46 | * @request DELETE:/key/{PK} 47 | */ 48 | export namespace KeyRevoke { 49 | export type RequestParams = { pk: string }; 50 | export type RequestQuery = { secret: string }; 51 | export type RequestBody = never; 52 | export type RequestHeaders = {}; 53 | export type ResponseBody = { status?: string }; 54 | } 55 | 56 | /** 57 | * @description Get public details of an Authentiq ID. 58 | * @tags key, get 59 | * @name GetKey 60 | * @request GET:/key/{PK} 61 | */ 62 | export namespace GetKey { 63 | export type RequestParams = { pk: string }; 64 | export type RequestQuery = {}; 65 | export type RequestBody = never; 66 | export type RequestHeaders = {}; 67 | export type ResponseBody = { since?: string; status?: string; sub?: string }; 68 | } 69 | 70 | /** 71 | * @description HEAD info on Authentiq ID 72 | * @tags key, head 73 | * @name HeadKey 74 | * @request HEAD:/key/{PK} 75 | */ 76 | export namespace HeadKey { 77 | export type RequestParams = { pk: string }; 78 | export type RequestQuery = {}; 79 | export type RequestBody = never; 80 | export type RequestHeaders = {}; 81 | export type ResponseBody = void; 82 | } 83 | 84 | /** 85 | * @description update properties of an Authentiq ID. (not operational in v4; use PUT for now) v5: POST issuer-signed email & phone scopes in a self-signed JWT See: https://github.com/skion/authentiq/wiki/JWT-Examples 86 | * @tags key, post 87 | * @name KeyUpdate 88 | * @request POST:/key/{PK} 89 | */ 90 | export namespace KeyUpdate { 91 | export type RequestParams = { pk: string }; 92 | export type RequestQuery = {}; 93 | export type RequestBody = AuthentiqID; 94 | export type RequestHeaders = {}; 95 | export type ResponseBody = { status?: string }; 96 | } 97 | 98 | /** 99 | * @description Update Authentiq ID by replacing the object. v4: `JWT(sub,email,phone)` to bind email/phone hash; v5: POST issuer-signed email & phone scopes and PUT to update registration `JWT(sub, pk, devtoken, ...)` See: https://github.com/skion/authentiq/wiki/JWT-Examples 100 | * @tags key, put 101 | * @name KeyBind 102 | * @request PUT:/key/{PK} 103 | */ 104 | export namespace KeyBind { 105 | export type RequestParams = { pk: string }; 106 | export type RequestQuery = {}; 107 | export type RequestBody = AuthentiqID; 108 | export type RequestHeaders = {}; 109 | export type ResponseBody = { status?: string }; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tests/spec/modular/Login.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | /* 4 | * ------------------------------------------------------------------ 5 | * # THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API-NEXTGEN # 6 | * # AUTHORS: acacode & grandsilence # 7 | * # https://github.com/grandsilence/swagger-typescript-api-nextgen # 8 | * ------------------------------------------------------------------ 9 | */ 10 | 11 | import { Error, PushToken } from "./data-contracts"; 12 | import { HttpClient, RequestParams } from "./http-client"; 13 | 14 | export class Login extends HttpClient { 15 | /** 16 | * @description push sign-in request See: https://github.com/skion/authentiq/wiki/JWT-Examples 17 | * 18 | * @tags login, post 19 | * @name PushLoginRequest 20 | * @request POST:/login 21 | */ 22 | pushLoginRequest = (query: { callback: string }, body: PushToken, params: RequestParams = {}) => 23 | this.request<{ status?: string }, Error>({ 24 | path: `/login`, 25 | method: "POST", 26 | query: query, 27 | body: body, 28 | format: "json", 29 | ...params, 30 | }); 31 | /** 32 | * @description Get a current key register 33 | * 34 | * @tags key, get 35 | * @name KeyRegister 36 | * @request GET:/login 37 | */ 38 | keyRegister = (params: RequestParams = {}) => 39 | this.request<{ secret?: string; status?: string }, Error>({ 40 | path: `/login`, 41 | method: "GET", 42 | format: "json", 43 | ...params, 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /tests/spec/modular/LoginRoute.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | /* 4 | * ------------------------------------------------------------------ 5 | * # THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API-NEXTGEN # 6 | * # AUTHORS: acacode & grandsilence # 7 | * # https://github.com/grandsilence/swagger-typescript-api-nextgen # 8 | * ------------------------------------------------------------------ 9 | */ 10 | 11 | import { PushToken } from "./data-contracts"; 12 | 13 | export namespace Login { 14 | /** 15 | * @description push sign-in request See: https://github.com/skion/authentiq/wiki/JWT-Examples 16 | * @tags login, post 17 | * @name PushLoginRequest 18 | * @request POST:/login 19 | */ 20 | export namespace PushLoginRequest { 21 | export type RequestParams = {}; 22 | export type RequestQuery = { callback: string }; 23 | export type RequestBody = PushToken; 24 | export type RequestHeaders = {}; 25 | export type ResponseBody = { status?: string }; 26 | } 27 | 28 | /** 29 | * @description Get a current key register 30 | * @tags key, get 31 | * @name KeyRegister 32 | * @request GET:/login 33 | */ 34 | export namespace KeyRegister { 35 | export type RequestParams = {}; 36 | export type RequestQuery = {}; 37 | export type RequestBody = never; 38 | export type RequestHeaders = {}; 39 | export type ResponseBody = { secret?: string; status?: string }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/spec/modular/Scope.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | /* 4 | * ------------------------------------------------------------------ 5 | * # THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API-NEXTGEN # 6 | * # AUTHORS: acacode & grandsilence # 7 | * # https://github.com/grandsilence/swagger-typescript-api-nextgen # 8 | * ------------------------------------------------------------------ 9 | */ 10 | 11 | import { Claims, Error } from "./data-contracts"; 12 | import { ContentType, HttpClient, RequestParams } from "./http-client"; 13 | 14 | export class Scope extends HttpClient { 15 | /** 16 | * @description scope verification request See: https://github.com/skion/authentiq/wiki/JWT-Examples 17 | * 18 | * @tags scope, post 19 | * @name SignRequest 20 | * @request POST:/scope 21 | */ 22 | signRequest = (body: Claims, query?: { test?: number }, params: RequestParams = {}) => 23 | this.request<{ job?: string; status?: string }, Error>({ 24 | path: `/scope`, 25 | method: "POST", 26 | query: query, 27 | body: body, 28 | format: "json", 29 | ...params, 30 | }); 31 | /** 32 | * @description delete a verification job 33 | * 34 | * @tags scope, delete 35 | * @name SignDelete 36 | * @request DELETE:/scope/{job} 37 | */ 38 | signDelete = (job: string, params: RequestParams = {}) => 39 | this.request<{ status?: string }, Error>({ 40 | path: `/scope/${job}`, 41 | method: "DELETE", 42 | format: "json", 43 | ...params, 44 | }); 45 | /** 46 | * @description get the status / current content of a verification job 47 | * 48 | * @tags scope, get 49 | * @name SignRetrieve 50 | * @request GET:/scope/{job} 51 | */ 52 | signRetrieve = (job: string, params: RequestParams = {}) => 53 | this.request<{ exp?: number; field?: string; sub?: string }, Error>({ 54 | path: `/scope/${job}`, 55 | method: "GET", 56 | format: "json", 57 | ...params, 58 | }); 59 | /** 60 | * @description HEAD to get the status of a verification job 61 | * 62 | * @tags scope, head 63 | * @name SignRetrieveHead 64 | * @request HEAD:/scope/{job} 65 | */ 66 | signRetrieveHead = (job: string, params: RequestParams = {}) => 67 | this.request({ 68 | path: `/scope/${job}`, 69 | method: "HEAD", 70 | ...params, 71 | }); 72 | /** 73 | * @description this is a scope confirmation 74 | * 75 | * @tags scope, post 76 | * @name SignConfirm 77 | * @request POST:/scope/{job} 78 | */ 79 | signConfirm = (job: string, params: RequestParams = {}) => 80 | this.request<{ status?: string }, Error>({ 81 | path: `/scope/${job}`, 82 | method: "POST", 83 | type: ContentType.Json, 84 | format: "json", 85 | ...params, 86 | }); 87 | /** 88 | * @description authority updates a JWT with its signature See: https://github.com/skion/authentiq/wiki/JWT-Examples 89 | * 90 | * @tags scope, put 91 | * @name SignUpdate 92 | * @request PUT:/scope/{job} 93 | */ 94 | signUpdate = (job: string, params: RequestParams = {}) => 95 | this.request<{ jwt?: string; status?: string }, Error>({ 96 | path: `/scope/${job}`, 97 | method: "PUT", 98 | ...params, 99 | }); 100 | } 101 | -------------------------------------------------------------------------------- /tests/spec/modular/ScopeRoute.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | /* 4 | * ------------------------------------------------------------------ 5 | * # THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API-NEXTGEN # 6 | * # AUTHORS: acacode & grandsilence # 7 | * # https://github.com/grandsilence/swagger-typescript-api-nextgen # 8 | * ------------------------------------------------------------------ 9 | */ 10 | 11 | import { Claims } from "./data-contracts"; 12 | 13 | export namespace Scope { 14 | /** 15 | * @description scope verification request See: https://github.com/skion/authentiq/wiki/JWT-Examples 16 | * @tags scope, post 17 | * @name SignRequest 18 | * @request POST:/scope 19 | */ 20 | export namespace SignRequest { 21 | export type RequestParams = {}; 22 | export type RequestQuery = { test?: number }; 23 | export type RequestBody = Claims; 24 | export type RequestHeaders = {}; 25 | export type ResponseBody = { job?: string; status?: string }; 26 | } 27 | 28 | /** 29 | * @description delete a verification job 30 | * @tags scope, delete 31 | * @name SignDelete 32 | * @request DELETE:/scope/{job} 33 | */ 34 | export namespace SignDelete { 35 | export type RequestParams = { job: string }; 36 | export type RequestQuery = {}; 37 | export type RequestBody = never; 38 | export type RequestHeaders = {}; 39 | export type ResponseBody = { status?: string }; 40 | } 41 | 42 | /** 43 | * @description get the status / current content of a verification job 44 | * @tags scope, get 45 | * @name SignRetrieve 46 | * @request GET:/scope/{job} 47 | */ 48 | export namespace SignRetrieve { 49 | export type RequestParams = { job: string }; 50 | export type RequestQuery = {}; 51 | export type RequestBody = never; 52 | export type RequestHeaders = {}; 53 | export type ResponseBody = { exp?: number; field?: string; sub?: string }; 54 | } 55 | 56 | /** 57 | * @description HEAD to get the status of a verification job 58 | * @tags scope, head 59 | * @name SignRetrieveHead 60 | * @request HEAD:/scope/{job} 61 | */ 62 | export namespace SignRetrieveHead { 63 | export type RequestParams = { job: string }; 64 | export type RequestQuery = {}; 65 | export type RequestBody = never; 66 | export type RequestHeaders = {}; 67 | export type ResponseBody = void; 68 | } 69 | 70 | /** 71 | * @description this is a scope confirmation 72 | * @tags scope, post 73 | * @name SignConfirm 74 | * @request POST:/scope/{job} 75 | */ 76 | export namespace SignConfirm { 77 | export type RequestParams = { job: string }; 78 | export type RequestQuery = {}; 79 | export type RequestBody = never; 80 | export type RequestHeaders = {}; 81 | export type ResponseBody = { status?: string }; 82 | } 83 | 84 | /** 85 | * @description authority updates a JWT with its signature See: https://github.com/skion/authentiq/wiki/JWT-Examples 86 | * @tags scope, put 87 | * @name SignUpdate 88 | * @request PUT:/scope/{job} 89 | */ 90 | export namespace SignUpdate { 91 | export type RequestParams = { job: string }; 92 | export type RequestQuery = {}; 93 | export type RequestBody = never; 94 | export type RequestHeaders = {}; 95 | export type ResponseBody = { jwt?: string; status?: string }; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/spec/modular/data-contracts.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | /* 4 | * ------------------------------------------------------------------ 5 | * # THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API-NEXTGEN # 6 | * # AUTHORS: acacode & grandsilence # 7 | * # https://github.com/grandsilence/swagger-typescript-api-nextgen # 8 | * ------------------------------------------------------------------ 9 | */ 10 | 11 | /** 12 | * Authentiq ID in JWT format, self-signed. 13 | */ 14 | export interface AuthentiqID { 15 | /** device token for push messages */ 16 | devtoken?: string; 17 | 18 | /** UUID and public signing key */ 19 | sub: string; 20 | } 21 | 22 | /** 23 | * Claim in JWT format, self- or issuer-signed. 24 | */ 25 | export interface Claims { 26 | email?: string; 27 | phone?: string; 28 | 29 | /** claim scope */ 30 | scope: string; 31 | 32 | /** UUID */ 33 | sub: string; 34 | type?: string; 35 | } 36 | 37 | export interface Error { 38 | detail?: string; 39 | error: number; 40 | title?: string; 41 | 42 | /** unique uri for this error */ 43 | type?: string; 44 | } 45 | 46 | /** 47 | * PushToken in JWT format, self-signed. 48 | */ 49 | export interface PushToken { 50 | /** audience (URI) */ 51 | aud: string; 52 | exp?: number; 53 | iat?: number; 54 | 55 | /** issuer (URI) */ 56 | iss: string; 57 | nbf?: number; 58 | 59 | /** UUID and public signing key */ 60 | sub: string; 61 | } 62 | -------------------------------------------------------------------------------- /tests/spec/modular/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, Exception }) => { 9 | generateApiForTest({ 10 | testName: "--modular option test", 11 | silent: true, 12 | input: absolutePath, 13 | output: resolve(__dirname, "./"), 14 | modular: true, 15 | generateClient: true, 16 | generateRouteTypes: true, 17 | }).then(() => { 18 | const diagnostics = [ 19 | ...validateGeneratedModule(resolve(__dirname, `./data-contracts.ts`)), 20 | ...validateGeneratedModule(resolve(__dirname, `./http-client.ts`)), 21 | ...validateGeneratedModule(resolve(__dirname, `./Key`)), 22 | ...validateGeneratedModule(resolve(__dirname, `./Login`)), 23 | ...validateGeneratedModule(resolve(__dirname, `./Scope`)), 24 | ]; 25 | if (diagnostics.length) throw new Exception("Failed"); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /tests/spec/moduleNameFirstTag/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--module-name-first-tag option test", 11 | silent: true, 12 | name: apiFileName, 13 | spec: require(absolutePath), 14 | output: resolve(__dirname, "./"), 15 | moduleNameFirstTag: true, 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/spec/moduleNameIndex/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--module-name-index option test", 11 | silent: true, 12 | name: apiFileName, 13 | spec: require(absolutePath), 14 | output: resolve(__dirname, "./"), 15 | moduleNameIndex: 2, 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/spec/noClient/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--no-client option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | generateClient: false, 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/spec/partialBaseTemplate/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Swagger Petstore", 6 | "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", 7 | "termsOfService": "http://swagger.io/terms/", 8 | "contact": { 9 | "name": "Swagger API Team" 10 | }, 11 | "license": { 12 | "name": "MIT" 13 | } 14 | }, 15 | "host": "petstore.swagger.io", 16 | "basePath": "/api", 17 | "schemes": ["http"], 18 | "consumes": ["application/json"], 19 | "produces": ["application/json"], 20 | "paths": { 21 | "/pets": { 22 | "get": { 23 | "description": "Returns all pets from the system that the user has access to", 24 | "produces": ["application/json"], 25 | "responses": { 26 | "200": { 27 | "description": "A list of pets.", 28 | "schema": { 29 | "type": "array", 30 | "items": { 31 | "$ref": "#/definitions/Pet" 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | }, 39 | "definitions": { 40 | "Pet": { 41 | "type": "object", 42 | "required": ["id", "name"], 43 | "properties": { 44 | "id": { 45 | "type": "integer", 46 | "format": "int64" 47 | }, 48 | "name": { 49 | "type": "string" 50 | }, 51 | "tag": { 52 | "type": "string" 53 | }, 54 | "multiple": { 55 | "type": ["string", "number"] 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/spec/partialBaseTemplate/spec_templates/data-contracts.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { modelTypes, utils } = it; 3 | const { formatDescription } = utils; 4 | 5 | 6 | const dataContractTemplates = { 7 | enum: (contract) => { 8 | return `enum ${contract.name} {\r\n${contract.content} \r\n }`; 9 | }, 10 | interface: (contract) => { 11 | return `interface ${contract.name} {\r\n${contract.content}}`; 12 | }, 13 | type: (contract) => { 14 | return `type ${contract.name} = ${contract.content}`; 15 | }, 16 | } 17 | %> 18 | /** PARTIAL TEMPLATES */ 19 | <% modelTypes.forEach((contract) => { %> 20 | /** 21 | * <%~ formatDescription(contract.description) %> 22 | * FOO BAR 23 | */ 24 | export <%~ (dataContractTemplates[contract.typeIdentifier] || dataContractTemplates.type)(contract) %> 25 | 26 | 27 | <% }) %> 28 | -------------------------------------------------------------------------------- /tests/spec/partialBaseTemplate/test.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 3 | const { resolve } = require("path"); 4 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 5 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 6 | 7 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 8 | 9 | schemas.forEach(({ absolutePath, apiFileName, Exception }) => { 10 | generateApiForTest({ 11 | testName: "partialBaseTemplate test", 12 | silent: true, 13 | name: apiFileName, 14 | input: absolutePath, 15 | output: resolve(__dirname, "./"), 16 | // because this script was called from package.json folder 17 | templates: "./tests/spec/partialBaseTemplate/spec_templates", 18 | }).then((output) => { 19 | if (!_.includes(_.get(output.files, "[0].content"), "/** PARTIAL TEMPLATES */")) { 20 | throw new Exception("Failed, spec templates are not applied"); 21 | } 22 | 23 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/spec/partialDefaultTemplate/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Swagger Petstore", 6 | "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", 7 | "termsOfService": "http://swagger.io/terms/", 8 | "contact": { 9 | "name": "Swagger API Team" 10 | }, 11 | "license": { 12 | "name": "MIT" 13 | } 14 | }, 15 | "host": "petstore.swagger.io", 16 | "basePath": "/api", 17 | "schemes": ["http"], 18 | "consumes": ["application/json"], 19 | "produces": ["application/json"], 20 | "paths": { 21 | "/pets": { 22 | "get": { 23 | "description": "Returns all pets from the system that the user has access to", 24 | "produces": ["application/json"], 25 | "responses": { 26 | "200": { 27 | "description": "A list of pets.", 28 | "schema": { 29 | "type": "array", 30 | "items": { 31 | "$ref": "#/definitions/Pet" 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | }, 39 | "definitions": { 40 | "Pet": { 41 | "type": "object", 42 | "required": ["id", "name"], 43 | "properties": { 44 | "id": { 45 | "type": "integer", 46 | "format": "int64" 47 | }, 48 | "name": { 49 | "type": "string" 50 | }, 51 | "tag": { 52 | "type": "string" 53 | }, 54 | "multiple": { 55 | "type": ["string", "number"] 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/spec/partialDefaultTemplate/spec_templates/api.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { apiConfig, routes, utils, config } = it; 3 | const { info, servers } = apiConfig; 4 | const { _, require, formatDescription } = utils; 5 | 6 | const server = (servers && servers[0]) || { url: "" }; 7 | 8 | const descriptionLines = _.compact([ 9 | `@title ${info.title || "No title"}`, 10 | info.version && `@version ${info.version}`, 11 | server.url && `@baseUrl ${server.url}`, 12 | info.description && _.replace(formatDescription(info.description), /\n/g, "\n * "), 13 | ]); 14 | 15 | %> 16 | /** PARTIAL TEMPLATES */ 17 | <% if (descriptionLines.length) { %> 18 | /** 19 | <% descriptionLines.forEach((descriptionLine) => { %> 20 | * <%~ descriptionLine %> 21 | 22 | <% }) %> 23 | */ 24 | <% } %> 25 | export class Api<% if (!config.singleHttpClient) { %> extends HttpClient <% } %> { 26 | 27 | <% if(config.singleHttpClient) { %> 28 | constructor (private http: HttpClient) {} 29 | <% } %> 30 | 31 | 32 | <% routes.outOfModule && routes.outOfModule.forEach((route) => { %> 33 | 34 | <%~ includeFile('./procedure-call.eta', { route, utils, config }) %> 35 | 36 | <% }) %> 37 | 38 | <% routes.combined && routes.combined.forEach(({ routes = [], moduleName }) => { %> 39 | <%~ moduleName %> = { 40 | <% routes.forEach((route) => { %> 41 | 42 | <%~ includeFile('./procedure-call.eta', { route, utils, config }) %> 43 | 44 | <% }) %> 45 | } 46 | <% }) %> 47 | } 48 | -------------------------------------------------------------------------------- /tests/spec/partialDefaultTemplate/test.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 3 | const { resolve } = require("path"); 4 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 5 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 6 | 7 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 8 | 9 | schemas.forEach(({ absolutePath, apiFileName, Exception }) => { 10 | generateApiForTest({ 11 | testName: "partialDefaultTemplate test", 12 | silent: true, 13 | name: apiFileName, 14 | input: absolutePath, 15 | output: resolve(__dirname, "./"), 16 | // because this script was called from package.json folder 17 | templates: "./tests/spec/partialDefaultTemplate/spec_templates", 18 | }).then((output) => { 19 | if (!_.includes(_.get(output.files, "[0].content"), "/** PARTIAL TEMPLATES */")) { 20 | throw new Exception("Failed, spec templates are not applied"); 21 | } 22 | 23 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/spec/patch/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--patch option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | patch: true, 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/spec/responses/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | silent: true, 11 | name: apiFileName, 12 | input: absolutePath, 13 | output: resolve(__dirname, "./"), 14 | generateResponses: true, 15 | }) 16 | .then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }) 19 | .catch((e) => { 20 | console.error("responses option test failed."); 21 | throw e; 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/spec/routeTypes/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--route-types option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | generateRouteTypes: true, 16 | generateClient: false, 17 | }).then(() => { 18 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/spec/singleHttpClient/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "schemes": ["https"], 4 | "host": "6-dot-authentiqio.appspot.com", 5 | "basePath": "/", 6 | "info": { 7 | "contact": { 8 | "email": "hello@authentiq.com", 9 | "name": "Authentiq team", 10 | "url": "http://authentiq.io/support" 11 | }, 12 | "description": "Strong authentication, without the passwords.", 13 | "license": { 14 | "name": "Apache 2.0", 15 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 16 | }, 17 | "termsOfService": "http://authentiq.com/terms/", 18 | "title": "Authentiq", 19 | "version": "6", 20 | "x-apisguru-categories": ["security"], 21 | "x-logo": { 22 | "backgroundColor": "#F26641", 23 | "url": "https://api.apis.guru/v2/cache/logo/https_www.authentiq.com_theme_images_authentiq-logo-a-inverse.svg" 24 | }, 25 | "x-origin": [ 26 | { 27 | "format": "swagger", 28 | "url": "https://raw.githubusercontent.com/AuthentiqID/authentiq-docs/master/docs/swagger/issuer.yaml", 29 | "version": "2.0" 30 | } 31 | ], 32 | "x-preferred": true, 33 | "x-providerName": "6-dot-authentiqio.appspot.com" 34 | }, 35 | "parameters": { 36 | "AuthentiqID": { 37 | "description": "Authentiq ID to register", 38 | "in": "body", 39 | "name": "body", 40 | "required": true, 41 | "schema": { 42 | "$ref": "#/definitions/AuthentiqID" 43 | } 44 | }, 45 | "JobID": { 46 | "description": "Job ID (20 chars)", 47 | "in": "path", 48 | "name": "job", 49 | "required": true, 50 | "type": "string" 51 | }, 52 | "PK": { 53 | "description": "Public Signing Key - Authentiq ID (43 chars)", 54 | "in": "path", 55 | "name": "PK", 56 | "required": true, 57 | "type": "string" 58 | }, 59 | "PushToken": { 60 | "description": "Push Token.", 61 | "in": "body", 62 | "name": "body", 63 | "required": true, 64 | "schema": { 65 | "$ref": "#/definitions/PushToken" 66 | } 67 | }, 68 | "Scope": { 69 | "description": "Claims of scope", 70 | "in": "body", 71 | "name": "body", 72 | "required": true, 73 | "schema": { 74 | "$ref": "#/definitions/Claims" 75 | } 76 | } 77 | }, 78 | "responses": { 79 | "ErrorResponse": { 80 | "description": "Error response", 81 | "schema": { 82 | "$ref": "#/definitions/Error" 83 | } 84 | } 85 | }, 86 | "paths": { 87 | "/key": { 88 | "post": { 89 | "consumes": ["application/jwt"], 90 | "description": "Register a new ID `JWT(sub, devtoken)`\n\nv5: `JWT(sub, pk, devtoken, ...)`\n\nSee: https://github.com/skion/authentiq/wiki/JWT-Examples\n", 91 | "operationId": "key_register", 92 | "parameters": [ 93 | { 94 | "$ref": "#/parameters/AuthentiqID" 95 | } 96 | ], 97 | "produces": ["application/json"], 98 | "responses": { 99 | "201": { 100 | "description": "Successfully registered", 101 | "schema": { 102 | "properties": { 103 | "secret": { 104 | "description": "revoke key", 105 | "type": "string" 106 | }, 107 | "status": { 108 | "description": "registered", 109 | "type": "string" 110 | } 111 | }, 112 | "type": "object" 113 | } 114 | }, 115 | "409": { 116 | "description": "Key already registered `duplicate-key`", 117 | "schema": { 118 | "$ref": "#/definitions/Error" 119 | } 120 | }, 121 | "default": { 122 | "$ref": "#/responses/ErrorResponse" 123 | } 124 | }, 125 | "tags": ["key", "post"] 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /tests/spec/singleHttpClient/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--single-http-client option test", 11 | silent: true, 12 | name: apiFileName, 13 | spec: require(absolutePath), 14 | output: resolve(__dirname, "./"), 15 | singleHttpClient: true, 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/spec/specProperty/schema.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | /* 4 | * ------------------------------------------------------------------ 5 | * # THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API-NEXTGEN # 6 | * # AUTHORS: acacode & grandsilence # 7 | * # https://github.com/grandsilence/swagger-typescript-api-nextgen # 8 | * ------------------------------------------------------------------ 9 | */ 10 | 11 | export type Pet = NewPet & { id: number }; 12 | 13 | export interface NewPet { 14 | name: string; 15 | tag?: string; 16 | } 17 | 18 | export interface Error { 19 | /** @format int32 */ 20 | code: number; 21 | message: string; 22 | } 23 | 24 | export interface PageTemplateResponseDto { 25 | content?: any[]; 26 | empty?: boolean; 27 | first?: boolean; 28 | last?: boolean; 29 | 30 | /** @format int32 */ 31 | number?: number; 32 | 33 | /** @format int32 */ 34 | numberOfElements?: number; 35 | pageable?: any; 36 | 37 | /** @format int32 */ 38 | size?: number; 39 | sort?: any; 40 | 41 | /** @format int64 */ 42 | totalElements?: number; 43 | 44 | /** @format int32 */ 45 | totalPages?: number; 46 | } 47 | 48 | export namespace Pets { 49 | /** 50 | * @description Returns all pets from the system that the user has access to Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. 51 | * @name FindPets 52 | * @request GET:/pets 53 | */ 54 | export namespace FindPets { 55 | export type RequestParams = {}; 56 | export type RequestQuery = { tags?: string[]; limit?: number }; 57 | export type RequestBody = never; 58 | export type RequestHeaders = {}; 59 | export type ResponseBody = Pet[]; 60 | } 61 | /** 62 | * @description Creates a new pet in the store. Duplicates are allowed 63 | * @name AddPet 64 | * @request POST:/pets 65 | */ 66 | export namespace AddPet { 67 | export type RequestParams = {}; 68 | export type RequestQuery = {}; 69 | export type RequestBody = NewPet; 70 | export type RequestHeaders = {}; 71 | export type ResponseBody = Pet; 72 | } 73 | /** 74 | * @description Returns a user based on a single ID, if the user does not have access to the pet 75 | * @name FindPetById 76 | * @request GET:/pets/{id} 77 | */ 78 | export namespace FindPetById { 79 | export type RequestParams = { id: number }; 80 | export type RequestQuery = {}; 81 | export type RequestBody = never; 82 | export type RequestHeaders = {}; 83 | export type ResponseBody = Pet; 84 | } 85 | /** 86 | * @description deletes a single pet based on the ID supplied 87 | * @name DeletePet 88 | * @request DELETE:/pets/{id} 89 | */ 90 | export namespace DeletePet { 91 | export type RequestParams = { id: number }; 92 | export type RequestQuery = {}; 93 | export type RequestBody = never; 94 | export type RequestHeaders = {}; 95 | export type ResponseBody = void; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/spec/specProperty/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "specProperty test", 11 | silent: true, 12 | name: apiFileName, 13 | spec: require(absolutePath), 14 | output: resolve(__dirname, "./"), 15 | generateRouteTypes: true, 16 | generateClient: false, 17 | }).then(() => { 18 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/spec/templates/spec_templates/api.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { apiConfig, routes, utils, config } = it; 3 | const { info, servers, externalDocs } = apiConfig; 4 | const { _, require, formatDescription } = utils; 5 | 6 | const server = (servers && servers[0]) || { url: "" }; 7 | 8 | const descriptionLines = _.compact([ 9 | `@title ${info.title || "No title"}`, 10 | info.version && `@version ${info.version}`, 11 | info.license && `@license ${_.compact([ 12 | info.license.name, 13 | info.license.url && `(${info.license.url})`, 14 | ]).join(" ")}`, 15 | info.termsOfService && `@termsOfService ${info.termsOfService}`, 16 | server.url && `@baseUrl ${server.url}`, 17 | externalDocs.url && `@externalDocs ${externalDocs.url}`, 18 | info.contact && `@contact ${_.compact([ 19 | info.contact.name, 20 | info.contact.email && `<${info.contact.email}>`, 21 | info.contact.url && `(${info.contact.url})`, 22 | ]).join(" ")}`, 23 | info.description && " ", 24 | info.description && _.replace(formatDescription(info.description), /\n/g, "\n * "), 25 | ]); 26 | 27 | %> 28 | 29 | <% if (config.httpClientType === config.constants.HTTP_CLIENT.AXIOS) { %> import { AxiosRequestConfig, AxiosResponse } from "axios"; <% } %> 30 | 31 | <% if (descriptionLines.length) { %> 32 | /** 33 | <% descriptionLines.forEach((descriptionLine) => { %> 34 | * <%~ descriptionLine %> 35 | 36 | <% }) %> 37 | */ 38 | <% } %> 39 | export class Api<% if (!config.singleHttpClient) { %> extends HttpClient <% } %> { 40 | 41 | <% if(config.singleHttpClient) { %> 42 | constructor (private http: HttpClient) {} 43 | <% } %> 44 | 45 | 46 | <% routes.outOfModule && routes.outOfModule.forEach((route) => { %> 47 | 48 | <%~ includeFile('./procedure-call.eta', { ...it, route }) %> 49 | 50 | <% }) %> 51 | 52 | <% routes.combined && routes.combined.forEach(({ routes = [], moduleName }) => { %> 53 | <%~ moduleName %> = { 54 | <% routes.forEach((route) => { %> 55 | 56 | <%~ includeFile('./procedure-call.eta', { ...it, route }) %> 57 | 58 | <% }) %> 59 | } 60 | <% }) %> 61 | } 62 | 63 | /* CUSTOM TEMPLATE */ -------------------------------------------------------------------------------- /tests/spec/templates/spec_templates/data-contracts.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { modelTypes, utils } = it; 3 | const { formatDescription } = utils; 4 | 5 | 6 | const dataContractTemplates = { 7 | enum: (contract) => { 8 | return `enum ${contract.name} {\r\n${contract.content} \r\n }`; 9 | }, 10 | interface: (contract) => { 11 | return `interface ${contract.name} {\r\n${contract.content}}`; 12 | }, 13 | type: (contract) => { 14 | return `type ${contract.name} = ${contract.content}`; 15 | }, 16 | } 17 | %> 18 | <% modelTypes.forEach((contract) => { %> 19 | <% if (contract.description) { %> 20 | /** 21 | * <%~ formatDescription(contract.description) %> 22 | 23 | */ 24 | <% } %> 25 | export <%~ (dataContractTemplates[contract.typeIdentifier] || dataContractTemplates.type)(contract) %> 26 | 27 | 28 | <% }) %> 29 | 30 | /* CUSTOM TEMPLATE */ -------------------------------------------------------------------------------- /tests/spec/templates/spec_templates/http-client.eta: -------------------------------------------------------------------------------- 1 | <% const { config } = it; %> 2 | <% /* https://github.com/grandsilence/swagger-typescript-api-nextgen/tree/master/templates/base/http-clients/ */ %> 3 | <%~ includeFile(`@base/http-clients/${config.httpClientType}-http-client`, it) %> 4 | 5 | /* HTTP CLIENT TEMPLATE */ -------------------------------------------------------------------------------- /tests/spec/templates/spec_templates/procedure-call.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { utils, route, config } = it; 3 | const { requestBodyInfo, responseBodyInfo, specificArgNameResolver } = route; 4 | const { _, getInlineParseContent, getParseContent, parseSchema, getComponentByRef, require } = utils; 5 | const { parameters, path, method, payload, query, formData, security, requestParams } = route.request; 6 | const { type, errorType, contentTypes } = route.response; 7 | const { HTTP_CLIENT, RESERVED_REQ_PARAMS_ARG_NAMES } = config.constants; 8 | const routeDocs = includeFile("@base/route-docs", { config, route, utils }); 9 | const queryName = (query && query.name) || "query"; 10 | const pathParams = _.values(parameters); 11 | const pathParamsNames = _.map(pathParams, "name"); 12 | 13 | const isFetchTemplate = config.httpClientType === HTTP_CLIENT.FETCH; 14 | 15 | const requestConfigParam = { 16 | name: specificArgNameResolver.resolve(RESERVED_REQ_PARAMS_ARG_NAMES), 17 | optional: true, 18 | type: "RequestParams", 19 | defaultValue: "{}", 20 | } 21 | 22 | const argToTmpl = ({ name, optional, type, defaultValue }) => `${name}${!defaultValue && optional ? '?' : ''}: ${type}${defaultValue ? ` = ${defaultValue}` : ''}`; 23 | 24 | const rawWrapperArgs = config.extractRequestParams ? 25 | _.compact([ 26 | requestParams && { 27 | name: pathParams.length ? `{ ${_.join(pathParamsNames, ", ")}, ...${queryName} }` : queryName, 28 | optional: false, 29 | type: getInlineParseContent(requestParams), 30 | }, 31 | ...(!requestParams ? pathParams : []), 32 | payload, 33 | requestConfigParam, 34 | ]) : 35 | _.compact([ 36 | ...pathParams, 37 | query, 38 | payload, 39 | requestConfigParam, 40 | ]) 41 | 42 | const wrapperArgs = _ 43 | // Sort by optionality 44 | .sortBy(rawWrapperArgs, [o => o.optional]) 45 | .map(argToTmpl) 46 | .join(', ') 47 | 48 | // RequestParams["type"] 49 | const requestContentKind = { 50 | "JSON": "ContentType.Json", 51 | "URL_ENCODED": "ContentType.UrlEncoded", 52 | "FORM_DATA": "ContentType.FormData", 53 | } 54 | // RequestParams["format"] 55 | const responseContentKind = { 56 | "JSON": '"json"', 57 | "IMAGE": '"blob"', 58 | "FORM_DATA": isFetchTemplate ? '"formData"' : '"document"' 59 | } 60 | 61 | const bodyTmpl = _.get(payload, "name") || null; 62 | const queryTmpl = (query != null && queryName) || null; 63 | const bodyContentKindTmpl = requestContentKind[requestBodyInfo.contentKind] || null; 64 | const responseFormatTmpl = responseContentKind[responseBodyInfo.success && responseBodyInfo.success.schema && responseBodyInfo.success.schema.contentKind] || null; 65 | const securityTmpl = security ? 'true' : null; 66 | 67 | const describeReturnType = () => { 68 | if (!config.toJS) return ""; 69 | 70 | switch(config.httpClientType) { 71 | case HTTP_CLIENT.AXIOS: { 72 | return `Promise>` 73 | } 74 | default: { 75 | return `Promise` 76 | } 77 | } 78 | } 79 | 80 | %> 81 | /** 82 | <%~ routeDocs.description %> 83 | 84 | * <% /* Here you can add some other JSDoc tags */ %> 85 | 86 | <%~ routeDocs.lines %> 87 | 88 | */ 89 | <%~ route.routeName.usage %><%~ route.namespace ? ': ' : ' = ' %>(<%~ wrapperArgs %>)<%~ config.toJS ? `: ${describeReturnType()}` : "" %> => 90 | <%~ config.singleHttpClient ? 'this.http.request' : 'this.request' %><<%~ type %>, <%~ errorType %>>({ 91 | path: `<%~ path %>`, 92 | method: '<%~ _.upperCase(method) %>', 93 | <%~ queryTmpl ? `query: ${queryTmpl},` : '' %> 94 | <%~ bodyTmpl ? `body: ${bodyTmpl},` : '' %> 95 | <%~ securityTmpl ? `secure: ${securityTmpl},` : '' %> 96 | <%~ bodyContentKindTmpl ? `type: ${bodyContentKindTmpl},` : '' %> 97 | <%~ responseFormatTmpl ? `format: ${responseFormatTmpl},` : '' %> 98 | ...<%~ _.get(requestConfigParam, "name") %>, 99 | })<%~ route.namespace ? ',' : '' %> 100 | 101 | 102 | /* CUSTOM TEMPLATE */ -------------------------------------------------------------------------------- /tests/spec/templates/spec_templates/route-docs.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { config, route, utils } = it; 3 | const { _, formatDescription, fmtToJSDocLine, classNameCase } = utils; 4 | const { raw, request, routeName } = route; 5 | const jsDocDescription = raw.description ? 6 | ` * @description ${formatDescription(raw.description, true)}` : 7 | fmtToJSDocLine('No description', { eol: false }); 8 | const jsDocLines = _.compact([ 9 | _.size(raw.tags) && ` * @tags ${raw.tags.join(", ")}`, 10 | ` * @name ${classNameCase(routeName.usage)}`, 11 | raw.summary && ` * @summary ${raw.summary}`, 12 | ` * @request ${_.upperCase(request.method)}:${raw.route}`, 13 | routeName.duplicate && ` * @originalName ${routeName.original}\n`, 14 | routeName.duplicate && ` * @duplicate\n`, 15 | request.security && ` * @secure`, 16 | ...(config.generateResponses && raw.responsesTypes.length 17 | ? raw.responsesTypes.map( 18 | ({ type, status, description, isSuccess }) => 19 | ` * @response \`${status}\` \`${type}\` ${description}`, 20 | ) 21 | : []), 22 | ]).join("\n"); 23 | 24 | 25 | return { 26 | description: jsDocDescription, 27 | lines: jsDocLines, 28 | } 29 | %> 30 | 31 | /* CUSTOM TEMPLATE */ -------------------------------------------------------------------------------- /tests/spec/templates/spec_templates/route-name.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { routeInfo, utils } = it; 3 | const { 4 | operationId, 5 | method, 6 | route, 7 | moduleName, 8 | responsesTypes, 9 | description, 10 | tags, 11 | summary, 12 | } = routeInfo; 13 | const { _, fmtToJSDocLine } = utils; 14 | 15 | const methodAliases = { 16 | get: (pathName, hasPathInserts) => 17 | _.camelCase(`${pathName}_${hasPathInserts ? "detail" : "list"}`), 18 | post: (pathName, hasPathInserts) => _.camelCase(`${pathName}_create`), 19 | put: (pathName, hasPathInserts) => _.camelCase(`${pathName}_update`), 20 | patch: (pathName, hasPathInserts) => _.camelCase(`${pathName}_partial_update`), 21 | delete: (pathName, hasPathInserts) => _.camelCase(`${pathName}_delete`), 22 | }; 23 | 24 | const createCustomOperationId = (method, route, moduleName) => { 25 | const hasPathInserts = /\{(\w){1,}\}/g.test(route); 26 | const splitedRouteBySlash = _.compact(_.replace(route, /\{(\w){1,}\}/g, "").split("/")); 27 | const routeParts = (splitedRouteBySlash.length > 1 28 | ? splitedRouteBySlash.splice(1) 29 | : splitedRouteBySlash 30 | ).join("_"); 31 | return routeParts.length > 3 && methodAliases[method] 32 | ? methodAliases[method](routeParts, hasPathInserts) 33 | : _.camelCase(_.lowerCase(method) + "_" + [moduleName].join("_")) || "index"; 34 | }; 35 | 36 | if (operationId) 37 | return _.camelCase(operationId); 38 | if (route === "/") 39 | return _.camelCase(`${_.lowerCase(method)}Root`); 40 | 41 | return createCustomOperationId(method, route, moduleName); 42 | %> 43 | 44 | /* CUSTOM TEMPLATE */ -------------------------------------------------------------------------------- /tests/spec/templates/spec_templates/route-type.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { route, utils, config } = it; 3 | const { _, classNameCase } = utils 4 | const { query, payload } = route.request 5 | const { type: responseType } = route.response 6 | 7 | const routeDocs = includeFile("./route-docs", { config, route, utils }); 8 | const routeNamespace = classNameCase(route.routeName.usage) 9 | const queryType = (query && query.type) || '{}' 10 | const bodyType = (payload && payload.type) || 'never' 11 | %> 12 | /** 13 | <%~ routeDocs.description %> 14 | 15 | * <% /* Here you can add some other JSDoc tags */ %> 16 | 17 | <%~ routeDocs.lines %> 18 | 19 | */ 20 | export namespace <%~ routeNamespace %> { 21 | export type RequestQuery = <%~ queryType %>; 22 | export type RequestBody = <%~ bodyType %>; 23 | export type ResponseBody = <%~ responseType %>; 24 | } 25 | 26 | /* CUSTOM TEMPLATE */ -------------------------------------------------------------------------------- /tests/spec/templates/spec_templates/route-types.eta: -------------------------------------------------------------------------------- 1 | <% 2 | const { utils, config, routes } = it; 3 | %> 4 | <% 5 | /* TODO: outOfModule, combined should be attributes of route, which will allow to avoid duplication of code */ 6 | %> 7 | 8 | <% routes.outOfModule && routes.outOfModule.forEach(({ routes = [] }) => { %> 9 | <% routes.forEach((route) => { %> 10 | <%~ includeFile('./route-type.eta', { route, utils, config }) %> 11 | <% }) %> 12 | <% }) %> 13 | 14 | <% routes.combined && routes.combined.forEach(({ routes = [], moduleName }) => { %> 15 | export namespace <%~ moduleName %> { 16 | <% routes.forEach((route) => { %> 17 | <%~ includeFile('./route-type.eta', { route, utils, config }) %> 18 | <% }) %> 19 | } 20 | 21 | <% }) %> 22 | 23 | /* CUSTOM TEMPLATE */ -------------------------------------------------------------------------------- /tests/spec/templates/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName, Exception }) => { 9 | generateApiForTest({ 10 | testName: "--templates option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | // because this script was called from package.json folder 16 | templates: "./tests/spec/templates/spec_templates", 17 | }) 18 | .then((output) => { 19 | if (!output.files[0]) throw new Exception("Failed. no output file") 20 | if (!output.files[0].content) throw new Exception("Failed. no output file content") 21 | 22 | const matches = output.files[0].content.match(/\/\* CUSTOM TEMPLATE \*\//g) 23 | 24 | if (!matches || matches.length < 4) throw Exception("Failed. too few comment matches") 25 | 26 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /tests/spec/typeSuffixPrefix/test.js: -------------------------------------------------------------------------------- 1 | const _ = require("lodash"); 2 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 3 | const { resolve } = require("path"); 4 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 5 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 6 | 7 | const schemas = createSchemaInfos({ 8 | absolutePathToSchemas: resolve(__dirname, "./"), 9 | absoluteOutputPath: resolve(__dirname, "./"), 10 | }); 11 | 12 | schemas.forEach(({ absolutePath, apiFileName, Exception }) => { 13 | generateApiForTest({ 14 | testName: "--type-suffix --type-prefix options test", 15 | silent: true, 16 | name: apiFileName, 17 | input: absolutePath, 18 | output: resolve(__dirname, "./"), 19 | typePrefix: "SwaggerType", 20 | typeSuffix: "GeneratedDataContract", 21 | generateClient: true, 22 | generateResponses: true, 23 | }).then((output) => { 24 | const content = _.split(output.files[0].content, "\n"); 25 | const reservedTypes = [ 26 | "QueryParamsType", 27 | "ResponseFormat", 28 | "FullRequestParams", 29 | "RequestParams", 30 | "ApiConfig", 31 | "HttpResponse", 32 | ]; 33 | 34 | for (const line of content) { 35 | if (_.startsWith(line, "export interface") || _.startsWith(line, "export type")) { 36 | const typeName = _.split(_.split(line, " ")[2], "<")[0] || ""; 37 | 38 | if (!_.includes(reservedTypes, typeName)) { 39 | if ( 40 | !_.startsWith(typeName, "SwaggerType") || 41 | !_.endsWith(typeName, "GeneratedDataContract") 42 | ) { 43 | throw new Exception( 44 | `Not at all data contracts have type prefix and type suffix`, 45 | `\n${content.indexOf(line) + 1}: ${line} ->`, 46 | `\n ${typeName} <-`, 47 | ); 48 | } 49 | } 50 | } 51 | } 52 | 53 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /tests/spec/unionEnums/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "examples": {}, 4 | "headers": {}, 5 | "parameters": {}, 6 | "requestBodies": {}, 7 | "responses": {}, 8 | "schemas": { 9 | "StringEnum": { 10 | "enum": ["String1", "String2", "String3", "String4"], 11 | "type": "string" 12 | }, 13 | "NumberEnum": { 14 | "enum": [1, 2, 3, 4], 15 | "type": "number" 16 | }, 17 | "BooleanEnum": { 18 | "enum": ["true", "false"], 19 | "type": "boolean" 20 | }, 21 | "IntEnumWithNames": { 22 | "enum": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 23 | "type": "integer", 24 | "description": "FooBar", 25 | "format": "int32", 26 | "x-enumNames": [ 27 | "Unknown", 28 | "String", 29 | "Int32", 30 | "Int64", 31 | "Double", 32 | "DateTime", 33 | "Test2", 34 | "Test23", 35 | "Tess44", 36 | "BooFar" 37 | ] 38 | } 39 | }, 40 | "securitySchemes": {} 41 | }, 42 | "info": { 43 | "title": "" 44 | }, 45 | "openapi": "3.0.0", 46 | "paths": {}, 47 | "servers": [ 48 | { 49 | "url": "http://localhost:8080/api/v1" 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /tests/spec/unionEnums/test.js: -------------------------------------------------------------------------------- 1 | const { generateApiForTest } = require("../../helpers/generateApiForTest"); 2 | const { resolve } = require("path"); 3 | const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); 4 | const createSchemaInfos = require("../../helpers/createSchemaInfos"); 5 | 6 | const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); 7 | 8 | schemas.forEach(({ absolutePath, apiFileName }) => { 9 | generateApiForTest({ 10 | testName: "--union-enums option test", 11 | silent: true, 12 | name: apiFileName, 13 | input: absolutePath, 14 | output: resolve(__dirname, "./"), 15 | generateUnionEnums: true, 16 | }).then(() => { 17 | validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/validate.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require("path"); 2 | const createGeneratedApiInfos = require("./helpers/createGeneratedApiInfos"); 3 | const validateGeneratedModule = require("./helpers/validateGeneratedModule"); 4 | 5 | const v2ApiPaths = createGeneratedApiInfos(resolve(__dirname, "./generated/v2.0")); 6 | const v3ApiPaths = createGeneratedApiInfos(resolve(__dirname, "./generated/v3.0")); 7 | 8 | [...v2ApiPaths, ...v3ApiPaths].forEach((pathToFile) => { 9 | validateGeneratedModule(pathToFile); 10 | }); 11 | 12 | console.log("everything is good:)"); 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "noEmitOnError": true, 5 | "noImplicitAny": true, 6 | "target": "esnext", 7 | "module": "esnext", 8 | "strict": true, 9 | "noEmit": true 10 | } 11 | } 12 | --------------------------------------------------------------------------------