mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
Merge branch 'master' into CancellablePromise
This commit is contained in:
commit
9845ce7d3b
@ -1,20 +1,21 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: cimg/node:lts-browsers
|
||||
resource_class: large
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "package.json" }}
|
||||
- v1-dependencies-{{ checksum "package-lock.json" }}
|
||||
- v1-dependencies-
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: npm install
|
||||
- save_cache:
|
||||
key: v1-dependencies-{{ checksum "package.json" }}
|
||||
key: v1-dependencies-{{ checksum "package-lock.json" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- run:
|
||||
@ -22,10 +23,10 @@ jobs:
|
||||
command: npm run release
|
||||
- run:
|
||||
name: Run unit tests
|
||||
command: npm run test:coverage
|
||||
- run:
|
||||
name: Run e2e tests
|
||||
command: npm run test:e2e
|
||||
command: npm run test
|
||||
# - run:
|
||||
# name: Run e2e tests
|
||||
# command: npm run test:e2e
|
||||
- run:
|
||||
name: Submit to Codecov
|
||||
command: npm run codecov
|
||||
|
||||
17
.github/dependabot.yml
vendored
17
.github/dependabot.yml
vendored
@ -3,9 +3,20 @@ updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "04:00"
|
||||
open-pull-requests-limit: 10
|
||||
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"
|
||||
29
.github/workflows/auto-merge.yml
vendored
Normal file
29
.github/workflows/auto-merge.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
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 }}
|
||||
20
.licrc
Normal file
20
.licrc
Normal file
@ -0,0 +1,20 @@
|
||||
[licenses]
|
||||
accepted = [
|
||||
"MIT",
|
||||
"BSD",
|
||||
"0BSD",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"Apache-2.0",
|
||||
"CC-BY-3.0",
|
||||
"CC-BY-4.0",
|
||||
"CC0-1.0",
|
||||
"ISC"
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
ignored = []
|
||||
|
||||
[behavior]
|
||||
run_only_on_dependency_modification = true
|
||||
do_not_block_pr = true
|
||||
36
CHANGELOG.md
36
CHANGELOG.md
@ -1,6 +1,42 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.23.0] - 2022-06-02
|
||||
### Fixed
|
||||
- Upgraded dependencies
|
||||
- Added blank line at the end of generated files
|
||||
- Added support for Node.js v12
|
||||
### Added
|
||||
- Added `request` property inside `ApiError`
|
||||
- Added support for `@depricated` inside models and operations
|
||||
|
||||
## [0.22.0] - 2022-04-26
|
||||
### Fixed
|
||||
- Upgraded dependencies
|
||||
- Fixed issue with `null` value inside comments for OpenAPI v2 enums
|
||||
- Fixed issue with compatibility for latest version of Axios (0.27.x)
|
||||
### Removed
|
||||
- Removed deprecated enum model generation
|
||||
|
||||
## [0.21.0] - 2022-04-06
|
||||
### Fixed
|
||||
- Return `undefined` to match `noImplicitReturns` rule
|
||||
- Made `BaseHttpRequest` class abstract
|
||||
- Removed private fields using `#` inside `CancelablePromise`
|
||||
- Removed unneeded import `AbortController` from `node-fetch` client
|
||||
- Filter out wrong enum values
|
||||
|
||||
## [0.20.1] - 2022-02-25
|
||||
### Fixed
|
||||
- Support enums with single quotes in names for V2
|
||||
|
||||
## [0.20.0] - 2022-02-25
|
||||
### Fixed
|
||||
- Updated dependencies
|
||||
- Support enums with single quotes in names for V3
|
||||
- Generating better names when `operationId` is not given (breaking change)
|
||||
- Fixed issue where `x-enum` flags where breaking due to non-string values
|
||||
|
||||
## [0.19.0] - 2022-02-02
|
||||
### Added
|
||||
- Support for Angular client with `--name` option
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Ferdi Koomen
|
||||
Copyright (c) Ferdi Koomen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@ -48,7 +48,8 @@ $ openapi --help
|
||||
--exportModels <value> Write models to disk (default: true)
|
||||
--exportSchemas <value> Write schemas to disk (default: false)
|
||||
--indent <value> Indentation options [4, 2, tab] (default: "4")
|
||||
--postfix <value> Service name postfix (default: "Service")
|
||||
--postfixServices Service name postfix (default: "Service")
|
||||
--postfixModels Model name postfix
|
||||
--request <value> Path to custom request file
|
||||
-h, --help display help for command
|
||||
|
||||
@ -69,6 +70,8 @@ Documentation
|
||||
- [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
|
||||
===
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": true
|
||||
"node": "12"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@ -21,7 +21,9 @@ const params = program
|
||||
.option('--exportModels <value>', 'Write models to disk', true)
|
||||
.option('--exportSchemas <value>', 'Write schemas to disk', false)
|
||||
.option('--indent <value>', 'Indentation options [4, 2, tabs]', '4')
|
||||
.option('--postfix <value>', 'Service name postfix', 'Service')
|
||||
.option('--postfix <value>', 'Deprecated: Use --postfixServices instead. Service name postfix', 'Service')
|
||||
.option('--postfixServices <value>', 'Service name postfix', 'Service')
|
||||
.option('--postfixModels <value>', 'Model name postfix')
|
||||
.option('--request <value>', 'Path to custom request file')
|
||||
.parse(process.argv)
|
||||
.opts();
|
||||
@ -41,7 +43,8 @@ if (OpenAPI) {
|
||||
exportModels: JSON.parse(params.exportModels) === true,
|
||||
exportSchemas: JSON.parse(params.exportSchemas) === true,
|
||||
indent: params.indent,
|
||||
postfix: params.postfix,
|
||||
postfixServices: params.postfixServices ?? params.postfix,
|
||||
postfixModels: params.postfixModels,
|
||||
request: params.request,
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
@ -34,8 +34,10 @@ describe('bin', () => {
|
||||
'true',
|
||||
'--indent',
|
||||
'4',
|
||||
'--postfix',
|
||||
'--postfixServices',
|
||||
'Service',
|
||||
'--postfixModels',
|
||||
'Dto',
|
||||
]);
|
||||
expect(result.stdout.toString()).toBe('');
|
||||
expect(result.stderr.toString()).toBe('');
|
||||
@ -67,4 +69,18 @@ describe('bin', () => {
|
||||
expect(result.stdout.toString()).toContain(`-o, --output <value>`);
|
||||
expect(result.stderr.toString()).toBe('');
|
||||
});
|
||||
|
||||
it('should still support the deprecated --postfix parameter', () => {
|
||||
const result = crossSpawn.sync('node', [
|
||||
'./bin/index.js',
|
||||
'--input',
|
||||
'./test/spec/v3.json',
|
||||
'--output',
|
||||
'./test/generated/bin',
|
||||
'--postfix',
|
||||
'Service',
|
||||
]);
|
||||
expect(result.stdout.toString()).toBe('');
|
||||
expect(result.stderr.toString()).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
@ -9,8 +9,8 @@ If you want to generate the Angular based client then you can specify `--client
|
||||
The Angular client has been tested with the following versions:
|
||||
|
||||
```
|
||||
"@angular/common": "13.1.x",
|
||||
"@angular/core": "13.1.x",
|
||||
"@angular/common": "14.0.x",
|
||||
"@angular/core": "14.0.x",
|
||||
"rxjs": "7.5.x",
|
||||
```
|
||||
|
||||
|
||||
@ -14,5 +14,13 @@ npm install axios --save-dev
|
||||
npm install form-data@4.x --save-dev
|
||||
```
|
||||
|
||||
In order to compile the project and resolve the imports, you will need to enable the `allowSyntheticDefaultImports`
|
||||
in your `tsconfig.json` file.
|
||||
In order to compile the project and resolve the imports, you will need to add the following properties
|
||||
in your `tsconfig.json` file:
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["...", "dom"],
|
||||
"allowSyntheticDefaultImports": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -18,7 +18,8 @@ $ openapi --help
|
||||
--exportModels <value> Write models to disk (default: true)
|
||||
--exportSchemas <value> Write schemas to disk (default: false)
|
||||
--indent <value> Indentation options [4, 2, tab] (default: "4")
|
||||
--postfix <value> Service name postfix (default: "Service")
|
||||
--postfixServices Service name postfix (default: "Service")
|
||||
--postfixModels Model name postfix
|
||||
--request <value> Path to custom request file
|
||||
-h, --help display help for command
|
||||
|
||||
|
||||
42
docs/canceling-requests.md
Normal file
42
docs/canceling-requests.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Canceling requests
|
||||
|
||||
The generated clients support canceling of requests, this works by canceling the promise that
|
||||
is returned from the request. Each method inside a service (operation) returns a `CancelablePromise`
|
||||
object. This promise can be canceled by calling the `cancel()` method.
|
||||
|
||||
Below is an example of canceling the request after a certain timeout:
|
||||
|
||||
```typescript
|
||||
import { UserService } from './myClient';
|
||||
|
||||
const getAllUsers = async () => {
|
||||
|
||||
const request = UserService.getAllUsers();
|
||||
|
||||
setTimeout(() => {
|
||||
if (!request.isResolved() && !request.isRejected()) {
|
||||
console.warn('Canceling request due to timeout');
|
||||
request.cancel();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
await request;
|
||||
};
|
||||
```
|
||||
|
||||
The API of the `CancelablePromise` is similar to a regular `Promise`, but it adds the
|
||||
`cancel()` method and some additional properties:
|
||||
|
||||
```typescript
|
||||
interface CancelablePromise<TResult> extends Promise<TResult> {
|
||||
readonly isResolved: boolean;
|
||||
readonly isRejected: boolean;
|
||||
readonly isCancelled: boolean;
|
||||
cancel: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
- `isResolved`: Indicates if the promise was resolved.
|
||||
- `isRejected`: Indicates if the promise was rejected.
|
||||
- `isCancelled`: Indicates if the promise was canceled.
|
||||
- `cancel()`: Cancels the promise (and request) and throws a rejection error: `Request aborted`.
|
||||
@ -7,7 +7,7 @@ The generated client uses an instance of the server configuration and not the gl
|
||||
To generate a client instance, set a custom name to the client class, use `--name` option.
|
||||
|
||||
```
|
||||
openapi --input ./spec.json --output ./generated ---name AppClient
|
||||
openapi --input ./spec.json --output ./generated --name AppClient
|
||||
```
|
||||
|
||||
The generated client will be exported from the `index` file and can be used as shown below:
|
||||
|
||||
61
docs/custom-request-file.md
Normal file
61
docs/custom-request-file.md
Normal file
@ -0,0 +1,61 @@
|
||||
# Custom request file
|
||||
|
||||
If you want to implement custom logic on the request level,
|
||||
or implement a client based on a different library, then
|
||||
one option is to write your own request file and tell
|
||||
the generator to use this.
|
||||
|
||||
The request file (`request.ts`) can be found inside the
|
||||
`/core` folder of the generated client. You can modify
|
||||
that file and use it, or alternatively, you can write
|
||||
your own. Below is a very simplified example of an Axios
|
||||
based request file:
|
||||
|
||||
```typescript
|
||||
import axios from 'axios';
|
||||
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import type { OpenAPIConfig } from './OpenAPI';
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
// Your custom Axios instance config
|
||||
});
|
||||
|
||||
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
|
||||
return new CancelablePromise((resolve, reject, onCancel) => {
|
||||
// Get the request URL. Depending on your needs, this might need additional processing,
|
||||
// @see ./src/templates/core/functions/getUrl.hbs
|
||||
const url = `${config.BASE}${options.path}`;
|
||||
|
||||
// Optional: Get and link the cancelation token, so the request can be aborted.
|
||||
const source = axiosInstance.CancelToken.source();
|
||||
onCancel(() => source.cancel('The user aborted a request.'));
|
||||
|
||||
// Execute the request. This is a minimal example, in real world scenarios
|
||||
// you will need to add headers, process form data, etc.
|
||||
// @see ./src/templates/core/axios/request.hbs
|
||||
axiosInstance.request({
|
||||
url,
|
||||
data: options.body,
|
||||
method: options.method,
|
||||
cancelToken: source.token,
|
||||
}).then(data => {
|
||||
resolve(data);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
To use this request file in your generated code you can execute the
|
||||
following command:
|
||||
|
||||
```
|
||||
npx openapi-typescript-codegen --input ./spec.json --output ./generated --request ./request.ts
|
||||
```
|
||||
|
||||
The `--request` parameter will tell the generator to not generate the default
|
||||
`request.ts` file, but instead copy over the custom file that was specified.
|
||||
15641
package-lock.json
generated
15641
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
102
package.json
102
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openapi-typescript-codegen",
|
||||
"version": "0.19.0",
|
||||
"version": "0.23.0",
|
||||
"description": "Library that generates Typescript clients based on the OpenAPI specification.",
|
||||
"author": "Ferdi Koomen",
|
||||
"homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen",
|
||||
@ -60,63 +60,67 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": "^6.3.0",
|
||||
"commander": "^9.0.0",
|
||||
"handlebars": "^4.7.6",
|
||||
"json-schema-ref-parser": "^9.0.7"
|
||||
"commander": "^9.4.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"json-schema-ref-parser": "^9.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "13.2.1",
|
||||
"@angular/animations": "13.2.1",
|
||||
"@angular/cli": "13.2.1",
|
||||
"@angular/common": "13.2.1",
|
||||
"@angular/compiler": "13.2.1",
|
||||
"@angular/compiler-cli": "13.2.1",
|
||||
"@angular/core": "13.2.1",
|
||||
"@angular/forms": "13.2.1",
|
||||
"@angular/platform-browser": "13.2.1",
|
||||
"@angular/platform-browser-dynamic": "13.2.1",
|
||||
"@angular/router": "13.2.1",
|
||||
"@babel/cli": "7.16.8",
|
||||
"@babel/core": "7.16.12",
|
||||
"@babel/preset-env": "7.16.11",
|
||||
"@babel/preset-typescript": "7.16.7",
|
||||
"@rollup/plugin-commonjs": "21.0.1",
|
||||
"@rollup/plugin-node-resolve": "13.1.3",
|
||||
"@rollup/plugin-typescript": "8.3.0",
|
||||
"@angular-devkit/build-angular": "14.2.7",
|
||||
"@angular/animations": "14.2.8",
|
||||
"@angular/cli": "14.2.7",
|
||||
"@angular/common": "14.2.8",
|
||||
"@angular/compiler": "14.2.8",
|
||||
"@angular/compiler-cli": "14.2.8",
|
||||
"@angular/core": "14.2.8",
|
||||
"@angular/forms": "14.2.8",
|
||||
"@angular/platform-browser": "14.2.8",
|
||||
"@angular/platform-browser-dynamic": "14.2.8",
|
||||
"@angular/router": "14.2.8",
|
||||
"@babel/cli": "7.20.7",
|
||||
"@babel/core": "7.20.12",
|
||||
"@babel/preset-env": "7.20.2",
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"@rollup/plugin-commonjs": "23.0.5",
|
||||
"@rollup/plugin-node-resolve": "15.0.1",
|
||||
"@rollup/plugin-typescript": "9.0.2",
|
||||
"@types/cross-spawn": "6.0.2",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/glob": "7.2.0",
|
||||
"@types/jest": "27.4.0",
|
||||
"@types/node": "17.0.14",
|
||||
"@types/node-fetch": "2.5.12",
|
||||
"@types/express": "4.17.15",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/glob": "8.0.1",
|
||||
"@types/jest": "29.2.5",
|
||||
"@types/node": "18.11.18",
|
||||
"@types/node-fetch": "2.6.2",
|
||||
"@types/qs": "6.9.7",
|
||||
"@typescript-eslint/eslint-plugin": "5.10.2",
|
||||
"@typescript-eslint/parser": "5.10.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.49.0",
|
||||
"@typescript-eslint/parser": "5.48.2",
|
||||
"abort-controller": "3.0.0",
|
||||
"axios": "0.25.0",
|
||||
"axios": "1.2.3",
|
||||
"codecov": "3.8.3",
|
||||
"cross-spawn": "7.0.3",
|
||||
"eslint": "8.8.0",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-prettier": "4.0.0",
|
||||
"eslint-plugin-simple-import-sort": "7.0.0",
|
||||
"express": "4.17.2",
|
||||
"eslint": "8.32.0",
|
||||
"eslint-config-prettier": "8.6.0",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"eslint-plugin-simple-import-sort": "8.0.0",
|
||||
"express": "4.18.2",
|
||||
"form-data": "4.0.0",
|
||||
"glob": "7.2.0",
|
||||
"jest": "27.4.7",
|
||||
"jest-cli": "27.4.7",
|
||||
"node-fetch": "2.6.6",
|
||||
"prettier": "2.5.1",
|
||||
"puppeteer": "13.1.3",
|
||||
"qs": "6.10.3",
|
||||
"glob": "8.1.0",
|
||||
"jest": "29.3.1",
|
||||
"jest-cli": "29.3.1",
|
||||
"node-fetch": "2.6.7",
|
||||
"prettier": "2.8.3",
|
||||
"puppeteer": "19.5.2",
|
||||
"qs": "6.11.0",
|
||||
"rimraf": "3.0.2",
|
||||
"rollup": "2.67.0",
|
||||
"rollup-plugin-node-externals": "3.1.2",
|
||||
"rollup": "3.2.3",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"rxjs": "7.5.2",
|
||||
"ts-node": "10.4.0",
|
||||
"tslib": "2.3.1",
|
||||
"typescript": "4.5.5",
|
||||
"zone.js": "0.11.4"
|
||||
"rxjs": "7.8.0",
|
||||
"ts-node": "10.9.1",
|
||||
"tslib": "2.4.1",
|
||||
"typescript": "4.8.4",
|
||||
"zone.js": "0.11.8"
|
||||
},
|
||||
"overrides" : {
|
||||
"rollup": "3.2.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,11 +2,12 @@ import commonjs from '@rollup/plugin-commonjs';
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import { readFileSync } from 'fs';
|
||||
import { precompile } from 'handlebars';
|
||||
import handlebars from 'handlebars';
|
||||
import { dirname, extname, resolve } from 'path';
|
||||
import externals from 'rollup-plugin-node-externals';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
const { precompile } = handlebars;
|
||||
|
||||
/**
|
||||
* Custom plugin to parse handlebar imports and precompile
|
||||
* the template on the fly. This reduces runtime by about
|
||||
@ -28,6 +29,7 @@ const handlebarsPlugin = () => ({
|
||||
preventIndent: true,
|
||||
knownHelpersOnly: true,
|
||||
knownHelpers: {
|
||||
ifdef: true,
|
||||
equals: true,
|
||||
notEquals: true,
|
||||
containsSpaces: true,
|
||||
@ -47,9 +49,6 @@ const handlebarsPlugin = () => ({
|
||||
|
||||
const getPlugins = () => {
|
||||
const plugins = [
|
||||
externals({
|
||||
deps: true,
|
||||
}),
|
||||
nodeResolve(),
|
||||
commonjs({
|
||||
sourceMap: false,
|
||||
@ -72,5 +71,6 @@ export default {
|
||||
file: './dist/index.js',
|
||||
format: 'cjs',
|
||||
},
|
||||
external: ['camelcase', 'commander', 'fs-extra', 'handlebars', 'json-schema-ref-parser'],
|
||||
plugins: getPlugins(),
|
||||
};
|
||||
1
src/client/interfaces/Model.d.ts
vendored
1
src/client/interfaces/Model.d.ts
vendored
@ -9,6 +9,7 @@ export interface Model extends Schema {
|
||||
template: string | null;
|
||||
link: Model | null;
|
||||
description: string | null;
|
||||
deprecated?: boolean;
|
||||
default?: string;
|
||||
imports: string[];
|
||||
enum: Enum[];
|
||||
|
||||
15
src/index.ts
15
src/index.ts
@ -24,7 +24,8 @@ export type Options = {
|
||||
exportModels?: boolean;
|
||||
exportSchemas?: boolean;
|
||||
indent?: Indent;
|
||||
postfix?: string;
|
||||
postfixServices?: string;
|
||||
postfixModels?: string;
|
||||
request?: string;
|
||||
write?: boolean;
|
||||
};
|
||||
@ -44,7 +45,8 @@ export type Options = {
|
||||
* @param exportModels Generate models
|
||||
* @param exportSchemas Generate schemas
|
||||
* @param indent Indentation options (4, 2 or tab)
|
||||
* @param postfix Service name postfix
|
||||
* @param postfixServices Service name postfix
|
||||
* @param postfixModels Model name postfix
|
||||
* @param request Path to custom request file
|
||||
* @param write Write the files to disk (true or false)
|
||||
*/
|
||||
@ -60,7 +62,8 @@ export const generate = async ({
|
||||
exportModels = true,
|
||||
exportSchemas = false,
|
||||
indent = Indent.SPACE_4,
|
||||
postfix = 'Service',
|
||||
postfixServices = 'Service',
|
||||
postfixModels = '',
|
||||
request,
|
||||
write = true,
|
||||
}: Options): Promise<void> => {
|
||||
@ -89,7 +92,8 @@ export const generate = async ({
|
||||
exportModels,
|
||||
exportSchemas,
|
||||
indent,
|
||||
postfix,
|
||||
postfixServices,
|
||||
postfixModels,
|
||||
clientName,
|
||||
request
|
||||
);
|
||||
@ -112,7 +116,8 @@ export const generate = async ({
|
||||
exportModels,
|
||||
exportSchemas,
|
||||
indent,
|
||||
postfix,
|
||||
postfixServices,
|
||||
postfixModels,
|
||||
clientName,
|
||||
request
|
||||
);
|
||||
|
||||
@ -2,7 +2,7 @@ import { escapeName } from './escapeName';
|
||||
|
||||
describe('escapeName', () => {
|
||||
it('should escape', () => {
|
||||
expect(escapeName('')).toEqual('');
|
||||
expect(escapeName('')).toEqual("''");
|
||||
expect(escapeName('fooBar')).toEqual('fooBar');
|
||||
expect(escapeName('Foo Bar')).toEqual(`'Foo Bar'`);
|
||||
expect(escapeName('foo bar')).toEqual(`'foo bar'`);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
import { isString } from '../../../utils/isString';
|
||||
import type { WithEnumExtension } from '../interfaces/Extensions/WithEnumExtension';
|
||||
|
||||
/**
|
||||
@ -8,8 +9,8 @@ import type { WithEnumExtension } from '../interfaces/Extensions/WithEnumExtensi
|
||||
* @param definition
|
||||
*/
|
||||
export const extendEnum = (enumerators: Enum[], definition: WithEnumExtension): Enum[] => {
|
||||
const names = definition['x-enum-varnames'];
|
||||
const descriptions = definition['x-enum-descriptions'];
|
||||
const names = definition['x-enum-varnames']?.filter(isString);
|
||||
const descriptions = definition['x-enum-descriptions']?.filter(isString);
|
||||
|
||||
return enumerators.map((enumerator, index) => ({
|
||||
name: names?.[index] || enumerator.name,
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
import { isDefined } from '../../../utils/isDefined';
|
||||
|
||||
export const getEnum = (values?: (string | number)[]): Enum[] => {
|
||||
if (Array.isArray(values)) {
|
||||
@ -7,7 +6,9 @@ export const getEnum = (values?: (string | number)[]): Enum[] => {
|
||||
.filter((value, index, arr) => {
|
||||
return arr.indexOf(value) === index;
|
||||
})
|
||||
.filter(isDefined)
|
||||
.filter((value: any) => {
|
||||
return typeof value === 'number' || typeof value === 'string';
|
||||
})
|
||||
.map(value => {
|
||||
if (typeof value === 'number') {
|
||||
return {
|
||||
@ -23,7 +24,7 @@ export const getEnum = (values?: (string | number)[]): Enum[] => {
|
||||
.replace(/^(\d+)/g, '_$1')
|
||||
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
|
||||
.toUpperCase(),
|
||||
value: `'${value}'`,
|
||||
value: `'${value.replace(/'/g, "\\'")}'`,
|
||||
type: 'string',
|
||||
description: null,
|
||||
};
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const getEnumFromDescription = (description: string): Enum[] => {
|
||||
// Check if we can find this special format string:
|
||||
// None=0,Something=1,AnotherThing=2
|
||||
if (/^(\w+=[0-9]+)/g.test(description)) {
|
||||
const matches = description.match(/(\w+=[0-9]+,?)/g);
|
||||
if (matches) {
|
||||
// Grab the values from the description
|
||||
const symbols: Enum[] = [];
|
||||
matches.forEach(match => {
|
||||
const name = match.split('=')[0];
|
||||
const value = parseInt(match.split('=')[1].replace(/[^0-9]/g, ''));
|
||||
if (name && Number.isInteger(value)) {
|
||||
symbols.push({
|
||||
name: name
|
||||
.replace(/\W+/g, '_')
|
||||
.replace(/^(\d+)/g, '_$1')
|
||||
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
|
||||
.toUpperCase(),
|
||||
value: String(value),
|
||||
type: 'number',
|
||||
description: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Filter out any duplicate names
|
||||
return symbols.filter((symbol, index, arr) => {
|
||||
return arr.map(item => item.name).indexOf(symbol.name) === index;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
@ -4,7 +4,6 @@ import type { OpenApi } from '../interfaces/OpenApi';
|
||||
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
import { extendEnum } from './extendEnum';
|
||||
import { getEnum } from './getEnum';
|
||||
import { getEnumFromDescription } from './getEnumFromDescription';
|
||||
import { getModelComposition } from './getModelComposition';
|
||||
import { getModelProperties } from './getModelProperties';
|
||||
import { getType } from './getType';
|
||||
@ -69,17 +68,6 @@ export const getModel = (
|
||||
}
|
||||
}
|
||||
|
||||
if ((definition.type === 'int' || definition.type === 'integer') && definition.description) {
|
||||
const enumerators = getEnumFromDescription(definition.description);
|
||||
if (enumerators.length) {
|
||||
model.export = 'enum';
|
||||
model.type = 'number';
|
||||
model.base = 'number';
|
||||
model.enum.push(...enumerators);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.type === 'array' && definition.items) {
|
||||
if (definition.items.$ref) {
|
||||
const arrayItems = getType(definition.items.$ref);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Model } from '../../../client/interfaces/Model';
|
||||
import { reservedWords } from '../../v3/parser/getOperationParameterName';
|
||||
import type { OpenApi } from '../interfaces/OpenApi';
|
||||
import { getModel } from './getModel';
|
||||
import { getType } from './getType';
|
||||
@ -9,7 +10,7 @@ export const getModels = (openApi: OpenApi): Model[] => {
|
||||
if (openApi.definitions.hasOwnProperty(definitionName)) {
|
||||
const definition = openApi.definitions[definitionName];
|
||||
const definitionType = getType(definitionName);
|
||||
const model = getModel(openApi, definition, true, definitionType.base);
|
||||
const model = getModel(openApi, definition, true, definitionType.base.replace(reservedWords, '_$1'));
|
||||
models.push(model);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ export const getOperation = (
|
||||
pathParams: OperationParameters
|
||||
): Operation => {
|
||||
const serviceName = getServiceName(tag);
|
||||
const operationName = getOperationName(op.operationId || `${method}`);
|
||||
const operationName = getOperationName(url, method, op.operationId);
|
||||
|
||||
// Create a new operation object for this method.
|
||||
const operation: Operation = {
|
||||
|
||||
@ -2,17 +2,26 @@ import { getOperationName } from './getOperationName';
|
||||
|
||||
describe('getOperationName', () => {
|
||||
it('should produce correct result', () => {
|
||||
expect(getOperationName('')).toEqual('');
|
||||
expect(getOperationName('FooBar')).toEqual('fooBar');
|
||||
expect(getOperationName('Foo Bar')).toEqual('fooBar');
|
||||
expect(getOperationName('foo bar')).toEqual('fooBar');
|
||||
expect(getOperationName('foo-bar')).toEqual('fooBar');
|
||||
expect(getOperationName('foo_bar')).toEqual('fooBar');
|
||||
expect(getOperationName('foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('@foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('$foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('_foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('-foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('123.foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', undefined)).toEqual('getApiUsers');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'POST', undefined)).toEqual('postApiUsers');
|
||||
expect(getOperationName('/api/v1/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
|
||||
expect(getOperationName('/api/v1/users', 'GET', undefined)).toEqual('getApiV1Users');
|
||||
expect(getOperationName('/api/v1/users', 'POST', undefined)).toEqual('postApiV1Users');
|
||||
expect(getOperationName('/api/v1/users/{id}', 'GET', undefined)).toEqual('getApiV1Users');
|
||||
expect(getOperationName('/api/v1/users/{id}', 'POST', undefined)).toEqual('postApiV1Users');
|
||||
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'fooBar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'FooBar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'Foo Bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo-bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo_bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '@foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '$foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '_foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '-foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '123.foo.bar')).toEqual('fooBar');
|
||||
});
|
||||
});
|
||||
|
||||
@ -2,13 +2,23 @@ import camelCase from 'camelcase';
|
||||
|
||||
/**
|
||||
* Convert the input value to a correct operation (method) classname.
|
||||
* This converts the input string to camelCase, so the method name follows
|
||||
* the most popular Javascript and Typescript writing style.
|
||||
* This will use the operation ID - if available - and otherwise fallback
|
||||
* on a generated name from the URL
|
||||
*/
|
||||
export const getOperationName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim();
|
||||
return camelCase(clean);
|
||||
export const getOperationName = (url: string, method: string, operationId?: string): string => {
|
||||
if (operationId) {
|
||||
return camelCase(
|
||||
operationId
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim()
|
||||
);
|
||||
}
|
||||
|
||||
const urlWithoutPlaceholders = url
|
||||
.replace(/[^/]*?{api-version}.*?\//g, '')
|
||||
.replace(/{(.*?)}/g, '')
|
||||
.replace(/\//g, '-');
|
||||
|
||||
return camelCase(`${method}-${urlWithoutPlaceholders}`);
|
||||
};
|
||||
|
||||
@ -5,7 +5,6 @@ import type { OpenApiParameter } from '../interfaces/OpenApiParameter';
|
||||
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
import { extendEnum } from './extendEnum';
|
||||
import { getEnum } from './getEnum';
|
||||
import { getEnumFromDescription } from './getEnumFromDescription';
|
||||
import { getModel } from './getModel';
|
||||
import { getOperationParameterDefault } from './getOperationParameterDefault';
|
||||
import { getOperationParameterName } from './getOperationParameterName';
|
||||
@ -70,18 +69,6 @@ export const getOperationParameter = (openApi: OpenApi, parameter: OpenApiParame
|
||||
}
|
||||
}
|
||||
|
||||
if ((parameter.type === 'int' || parameter.type === 'integer') && parameter.description) {
|
||||
const enumerators = getEnumFromDescription(parameter.description);
|
||||
if (enumerators.length) {
|
||||
operationParameter.export = 'enum';
|
||||
operationParameter.type = 'number';
|
||||
operationParameter.base = 'number';
|
||||
operationParameter.enum.push(...enumerators);
|
||||
operationParameter.default = getOperationParameterDefault(parameter, operationParameter);
|
||||
return operationParameter;
|
||||
}
|
||||
}
|
||||
|
||||
if (parameter.type === 'array' && parameter.items) {
|
||||
const items = getType(parameter.items.type, parameter.items.format);
|
||||
operationParameter.export = 'array';
|
||||
|
||||
@ -6,7 +6,7 @@ export const getOperationParameterDefault = (
|
||||
operationParameter: OperationParameter
|
||||
): string | undefined => {
|
||||
if (parameter.default === undefined) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (parameter.default === null) {
|
||||
@ -38,5 +38,5 @@ export const getOperationParameterDefault = (
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
import { isString } from '../../../utils/isString';
|
||||
import type { WithEnumExtension } from '../interfaces/Extensions/WithEnumExtension';
|
||||
|
||||
/**
|
||||
@ -8,8 +9,8 @@ import type { WithEnumExtension } from '../interfaces/Extensions/WithEnumExtensi
|
||||
* @param definition
|
||||
*/
|
||||
export const extendEnum = (enumerators: Enum[], definition: WithEnumExtension): Enum[] => {
|
||||
const names = definition['x-enum-varnames'];
|
||||
const descriptions = definition['x-enum-descriptions'];
|
||||
const names = definition['x-enum-varnames']?.filter(isString);
|
||||
const descriptions = definition['x-enum-descriptions']?.filter(isString);
|
||||
|
||||
return enumerators.map((enumerator, index) => ({
|
||||
name: names?.[index] || enumerator.name,
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
import { isDefined } from '../../../utils/isDefined';
|
||||
|
||||
export const getEnum = (values?: (string | number)[]): Enum[] => {
|
||||
if (Array.isArray(values)) {
|
||||
@ -7,7 +6,9 @@ export const getEnum = (values?: (string | number)[]): Enum[] => {
|
||||
.filter((value, index, arr) => {
|
||||
return arr.indexOf(value) === index;
|
||||
})
|
||||
.filter(isDefined)
|
||||
.filter((value: any) => {
|
||||
return typeof value === 'number' || typeof value === 'string';
|
||||
})
|
||||
.map(value => {
|
||||
if (typeof value === 'number') {
|
||||
return {
|
||||
@ -23,7 +24,7 @@ export const getEnum = (values?: (string | number)[]): Enum[] => {
|
||||
.replace(/^(\d+)/g, '_$1')
|
||||
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
|
||||
.toUpperCase(),
|
||||
value: `'${value}'`,
|
||||
value: `'${value.replace(/'/g, "\\'")}'`,
|
||||
type: 'string',
|
||||
description: null,
|
||||
};
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const getEnumFromDescription = (description: string): Enum[] => {
|
||||
// Check if we can find this special format string:
|
||||
// None=0,Something=1,AnotherThing=2
|
||||
if (/^(\w+=[0-9]+)/g.test(description)) {
|
||||
const matches = description.match(/(\w+=[0-9]+,?)/g);
|
||||
if (matches) {
|
||||
// Grab the values from the description
|
||||
const symbols: Enum[] = [];
|
||||
matches.forEach(match => {
|
||||
const name = match.split('=')[0];
|
||||
const value = parseInt(match.split('=')[1].replace(/[^0-9]/g, ''));
|
||||
if (name && Number.isInteger(value)) {
|
||||
symbols.push({
|
||||
name: name
|
||||
.replace(/\W+/g, '_')
|
||||
.replace(/^(\d+)/g, '_$1')
|
||||
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
|
||||
.toUpperCase(),
|
||||
value: String(value),
|
||||
type: 'number',
|
||||
description: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Filter out any duplicate names
|
||||
return symbols.filter((symbol, index, arr) => {
|
||||
return arr.map(item => item.name).indexOf(symbol.name) === index;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
@ -4,7 +4,6 @@ import type { OpenApi } from '../interfaces/OpenApi';
|
||||
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
import { extendEnum } from './extendEnum';
|
||||
import { getEnum } from './getEnum';
|
||||
import { getEnumFromDescription } from './getEnumFromDescription';
|
||||
import { getModelComposition } from './getModelComposition';
|
||||
import { getModelDefault } from './getModelDefault';
|
||||
import { getModelProperties } from './getModelProperties';
|
||||
@ -24,6 +23,7 @@ export const getModel = (
|
||||
template: null,
|
||||
link: null,
|
||||
description: definition.description || null,
|
||||
deprecated: definition.deprecated === true,
|
||||
isDefinition,
|
||||
isReadOnly: definition.readOnly === true,
|
||||
isNullable: definition.nullable === true,
|
||||
@ -72,18 +72,6 @@ export const getModel = (
|
||||
}
|
||||
}
|
||||
|
||||
if ((definition.type === 'int' || definition.type === 'integer') && definition.description) {
|
||||
const enumerators = getEnumFromDescription(definition.description);
|
||||
if (enumerators.length) {
|
||||
model.export = 'enum';
|
||||
model.type = 'number';
|
||||
model.base = 'number';
|
||||
model.enum.push(...enumerators);
|
||||
model.default = getModelDefault(definition, model);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.type === 'array' && definition.items) {
|
||||
if (definition.items.$ref) {
|
||||
const arrayItems = getType(definition.items.$ref);
|
||||
@ -107,9 +95,13 @@ export const getModel = (
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.type === 'object' && typeof definition.additionalProperties === 'object') {
|
||||
if (definition.additionalProperties.$ref) {
|
||||
const additionalProperties = getType(definition.additionalProperties.$ref);
|
||||
if (
|
||||
definition.type === 'object' &&
|
||||
(typeof definition.additionalProperties === 'object' || definition.additionalProperties === true)
|
||||
) {
|
||||
const ap = typeof definition.additionalProperties === 'object' ? definition.additionalProperties : {};
|
||||
if (ap.$ref) {
|
||||
const additionalProperties = getType(ap.$ref);
|
||||
model.export = 'dictionary';
|
||||
model.type = additionalProperties.type;
|
||||
model.base = additionalProperties.base;
|
||||
@ -118,7 +110,7 @@ export const getModel = (
|
||||
model.default = getModelDefault(definition, model);
|
||||
return model;
|
||||
} else {
|
||||
const additionalProperties = getModel(openApi, definition.additionalProperties);
|
||||
const additionalProperties = getModel(openApi, ap);
|
||||
model.export = 'dictionary';
|
||||
model.type = additionalProperties.type;
|
||||
model.base = additionalProperties.base;
|
||||
@ -158,12 +150,12 @@ export const getModel = (
|
||||
}
|
||||
|
||||
if (definition.type === 'object') {
|
||||
model.export = 'interface';
|
||||
model.type = 'any';
|
||||
model.base = 'any';
|
||||
model.default = getModelDefault(definition, model);
|
||||
|
||||
if (definition.properties) {
|
||||
model.export = 'interface';
|
||||
model.type = 'any';
|
||||
model.base = 'any';
|
||||
model.default = getModelDefault(definition, model);
|
||||
|
||||
const modelProperties = getModelProperties(openApi, definition, getModel, model);
|
||||
modelProperties.forEach(modelProperty => {
|
||||
model.imports.push(...modelProperty.imports);
|
||||
@ -173,8 +165,18 @@ export const getModel = (
|
||||
model.enums.push(modelProperty);
|
||||
}
|
||||
});
|
||||
return model;
|
||||
} else {
|
||||
const additionalProperties = getModel(openApi, {});
|
||||
model.export = 'dictionary';
|
||||
model.type = additionalProperties.type;
|
||||
model.base = additionalProperties.base;
|
||||
model.template = additionalProperties.template;
|
||||
model.link = additionalProperties;
|
||||
model.imports.push(...additionalProperties.imports);
|
||||
model.default = getModelDefault(definition, model);
|
||||
return model;
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
// If the schema has a type than it can be a basic or generic type.
|
||||
|
||||
@ -31,8 +31,9 @@ export const getModelComposition = (
|
||||
const hasProperties = model.properties.length;
|
||||
const hasEnums = model.enums.length;
|
||||
const isObject = model.type === 'any';
|
||||
const isDictionary = model.export === 'dictionary';
|
||||
const isEmpty = isObject && !hasProperties && !hasEnums;
|
||||
return !isEmpty;
|
||||
return !isEmpty || isDictionary;
|
||||
})
|
||||
.forEach(model => {
|
||||
composition.imports.push(...model.imports);
|
||||
|
||||
@ -3,7 +3,7 @@ import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
|
||||
export const getModelDefault = (definition: OpenApiSchema, model?: Model): string | undefined => {
|
||||
if (definition.default === undefined) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (definition.default === null) {
|
||||
@ -35,5 +35,5 @@ export const getModelDefault = (definition: OpenApiSchema, model?: Model): strin
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -37,6 +37,7 @@ export const getModelProperties = (
|
||||
> = {
|
||||
name: escapeName(propertyName),
|
||||
description: property.description || null,
|
||||
deprecated: property.deprecated === true,
|
||||
isDefinition: false,
|
||||
isReadOnly: property.readOnly === true,
|
||||
isRequired: propertyRequired,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { Model } from '../../../client/interfaces/Model';
|
||||
import type { OpenApi } from '../interfaces/OpenApi';
|
||||
import { getModel } from './getModel';
|
||||
import { reservedWords } from './getOperationParameterName';
|
||||
import { getType } from './getType';
|
||||
|
||||
export const getModels = (openApi: OpenApi): Model[] => {
|
||||
@ -10,7 +11,7 @@ export const getModels = (openApi: OpenApi): Model[] => {
|
||||
if (openApi.components.schemas.hasOwnProperty(definitionName)) {
|
||||
const definition = openApi.components.schemas[definitionName];
|
||||
const definitionType = getType(definitionName);
|
||||
const model = getModel(openApi, definition, true, definitionType.base);
|
||||
const model = getModel(openApi, definition, true, definitionType.base.replace(reservedWords, '_$1'));
|
||||
models.push(model);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ export const getOperation = (
|
||||
pathParams: OperationParameters
|
||||
): Operation => {
|
||||
const serviceName = getServiceName(tag);
|
||||
const operationName = getOperationName(op.operationId || `${method}`);
|
||||
const operationName = getOperationName(url, method, op.operationId);
|
||||
|
||||
// Create a new operation object for this method.
|
||||
const operation: Operation = {
|
||||
|
||||
@ -2,17 +2,26 @@ import { getOperationName } from './getOperationName';
|
||||
|
||||
describe('getOperationName', () => {
|
||||
it('should produce correct result', () => {
|
||||
expect(getOperationName('')).toEqual('');
|
||||
expect(getOperationName('FooBar')).toEqual('fooBar');
|
||||
expect(getOperationName('Foo Bar')).toEqual('fooBar');
|
||||
expect(getOperationName('foo bar')).toEqual('fooBar');
|
||||
expect(getOperationName('foo-bar')).toEqual('fooBar');
|
||||
expect(getOperationName('foo_bar')).toEqual('fooBar');
|
||||
expect(getOperationName('foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('@foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('$foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('_foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('-foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('123.foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', undefined)).toEqual('getApiUsers');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'POST', undefined)).toEqual('postApiUsers');
|
||||
expect(getOperationName('/api/v1/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
|
||||
expect(getOperationName('/api/v1/users', 'GET', undefined)).toEqual('getApiV1Users');
|
||||
expect(getOperationName('/api/v1/users', 'POST', undefined)).toEqual('postApiV1Users');
|
||||
expect(getOperationName('/api/v1/users/{id}', 'GET', undefined)).toEqual('getApiV1Users');
|
||||
expect(getOperationName('/api/v1/users/{id}', 'POST', undefined)).toEqual('postApiV1Users');
|
||||
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'fooBar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'FooBar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'Foo Bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo-bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo_bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '@foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '$foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '_foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '-foo.bar')).toEqual('fooBar');
|
||||
expect(getOperationName('/api/v{api-version}/users', 'GET', '123.foo.bar')).toEqual('fooBar');
|
||||
});
|
||||
});
|
||||
|
||||
@ -2,13 +2,23 @@ import camelCase from 'camelcase';
|
||||
|
||||
/**
|
||||
* Convert the input value to a correct operation (method) classname.
|
||||
* This converts the input string to camelCase, so the method name follows
|
||||
* the most popular Javascript and Typescript writing style.
|
||||
* This will use the operation ID - if available - and otherwise fallback
|
||||
* on a generated name from the URL
|
||||
*/
|
||||
export const getOperationName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim();
|
||||
return camelCase(clean);
|
||||
export const getOperationName = (url: string, method: string, operationId?: string): string => {
|
||||
if (operationId) {
|
||||
return camelCase(
|
||||
operationId
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim()
|
||||
);
|
||||
}
|
||||
|
||||
const urlWithoutPlaceholders = url
|
||||
.replace(/[^/]*?{api-version}.*?\//g, '')
|
||||
.replace(/{(.*?)}/g, '')
|
||||
.replace(/\//g, '-');
|
||||
|
||||
return camelCase(`${method}-${urlWithoutPlaceholders}`);
|
||||
};
|
||||
|
||||
@ -20,6 +20,7 @@ export const getOperationParameter = (openApi: OpenApi, parameter: OpenApiParame
|
||||
template: null,
|
||||
link: null,
|
||||
description: parameter.description || null,
|
||||
deprecated: parameter.deprecated === true,
|
||||
isDefinition: false,
|
||||
isReadOnly: false,
|
||||
isRequired: parameter.required === true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import camelCase from 'camelcase';
|
||||
|
||||
const reservedWords =
|
||||
export const reservedWords =
|
||||
/^(arguments|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|eval|export|extends|false|finally|for|function|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)$/g;
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{{>header}}
|
||||
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { ApiResult } from './ApiResult';
|
||||
|
||||
export class ApiError extends Error {
|
||||
@ -7,8 +8,9 @@ export class ApiError extends Error {
|
||||
public readonly status: number;
|
||||
public readonly statusText: string;
|
||||
public readonly body: any;
|
||||
public readonly request: ApiRequestOptions;
|
||||
|
||||
constructor(response: ApiResult, message: string) {
|
||||
constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
|
||||
super(message);
|
||||
|
||||
this.name = 'ApiError';
|
||||
@ -16,5 +18,6 @@ export class ApiError extends Error {
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
this.body = response.body;
|
||||
this.request = request;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import type { CancelablePromise } from './CancelablePromise';
|
||||
import type { OpenAPIConfig } from './OpenAPI';
|
||||
{{/equals}}
|
||||
|
||||
export class BaseHttpRequest {
|
||||
export abstract class BaseHttpRequest {
|
||||
|
||||
{{#equals @root.httpClient 'angular'}}
|
||||
constructor(
|
||||
@ -24,12 +24,8 @@ export class BaseHttpRequest {
|
||||
{{/equals}}
|
||||
|
||||
{{#equals @root.httpClient 'angular'}}
|
||||
public request<T>(options: ApiRequestOptions): Observable<T> {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
public abstract request<T>(options: ApiRequestOptions): Observable<T>;
|
||||
{{else}}
|
||||
public request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
public abstract request<T>(options: ApiRequestOptions): CancelablePromise<T>;
|
||||
{{/equals}}
|
||||
}
|
||||
|
||||
@ -36,47 +36,47 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
onCancel: OnCancel
|
||||
) => void
|
||||
) {
|
||||
this.#isResolved = false;
|
||||
this.#isRejected = false;
|
||||
this.#isCancelled = false;
|
||||
this.#cancelHandlers = [];
|
||||
this.#promise = new Promise<T>((resolve, reject) => {
|
||||
this.#resolve = resolve;
|
||||
this.#reject = reject;
|
||||
this._isResolved = false;
|
||||
this._isRejected = false;
|
||||
this._isCancelled = false;
|
||||
this._cancelHandlers = [];
|
||||
this._promise = new Promise<T>((resolve, reject) => {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
|
||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isResolved = true;
|
||||
this.#resolve?.(value);
|
||||
this._isResolved = true;
|
||||
this._resolve?.(value);
|
||||
};
|
||||
|
||||
const onReject = (reason?: any): void => {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isRejected = true;
|
||||
this.#reject?.(reason);
|
||||
this._isRejected = true;
|
||||
this._reject?.(reason);
|
||||
};
|
||||
|
||||
const onCancel = (cancelHandler: () => void): void => {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#cancelHandlers.push(cancelHandler);
|
||||
this._cancelHandlers.push(cancelHandler);
|
||||
};
|
||||
|
||||
Object.defineProperty(onCancel, 'isResolved', {
|
||||
get: (): boolean => this.#isResolved,
|
||||
get: (): boolean => this._isResolved,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isRejected', {
|
||||
get: (): boolean => this.#isRejected,
|
||||
get: (): boolean => this._isRejected,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isCancelled', {
|
||||
get: (): boolean => this.#isCancelled,
|
||||
get: (): boolean => this._isCancelled,
|
||||
});
|
||||
|
||||
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||
@ -91,27 +91,27 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
||||
): Promise<TResult1 | TResult2> {
|
||||
return this.#promise.then(onFulfilled, onRejected);
|
||||
return this._promise.then(onFulfilled, onRejected);
|
||||
}
|
||||
|
||||
public catch<TResult = never>(
|
||||
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
|
||||
): Promise<T | TResult> {
|
||||
return this.#promise.catch(onRejected);
|
||||
return this._promise.catch(onRejected);
|
||||
}
|
||||
|
||||
public finally(onFinally?: (() => void) | null): Promise<T> {
|
||||
return this.#promise.finally(onFinally);
|
||||
return this._promise.finally(onFinally);
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isCancelled = true;
|
||||
if (this.#cancelHandlers.length) {
|
||||
this._isCancelled = true;
|
||||
if (this._cancelHandlers.length) {
|
||||
try {
|
||||
for (const cancelHandler of this.#cancelHandlers) {
|
||||
for (const cancelHandler of this._cancelHandlers) {
|
||||
cancelHandler();
|
||||
}
|
||||
} catch (error) {
|
||||
@ -119,11 +119,11 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.#cancelHandlers.length = 0;
|
||||
this.#reject?.(new CancelError('Request aborted'));
|
||||
this._cancelHandlers.length = 0;
|
||||
this._reject?.(new CancelError('Request aborted'));
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return this.#isCancelled;
|
||||
return this._isCancelled;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,5 +8,5 @@ const getRequestBody = (options: ApiRequestOptions): any => {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -2,5 +2,5 @@ const getResponseBody = <T>(response: HttpResponse<T>): T | undefined => {
|
||||
if (response.status !== 204 && response.body !== null) {
|
||||
return response.body;
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -5,5 +5,5 @@ const getResponseHeader = <T>(response: HttpResponse<T>, responseHeader?: string
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -26,5 +26,17 @@ const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, for
|
||||
headers['Authorization'] = `Basic ${credentials}`;
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
if (options.mediaType) {
|
||||
headers['Content-Type'] = options.mediaType;
|
||||
} else if (isBlob(options.body)) {
|
||||
headers['Content-Type'] = options.body.type || 'application/octet-stream';
|
||||
} else if (isString(options.body)) {
|
||||
headers['Content-Type'] = 'text/plain';
|
||||
} else if (!isFormData(options.body)) {
|
||||
headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
};
|
||||
|
||||
@ -2,5 +2,5 @@ const getRequestBody = (options: ApiRequestOptions): any => {
|
||||
if (options.body) {
|
||||
return options.body;
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -2,5 +2,5 @@ const getResponseBody = (response: AxiosResponse<any>): any => {
|
||||
if (response.status !== 204) {
|
||||
return response.data;
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -5,5 +5,5 @@ const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{{>header}}
|
||||
|
||||
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import axios from 'axios';
|
||||
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import FormData from 'form-data';
|
||||
|
||||
import { ApiError } from './ApiError';
|
||||
@ -22,6 +23,9 @@ import type { OpenAPIConfig } from './OpenAPI';
|
||||
{{>functions/isBlob}}
|
||||
|
||||
|
||||
{{>functions/isFormData}}
|
||||
|
||||
|
||||
{{>functions/isSuccess}}
|
||||
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ const sendRequest = async <T>(
|
||||
try {
|
||||
return await axios.request(requestConfig);
|
||||
} catch (error) {
|
||||
const axiosError = error as AxiosError;
|
||||
const axiosError = error as AxiosError<T>;
|
||||
if (axiosError.response) {
|
||||
return axiosError.response;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const getRequestBody = (options: ApiRequestOptions): any => {
|
||||
if (options.body) {
|
||||
if (options.body !== undefined) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
} else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
|
||||
@ -8,5 +8,5 @@ const getRequestBody = (options: ApiRequestOptions): any => {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -3,7 +3,8 @@ const getResponseBody = async (response: Response): Promise<any> => {
|
||||
try {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
const isJSON = contentType.toLowerCase().startsWith('application/json');
|
||||
const jsonTypes = ['application/json', 'application/problem+json']
|
||||
const isJSON = jsonTypes.some(type => contentType.toLowerCase().startsWith(type));
|
||||
if (isJSON) {
|
||||
return await response.json();
|
||||
} else {
|
||||
@ -14,5 +15,5 @@ const getResponseBody = async (response: Response): Promise<any> => {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -5,5 +5,5 @@ const getResponseHeader = (response: Response, responseHeader?: string): string
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -12,10 +12,10 @@ const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void =>
|
||||
|
||||
const error = errors[result.status];
|
||||
if (error) {
|
||||
throw new ApiError(result, error);
|
||||
throw new ApiError(options, result, error);
|
||||
}
|
||||
|
||||
if (!result.ok) {
|
||||
throw new ApiError(result, 'Generic Error');
|
||||
throw new ApiError(options, result, 'Generic Error');
|
||||
}
|
||||
};
|
||||
|
||||
@ -22,5 +22,5 @@ const getFormData = (options: ApiRequestOptions): FormData | undefined => {
|
||||
|
||||
return formData;
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const getRequestBody = (options: ApiRequestOptions): any => {
|
||||
if (options.body) {
|
||||
if (options.body !== undefined) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
} else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
|
||||
@ -8,5 +8,5 @@ const getRequestBody = (options: ApiRequestOptions): any => {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -3,7 +3,8 @@ const getResponseBody = async (response: Response): Promise<any> => {
|
||||
try {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
const isJSON = contentType.toLowerCase().startsWith('application/json');
|
||||
const jsonTypes = ['application/json', 'application/problem+json']
|
||||
const isJSON = jsonTypes.some(type => contentType.toLowerCase().startsWith(type));
|
||||
if (isJSON) {
|
||||
return await response.json();
|
||||
} else {
|
||||
@ -14,5 +15,5 @@ const getResponseBody = async (response: Response): Promise<any> => {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -5,5 +5,5 @@ const getResponseHeader = (response: Response, responseHeader?: string): string
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
{{>header}}
|
||||
|
||||
import { AbortController } from 'abort-controller';
|
||||
import FormData from 'form-data';
|
||||
import fetch, { BodyInit, Headers, RequestInit, Response } from 'node-fetch';
|
||||
import fetch, { Headers } from 'node-fetch';
|
||||
import type { RequestInit, Response } from 'node-fetch';
|
||||
import type { AbortSignal } from 'node-fetch/externals';
|
||||
|
||||
import { ApiError } from './ApiError';
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
|
||||
@ -12,7 +12,7 @@ export const sendRequest = async (
|
||||
headers,
|
||||
method: options.method,
|
||||
body: body ?? formData,
|
||||
signal: controller.signal,
|
||||
signal: controller.signal as AbortSignal,
|
||||
};
|
||||
|
||||
onCancel(() => controller.abort());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const getRequestBody = (options: ApiRequestOptions): any => {
|
||||
if (options.body) {
|
||||
if (options.body !== undefined) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
} else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
|
||||
@ -8,6 +8,5 @@ const getRequestBody = (options: ApiRequestOptions): any => {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -3,7 +3,8 @@ const getResponseBody = (xhr: XMLHttpRequest): any => {
|
||||
try {
|
||||
const contentType = xhr.getResponseHeader('Content-Type');
|
||||
if (contentType) {
|
||||
const isJSON = contentType.toLowerCase().startsWith('application/json');
|
||||
const jsonTypes = ['application/json', 'application/problem+json']
|
||||
const isJSON = jsonTypes.some(type => contentType.toLowerCase().startsWith(type));
|
||||
if (isJSON) {
|
||||
return JSON.parse(xhr.responseText);
|
||||
} else {
|
||||
@ -14,5 +15,5 @@ const getResponseBody = (xhr: XMLHttpRequest): any => {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -5,5 +5,5 @@ const getResponseHeader = (xhr: XMLHttpRequest, responseHeader?: string): string
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -32,7 +32,9 @@ import { request as __request } from '../core/request';
|
||||
{{/if}}
|
||||
|
||||
{{#equals @root.httpClient 'angular'}}
|
||||
@Injectable()
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
{{/equals}}
|
||||
export class {{{name}}}{{{@root.postfix}}} {
|
||||
{{#if @root.exportClient}}
|
||||
|
||||
@ -18,13 +18,13 @@ export type { OpenAPIConfig } from './core/OpenAPI';
|
||||
|
||||
{{#each models}}
|
||||
{{#if @root.useUnionTypes}}
|
||||
export type { {{{name}}} } from './models/{{{name}}}';
|
||||
export type { {{{name}}}{{#if @root.postfixModels}} as {{{name}}}{{{@root.postfixModels}}}{{/if}} } from './models/{{{name}}}';
|
||||
{{else if enum}}
|
||||
export { {{{name}}} } from './models/{{{name}}}';
|
||||
export { {{{name}}}{{#if @root.postfixModels}} as {{{name}}}{{{@root.postfixModels}}}{{/if}} } from './models/{{{name}}}';
|
||||
{{else if enums}}
|
||||
export { {{{name}}} } from './models/{{{name}}}';
|
||||
export { {{{name}}}{{#if @root.postfixModels}} as {{{name}}}{{{@root.postfixModels}}}{{/if}} } from './models/{{{name}}}';
|
||||
{{else}}
|
||||
export type { {{{name}}} } from './models/{{{name}}}';
|
||||
export type { {{{name}}}{{#if @root.postfixModels}} as {{{name}}}{{{@root.postfixModels}}}{{/if}} } from './models/{{{name}}}';
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
@ -41,7 +41,7 @@ export { ${{{name}}} } from './schemas/${{{name}}}';
|
||||
{{#if services}}
|
||||
|
||||
{{#each services}}
|
||||
export { {{{name}}}{{{@root.postfix}}} } from './services/{{{name}}}{{{@root.postfix}}}';
|
||||
export { {{{name}}}{{{@root.postfixServices}}} } from './services/{{{name}}}{{{@root.postfixServices}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
{{#if description}}
|
||||
{{#ifdef description deprecated}}
|
||||
/**
|
||||
{{#if description}}
|
||||
* {{{escapeComment description}}}
|
||||
*/
|
||||
{{/if}}
|
||||
{{#if deprecated}}
|
||||
* @deprecated
|
||||
{{/if}}
|
||||
*/
|
||||
{{/ifdef}}
|
||||
export type {{{name}}} = {{>type parent=name}};
|
||||
{{#if enums}}
|
||||
{{#unless @root.useUnionTypes}}
|
||||
@ -10,11 +15,16 @@ export type {{{name}}} = {{>type parent=name}};
|
||||
export namespace {{{name}}} {
|
||||
|
||||
{{#each enums}}
|
||||
{{#if description}}
|
||||
{{#ifdef description deprecated}}
|
||||
/**
|
||||
{{#if description}}
|
||||
* {{{escapeComment description}}}
|
||||
*/
|
||||
{{/if}}
|
||||
{{#if deprecated}}
|
||||
* @deprecated
|
||||
{{/if}}
|
||||
*/
|
||||
{{/ifdef}}
|
||||
export enum {{{name}}} {
|
||||
{{#each enum}}
|
||||
{{{name}}} = {{{value}}},
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
{{#if description}}
|
||||
{{#ifdef description deprecated}}
|
||||
/**
|
||||
{{#if description}}
|
||||
* {{{escapeComment description}}}
|
||||
*/
|
||||
{{/if}}
|
||||
{{#if deprecated}}
|
||||
* @deprecated
|
||||
{{/if}}
|
||||
*/
|
||||
{{/ifdef}}
|
||||
export enum {{{name}}} {
|
||||
{{#each enum}}
|
||||
{{#if description}}
|
||||
@ -11,7 +16,7 @@ export enum {{{name}}} {
|
||||
*/
|
||||
{{/if}}
|
||||
{{#containsSpaces name}}
|
||||
"{{{name}}}" = {{{value}}},
|
||||
'{{{name}}}' = {{{value}}},
|
||||
{{else}}
|
||||
{{{name}}} = {{{value}}},
|
||||
{{/containsSpaces}}
|
||||
|
||||
@ -1,15 +1,25 @@
|
||||
{{#if description}}
|
||||
{{#ifdef description deprecated}}
|
||||
/**
|
||||
{{#if description}}
|
||||
* {{{escapeComment description}}}
|
||||
*/
|
||||
{{/if}}
|
||||
{{#if deprecated}}
|
||||
* @deprecated
|
||||
{{/if}}
|
||||
*/
|
||||
{{/ifdef}}
|
||||
export type {{{name}}} = {
|
||||
{{#each properties}}
|
||||
{{#if description}}
|
||||
{{#ifdef description deprecated}}
|
||||
/**
|
||||
{{#if description}}
|
||||
* {{{escapeComment description}}}
|
||||
*/
|
||||
{{/if}}
|
||||
{{#if deprecated}}
|
||||
* @deprecated
|
||||
{{/if}}
|
||||
*/
|
||||
{{/ifdef}}
|
||||
{{>isReadOnly}}{{{name}}}{{>isRequired}}: {{>type parent=../name}};
|
||||
{{/each}}
|
||||
};
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
{{#if description}}
|
||||
{{#ifdef description deprecated}}
|
||||
/**
|
||||
{{#if description}}
|
||||
* {{{escapeComment description}}}
|
||||
*/
|
||||
{{/if}}
|
||||
{{#if deprecated}}
|
||||
* @deprecated
|
||||
{{/if}}
|
||||
*/
|
||||
{{/ifdef}}
|
||||
export type {{{name}}} = {{>type}};
|
||||
|
||||
@ -6,9 +6,16 @@
|
||||
{{/each}}
|
||||
}: {
|
||||
{{#each parameters}}
|
||||
{{#ifdef description deprecated}}
|
||||
/**
|
||||
{{#if description}}
|
||||
/** {{{escapeComment description}}} **/
|
||||
* {{{escapeComment description}}}
|
||||
{{/if}}
|
||||
{{#if deprecated}}
|
||||
* @deprecated
|
||||
{{/if}}
|
||||
*/
|
||||
{{/ifdef}}
|
||||
{{{name}}}{{>isRequired}}: {{>type}},
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
{{~#if properties~}}
|
||||
{
|
||||
{{#each properties}}
|
||||
{{#if description}}
|
||||
{{#ifdef description deprecated}}
|
||||
/**
|
||||
{{#if description}}
|
||||
* {{{escapeComment description}}}
|
||||
*/
|
||||
{{/if}}
|
||||
{{#if deprecated}}
|
||||
* @deprecated
|
||||
{{/if}}
|
||||
*/
|
||||
{{/ifdef}}
|
||||
{{#if ../parent}}
|
||||
{{>isReadOnly}}{{{name}}}{{>isRequired}}: {{>type parent=../parent}};
|
||||
{{else}}
|
||||
|
||||
@ -27,7 +27,7 @@ export const findOneOfParentDiscriminator = (openApi: OpenApi, parent?: Model):
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const mapPropertyValue = (discriminator: OpenApiDiscriminator, parent: Model): string => {
|
||||
|
||||
@ -1,50 +1,16 @@
|
||||
import {
|
||||
copyFile as __copyFile,
|
||||
exists as __exists,
|
||||
mkdir as __mkdir,
|
||||
mkdirp as __mkdirp,
|
||||
pathExists as __pathExists,
|
||||
readFile as __readFile,
|
||||
rm as __rm,
|
||||
remove as __remove,
|
||||
writeFile as __writeFile,
|
||||
} from 'fs';
|
||||
import { promisify } from 'util';
|
||||
} from 'fs-extra';
|
||||
|
||||
// Wrapped file system calls
|
||||
export const readFile = promisify(__readFile);
|
||||
export const writeFile = promisify(__writeFile);
|
||||
export const copyFile = promisify(__copyFile);
|
||||
export const exists = promisify(__exists);
|
||||
|
||||
export const mkdir = (path: string): Promise<void> =>
|
||||
new Promise((resolve, reject) => {
|
||||
__mkdir(
|
||||
path,
|
||||
{
|
||||
recursive: true,
|
||||
},
|
||||
error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export const rmdir = (path: string): Promise<void> =>
|
||||
new Promise((resolve, reject) => {
|
||||
__rm(
|
||||
path,
|
||||
{
|
||||
recursive: true,
|
||||
force: true,
|
||||
},
|
||||
error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
// Export calls (needed for mocking)
|
||||
export const readFile = __readFile;
|
||||
export const writeFile = __writeFile;
|
||||
export const copyFile = __copyFile;
|
||||
export const exists = __pathExists;
|
||||
export const mkdir = __mkdirp;
|
||||
export const rmdir = __remove;
|
||||
|
||||
@ -14,5 +14,7 @@ export const formatIndentation = (s: string, indent: Indent): string => {
|
||||
return line; // Default output is tab formatted
|
||||
}
|
||||
});
|
||||
return lines.join(EOL);
|
||||
// Make sure we have a blank line at the end
|
||||
const content = lines.join(EOL);
|
||||
return `${content}${EOL}`;
|
||||
};
|
||||
|
||||
@ -10,5 +10,7 @@ describe('getPattern', () => {
|
||||
expect(getPattern('\\')).toEqual('\\\\');
|
||||
expect(getPattern('\\/')).toEqual('\\\\/');
|
||||
expect(getPattern('\\/\\/')).toEqual('\\\\/\\\\/');
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
expect(getPattern("'")).toEqual("\\'");
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,8 +3,12 @@
|
||||
* However, to use it in HTML or inside new RegExp() we need to
|
||||
* escape the pattern to become: '^\\d{3}-\\d{2}-\\d{4}$' in order
|
||||
* to make it a valid regexp string.
|
||||
*
|
||||
* Also, escape single quote characters, because the output uses single quotes for strings
|
||||
*
|
||||
* @param pattern
|
||||
*/
|
||||
export const getPattern = (pattern?: string): string | undefined => {
|
||||
return pattern?.replace(/\\/g, '\\\\');
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
return pattern?.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
||||
};
|
||||
|
||||
@ -11,6 +11,7 @@ describe('registerHandlebarHelpers', () => {
|
||||
useUnionTypes: false,
|
||||
});
|
||||
const helpers = Object.keys(Handlebars.helpers);
|
||||
expect(helpers).toContain('ifdef');
|
||||
expect(helpers).toContain('equals');
|
||||
expect(helpers).toContain('notEquals');
|
||||
expect(helpers).toContain('containsSpaces');
|
||||
|
||||
@ -12,6 +12,14 @@ export const registerHandlebarHelpers = (root: {
|
||||
useOptions: boolean;
|
||||
useUnionTypes: boolean;
|
||||
}): void => {
|
||||
Handlebars.registerHelper('ifdef', function (this: any, ...args): string {
|
||||
const options = args.pop();
|
||||
if (!args.every(value => !value)) {
|
||||
return options.fn(this);
|
||||
}
|
||||
return options.inverse(this);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper(
|
||||
'equals',
|
||||
function (this: any, a: string, b: string, options: Handlebars.HelperOptions): string {
|
||||
|
||||
@ -28,7 +28,8 @@ import { writeClientServices } from './writeClientServices';
|
||||
* @param exportSchemas Generate schemas
|
||||
* @param exportSchemas Generate schemas
|
||||
* @param indent Indentation options (4, 2 or tab)
|
||||
* @param postfix Service name postfix
|
||||
* @param postfixServices Service name postfix
|
||||
* @param postfixModels Model name postfix
|
||||
* @param clientName Custom client class name
|
||||
* @param request Path to custom request file
|
||||
*/
|
||||
@ -44,7 +45,8 @@ export const writeClient = async (
|
||||
exportModels: boolean,
|
||||
exportSchemas: boolean,
|
||||
indent: Indent,
|
||||
postfix: string,
|
||||
postfixServices: string,
|
||||
postfixModels: string,
|
||||
clientName?: string,
|
||||
request?: string
|
||||
): Promise<void> => {
|
||||
@ -75,7 +77,7 @@ export const writeClient = async (
|
||||
useUnionTypes,
|
||||
useOptions,
|
||||
indent,
|
||||
postfix,
|
||||
postfixServices,
|
||||
clientName
|
||||
);
|
||||
}
|
||||
@ -94,7 +96,7 @@ export const writeClient = async (
|
||||
|
||||
if (isDefined(clientName)) {
|
||||
await mkdir(outputPath);
|
||||
await writeClientClass(client, templates, outputPath, httpClient, clientName, indent, postfix);
|
||||
await writeClientClass(client, templates, outputPath, httpClient, clientName, indent, postfixServices);
|
||||
}
|
||||
|
||||
if (exportCore || exportServices || exportSchemas || exportModels) {
|
||||
@ -108,7 +110,8 @@ export const writeClient = async (
|
||||
exportServices,
|
||||
exportModels,
|
||||
exportSchemas,
|
||||
postfix,
|
||||
postfixServices,
|
||||
postfixModels,
|
||||
clientName
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { EOL } from 'os';
|
||||
|
||||
import type { Client } from '../client/interfaces/Client';
|
||||
import { HttpClient } from '../HttpClient';
|
||||
import { Indent } from '../Indent';
|
||||
@ -38,11 +40,11 @@ describe('writeClientCore', () => {
|
||||
|
||||
await writeClientCore(client, templates, '/', HttpClient.FETCH, Indent.SPACE_4);
|
||||
|
||||
expect(writeFile).toBeCalledWith('/OpenAPI.ts', 'settings');
|
||||
expect(writeFile).toBeCalledWith('/ApiError.ts', 'apiError');
|
||||
expect(writeFile).toBeCalledWith('/ApiRequestOptions.ts', 'apiRequestOptions');
|
||||
expect(writeFile).toBeCalledWith('/ApiResult.ts', 'apiResult');
|
||||
expect(writeFile).toBeCalledWith('/CancelablePromise.ts', 'cancelablePromise');
|
||||
expect(writeFile).toBeCalledWith('/request.ts', 'request');
|
||||
expect(writeFile).toBeCalledWith('/OpenAPI.ts', `settings${EOL}`);
|
||||
expect(writeFile).toBeCalledWith('/ApiError.ts', `apiError${EOL}`);
|
||||
expect(writeFile).toBeCalledWith('/ApiRequestOptions.ts', `apiRequestOptions${EOL}`);
|
||||
expect(writeFile).toBeCalledWith('/ApiResult.ts', `apiResult${EOL}`);
|
||||
expect(writeFile).toBeCalledWith('/CancelablePromise.ts', `cancelablePromise${EOL}`);
|
||||
expect(writeFile).toBeCalledWith('/request.ts', `request${EOL}`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -34,7 +34,7 @@ describe('writeClientIndex', () => {
|
||||
},
|
||||
};
|
||||
|
||||
await writeClientIndex(client, templates, '/', true, true, true, true, true, 'Service');
|
||||
await writeClientIndex(client, templates, '/', true, true, true, true, true, 'Service', '');
|
||||
|
||||
expect(writeFile).toBeCalledWith('/index.ts', 'index');
|
||||
});
|
||||
|
||||
@ -19,7 +19,8 @@ import { sortServicesByName } from './sortServicesByName';
|
||||
* @param exportServices Generate services
|
||||
* @param exportModels Generate models
|
||||
* @param exportSchemas Generate schemas
|
||||
* @param postfix Service name postfix
|
||||
* @param postfixServices Service name postfix
|
||||
* @param postfixModels Model name postfix
|
||||
* @param clientName Custom client class name
|
||||
*/
|
||||
export const writeClientIndex = async (
|
||||
@ -31,7 +32,8 @@ export const writeClientIndex = async (
|
||||
exportServices: boolean,
|
||||
exportModels: boolean,
|
||||
exportSchemas: boolean,
|
||||
postfix: string,
|
||||
postfixServices: string,
|
||||
postfixModels: string,
|
||||
clientName?: string
|
||||
): Promise<void> => {
|
||||
const templateResult = templates.index({
|
||||
@ -40,7 +42,8 @@ export const writeClientIndex = async (
|
||||
exportModels,
|
||||
exportSchemas,
|
||||
useUnionTypes,
|
||||
postfix,
|
||||
postfixServices,
|
||||
postfixModels,
|
||||
clientName,
|
||||
server: client.server,
|
||||
version: client.version,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { EOL } from 'os';
|
||||
|
||||
import type { Model } from '../client/interfaces/Model';
|
||||
import { HttpClient } from '../HttpClient';
|
||||
import { Indent } from '../Indent';
|
||||
@ -51,6 +53,6 @@ describe('writeClientModels', () => {
|
||||
|
||||
await writeClientModels(models, templates, '/', HttpClient.FETCH, false, Indent.SPACE_4);
|
||||
|
||||
expect(writeFile).toBeCalledWith('/User.ts', 'model');
|
||||
expect(writeFile).toBeCalledWith('/User.ts', `model${EOL}`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { EOL } from 'os';
|
||||
|
||||
import type { Model } from '../client/interfaces/Model';
|
||||
import { HttpClient } from '../HttpClient';
|
||||
import { Indent } from '../Indent';
|
||||
@ -51,6 +53,6 @@ describe('writeClientSchemas', () => {
|
||||
|
||||
await writeClientSchemas(models, templates, '/', HttpClient.FETCH, false, Indent.SPACE_4);
|
||||
|
||||
expect(writeFile).toBeCalledWith('/$User.ts', 'schema');
|
||||
expect(writeFile).toBeCalledWith('/$User.ts', `schema${EOL}`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { EOL } from 'os';
|
||||
|
||||
import type { Service } from '../client/interfaces/Service';
|
||||
import { HttpClient } from '../HttpClient';
|
||||
import { Indent } from '../Indent';
|
||||
@ -39,6 +41,6 @@ describe('writeClientServices', () => {
|
||||
|
||||
await writeClientServices(services, templates, '/', HttpClient.FETCH, false, false, Indent.SPACE_4, 'Service');
|
||||
|
||||
expect(writeFile).toBeCalledWith('/UserService.ts', 'service');
|
||||
expect(writeFile).toBeCalledWith('/UserService.ts', `service${EOL}`);
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -7,14 +7,14 @@ const generate = async (input, output) => {
|
||||
await OpenAPI.generate({
|
||||
input,
|
||||
output,
|
||||
httpClient: OpenAPI.HttpClient.ANGULAR,
|
||||
httpClient: OpenAPI.HttpClient.FETCH,
|
||||
useOptions: true,
|
||||
useUnionTypes: false,
|
||||
exportCore: true,
|
||||
exportSchemas: true,
|
||||
exportModels: true,
|
||||
exportServices: true,
|
||||
clientName: 'Demo',
|
||||
// clientName: 'Demo',
|
||||
// indent: OpenAPI.Indent.SPACE_2,
|
||||
// postfix: 'Service',
|
||||
// request: './test/custom/request.ts',
|
||||
|
||||
@ -15,6 +15,7 @@ describe('v2', () => {
|
||||
exportSchemas: true,
|
||||
exportModels: true,
|
||||
exportServices: true,
|
||||
postfixModels: 'Dto',
|
||||
});
|
||||
|
||||
sync('./test/generated/v2/**/*.ts').forEach(file => {
|
||||
@ -36,6 +37,7 @@ describe('v3', () => {
|
||||
exportSchemas: true,
|
||||
exportModels: true,
|
||||
exportServices: true,
|
||||
postfixModels: 'Dto',
|
||||
});
|
||||
|
||||
sync('./test/generated/v3/**/*.ts').forEach(file => {
|
||||
|
||||
@ -963,7 +963,9 @@
|
||||
"enum": [
|
||||
"Success",
|
||||
"Warning",
|
||||
"Error"
|
||||
"Error",
|
||||
"'Single Quote'",
|
||||
"\"Double Quotes\""
|
||||
]
|
||||
},
|
||||
"EnumWithNumbers": {
|
||||
@ -1464,6 +1466,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModelWithPattern": {
|
||||
"description": "This is a model that contains a some patterns",
|
||||
"type": "object",
|
||||
@ -1497,6 +1507,10 @@
|
||||
"text": {
|
||||
"type": "string",
|
||||
"pattern": "^\\w+$"
|
||||
},
|
||||
"patternWithSingleQuotes": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9']*$"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,6 +106,28 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v{api-version}/parameters/deprecated": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Deprecated"
|
||||
],
|
||||
"deprecated": true,
|
||||
"operationId": "DeprecatedCall",
|
||||
"parameters": [
|
||||
{
|
||||
"deprecated": true,
|
||||
"description": "This parameter is deprecated",
|
||||
"name": "parameter",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"nullable": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/DeprecatedModel"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v{api-version}/parameters/{parameterPath}": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@ -1537,7 +1559,9 @@
|
||||
"enum": [
|
||||
"Success",
|
||||
"Warning",
|
||||
"Error"
|
||||
"Error",
|
||||
"'Single Quote'",
|
||||
"\"Double Quotes\""
|
||||
]
|
||||
},
|
||||
"EnumWithNumbers": {
|
||||
@ -1875,6 +1899,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"DeprecatedModel": {
|
||||
"deprecated": true,
|
||||
"description": "This is a deprecated model with a deprecated property",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prop": {
|
||||
"deprecated": true,
|
||||
"description": "This is a deprecated property",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModelWithCircularReference": {
|
||||
"description": "This is a model with one property containing a circular reference",
|
||||
"type": "object",
|
||||
@ -2064,6 +2100,76 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CompositionWithOneOfAndSimpleDictionary": {
|
||||
"description": "This is a model that contains a simple dictionary within composition",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"propA": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"CompositionWithOneOfAndSimpleArrayDictionary": {
|
||||
"description": "This is a model that contains a dictionary of simple arrays within composition",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"propA": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"CompositionWithOneOfAndComplexArrayDictionary": {
|
||||
"description": "This is a model that contains a dictionary of complex arrays (composited) within composition",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"propA": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"CompositionWithAllOfAndNullable": {
|
||||
"description": "This is a model with one property with a 'all of' relationship",
|
||||
"type": "object",
|
||||
@ -2360,6 +2466,10 @@
|
||||
"text": {
|
||||
"type": "string",
|
||||
"pattern": "^\\w+$"
|
||||
},
|
||||
"patternWithSingleQuotes": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9']*$"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2401,6 +2511,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Pageable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -2421,6 +2539,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"FreeFormObjectWithoutAdditionalProperties": {
|
||||
"description": "This is a free-form object without additionalProperties.",
|
||||
"type": "object"
|
||||
},
|
||||
"FreeFormObjectWithAdditionalPropertiesEqTrue": {
|
||||
"description": "This is a free-form object with additionalProperties: true.",
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"FreeFormObjectWithAdditionalPropertiesEqEmptyObject": {
|
||||
"description": "This is a free-form object with additionalProperties: {}.",
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"target": "es2020",
|
||||
"target": "es2019",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"lib": ["es2020", "dom"],
|
||||
"lib": ["es2019", "dom"],
|
||||
"types": ["jest", "node"],
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
|
||||
3
types/index.d.ts
vendored
3
types/index.d.ts
vendored
@ -24,7 +24,8 @@ export type Options = {
|
||||
exportModels?: boolean;
|
||||
exportSchemas?: boolean;
|
||||
indent?: Indent | '4' | '2' | 'tab';
|
||||
postfix?: string;
|
||||
postfixServices?: string;
|
||||
postfixModels?: string;
|
||||
request?: string;
|
||||
write?: boolean;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user