diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index fe53ab1f2..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: 2 -jobs: - build: - docker: - - image: cimg/node:lts-browsers - resource_class: large - working_directory: ~/repo - steps: - - checkout - - restore_cache: - keys: - - v1-dependencies-{{ checksum "package-lock.json" }} - - v1-dependencies- - - run: - name: Install dependencies - command: npm install - - save_cache: - key: v1-dependencies-{{ checksum "package-lock.json" }} - paths: - - node_modules - - run: - name: Build library - command: npm run release - - run: - name: Run unit tests - command: npm run test -# - run: -# name: Run e2e tests -# command: npm run test:e2e - - run: - name: Submit to Codecov - command: npm run codecov diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 77d50f37b..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,22 +0,0 @@ -version: 2 -updates: -- package-ecosystem: npm - directory: "/" - schedule: - interval: "daily" - ignore: - - dependency-name: "@types/node-fetch" - - dependency-name: "node-fetch" - - dependency-name: "camelcase" - - dependency-name: "@angular-devkit/build-angular" - - dependency-name: "@angular/animations" - - dependency-name: "@angular/cli" - - dependency-name: "@angular/common" - - dependency-name: "@angular/compiler" - - dependency-name: "@angular/compiler-cli" - - dependency-name: "@angular/core" - - dependency-name: "@angular/forms" - - dependency-name: "@angular/platform-browser" - - dependency-name: "@angular/platform-browser-dynamic" - - dependency-name: "@angular/router" - - dependency-name: "typescript" \ No newline at end of file diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml deleted file mode 100644 index 5d5d5301d..000000000 --- a/.github/workflows/auto-merge.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: auto-merge - -on: pull_request_target - -permissions: - pull-requests: write - contents: write - -jobs: - dependabot: - runs-on: ubuntu-latest - if: ${{ github.actor == 'dependabot[bot]' }} - steps: - - name: Fetch Dependabot metadata - id: dependabot-metadata - uses: dependabot/fetch-metadata@v1 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Approve PR - run: gh pr review --approve "$PR_URL" - env: - PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Merge PR - if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} - run: gh pr merge --auto --squash "$PR_URL" - env: - PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 9041ade65..000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - schedule: - - cron: "44 20 * * 3" - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ javascript ] - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - queries: +security-and-quality - - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 000000000..d49f42fd3 --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,64 @@ +name: "Create Release" + +on: + workflow_dispatch: + inputs: + releaseType: + type: choice + description: Release Type + options: + - minor + - patch + - major + +jobs: + publish: + name: "Build, Tag & Publish" + runs-on: ubuntu-latest + steps: + - name: "Checkout Code" + uses: actions/checkout@v3 + with: + ref: main + + - name: "Setup NodeJS Environment" + uses: actions/setup-node@v3 + with: + node-version: 16 + registry-url: "https://npm.pkg.github.com" + scope: "@hypoport" + + - name: "Install Dependencies" + run: npm ci + + - name: "Configure Git Author" + run: | + git config user.name "github-actions" + git config user.email "github-actions@github.com" + + - name: "Increase Version" + run: npm version ${{ inputs.releaseType }} --no-git-tag-version + + - name: "Extract Version as Environment Variable" + run: | + echo "version=$(npx -c 'echo "$npm_package_version"')" >> $GITHUB_ENV + + - name: "Build" + run: npm run release + + - name: "Configure NPM-Package Write Credentials" + run: | + npm config set @hypoport:registry https://npm.pkg.github.com + npm config set //npm.pkg.github.com/:_authToken ${{ secrets.GITHUB_TOKEN }} + + - name: "Create and Push Release" + run: | + git add . + git commit -m "increased version to ${{ env.version }}" + git tag v${{ env.version }} + git push + git push origin v${{ env.version }} + npm publish + + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 2b7422568..57899e20f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ junit.xml dist coverage test/generated +test/models test/e2e/generated samples/generated samples/swagger-codegen-cli-v2.jar diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..20bbb1f5c --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,12 @@ +# Ampr Code Owners / Trusted Committers +# +# Siehe auch: +# - https://help.github.com/articles/about-codeowners/ +# - https://github.com/hypoport/ep-innersource/blob/master/src/inner-source/code-ownership.md +# +# Bitte die Sortierung dieser Datei beibehalten. Die zuletzt zutreffende Regel +# hat Vorrang. + +* @MaykAkifovski @denwehrle @minizwergi @v-bykovski +package.json +package-lock.json diff --git a/README.md b/README.md index b851329a8..4bf025fa8 100644 --- a/README.md +++ b/README.md @@ -1,106 +1,23 @@ # OpenAPI Typescript Codegen -[![NPM][npm-image]][npm-url] -[![License][license-image]][license-url] -[![Coverage][coverage-image]][coverage-url] -[![Coverage][coverage-image]][coverage-url] -[![Downloads][downloads-image]][downloads-url] -[![Build][build-image]][build-url] +Fork of https://github.com/hypoport/openapi-typescript-codegen -> Node.js library that generates Typescript clients based on the OpenAPI specification. +# How to test +Make sure the correct api spec is reference under `/test/index.spec.ts` (for example `./test/homecloud-api-specs.json` if you want to test against a local file) -## Why? -- Frontend ❤️ OpenAPI, but we do not want to use JAVA codegen in our builds -- Quick, lightweight, robust and framework-agnostic 🚀 -- Supports generation of TypeScript clients -- Supports generations of Fetch, Node-Fetch, Axios, Angular and XHR http clients -- Supports OpenAPI specification v2.0 and v3.0 -- Supports JSON and YAML files for input -- Supports generation through CLI, Node.js and NPX -- Supports tsc and @babel/plugin-transform-typescript -- Supports aborting of requests (cancelable promise pattern) -- Supports external references using [json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser/) - -## Install - -``` -npm install openapi-typescript-codegen --save-dev -``` - -## Usage - -``` -$ openapi --help - - Usage: openapi [options] - - Options: - -V, --version output the version number - -i, --input OpenAPI specification, can be a path, url or string content (required) - -o, --output Output directory (required) - -c, --client HTTP client to generate [fetch, xhr, node, axios, angular] (default: "fetch") - --name Custom client class name - --useOptions Use options instead of arguments - --useUnionTypes Use union types instead of enums - --exportCore Write core files to disk (default: true) - --exportServices Write services to disk (default: true) - --exportModels Write models to disk (default: true) - --exportSchemas Write schemas to disk (default: false) - --indent Indentation options [4, 2, tab] (default: "4") - --postfixServices Service name postfix (default: "Service") - --postfixModels Model name postfix - --request Path to custom request file - -h, --help display help for command - - Examples - $ openapi --input ./spec.json --output ./generated - $ openapi --input ./spec.json --output ./generated --client xhr +The test command will generate the services under `test/generated` and do some basic validation tests. +```shell + npm run test ``` -## Docker usage +# How to release -To build the Docker container, execute the following command: - -``` -docker build . --tag openapi-typescript-codegen +```shell + npm run release ``` -After this is done, you can execute the CLI commands: - -``` -docker run openapi-typescript-codegen --help -docker run openapi-typescript-codegen --input sample.yaml --output client +```shell + npm pack ``` -Documentation -=== -- [Basic usage](docs/basic-usage.md) -- [OpenAPI object](docs/openapi-object.md) -- [Client instances](docs/client-instances.md) `--name` -- [Argument vs. Object style](docs/arguments-vs-object-style.md) `--useOptions` -- [Enums vs. Union types](docs/enum-vs-union-types.md) `--useUnionTypes` -- [Runtime schemas](docs/runtime-schemas.md) `--exportSchemas` -- [Enum with custom names and descriptions](docs/custom-enums.md) -- [Nullable props (OpenAPI v2)](docs/nullable-props.md) -- [Authorization](docs/authorization.md) -- [External references](docs/external-references.md) -- [Canceling requests](docs/canceling-requests.md) -- [Custom request file](docs/custom-request-file.md) - -Support -=== -- [Babel support](docs/babel-support.md) -- [Axios support](docs/axios-support.md) -- [Angular support](docs/angular-support.md) -- [Node-Fetch support](docs/node-fetch-support.md) - -[npm-url]: https://npmjs.org/package/openapi-typescript-codegen -[npm-image]: https://img.shields.io/npm/v/openapi-typescript-codegen.svg -[license-url]: LICENSE -[license-image]: http://img.shields.io/npm/l/openapi-typescript-codegen.svg -[coverage-url]: https://codecov.io/gh/ferdikoomen/openapi-typescript-codegen -[coverage-image]: https://img.shields.io/codecov/c/github/ferdikoomen/openapi-typescript-codegen.svg -[downloads-url]: http://npm-stat.com/charts.html?package=openapi-typescript-codegen -[downloads-image]: http://img.shields.io/npm/dm/openapi-typescript-codegen.svg -[build-url]: https://circleci.com/gh/ferdikoomen/openapi-typescript-codegen/tree/master -[build-image]: https://circleci.com/gh/ferdikoomen/openapi-typescript-codegen/tree/master.svg?style=svg +Copy and paste the file diff --git a/bin/index.js b/bin/index.js deleted file mode 100755 index 32f2fecbc..000000000 --- a/bin/index.js +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env node - -'use strict'; - -const path = require('path'); -const { program } = require('commander'); -const pkg = require('../package.json'); - -const params = program - .name('openapi') - .usage('[options]') - .version(pkg.version) - .requiredOption('-i, --input ', 'OpenAPI specification, can be a path, url or string content (required)') - .requiredOption('-o, --output ', 'Output directory (required)') - .option('-c, --client ', 'HTTP client to generate [fetch, xhr, node, axios, angular]', 'fetch') - .option('--name ', 'Custom client class name') - .option('--useOptions', 'Use options instead of arguments') - .option('--useUnionTypes', 'Use union types instead of enums') - .option('--exportCore ', 'Write core files to disk', true) - .option('--exportServices ', 'Write services to disk', true) - .option('--exportModels ', 'Write models to disk', true) - .option('--exportSchemas ', 'Write schemas to disk', false) - .option('--indent ', 'Indentation options [4, 2, tabs]', '4') - .option('--postfixServices ', 'Service name postfix', 'Service') - .option('--postfixModels ', 'Model name postfix') - .option('--request ', 'Path to custom request file') - .parse(process.argv) - .opts(); - -const OpenAPI = require(path.resolve(__dirname, '../dist/index.js')); - -if (OpenAPI) { - OpenAPI.generate({ - input: params.input, - output: params.output, - httpClient: params.client, - clientName: params.name, - useOptions: params.useOptions, - useUnionTypes: params.useUnionTypes, - exportCore: JSON.parse(params.exportCore) === true, - exportServices: JSON.parse(params.exportServices) === true, - exportModels: JSON.parse(params.exportModels) === true, - exportSchemas: JSON.parse(params.exportSchemas) === true, - indent: params.indent, - postfixServices: params.postfixServices, - postfixModels: params.postfixModels, - request: params.request, - }) - .then(() => { - process.exit(0); - }) - .catch(error => { - console.error(error); - process.exit(1); - }); -} diff --git a/bin/index.spec.js b/bin/index.spec.js deleted file mode 100755 index 6030c07c8..000000000 --- a/bin/index.spec.js +++ /dev/null @@ -1,72 +0,0 @@ -const crossSpawn = require('cross-spawn'); - -describe('bin', () => { - it('it should support minimal params', async () => { - const result = crossSpawn.sync('node', [ - './bin/index.js', - '--input', - './test/spec/v3.json', - '--output', - './test/generated/bin', - ]); - expect(result.stdout.toString()).toBe(''); - expect(result.stderr.toString()).toBe(''); - }); - - it('it should support all params', async () => { - const result = crossSpawn.sync('node', [ - './bin/index.js', - '--input', - './test/spec/v3.json', - '--output', - './test/generated/bin', - '--client', - 'fetch', - '--useOptions', - '--useUnionTypes', - '--exportCore', - 'true', - '--exportServices', - 'true', - '--exportModels', - 'true', - '--exportSchemas', - 'true', - '--indent', - '4', - '--postfixServices', - 'Service', - '--postfixModels', - 'Dto', - ]); - expect(result.stdout.toString()).toBe(''); - expect(result.stderr.toString()).toBe(''); - }); - - it('it should throw error without params', async () => { - const result = crossSpawn.sync('node', ['./bin/index.js']); - expect(result.stdout.toString()).toBe(''); - expect(result.stderr.toString()).toContain(`error: required option '-i, --input ' not specified`); - }); - - it('it should throw error with wrong params', async () => { - const result = crossSpawn.sync('node', [ - './bin/index.js', - '--input', - './test/spec/v3.json', - '--output', - './test/generated/bin', - '--unknown', - ]); - expect(result.stdout.toString()).toBe(''); - expect(result.stderr.toString()).toContain(`error: unknown option '--unknown'`); - }); - - it('it should display help', async () => { - const result = crossSpawn.sync('node', ['./bin/index.js', '--help']); - expect(result.stdout.toString()).toContain(`Usage: openapi [options]`); - expect(result.stdout.toString()).toContain(`-i, --input `); - expect(result.stdout.toString()).toContain(`-o, --output `); - expect(result.stderr.toString()).toBe(''); - }); -}); diff --git a/hypoport-openapi-typescript-codegen-0.37.0.tgz b/hypoport-openapi-typescript-codegen-0.37.0.tgz new file mode 100644 index 000000000..6eccebfa2 Binary files /dev/null and b/hypoport-openapi-typescript-codegen-0.37.0.tgz differ diff --git a/package-lock.json b/package-lock.json index 0cebbe4c3..8820522d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "openapi-typescript-codegen", - "version": "0.24.0", + "name": "@hypoport/openapi-typescript-codegen", + "version": "0.39.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "openapi-typescript-codegen", - "version": "0.24.0", + "name": "@hypoport/openapi-typescript-codegen", + "version": "0.39.0", "license": "MIT", "dependencies": { "camelcase": "^6.3.0", @@ -45,7 +45,7 @@ "@types/node": "18.16.3", "@types/node-fetch": "2.6.3", "@types/qs": "6.9.7", - "@typescript-eslint/eslint-plugin": "5.60.0", + "@typescript-eslint/eslint-plugin": "5.60.1", "@typescript-eslint/parser": "5.60.0", "abort-controller": "3.0.0", "axios": "1.4.0", @@ -69,7 +69,7 @@ "rollup-plugin-terser": "7.0.2", "rxjs": "7.8.1", "ts-node": "10.9.1", - "tslib": "2.5.3", + "tslib": "2.6.0", "typescript": "4.9.5", "zone.js": "0.13.1" } @@ -5634,15 +5634,15 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", - "integrity": "sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", + "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/type-utils": "5.60.0", - "@typescript-eslint/utils": "5.60.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/type-utils": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -5667,6 +5667,53 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", + "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", + "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", + "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/parser": { "version": "5.60.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", @@ -5712,13 +5759,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz", - "integrity": "sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", + "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.60.0", - "@typescript-eslint/utils": "5.60.0", + "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -5738,6 +5785,63 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", + "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", + "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", + "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/types": { "version": "5.60.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", @@ -5779,17 +5883,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", - "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", + "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -5804,6 +5908,80 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", + "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", + "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", + "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", + "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.60.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", @@ -16438,9 +16616,9 @@ } }, "node_modules/tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", "dev": true }, "node_modules/tsutils": { diff --git a/package.json b/package.json index 9232fbc62..9f2134e0f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "openapi-typescript-codegen", - "version": "0.24.0", + "name": "@hypoport/openapi-typescript-codegen", + "version": "0.37.0", "description": "Library that generates Typescript clients based on the OpenAPI specification.", "author": "Ferdi Koomen", "homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen", @@ -48,7 +48,7 @@ "release": "rollup --config --environment NODE_ENV:production", "validate": "tsc --project tsconfig.json --noEmit", "run": "node ./test/index.js", - "test": "jest --selectProjects UNIT", + "test": "npm run build && jest --selectProjects UNIT", "test:update": "jest --selectProjects UNIT --updateSnapshot", "test:watch": "jest --selectProjects UNIT --watch", "test:coverage": "jest --selectProjects UNIT --coverage", @@ -57,7 +57,9 @@ "eslint:fix": "eslint . --fix", "prepublishOnly": "npm run clean && npm run release", "codecov": "codecov --token=66c30c23-8954-4892-bef9-fbaed0a2e42b", - "docker": "docker build -t eeelenbaas/openapi-typescript-codegen ." + "docker": "docker build -t eeelenbaas/openapi-typescript-codegen .", + "snapshot:publish": "npm run snapshot:version && npm publish --tag snapshot", + "snapshot:version": "npm version prerelease --preid snapshot --git-tag-version=false" }, "dependencies": { "camelcase": "^6.3.0", @@ -93,7 +95,7 @@ "@types/node": "18.16.3", "@types/node-fetch": "2.6.3", "@types/qs": "6.9.7", - "@typescript-eslint/eslint-plugin": "5.60.0", + "@typescript-eslint/eslint-plugin": "5.60.1", "@typescript-eslint/parser": "5.60.0", "abort-controller": "3.0.0", "axios": "1.4.0", @@ -117,11 +119,11 @@ "rollup-plugin-terser": "7.0.2", "rxjs": "7.8.1", "ts-node": "10.9.1", - "tslib": "2.5.3", + "tslib": "2.6.0", "typescript": "4.9.5", "zone.js": "0.13.1" }, - "overrides" : { + "overrides": { "node-fetch": "2.6.9", "rollup": "3.20.2", "typescript": "4.9.5" diff --git a/rollup.config.mjs b/rollup.config.mjs index df7373395..acfe0bd8a 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -39,6 +39,9 @@ const handlebarsPlugin = () => ({ escapeComment: true, escapeDescription: true, camelCase: true, + lowerCase: true, + escapePath: true, + isEqual: true, }, }); return `export default ${templateSpec};`; diff --git a/src/client/interfaces/Operation.d.ts b/src/client/interfaces/Operation.d.ts index 779144325..016c7c2b4 100644 --- a/src/client/interfaces/Operation.d.ts +++ b/src/client/interfaces/Operation.d.ts @@ -8,6 +8,7 @@ export interface Operation extends OperationParameters { summary: string | null; description: string | null; deprecated: boolean; + hasSecurity: boolean; method: string; path: string; errors: OperationError[]; diff --git a/src/index.spec.ts b/src/index.spec.ts index 1e42c68e4..dfe9a567b 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -32,4 +32,12 @@ describe('index', () => { write: false, }); }); + + it('downloads and parses homecloud without issues', async () => { + await OpenAPI.generate({ + input: 'https://test.docs.api.homecloud.de/homecloud-api-specs.json', + output: './test/generated/homecloud/', + write: false, + }); + }); }); diff --git a/src/openApi/v2/parser/getOperation.ts b/src/openApi/v2/parser/getOperation.ts index 9aa157460..295268a1f 100644 --- a/src/openApi/v2/parser/getOperation.ts +++ b/src/openApi/v2/parser/getOperation.ts @@ -30,6 +30,7 @@ export const getOperation = ( description: op.description || null, deprecated: op.deprecated === true, method: method.toUpperCase(), + hasSecurity: !!(op.security && op.security.length > 0), path: url, parameters: [...pathParams.parameters], parametersPath: [...pathParams.parametersPath], diff --git a/src/openApi/v3/parser/getOperation.ts b/src/openApi/v3/parser/getOperation.ts index aee4bd0c2..c5944cd19 100644 --- a/src/openApi/v3/parser/getOperation.ts +++ b/src/openApi/v3/parser/getOperation.ts @@ -33,6 +33,7 @@ export const getOperation = ( description: op.description || null, deprecated: op.deprecated === true, method: method.toUpperCase(), + hasSecurity: !!(op.security && op.security.length > 0), path: url, parameters: [...pathParams.parameters], parametersPath: [...pathParams.parametersPath], diff --git a/src/templates/client.hbs b/src/templates/client.hbs index 601f27d8f..ad6d74bc6 100644 --- a/src/templates/client.hbs +++ b/src/templates/client.hbs @@ -1,80 +1,40 @@ -{{>header}} +import { createContainer } from 'thirty/inject'; -{{#equals @root.httpClient 'angular'}} -import { NgModule} from '@angular/core'; -import { HttpClientModule } from '@angular/common/http'; - -import { AngularHttpRequest } from './core/AngularHttpRequest'; -import { BaseHttpRequest } from './core/BaseHttpRequest'; -import type { OpenAPIConfig } from './core/OpenAPI'; -import { OpenAPI } from './core/OpenAPI'; -{{else}} -import type { BaseHttpRequest } from './core/BaseHttpRequest'; -import type { OpenAPIConfig } from './core/OpenAPI'; -import { {{{httpRequest}}} } from './core/{{{httpRequest}}}'; -{{/equals}} - -{{#if services}} +import { httpClientFactory } from '../http/http-client'; {{#each services}} -import { {{{name}}}{{{@root.postfix}}} } from './services/{{{name}}}{{{@root.postfix}}}'; +import { {{{name}}}{{{@root.postfix}}}, {{{lowerCase name}}}{{{@root.postfix}}}Factory } from './services/{{{name}}}{{{@root.postfix}}}'; {{/each}} -{{/if}} - -{{#equals @root.httpClient 'angular'}} -@NgModule({ - imports: [HttpClientModule], - providers: [ - { - provide: OpenAPI, - useValue: { - BASE: OpenAPI?.BASE ?? '{{{server}}}', - VERSION: OpenAPI?.VERSION ?? '{{{version}}}', - WITH_CREDENTIALS: OpenAPI?.WITH_CREDENTIALS ?? false, - CREDENTIALS: OpenAPI?.CREDENTIALS ?? 'include', - TOKEN: OpenAPI?.TOKEN, - USERNAME: OpenAPI?.USERNAME, - PASSWORD: OpenAPI?.PASSWORD, - HEADERS: OpenAPI?.HEADERS, - ENCODE_PATH: OpenAPI?.ENCODE_PATH, - } as OpenAPIConfig, - }, - { - provide: BaseHttpRequest, - useClass: AngularHttpRequest, - }, - {{#each services}} - {{{name}}}{{{@root.postfix}}}, - {{/each}} - ] -}) -export class {{{clientName}}} {} -{{else}} -type HttpRequestConstructor = new (config: OpenAPIConfig) => BaseHttpRequest; - -export class {{{clientName}}} { - {{#each services}} - public readonly {{{camelCase name}}}: {{{name}}}{{{@root.postfix}}}; - {{/each}} - - public readonly request: BaseHttpRequest; - - constructor(config?: Partial, HttpRequest: HttpRequestConstructor = {{{httpRequest}}}) { - this.request = new HttpRequest({ - BASE: config?.BASE ?? '{{{server}}}', - VERSION: config?.VERSION ?? '{{{version}}}', - WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false, - CREDENTIALS: config?.CREDENTIALS ?? 'include', - TOKEN: config?.TOKEN, - USERNAME: config?.USERNAME, - PASSWORD: config?.PASSWORD, - HEADERS: config?.HEADERS, - ENCODE_PATH: config?.ENCODE_PATH, - }); - - {{#each services}} - this.{{{camelCase name}}} = new {{{name}}}{{{@root.postfix}}}(this.request); - {{/each}} - } +type ApiServiceOptions = { stage: 'prod' | 'test' | 'int' } | { baseUrl: string }; + +export class HomecloudApiService { + {{#each services}} + readonly {{{lowerCase name}}}Api: {{{name}}}{{{@root.postfix}}}; + {{/each}} + + constructor(serviceConfig: ApiServiceOptions) { + if ('baseUrl' in serviceConfig && 'stage' in serviceConfig) { + throw new Error('Please provide only baseUrl or stage, not both!'); + } + const baseUrl = 'baseUrl' in serviceConfig ? serviceConfig.baseUrl : constructExternalUrl(serviceConfig.stage, 'api'); + const config = createContainer({ + baseUrl: () => baseUrl, + httpClient: httpClientFactory, + {{#each services}} + {{{lowerCase name}}}ApiService: {{{lowerCase name}}}{{{@root.postfix}}}Factory, + {{/each}} + }); + + {{#each services}} + this.{{{lowerCase name}}}Api = config.{{{lowerCase name}}}{{{@root.postfix}}}; + {{/each}} + } } -{{/equals}} + +const constructExternalUrl = (stage: string, name: string): string => { + if (stage === 'prod') { + return `https://${name}.homecloud.de`; + } else { + return `https://${stage}.${name}.homecloud.de`; + } +}; \ No newline at end of file diff --git a/src/templates/exportService.hbs b/src/templates/exportService.hbs index d6bccbbeb..c7eb7cfe2 100644 --- a/src/templates/exportService.hbs +++ b/src/templates/exportService.hbs @@ -1,152 +1,78 @@ -{{>header}} +import { AxiosResponse } from 'axios'; -{{#equals @root.httpClient 'angular'}} -{{#if @root.exportClient}} -import { Injectable } from '@angular/core'; -import type { Observable } from 'rxjs'; +{{#each imports}} +{{#if (isEqual this 'CreateAuthTokenRequestModel')}} {{else}} -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import type { Observable } from 'rxjs'; +import { {{{this}}} } from '../../../models'; {{/if}} - -{{/equals}} -{{#if imports}} -{{#each imports}} -import type { {{{this}}} } from '../models/{{{this}}}'; {{/each}} +import { HttpClient } from '../../http/http-client'; -{{/if}} -{{#notEquals @root.httpClient 'angular'}} -import type { CancelablePromise } from '../core/CancelablePromise'; -{{/notEquals}} -{{#if @root.exportClient}} -{{#equals @root.httpClient 'angular'}} -import { BaseHttpRequest } from '../core/BaseHttpRequest'; -{{else}} -import type { BaseHttpRequest } from '../core/BaseHttpRequest'; -{{/equals}} -{{else}} -import { OpenAPI } from '../core/OpenAPI'; -import { request as __request } from '../core/request'; -{{/if}} +export type {{{name}}}{{{@root.postfix}}} = ReturnType; +export type {{{name}}}{{{@root.postfix}}}Options = { + baseUrl: string; + httpClient: HttpClient; +}; -{{#equals @root.httpClient 'angular'}} -@Injectable({ - providedIn: 'root', -}) -{{/equals}} -export class {{{name}}}{{{@root.postfix}}} { - {{#if @root.exportClient}} +export const {{{lowerCase name}}}{{{@root.postfix}}}Factory = ({ baseUrl, httpClient }: {{{name}}}{{{@root.postfix}}}Options) => ({ + {{#each operations}} + {{#if (isEqual name 'getStatus')}} + {{{name}}}: async (timeout?: number): Promiseresult}}>> => { + const url = `${baseUrl}{{{escapePath path}}}`; + return httpClient.{{{lowerCase method}}}(url, undefined, timeout); + }, + {{else if (isEqual name 'createTrackingEvent')}} + {{{name}}}: async ({username, password, {{>deconstruction}}timeout}: {username: string, password: string, {{>parameters}}timeout?: number}): Promiseresult}}>> => { + const url = `${baseUrl}{{{escapePath path}}}`; + return httpClient.{{{lowerCase method}}}(url, {{>requestBodyOrUndefined}} {username, password}, timeout); + }, + {{else if (isEqual name 'patchUserProfilePicture')}} + {{{name}}}: async ({ {{>accessToken}} formData, timeout}: { {{>accessTokenParam}}{{>parameters}}timeout?: number}): Promiseresult}}>> => { + const url = `${baseUrl}{{{escapePath path}}}`; + return httpClient.{{{lowerCase method}}}(url, formData, {{>accessTokenOrUndefined}}timeout, 'multipart/form-data'); + }, + {{else if (isEqual name 'createAuthToken')}} + {{{name}}}: async ({clientId, clientSecret, scopes, actor, subject, timeout}:{clientId: string, clientSecret: string, scopes: string, actor?: string, subject?: string, timeout?: number}): Promiseresult}}>> => { + const url = `${baseUrl}{{{escapePath path}}}`; - constructor(public readonly httpRequest: BaseHttpRequest) {} - {{else}} - {{#equals @root.httpClient 'angular'}} + const params = new URLSearchParams(); + params.append('client_id', clientId); + params.append('client_secret', clientSecret); + params.append('scope', scopes); + params.append('grant_type', 'client_credentials'); - constructor(public readonly http: HttpClient) {} - {{/equals}} - {{/if}} + if (actor) { + params.append('actor', actor); + } + if (subject) { + params.append('subject', subject); + } - {{#each operations}} - /** - {{#if deprecated}} - * @deprecated - {{/if}} - {{#if summary}} - * {{{escapeComment summary}}} - {{/if}} - {{#if description}} - * {{{escapeComment description}}} - {{/if}} - {{#unless @root.useOptions}} - {{#if parameters}} - {{#each parameters}} - * @param {{{name}}} {{#if description}}{{{escapeComment description}}}{{/if}} - {{/each}} - {{/if}} - {{/unless}} - {{#each results}} - * @returns {{{type}}} {{#if description}}{{{escapeComment description}}}{{/if}} - {{/each}} - * @throws ApiError - */ - {{#if @root.exportClient}} - {{#equals @root.httpClient 'angular'}} - public {{{name}}}({{>parameters}}): Observable<{{>result}}> { - return this.httpRequest.request({ - {{else}} - public {{{name}}}({{>parameters}}): CancelablePromise<{{>result}}> { - return this.httpRequest.request({ - {{/equals}} - {{else}} - {{#equals @root.httpClient 'angular'}} - public {{{name}}}({{>parameters}}): Observable<{{>result}}> { - return __request(OpenAPI, this.http, { - {{else}} - public static {{{name}}}({{>parameters}}): CancelablePromise<{{>result}}> { - return __request(OpenAPI, { - {{/equals}} - {{/if}} - method: '{{{method}}}', - url: '{{{path}}}', - {{#if parametersPath}} - path: { - {{#each parametersPath}} - '{{{prop}}}': {{{name}}}, - {{/each}} - }, - {{/if}} - {{#if parametersCookie}} - cookies: { - {{#each parametersCookie}} - '{{{prop}}}': {{{name}}}, - {{/each}} - }, - {{/if}} - {{#if parametersHeader}} - headers: { - {{#each parametersHeader}} - '{{{prop}}}': {{{name}}}, - {{/each}} - }, - {{/if}} - {{#if parametersQuery}} - query: { - {{#each parametersQuery}} - '{{{prop}}}': {{{name}}}, - {{/each}} - }, - {{/if}} - {{#if parametersForm}} - formData: { - {{#each parametersForm}} - '{{{prop}}}': {{{name}}}, - {{/each}} - }, - {{/if}} - {{#if parametersBody}} - {{#equals parametersBody.in 'formData'}} - formData: {{{parametersBody.name}}}, - {{/equals}} - {{#equals parametersBody.in 'body'}} - body: {{{parametersBody.name}}}, - {{/equals}} - {{#if parametersBody.mediaType}} - mediaType: '{{{parametersBody.mediaType}}}', - {{/if}} - {{/if}} - {{#if responseHeader}} - responseHeader: '{{{responseHeader}}}', - {{/if}} - {{#if errors}} - errors: { - {{#each errors}} - {{{code}}}: `{{{escapeDescription description}}}`, - {{/each}} - }, - {{/if}} - }); - } + return httpClient.{{{lowerCase method}}}(url, params, undefined, timeout, 'application/x-www-form-urlencoded'); + }, + {{else}} + {{{name}}}: async ({ {{>accessToken}}{{>deconstruction}}timeout}:{ {{>accessTokenParam}}{{>parameters}}timeout?: number}): Promiseresult}}>> => { + let url = `${baseUrl}{{{escapePath path}}}`; + {{#if parametersQuery}} + + const urlWithParams = new URL(url); + {{#each parametersQuery}} + + if ({{{name}}} !== null && {{{name}}} !== undefined) { + urlWithParams.searchParams.append('{{{name}}}', `${ {{{name}}} }`); + } + {{/each}} + url = urlWithParams.href + {{/if}} + {{#if (isEqual method 'GET')}} + return httpClient.{{{lowerCase method}}}(url, {{>accessTokenOrUndefined}} timeout); + {{else if (isEqual method 'DELETE')}} + return httpClient.{{{lowerCase method}}}(url, {{>accessTokenOrUndefined}} timeout); + {{else}} + return httpClient.{{{lowerCase method}}}(url, {{>requestBodyOrUndefined}} {{>accessTokenOrUndefined}} timeout); + {{/if}} + }, + {{/if}} {{/each}} -} +}); diff --git a/src/templates/index.hbs b/src/templates/index.hbs index 6f5b27d8c..172f31bef 100644 --- a/src/templates/index.hbs +++ b/src/templates/index.hbs @@ -1,47 +1 @@ -{{>header}} - -{{#if @root.exportClient}} export { {{{clientName}}} } from './{{{clientName}}}'; - -{{/if}} -{{#if @root.exportCore}} -export { ApiError } from './core/ApiError'; -{{#if @root.exportClient}} -export { BaseHttpRequest } from './core/BaseHttpRequest'; -{{/if}} -export { CancelablePromise, CancelError } from './core/CancelablePromise'; -export { OpenAPI } from './core/OpenAPI'; -export type { OpenAPIConfig } from './core/OpenAPI'; -{{/if}} -{{#if @root.exportModels}} -{{#if models}} - -{{#each models}} -{{#if @root.useUnionTypes}} -export type { {{{name}}}{{#if @root.postfixModels}} as {{{name}}}{{{@root.postfixModels}}}{{/if}} } from './models/{{{name}}}'; -{{else if enum}} -export { {{{name}}}{{#if @root.postfixModels}} as {{{name}}}{{{@root.postfixModels}}}{{/if}} } from './models/{{{name}}}'; -{{else if enums}} -export { {{{name}}}{{#if @root.postfixModels}} as {{{name}}}{{{@root.postfixModels}}}{{/if}} } from './models/{{{name}}}'; -{{else}} -export type { {{{name}}}{{#if @root.postfixModels}} as {{{name}}}{{{@root.postfixModels}}}{{/if}} } from './models/{{{name}}}'; -{{/if}} -{{/each}} -{{/if}} -{{/if}} -{{#if @root.exportSchemas}} -{{#if models}} - -{{#each models}} -export { ${{{name}}} } from './schemas/${{{name}}}'; -{{/each}} -{{/if}} -{{/if}} -{{#if @root.exportServices}} -{{#if services}} - -{{#each services}} -export { {{{name}}}{{{@root.postfixServices}}} } from './services/{{{name}}}{{{@root.postfixServices}}}'; -{{/each}} -{{/if}} -{{/if}} diff --git a/src/templates/partials/accessToken.hbs b/src/templates/partials/accessToken.hbs new file mode 100644 index 000000000..0634bc33b --- /dev/null +++ b/src/templates/partials/accessToken.hbs @@ -0,0 +1 @@ +{{#if hasSecurity}}accessToken,{{/if}} \ No newline at end of file diff --git a/src/templates/partials/accessTokenOrUndefined.hbs b/src/templates/partials/accessTokenOrUndefined.hbs new file mode 100644 index 000000000..eadafa45c --- /dev/null +++ b/src/templates/partials/accessTokenOrUndefined.hbs @@ -0,0 +1 @@ +{{#if hasSecurity}}accessToken,{{else}}undefined,{{/if}} \ No newline at end of file diff --git a/src/templates/partials/accessTokenParam.hbs b/src/templates/partials/accessTokenParam.hbs new file mode 100644 index 000000000..b65094183 --- /dev/null +++ b/src/templates/partials/accessTokenParam.hbs @@ -0,0 +1 @@ +{{#if hasSecurity}}accessToken: string,{{/if}} \ No newline at end of file diff --git a/src/templates/partials/deconstruction.hbs b/src/templates/partials/deconstruction.hbs new file mode 100644 index 000000000..ad3ca9794 --- /dev/null +++ b/src/templates/partials/deconstruction.hbs @@ -0,0 +1,3 @@ +{{#each parameters}} +{{{name}}}, +{{/each}} diff --git a/src/templates/partials/isRequired.hbs b/src/templates/partials/isRequired.hbs index b829272b9..cb6d72392 100644 --- a/src/templates/partials/isRequired.hbs +++ b/src/templates/partials/isRequired.hbs @@ -1,5 +1,5 @@ {{~#if @root.useOptions~}} {{#unless isRequired}}?{{else if default}}?{{/unless}} {{~else~}} -{{#unless isRequired}}{{#unless default}}?{{/unless}}{{/unless}} +{{#unless isRequired}}?{{#unless default}}{{/unless}}{{/unless}} {{~/if~}} diff --git a/src/templates/partials/parameters.hbs b/src/templates/partials/parameters.hbs index 57ab5a7d1..959cc7a80 100644 --- a/src/templates/partials/parameters.hbs +++ b/src/templates/partials/parameters.hbs @@ -2,7 +2,7 @@ {{#if @root.useOptions~}} { {{#each parameters}} -{{{name}}}{{#if default}} = {{{default}}}{{/if}}, +{{{name}}}, {{/each}} }: { {{#each parameters}} @@ -22,7 +22,7 @@ {{~else}} {{#each parameters}} -{{{name}}}{{>isRequired}}: {{>type}}{{#if default}} = {{{default}}}{{/if}}, +{{{name}}}{{>isRequired}}: {{>type}}, {{/each}} {{/if}} {{/if}} diff --git a/src/templates/partials/requestBodyOrUndefined.hbs b/src/templates/partials/requestBodyOrUndefined.hbs new file mode 100644 index 000000000..869557f9d --- /dev/null +++ b/src/templates/partials/requestBodyOrUndefined.hbs @@ -0,0 +1 @@ +{{#if parametersBody}}requestBody,{{else}}undefined,{{/if}} \ No newline at end of file diff --git a/src/templates/partials/typeArray.hbs b/src/templates/partials/typeArray.hbs index c3d44374f..1bd5498b3 100644 --- a/src/templates/partials/typeArray.hbs +++ b/src/templates/partials/typeArray.hbs @@ -1,5 +1,5 @@ {{~#if link~}} -Array<{{>type link}}>{{>isNullable}} +{{>type link}}{{>isNullable}}[] {{~else~}} -Array<{{>base}}>{{>isNullable}} +{{>base}}{{>isNullable}}[] {{~/if~}} diff --git a/src/utils/registerHandlebarHelpers.ts b/src/utils/registerHandlebarHelpers.ts index 88f47c19b..370d7ea16 100644 --- a/src/utils/registerHandlebarHelpers.ts +++ b/src/utils/registerHandlebarHelpers.ts @@ -104,4 +104,16 @@ export const registerHandlebarHelpers = (root: { Handlebars.registerHelper('camelCase', function (value: string): string { return camelCase(value); }); + + Handlebars.registerHelper('lowerCase', function (value: string): string { + return value.toLowerCase(); + }); + + Handlebars.registerHelper('escapePath', function (value: string): string { + return value.replaceAll('{', `\${`); + }); + + Handlebars.registerHelper('isEqual', (value1, value2) => { + return value1 === value2; + }); }; diff --git a/src/utils/registerHandlebarTemplates.ts b/src/utils/registerHandlebarTemplates.ts index bf77cbdc1..1cda37be3 100644 --- a/src/utils/registerHandlebarTemplates.ts +++ b/src/utils/registerHandlebarTemplates.ts @@ -56,7 +56,11 @@ import templateExportModel from '../templates/exportModel.hbs'; import templateExportSchema from '../templates/exportSchema.hbs'; import templateExportService from '../templates/exportService.hbs'; import templateIndex from '../templates/index.hbs'; +import partialAccessToken from '../templates/partials/accessToken.hbs'; +import partialAccessTokenOrUndefined from '../templates/partials/accessTokenOrUndefined.hbs'; +import partialAccessTokenParam from '../templates/partials/accessTokenParam.hbs'; import partialBase from '../templates/partials/base.hbs'; +import partialDeconstruction from '../templates/partials/deconstruction.hbs'; import partialExportComposition from '../templates/partials/exportComposition.hbs'; import partialExportEnum from '../templates/partials/exportEnum.hbs'; import partialExportInterface from '../templates/partials/exportInterface.hbs'; @@ -66,6 +70,7 @@ import partialIsNullable from '../templates/partials/isNullable.hbs'; import partialIsReadOnly from '../templates/partials/isReadOnly.hbs'; import partialIsRequired from '../templates/partials/isRequired.hbs'; import partialParameters from '../templates/partials/parameters.hbs'; +import partialRequestBodyOrUndefined from '../templates/partials/requestBodyOrUndefined.hbs'; import partialResult from '../templates/partials/result.hbs'; import partialSchema from '../templates/partials/schema.hbs'; import partialSchemaArray from '../templates/partials/schemaArray.hbs'; @@ -138,6 +143,9 @@ export const registerHandlebarTemplates = (root: { }; // Partials for the generations of the models, services, etc. + Handlebars.registerPartial('accessToken', Handlebars.template(partialAccessToken)); + Handlebars.registerPartial('accessTokenParam', Handlebars.template(partialAccessTokenParam)); + Handlebars.registerPartial('accessTokenOrUndefined', Handlebars.template(partialAccessTokenOrUndefined)); Handlebars.registerPartial('exportEnum', Handlebars.template(partialExportEnum)); Handlebars.registerPartial('exportInterface', Handlebars.template(partialExportInterface)); Handlebars.registerPartial('exportComposition', Handlebars.template(partialExportComposition)); @@ -147,7 +155,9 @@ export const registerHandlebarTemplates = (root: { Handlebars.registerPartial('isReadOnly', Handlebars.template(partialIsReadOnly)); Handlebars.registerPartial('isRequired', Handlebars.template(partialIsRequired)); Handlebars.registerPartial('parameters', Handlebars.template(partialParameters)); + Handlebars.registerPartial('deconstruction', Handlebars.template(partialDeconstruction)); Handlebars.registerPartial('result', Handlebars.template(partialResult)); + Handlebars.registerPartial('requestBodyOrUndefined', Handlebars.template(partialRequestBodyOrUndefined)); Handlebars.registerPartial('schema', Handlebars.template(partialSchema)); Handlebars.registerPartial('schemaArray', Handlebars.template(partialSchemaArray)); Handlebars.registerPartial('schemaDictionary', Handlebars.template(partialSchemaDictionary)); diff --git a/test/index.spec.ts b/test/index.spec.ts index 78a0197d5..bee97e56d 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -1,46 +1,20 @@ -import { readFileSync } from 'fs'; -import { sync } from 'glob'; +import { generate, HttpClient, Indent } from '../'; -import { generate, HttpClient } from '../'; - -describe('v2', () => { +describe('homecloud', () => { it('should generate', async () => { await generate({ - input: './test/spec/v2.json', - output: './test/generated/v2/', - httpClient: HttpClient.FETCH, - useOptions: false, - useUnionTypes: false, - exportCore: true, - exportSchemas: true, - exportModels: true, + clientName: 'HomecloudApiService', + exportCore: false, + exportModels: false, + exportSchemas: false, exportServices: true, - }); - - sync('./test/generated/v2/**/*.ts').forEach(file => { - const content = readFileSync(file, 'utf8').toString(); - expect(content).toMatchSnapshot(file); - }); - }); -}); - -describe('v3', () => { - it('should generate', async () => { - await generate({ - input: './test/spec/v3.json', - output: './test/generated/v3/', - httpClient: HttpClient.FETCH, + httpClient: HttpClient.AXIOS, + indent: Indent.SPACE_2, + input: 'https://test.docs.api.homecloud.de/homecloud-api-specs.json', + output: './test/generated/homecloud/', + postfixServices: 'ApiService', useOptions: false, useUnionTypes: false, - exportCore: true, - exportSchemas: true, - exportModels: true, - exportServices: true, - }); - - sync('./test/generated/v3/**/*.ts').forEach(file => { - const content = readFileSync(file, 'utf8').toString(); - expect(content).toMatchSnapshot(file); }); }); }); diff --git a/tsconfig.json b/tsconfig.json index 8d27e49a8..5b397137c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "compilerOptions": { "outDir": "./dist", - "target": "es2019", + "target": "es2021", "module": "commonjs", "moduleResolution": "node", - "lib": ["es2019", "dom"], + "lib": ["es2021", "dom"], "types": ["jest", "node"], "declaration": false, "declarationMap": false,