diff --git a/.changeset/curvy-beans-fold.md b/.changeset/curvy-beans-fold.md new file mode 100644 index 000000000..b33828537 --- /dev/null +++ b/.changeset/curvy-beans-fold.md @@ -0,0 +1,5 @@ +--- +"openapi-ts-docs": patch +--- + +Add documentation for new enums configuration option diff --git a/.changeset/quick-ladybugs-argue.md b/.changeset/quick-ladybugs-argue.md new file mode 100644 index 000000000..d7e675bc6 --- /dev/null +++ b/.changeset/quick-ladybugs-argue.md @@ -0,0 +1,5 @@ +--- +"@hey-api/openapi-ts": minor +--- + +Add configuration option to closely preserve JavaScript enum names diff --git a/docs/openapi-ts/configuration.md b/docs/openapi-ts/configuration.md index 62b500457..b4f0026d6 100644 --- a/docs/openapi-ts/configuration.md +++ b/docs/openapi-ts/configuration.md @@ -121,6 +121,8 @@ You can also prevent your client from being processed by linters by adding your ## Enums +> Omitting the `enums` configuration value will emit unions instead of enums. + If you need to iterate through possible field values without manually typing arrays, you can export enums with ```js{2} @@ -140,6 +142,25 @@ export const FooEnum = { } as const; ``` +There is an additional JavaScript option for enums: + +```js{2} +export default { + enums: 'javascript-preserve-name, + input: 'path/to/openapi.json', + output: 'src/client', +} +``` + +As the name indicates this will not rename the enums from your input and will preserve the original names while maintaing the type-safety of the JavaScript object as a constant. + +```js +export const MY_FOO_ENUM = { + FOO: 'foo', + BAR: 'bar', +} as const; +``` + We discourage generating [TypeScript enums](https://www.typescriptlang.org/docs/handbook/enums.html) because they are not standard JavaScript and pose [typing challenges](https://dev.to/ivanzm123/dont-use-enums-in-typescript-they-are-very-dangerous-57bh). If you really need TypeScript enums, you can export them with ```js{2} diff --git a/packages/openapi-ts/bin/index.js b/packages/openapi-ts/bin/index.js index 72f131cea..48e53143e 100755 --- a/packages/openapi-ts/bin/index.js +++ b/packages/openapi-ts/bin/index.js @@ -20,7 +20,7 @@ const params = program .option('-d, --debug', 'Run in debug mode?') .option('--base [value]', 'Manually set base in OpenAPI config instead of inferring from server value') .option('--dry-run [value]', 'Skip writing files to disk?') - .option('--enums ', 'Export enum definitions (javascript, typescript)') + .option('--enums ', 'Export enum definitions (javascript, javascript-preserve-name, typescript)') .option('--exportCore [value]', 'Write core files to disk') .option('--exportModels [value]', 'Write models to disk') .option('--exportServices [value]', 'Write services to disk') diff --git a/packages/openapi-ts/src/types/config.ts b/packages/openapi-ts/src/types/config.ts index a8c510342..575912473 100644 --- a/packages/openapi-ts/src/types/config.ts +++ b/packages/openapi-ts/src/types/config.ts @@ -22,7 +22,7 @@ export interface UserConfig { * Export enum definitions? * @default false */ - enums?: 'javascript' | 'typescript' | false; + enums?: 'javascript' | 'javascript-preserve-name' | 'typescript' | false; /** * Generate core client classes? * @default true diff --git a/packages/openapi-ts/src/utils/enum.ts b/packages/openapi-ts/src/utils/enum.ts index 74fecbf11..d6271d0b3 100644 --- a/packages/openapi-ts/src/utils/enum.ts +++ b/packages/openapi-ts/src/utils/enum.ts @@ -36,18 +36,9 @@ export const enumKey = (value?: string | number, customName?: string) => { return key.toUpperCase(); }; -/** - * Enums can't contain hyphens in their name. Additionally, name might've been - * already escaped, so we need to remove quotes around it. - * {@link https://github.com/ferdikoomen/openapi-typescript-codegen/issues/1969} - */ -export const enumName = (client: Client, name?: string) => { - if (!name) { - return name; - } - const escapedName = unescapeName(name).replace(/[-_]([a-z])/gi, ($0, $1: string) => $1.toLocaleUpperCase()); - let result = `${escapedName.charAt(0).toLocaleUpperCase() + escapedName.slice(1)}Enum`; +const updateClientEnums = (client: Client, currentEnum: string) => { let index = 1; + let result = currentEnum; while (client.enumNames.includes(result)) { if (result.endsWith(index.toString())) { result = result.slice(0, result.length - index.toString().length); @@ -56,9 +47,34 @@ export const enumName = (client: Client, name?: string) => { result = result + index.toString(); } client.enumNames = [...client.enumNames, result]; +}; + +/** + * Enums can't contain hyphens in their name. Additionally, name might've been + * already escaped, so we need to remove quotes around it. + * {@link https://github.com/ferdikoomen/openapi-typescript-codegen/issues/1969} + */ +export const javascriptEnumName = (client: Client, name?: string) => { + if (!name) { + return name; + } + const escapedName = unescapeName(name).replace(/[-_]([a-z])/gi, ($0, $1: string) => $1.toLocaleUpperCase()); + const result = `${escapedName.charAt(0).toLocaleUpperCase() + escapedName.slice(1)}Enum`; + updateClientEnums(client, result); return result; }; +export const javascriptPreservedEnumName = (client: Client, name?: string) => { + if (!name) { + return name; + } + + // Remove all invalid characters + const cleanEnum = unescapeName(name).replace(/[^a-zA-Z0-9_$]/gi, ''); + updateClientEnums(client, cleanEnum); + return cleanEnum; +}; + export const enumUnionType = (enums: Enum[]) => enums .map(enumerator => enumValue(enumerator.value)) diff --git a/packages/openapi-ts/src/utils/write/models.ts b/packages/openapi-ts/src/utils/write/models.ts index 312e1b53a..f613de9d5 100644 --- a/packages/openapi-ts/src/utils/write/models.ts +++ b/packages/openapi-ts/src/utils/write/models.ts @@ -4,7 +4,7 @@ import { type Comments, compiler, type Node, TypeScriptFile } from '../../compil import type { Model, OpenApi, OperationParameter, Service } from '../../openApi'; import type { Client } from '../../types/client'; import { getConfig } from '../config'; -import { enumKey, enumName, enumUnionType, enumValue } from '../enum'; +import { enumKey, enumUnionType, enumValue, javascriptEnumName, javascriptPreservedEnumName } from '../enum'; import { escapeComment } from '../escape'; import { serviceExportedNamespace } from '../handlebars'; import { sortByName } from '../sort'; @@ -62,13 +62,17 @@ const processEnum = (client: Client, model: Model, exportType: boolean) => { } } - if (config.enums === 'javascript') { + if (['javascript', 'javascript-preserve-name'].includes(config.enums as string)) { const expression = compiler.types.object(properties, { comments, multiLine: true, unescape: true, }); - nodes = [...nodes, compiler.export.asConst(enumName(client, model.name)!, expression)]; + const preserveName = config.enums === 'javascript-preserve-name'; + const outputEnumName = preserveName + ? javascriptPreservedEnumName(client, model.name)! + : javascriptEnumName(client, model.name)!; + nodes = [...nodes, compiler.export.asConst(outputEnumName, expression)]; } return nodes; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v2/models.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v2/models.ts.snap index 82639e745..e9959f6bf 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v2/models.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v2/models.ts.snap @@ -141,6 +141,26 @@ export const EnumWithExtensionsEnum = { CUSTOM_ERROR: 500, } as const; +/** + * This is a simple enum with a non-PascalCase name. + */ +export type UPPER_SNAKE_ENUM = 'UPPER_SNAKE_0' | 'UPPER_SNAKE_1' | 'UPPER_SNAKE_2'; + +export const UPPERSNAKEENUMEnum = { + /** + * UPPER_SNAKE_0 + */ + UPPER_SNAKE_0: 'UPPER_SNAKE_0', + /** + * UPPER_SNAKE_1 + */ + UPPER_SNAKE_1: 'UPPER_SNAKE_1', + /** + * UPPER_SNAKE_2 + */ + UPPER_SNAKE_2: 'UPPER_SNAKE_2', +} as const; + /** * This is a simple array with numbers */ diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v2/schemas.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v2/schemas.ts.snap index dad60c490..23834aba3 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v2/schemas.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v2/schemas.ts.snap @@ -101,6 +101,13 @@ export const $EnumWithExtensions = { ], } as const; +export const $UPPER_SNAKE_ENUM = { + description: 'This is a simple enum with a non-PascalCase name.', + enum: ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], + 'x-enum-varnames': [0, 1, 2], + 'x-enum-descriptions': ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], +} as const; + export const $ArrayWithNumbers = { description: 'This is a simple array with numbers', type: 'array', diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3/models.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3/models.ts.snap index 61e9914f8..9d8cfcc72 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3/models.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3/models.ts.snap @@ -159,6 +159,26 @@ export const EnumWithXEnumNamesEnum = { two: 2, } as const; +/** + * This is a simple enum with a non-PascalCase name. + */ +export type UPPER_SNAKE_ENUM = 'UPPER_SNAKE_0' | 'UPPER_SNAKE_1' | 'UPPER_SNAKE_2'; + +export const UPPERSNAKEENUMEnum = { + /** + * UPPER_SNAKE_0 + */ + UPPER_SNAKE_0: 'UPPER_SNAKE_0', + /** + * UPPER_SNAKE_1 + */ + UPPER_SNAKE_1: 'UPPER_SNAKE_1', + /** + * UPPER_SNAKE_2 + */ + UPPER_SNAKE_2: 'UPPER_SNAKE_2', +} as const; + /** * This is a simple array with numbers */ diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3/schemas.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3/schemas.ts.snap index 01cc30888..d92d8fc63 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3/schemas.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3/schemas.ts.snap @@ -113,6 +113,13 @@ export const $EnumWithXEnumNames = { 'x-enumNames': ['zero', 'one', 'two'], } as const; +export const $UPPER_SNAKE_ENUM = { + description: 'This is a simple enum with a non-PascalCase name.', + enum: ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], + 'x-enum-varnames': [0, 1, 2], + 'x-enum-descriptions': ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], +} as const; + export const $ArrayWithNumbers = { description: 'This is a simple array with numbers', type: 'array', diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/models.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/models.ts.snap index b4fdd5a5c..8d7a5f404 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/models.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/models.ts.snap @@ -103,6 +103,11 @@ export type EnumWithExtensions = 200 | 400 | 500; export type EnumWithXEnumNames = 0 | 1 | 2; +/** + * This is a simple enum with a non-PascalCase name. + */ +export type UPPER_SNAKE_ENUM = 'UPPER_SNAKE_0' | 'UPPER_SNAKE_1' | 'UPPER_SNAKE_2'; + /** * This is a simple array with numbers */ diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/schemas.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/schemas.ts.snap index 01cc30888..d92d8fc63 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/schemas.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_angular/schemas.ts.snap @@ -113,6 +113,13 @@ export const $EnumWithXEnumNames = { 'x-enumNames': ['zero', 'one', 'two'], } as const; +export const $UPPER_SNAKE_ENUM = { + description: 'This is a simple enum with a non-PascalCase name.', + enum: ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], + 'x-enum-varnames': [0, 1, 2], + 'x-enum-descriptions': ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], +} as const; + export const $ArrayWithNumbers = { description: 'This is a simple array with numbers', type: 'array', diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_client/models.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_client/models.ts.snap index 233caa54d..55224084b 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3_client/models.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_client/models.ts.snap @@ -159,6 +159,26 @@ export const EnumWithXEnumNamesEnum = { two: 2, } as const; +/** + * This is a simple enum with a non-PascalCase name. + */ +export type UPPER_SNAKE_ENUM = 'UPPER_SNAKE_0' | 'UPPER_SNAKE_1' | 'UPPER_SNAKE_2'; + +export const UPPERSNAKEENUMEnum = { + /** + * UPPER_SNAKE_0 + */ + UPPER_SNAKE_0: 'UPPER_SNAKE_0', + /** + * UPPER_SNAKE_1 + */ + UPPER_SNAKE_1: 'UPPER_SNAKE_1', + /** + * UPPER_SNAKE_2 + */ + UPPER_SNAKE_2: 'UPPER_SNAKE_2', +} as const; + /** * This is a simple array with numbers */ diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_date/schemas.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_date/schemas.ts.snap index 01cc30888..d92d8fc63 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3_date/schemas.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_date/schemas.ts.snap @@ -113,6 +113,13 @@ export const $EnumWithXEnumNames = { 'x-enumNames': ['zero', 'one', 'two'], } as const; +export const $UPPER_SNAKE_ENUM = { + description: 'This is a simple enum with a non-PascalCase name.', + enum: ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], + 'x-enum-varnames': [0, 1, 2], + 'x-enum-descriptions': ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], +} as const; + export const $ArrayWithNumbers = { description: 'This is a simple array with numbers', type: 'array', diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/ApiError.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/ApiError.ts.snap new file mode 100644 index 000000000..2c11b4136 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/ApiError.ts.snap @@ -0,0 +1,21 @@ +import type { ApiRequestOptions } from './ApiRequestOptions'; +import type { ApiResult } from './ApiResult'; + +export class ApiError extends Error { + public readonly url: string; + public readonly status: number; + public readonly statusText: string; + public readonly body: unknown; + public readonly request: ApiRequestOptions; + + constructor(request: ApiRequestOptions, response: ApiResult, message: string) { + super(message); + + this.name = 'ApiError'; + this.url = response.url; + this.status = response.status; + this.statusText = response.statusText; + this.body = response.body; + this.request = request; + } +} diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/ApiRequestOptions.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/ApiRequestOptions.ts.snap new file mode 100644 index 000000000..e93003ee7 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/ApiRequestOptions.ts.snap @@ -0,0 +1,13 @@ +export type ApiRequestOptions = { + readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH'; + readonly url: string; + readonly path?: Record; + readonly cookies?: Record; + readonly headers?: Record; + readonly query?: Record; + readonly formData?: Record; + readonly body?: any; + readonly mediaType?: string; + readonly responseHeader?: string; + readonly errors?: Record; +}; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/ApiResult.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/ApiResult.ts.snap new file mode 100644 index 000000000..caa79c2ea --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/ApiResult.ts.snap @@ -0,0 +1,7 @@ +export type ApiResult = { + readonly body: TData; + readonly ok: boolean; + readonly status: number; + readonly statusText: string; + readonly url: string; +}; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/CancelablePromise.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/CancelablePromise.ts.snap new file mode 100644 index 000000000..e6b03b6a2 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/CancelablePromise.ts.snap @@ -0,0 +1,126 @@ +export class CancelError extends Error { + constructor(message: string) { + super(message); + this.name = 'CancelError'; + } + + public get isCancelled(): boolean { + return true; + } +} + +export interface OnCancel { + readonly isResolved: boolean; + readonly isRejected: boolean; + readonly isCancelled: boolean; + + (cancelHandler: () => void): void; +} + +export class CancelablePromise implements Promise { + private _isResolved: boolean; + private _isRejected: boolean; + private _isCancelled: boolean; + readonly cancelHandlers: (() => void)[]; + readonly promise: Promise; + private _resolve?: (value: T | PromiseLike) => void; + private _reject?: (reason?: unknown) => void; + + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason?: unknown) => void, + onCancel: OnCancel + ) => void + ) { + this._isResolved = false; + this._isRejected = false; + this._isCancelled = false; + this.cancelHandlers = []; + this.promise = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + + const onResolve = (value: T | PromiseLike): void => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isResolved = true; + if (this._resolve) this._resolve(value); + }; + + const onReject = (reason?: unknown): void => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isRejected = true; + if (this._reject) this._reject(reason); + }; + + const onCancel = (cancelHandler: () => void): void => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this.cancelHandlers.push(cancelHandler); + }; + + Object.defineProperty(onCancel, 'isResolved', { + get: (): boolean => this._isResolved, + }); + + Object.defineProperty(onCancel, 'isRejected', { + get: (): boolean => this._isRejected, + }); + + Object.defineProperty(onCancel, 'isCancelled', { + get: (): boolean => this._isCancelled, + }); + + return executor(onResolve, onReject, onCancel as OnCancel); + }); + } + + get [Symbol.toStringTag]() { + return 'Cancellable Promise'; + } + + public then( + onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + ): Promise { + return this.promise.then(onFulfilled, onRejected); + } + + public catch( + onRejected?: ((reason: unknown) => TResult | PromiseLike) | null + ): Promise { + return this.promise.catch(onRejected); + } + + public finally(onFinally?: (() => void) | null): Promise { + return this.promise.finally(onFinally); + } + + public cancel(): void { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isCancelled = true; + if (this.cancelHandlers.length) { + try { + for (const cancelHandler of this.cancelHandlers) { + cancelHandler(); + } + } catch (error) { + console.warn('Cancellation threw an error', error); + return; + } + } + this.cancelHandlers.length = 0; + if (this._reject) this._reject(new CancelError('Request aborted')); + } + + public get isCancelled(): boolean { + return this._isCancelled; + } +} diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/OpenAPI.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/OpenAPI.ts.snap new file mode 100644 index 000000000..64be05544 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/OpenAPI.ts.snap @@ -0,0 +1,50 @@ +import type { ApiRequestOptions } from './ApiRequestOptions'; + +type Headers = Record; +type Middleware = (value: T) => T | Promise; +type Resolver = (options: ApiRequestOptions) => Promise; + +export class Interceptors { + _fns: Middleware[]; + + constructor() { + this._fns = []; + } + + eject(fn: Middleware) { + const index = this._fns.indexOf(fn); + if (index !== -1) { + this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]; + } + } + + use(fn: Middleware) { + this._fns = [...this._fns, fn]; + } +} + +export type OpenAPIConfig = { + BASE: string; + CREDENTIALS: 'include' | 'omit' | 'same-origin'; + ENCODE_PATH?: ((path: string) => string) | undefined; + HEADERS?: Headers | Resolver | undefined; + PASSWORD?: string | Resolver | undefined; + TOKEN?: string | Resolver | undefined; + USERNAME?: string | Resolver | undefined; + VERSION: string; + WITH_CREDENTIALS: boolean; + interceptors: { request: Interceptors; response: Interceptors }; +}; + +export const OpenAPI: OpenAPIConfig = { + BASE: 'http://localhost:3000/base', + CREDENTIALS: 'include', + ENCODE_PATH: undefined, + HEADERS: undefined, + PASSWORD: undefined, + TOKEN: undefined, + USERNAME: undefined, + VERSION: '1.0', + WITH_CREDENTIALS: false, + interceptors: { request: new Interceptors(), response: new Interceptors() }, +}; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/request.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/request.ts.snap new file mode 100644 index 000000000..bee3d3694 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/core/request.ts.snap @@ -0,0 +1,351 @@ +import { ApiError } from './ApiError'; +import type { ApiRequestOptions } from './ApiRequestOptions'; +import type { ApiResult } from './ApiResult'; +import { CancelablePromise } from './CancelablePromise'; +import type { OnCancel } from './CancelablePromise'; +import type { OpenAPIConfig } from './OpenAPI'; + +export const isString = (value: unknown): value is string => { + return typeof value === 'string'; +}; + +export const isStringWithValue = (value: unknown): value is string => { + return isString(value) && value !== ''; +}; + +export const isBlob = (value: any): value is Blob => { + return value instanceof Blob; +}; + +export const isFormData = (value: unknown): value is FormData => { + return value instanceof FormData; +}; + +export const base64 = (str: string): string => { + try { + return btoa(str); + } catch (err) { + // @ts-ignore + return Buffer.from(str).toString('base64'); + } +}; + +export const getQueryString = (params: Record): string => { + const qs: string[] = []; + + const append = (key: string, value: unknown) => { + qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); + }; + + const encodePair = (key: string, value: unknown) => { + if (value === undefined || value === null) { + return; + } + + if (Array.isArray(value)) { + value.forEach(v => encodePair(key, v)); + } else if (typeof value === 'object') { + Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v)); + } else { + append(key, value); + } + }; + + Object.entries(params).forEach(([key, value]) => encodePair(key, value)); + + return qs.length ? `?${qs.join('&')}` : ''; +}; + +const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { + const encoder = config.ENCODE_PATH || encodeURI; + + const path = options.url + .replace('{api-version}', config.VERSION) + .replace(/{(.*?)}/g, (substring: string, group: string) => { + if (options.path?.hasOwnProperty(group)) { + return encoder(String(options.path[group])); + } + return substring; + }); + + const url = config.BASE + path; + return options.query ? url + getQueryString(options.query) : url; +}; + +export const getFormData = (options: ApiRequestOptions): FormData | undefined => { + if (options.formData) { + const formData = new FormData(); + + const process = (key: string, value: unknown) => { + if (isString(value) || isBlob(value)) { + formData.append(key, value); + } else { + formData.append(key, JSON.stringify(value)); + } + }; + + Object.entries(options.formData) + .filter(([, value]) => value !== undefined && value !== null) + .forEach(([key, value]) => { + if (Array.isArray(value)) { + value.forEach(v => process(key, v)); + } else { + process(key, value); + } + }); + + return formData; + } + return undefined; +}; + +type Resolver = (options: ApiRequestOptions) => Promise; + +export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => { + if (typeof resolver === 'function') { + return (resolver as Resolver)(options); + } + return resolver; +}; + +export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise => { + const [token, username, password, additionalHeaders] = await Promise.all([ + resolve(options, config.TOKEN), + resolve(options, config.USERNAME), + resolve(options, config.PASSWORD), + resolve(options, config.HEADERS), + ]); + + const headers = Object.entries({ + Accept: 'application/json', + ...additionalHeaders, + ...options.headers, + }) + .filter(([, value]) => value !== undefined && value !== null) + .reduce( + (headers, [key, value]) => ({ + ...headers, + [key]: String(value), + }), + {} as Record + ); + + if (isStringWithValue(token)) { + headers['Authorization'] = `Bearer ${token}`; + } + + if (isStringWithValue(username) && isStringWithValue(password)) { + const credentials = base64(`${username}:${password}`); + headers['Authorization'] = `Basic ${credentials}`; + } + + if (options.body !== undefined) { + 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 new Headers(headers); +}; + +export const getRequestBody = (options: ApiRequestOptions): unknown => { + if (options.body !== undefined) { + if (options.mediaType?.includes('application/json') || options.mediaType?.includes('+json')) { + return JSON.stringify(options.body); + } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) { + return options.body; + } else { + return JSON.stringify(options.body); + } + } + return undefined; +}; + +export const sendRequest = async ( + config: OpenAPIConfig, + options: ApiRequestOptions, + url: string, + body: any, + formData: FormData | undefined, + headers: Headers, + onCancel: OnCancel +): Promise => { + const controller = new AbortController(); + + let request: RequestInit = { + headers, + body: body ?? formData, + method: options.method, + signal: controller.signal, + }; + + if (config.WITH_CREDENTIALS) { + request.credentials = config.CREDENTIALS; + } + + for (const fn of config.interceptors.request._fns) { + request = await fn(request); + } + + onCancel(() => controller.abort()); + + return await fetch(url, request); +}; + +export const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => { + if (responseHeader) { + const content = response.headers.get(responseHeader); + if (isString(content)) { + return content; + } + } + return undefined; +}; + +export const getResponseBody = async (response: Response): Promise => { + if (response.status !== 204) { + try { + const contentType = response.headers.get('Content-Type'); + if (contentType) { + const binaryTypes = [ + 'application/octet-stream', + 'application/pdf', + 'application/zip', + 'audio/', + 'image/', + 'video/', + ]; + if (contentType.includes('application/json') || contentType.includes('+json')) { + return await response.json(); + } else if (binaryTypes.some(type => contentType.includes(type))) { + return await response.blob(); + } else if (contentType.includes('multipart/form-data')) { + return await response.formData(); + } else if (contentType.includes('text/')) { + return await response.text(); + } + } + } catch (error) { + console.error(error); + } + } + return undefined; +}; + +export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { + const errors: Record = { + 400: 'Bad Request', + 401: 'Unauthorized', + 402: 'Payment Required', + 403: 'Forbidden', + 404: 'Not Found', + 405: 'Method Not Allowed', + 406: 'Not Acceptable', + 407: 'Proxy Authentication Required', + 408: 'Request Timeout', + 409: 'Conflict', + 410: 'Gone', + 411: 'Length Required', + 412: 'Precondition Failed', + 413: 'Payload Too Large', + 414: 'URI Too Long', + 415: 'Unsupported Media Type', + 416: 'Range Not Satisfiable', + 417: 'Expectation Failed', + 418: 'Im a teapot', + 421: 'Misdirected Request', + 422: 'Unprocessable Content', + 423: 'Locked', + 424: 'Failed Dependency', + 425: 'Too Early', + 426: 'Upgrade Required', + 428: 'Precondition Required', + 429: 'Too Many Requests', + 431: 'Request Header Fields Too Large', + 451: 'Unavailable For Legal Reasons', + 500: 'Internal Server Error', + 501: 'Not Implemented', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + 504: 'Gateway Timeout', + 505: 'HTTP Version Not Supported', + 506: 'Variant Also Negotiates', + 507: 'Insufficient Storage', + 508: 'Loop Detected', + 510: 'Not Extended', + 511: 'Network Authentication Required', + ...options.errors, + }; + + const error = errors[result.status]; + if (error) { + throw new ApiError(options, result, error); + } + + if (!result.ok) { + const errorStatus = result.status ?? 'unknown'; + const errorStatusText = result.statusText ?? 'unknown'; + const errorBody = (() => { + try { + return JSON.stringify(result.body, null, 2); + } catch (e) { + return undefined; + } + })(); + + throw new ApiError( + options, + result, + `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` + ); + } +}; + +/** + * Request method + * @param config The OpenAPI configuration object + * @param options The request options from the service + * @returns CancelablePromise + * @throws ApiError + */ +export const request = (config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise => { + return new CancelablePromise(async (resolve, reject, onCancel) => { + try { + const url = getUrl(config, options); + const formData = getFormData(options); + const body = getRequestBody(options); + const headers = await getHeaders(config, options); + + if (!onCancel.isCancelled) { + let response = await sendRequest(config, options, url, body, formData, headers, onCancel); + + for (const fn of config.interceptors.response._fns) { + response = await fn(response); + } + + const responseBody = await getResponseBody(response); + const responseHeader = getResponseHeader(response, options.responseHeader); + + const result: ApiResult = { + url, + ok: response.ok, + status: response.status, + statusText: response.statusText, + body: responseHeader ?? responseBody, + }; + + catchErrorCodes(options, result); + + resolve(result.body); + } + } catch (error) { + reject(error); + } + }); +}; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/index.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/index.ts.snap new file mode 100644 index 000000000..c14945757 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/index.ts.snap @@ -0,0 +1,8 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export { ApiError } from './core/ApiError'; +export { CancelablePromise, CancelError } from './core/CancelablePromise'; +export { OpenAPI, type OpenAPIConfig } from './core/OpenAPI'; +export * from './models'; +export * from './schemas'; +export * from './services'; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/models.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/models.ts.snap new file mode 100644 index 000000000..5c3285758 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/models.ts.snap @@ -0,0 +1,1445 @@ +// This file is auto-generated by @hey-api/openapi-ts + + +/** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ +export type CommentWithBreaks = number; + +/** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ +export type CommentWithBackticks = number; + +/** + * Testing backticks and quotes in string: `backticks`, 'quotes', "double quotes" and ```multiple backticks``` should work + */ +export type CommentWithBackticksAndQuotes = number; + +/** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ +export type CommentWithSlashes = number; + +/** + * Testing expression placeholders in string: ${expression} should work + */ +export type CommentWithExpressionPlaceholders = number; + +/** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ +export type CommentWithQuotes = number; + +/** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ +export type CommentWithReservedCharacters = number; + +/** + * This is a simple number + */ +export type SimpleInteger = number; + +/** + * This is a simple boolean + */ +export type SimpleBoolean = boolean; + +/** + * This is a simple string + */ +export type SimpleString = string; + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export type NonAsciiStringæøåÆØÅöôêÊ字符串 = string; + +/** + * This is a simple file + */ +export type SimpleFile = (Blob | File); + +/** + * This is a simple reference + */ +export type SimpleReference = ModelWithString; + +/** + * This is a simple string + */ +export type SimpleStringWithPattern = string | null; + +/** + * This is a simple enum with strings + */ +export type EnumWithStrings = 'Success' | 'Warning' | 'Error' | '\'Single Quote\'' | '"Double Quotes"' | 'Non-ascii: øæåôöØÆÅÔÖ字符串'; + +export const EnumWithStrings = { + SUCCESS: 'Success', + WARNING: 'Warning', + ERROR: 'Error', + _SINGLE_QUOTE_: "\'Single Quote\'", + _DOUBLE_QUOTES_: '"Double Quotes"', + 'NON_ASCII__ØÆÅÔÖ_ØÆÅÔÖ字符串': 'Non-ascii: øæåôöØÆÅÔÖ字符串' +} as const; + +export type EnumWithReplacedCharacters = '\'Single Quote\'' | '"Double Quotes"' | 'øæåôöØÆÅÔÖ字符串' | 3.1 | ''; + +export const EnumWithReplacedCharacters = { + _SINGLE_QUOTE_: "\'Single Quote\'", + _DOUBLE_QUOTES_: '"Double Quotes"', + 'ØÆÅÔÖ_ØÆÅÔÖ字符串': 'øæåôöØÆÅÔÖ字符串', + '_3.1': 3.1, + EMPTY_STRING: '' +} as const; + +/** + * This is a simple enum with numbers + */ +export type EnumWithNumbers = 1 | 2 | 3 | 1.1 | 1.2 | 1.3 | 100 | 200 | 300 | -100 | -200 | -300 | -1.1 | -1.2 | -1.3; + +export const EnumWithNumbers = { + '_1': 1, + '_2': 2, + '_3': 3, + '_1.1': 1.1, + '_1.2': 1.2, + '_1.3': 1.3, + '_100': 100, + '_200': 200, + '_300': 300, + '_-100': -100, + '_-200': -200, + '_-300': -300, + '_-1.1': -1.1, + '_-1.2': -1.2, + '_-1.3': -1.3 +} as const; + +/** + * Success=1,Warning=2,Error=3 + */ +export type EnumFromDescription = number; + +/** + * This is a simple enum with numbers + */ +export type EnumWithExtensions = 200 | 400 | 500; + +export const EnumWithExtensions = { + /** + * Used when the status of something is successful + */ + CUSTOM_SUCCESS: 200, + /** + * Used when the status of something has a warning + */ + CUSTOM_WARNING: 400, + /** + * Used when the status of something has an error + */ + CUSTOM_ERROR: 500 +} as const; + +export type EnumWithXEnumNames = 0 | 1 | 2; + +export const EnumWithXEnumNames = { + zero: 0, + one: 1, + two: 2 +} as const; + +/** + * This is a simple enum with a non-PascalCase name. + */ +export type UPPER_SNAKE_ENUM = 'UPPER_SNAKE_0' | 'UPPER_SNAKE_1' | 'UPPER_SNAKE_2'; + +export const UPPER_SNAKE_ENUM = { + /** + * UPPER_SNAKE_0 + */ + UPPER_SNAKE_0: 'UPPER_SNAKE_0', + /** + * UPPER_SNAKE_1 + */ + UPPER_SNAKE_1: 'UPPER_SNAKE_1', + /** + * UPPER_SNAKE_2 + */ + UPPER_SNAKE_2: 'UPPER_SNAKE_2' +} as const; + +/** + * This is a simple array with numbers + */ +export type ArrayWithNumbers = Array<(number)>; + +/** + * This is a simple array with booleans + */ +export type ArrayWithBooleans = Array<(boolean)>; + +/** + * This is a simple array with strings + */ +export type ArrayWithStrings = Array<(string)>; + +/** + * This is a simple array with references + */ +export type ArrayWithReferences = Array; + +/** + * This is a simple array containing an array + */ +export type ArrayWithArray = Array>; + +/** + * This is a simple array with properties + */ +export type ArrayWithProperties = Array<{ + foo?: string; + bar?: string; +}>; + +/** + * This is a simple array with any of properties + */ +export type ArrayWithAnyOfProperties = Array<({ + foo?: string; +} | { + bar?: string; +})>; + +export type AnyOfAnyAndNull = { + data?: unknown | null; +}; + +/** + * This is a simple array with any of properties + */ +export type AnyOfArrays = { + results?: Array<({ + foo?: string; +} | { + bar?: string; +})>; +}; + +/** + * This is a string dictionary + */ +export type DictionaryWithString = Record; + +export type DictionaryWithPropertiesAndAdditionalProperties = { + foo?: string; + [key: string]: (string) | undefined; +}; + +/** + * This is a string reference + */ +export type DictionaryWithReference = Record; + +/** + * This is a complex dictionary + */ +export type DictionaryWithArray = Record>; + +/** + * This is a string dictionary + */ +export type DictionaryWithDictionary = Record>; + +/** + * This is a complex dictionary + */ +export type DictionaryWithProperties = Record; + +/** + * This is a model with one number property + */ +export type ModelWithInteger = { + /** + * This is a simple number property + */ + prop?: number; +}; + +/** + * This is a model with one boolean property + */ +export type ModelWithBoolean = { + /** + * This is a simple boolean property + */ + prop?: boolean; +}; + +/** + * This is a model with one string property + */ +export type ModelWithString = { + /** + * This is a simple string property + */ + prop?: string; +}; + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export type Model_From_Zendesk = string; + +/** + * This is a model with one string property + */ +export type ModelWithNullableString = { + /** + * This is a simple string property + */ + nullableProp1?: string | null; + /** + * This is a simple string property + */ + nullableRequiredProp1: string | null; + /** + * This is a simple string property + */ + nullableProp2?: string | null; + /** + * This is a simple string property + */ + nullableRequiredProp2: string | null; + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; +}; + +export const 'foo_bar-enum' = { + SUCCESS: 'Success', + WARNING: 'Warning', + ERROR: 'Error', + 'ØÆÅ字符串': 'ØÆÅ字符串' +} as const; + +/** + * This is a model with one enum + */ +export type ModelWithEnum = { + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; + /** + * These are the HTTP error code enums + */ + statusCode?: '100' | '200 FOO' | '300 FOO_BAR' | '400 foo-bar' | '500 foo.bar' | '600 foo&bar'; + /** + * Simple boolean enum + */ + bool?: boolean; +}; + +export const 'foo_bar-enum' = { + SUCCESS: 'Success', + WARNING: 'Warning', + ERROR: 'Error', + 'ØÆÅ字符串': 'ØÆÅ字符串' +} as const; + +export const statusCode = { + _100: '100', + _200_FOO: '200 FOO', + _300_FOO_BAR: '300 FOO_BAR', + _400_FOO_BAR: '400 foo-bar', + _500_FOO_BAR: '500 foo.bar', + _600_FOO_BAR: '600 foo&bar' +} as const; + +/** + * This is a model with one enum with escaped name + */ +export type ModelWithEnumWithHyphen = { + 'foo-bar-baz-qux'?: '3.0'; +}; + +export const 'foo-bar-baz-qux' = { + _3_0: '3.0' +} as const; + +/** + * This is a model with one enum + */ +export type ModelWithEnumFromDescription = { + /** + * Success=1,Warning=2,Error=3 + */ + test?: number; +}; + +/** + * This is a model with nested enums + */ +export type ModelWithNestedEnums = { + dictionaryWithEnum?: Record; + dictionaryWithEnumFromDescription?: Record; + arrayWithEnum?: Array<('Success' | 'Warning' | 'Error')>; + arrayWithDescription?: Array<(number)>; + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; +}; + +export const 'foo_bar-enum' = { + SUCCESS: 'Success', + WARNING: 'Warning', + ERROR: 'Error', + 'ØÆÅ字符串': 'ØÆÅ字符串' +} as const; + +/** + * This is a model with one property containing a reference + */ +export type ModelWithReference = { + prop?: ModelWithProperties; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArrayReadOnlyAndWriteOnly = { + prop?: Array; + propWithFile?: Array<((Blob | File))>; + propWithNumber?: Array<(number)>; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArray = { + prop?: Array; + propWithFile?: Array<((Blob | File))>; + propWithNumber?: Array<(number)>; +}; + +/** + * This is a model with one property containing a dictionary + */ +export type ModelWithDictionary = { + prop?: Record; +}; + +/** + * This is a deprecated model with a deprecated property + * @deprecated + */ +export type DeprecatedModel = { + /** + * This is a deprecated property + * @deprecated + */ + prop?: string; +}; + +/** + * This is a model with one property containing a circular reference + */ +export type ModelWithCircularReference = { + prop?: ModelWithCircularReference; +}; + +/** + * This is a model with one property with a 'one of' relationship + */ +export type CompositionWithOneOf = { + propA?: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; +}; + +/** + * This is a model with one property with a 'one of' relationship where the options are not $ref + */ +export type CompositionWithOneOfAnonymous = { + propA?: { + propA?: string; +} | string | number; +}; + +/** + * Circle + */ +export type ModelCircle = { + kind: 'circle'; + radius?: number; +}; + +/** + * Square + */ +export type ModelSquare = { + kind: 'square'; + sideLength?: number; +}; + +/** + * This is a model with one property with a 'one of' relationship where the options are not $ref + */ +export type CompositionWithOneOfDiscriminator = ModelCircle | ModelSquare; + +/** + * This is a model with one property with a 'any of' relationship + */ +export type CompositionWithAnyOf = { + propA?: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; +}; + +/** + * This is a model with one property with a 'any of' relationship where the options are not $ref + */ +export type CompositionWithAnyOfAnonymous = { + propA?: { + propA?: string; +} | string | number; +}; + +/** + * This is a model with nested 'any of' property with a type null + */ +export type CompositionWithNestedAnyAndTypeNull = { + propA?: Array<(ModelWithDictionary | null)> | Array<(ModelWithArray | null)>; +}; + +export type Enum1 = 'Bird' | 'Dog'; + +export const Enum1 = { + BIRD: 'Bird', + DOG: 'Dog' +} as const; + +export type ConstValue = "ConstValue"; + +/** + * This is a model with one property with a 'any of' relationship where the options are not $ref + */ +export type CompositionWithNestedAnyOfAndNull = { + propA?: Array<(Enum1 | ConstValue)> | null; +}; + +/** + * This is a model with one property with a 'one of' relationship + */ +export type CompositionWithOneOfAndNullable = { + propA?: { + boolean?: boolean; +} | ModelWithEnum | ModelWithArray | ModelWithDictionary | null; +}; + +/** + * This is a model that contains a simple dictionary within composition + */ +export type CompositionWithOneOfAndSimpleDictionary = { + propA?: boolean | Record; +}; + +/** + * This is a model that contains a dictionary of simple arrays within composition + */ +export type CompositionWithOneOfAndSimpleArrayDictionary = { + propA?: boolean | Record>; +}; + +/** + * This is a model that contains a dictionary of complex arrays (composited) within composition + */ +export type CompositionWithOneOfAndComplexArrayDictionary = { + propA?: boolean | Record>; +}; + +/** + * This is a model with one property with a 'all of' relationship + */ +export type CompositionWithAllOfAndNullable = { + propA?: ({ + boolean?: boolean; +} & ModelWithEnum & ModelWithArray & ModelWithDictionary) | null; +}; + +/** + * This is a model with one property with a 'any of' relationship + */ +export type CompositionWithAnyOfAndNullable = { + propA?: { + boolean?: boolean; +} | ModelWithEnum | ModelWithArray | ModelWithDictionary | null; +}; + +/** + * This is a base model with two simple optional properties + */ +export type CompositionBaseModel = { + firstName?: string; + lastname?: string; +}; + +/** + * This is a model that extends the base model + */ +export type CompositionExtendedModel = CompositionBaseModel & { + firstName: string; + lastname: string; + age: number; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithProperties = { + required: string; + readonly requiredAndReadOnly: string; + requiredAndNullable: string | null; + string?: string; + number?: number; + boolean?: boolean; + reference?: ModelWithString; + 'property with space'?: string; + default?: string; + try?: string; + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithNestedProperties = { + readonly first: { + readonly second: { + readonly third: string | null; + } | null; + } | null; +}; + +/** + * This is a model with duplicated properties + */ +export type ModelWithDuplicateProperties = { + prop?: ModelWithString; +}; + +/** + * This is a model with ordered properties + */ +export type ModelWithOrderedProperties = { + zebra?: string; + apple?: string; + hawaii?: string; +}; + +/** + * This is a model with duplicated imports + */ +export type ModelWithDuplicateImports = { + propA?: ModelWithString; + propB?: ModelWithString; + propC?: ModelWithString; +}; + +/** + * This is a model that extends another model + */ +export type ModelThatExtends = ModelWithString & { + propExtendsA?: string; + propExtendsB?: ModelWithString; +}; + +/** + * This is a model that extends another model + */ +export type ModelThatExtendsExtends = ModelWithString & ModelThatExtends & { + propExtendsC?: string; + propExtendsD?: ModelWithString; +}; + +/** + * This is a model that contains a some patterns + */ +export type ModelWithPattern = { + key: string; + name: string; + readonly enabled?: boolean; + readonly modified?: string; + id?: string; + text?: string; + patternWithSingleQuotes?: string; + patternWithNewline?: string; + patternWithBacktick?: string; +}; + +export type File = { + readonly id?: string; + readonly updated_at?: string; + readonly created_at?: string; + mime: string; + readonly file?: string; +}; + +export type _default = { + name?: string; +}; + +export type Pageable = { + page?: number; + size?: number; + sort?: Array<(string)>; +}; + +/** + * This is a free-form object without additionalProperties. + */ +export type FreeFormObjectWithoutAdditionalProperties = Record; + +/** + * This is a free-form object with additionalProperties: true. + */ +export type FreeFormObjectWithAdditionalPropertiesEqTrue = Record; + +/** + * This is a free-form object with additionalProperties: {}. + */ +export type FreeFormObjectWithAdditionalPropertiesEqEmptyObject = Record; + +export type ModelWithConst = { + String?: "String"; + number?: 0; + null?: null; + withType?: "Some string"; +}; + +/** + * This is a model with one property and additionalProperties: true + */ +export type ModelWithAdditionalPropertiesEqTrue = { + /** + * This is a simple string property + */ + prop?: string; + [key: string]: unknown; +}; + +export type NestedAnyOfArraysNullable = { + nullableArray?: Array<(string | boolean)> | null; +}; + +export type CompositionWithOneOfAndProperties = { + foo: SimpleParameter; + baz: number | null; + qux: number; +} | { + bar: NonAsciiStringæøåÆØÅöôêÊ字符串; + baz: number | null; + qux: number; +}; + +/** + * An object that can be null + */ +export type NullableObject = { + foo?: string; +} | null; + +export type ModelWithNullableObject = { + data?: NullableObject; +}; + +export type ModelWithOneOfEnum = { + foo: 'Bar'; +} | { + foo: 'Baz'; +} | { + foo: 'Qux'; +} | { + content: string; + foo: 'Quux'; +} | { + content: [ + string, + string + ]; + foo: 'Corge'; +}; + +export const foo = { + BAR: 'Bar' +} as const; + +export type ModelWithNestedArrayEnumsDataFoo = 'foo' | 'bar'; + +export const ModelWithNestedArrayEnumsDataFoo = { + FOO: 'foo', + BAR: 'bar' +} as const; + +export type ModelWithNestedArrayEnumsDataBar = 'baz' | 'qux'; + +export const ModelWithNestedArrayEnumsDataBar = { + BAZ: 'baz', + QUX: 'qux' +} as const; + +export type ModelWithNestedArrayEnumsData = { + foo?: Array; + bar?: Array; +}; + +export type ModelWithNestedArrayEnums = { + array_strings?: Array<(string)>; + data?: ModelWithNestedArrayEnumsData; +}; + +export type ModelWithNestedCompositionEnums = { + foo?: ModelWithNestedArrayEnumsDataFoo; +}; + +export type ModelWithReadOnlyAndWriteOnly = { + foo: string; + readonly bar: string; + baz: string; +}; + +export type ModelWithConstantSizeArray = [ + number, + number +]; + +export type ModelWithAnyOfConstantSizeArray = [ + number | string, + number | string, + number | string +]; + +export type ModelWithAnyOfConstantSizeArrayNullable = [ + number | null | string, + number | null | string, + number | null | string +]; + +export type ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = [ + number | string, + number | string +]; + +export type ModelWithAnyOfConstantSizeArrayAndIntersect = [ + number & string, + number & string +]; + +/** + * This is a reusable parameter + */ +export type SimpleParameter = string; + +export type $OpenApiTs = { + '/api/v{api-version}/no-tag': { + post: { + req: { + requestBody: ModelWithReadOnlyAndWriteOnly | ModelWithArrayReadOnlyAndWriteOnly; + }; + res: { + 200: ModelWithReadOnlyAndWriteOnly; + }; + }; + }; + '/api/v{api-version}/simple/$count': { + get: { + res: { + /** + * Success + */ + 200: Model_From_Zendesk; + }; + }; + }; + '/api/v{api-version}/foo/{foo}/bar/{bar}': { + delete: { + req: { + /** + * bar in method + */ + bar: string; + /** + * foo in method + */ + foo: string; + }; + }; + }; + '/api/v{api-version}/parameters/{parameterPath}': { + post: { + req: { + fooAllOfEnum: ModelWithNestedArrayEnumsDataFoo; + fooRefEnum?: ModelWithNestedArrayEnumsDataFoo; + /** + * This is the parameter that goes into the cookie + */ + parameterCookie: string | null; + /** + * This is the parameter that goes into the form data + */ + parameterForm: string | null; + /** + * This is the parameter that goes into the header + */ + parameterHeader: string | null; + /** + * This is the parameter that goes into the path + */ + parameterPath: string | null; + /** + * This is the parameter that goes into the query params + */ + parameterQuery: string | null; + /** + * This is the parameter that goes into the body + */ + requestBody: ModelWithString | null; + }; + }; + }; + '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}': { + post: { + req: { + /** + * This is the parameter with a reserved keyword + */ + _default?: string; + /** + * This is the parameter that goes into the cookie + */ + parameterCookie: string | null; + /** + * This is the parameter that goes into the request form data + */ + parameterForm: string | null; + /** + * This is the parameter that goes into the request header + */ + parameterHeader: string | null; + /** + * This is the parameter that goes into the path + */ + parameterPath1?: string; + /** + * This is the parameter that goes into the path + */ + parameterPath2?: string; + /** + * This is the parameter that goes into the path + */ + parameterPath3?: string; + /** + * This is the parameter that goes into the request query params + */ + parameterQuery: string | null; + /** + * This is the parameter that goes into the body + */ + requestBody: ModelWithString | null; + }; + }; + }; + '/api/v{api-version}/parameters/': { + get: { + req: { + /** + * This is an optional parameter + */ + parameter?: string; + /** + * This is a required parameter + */ + requestBody: ModelWithOneOfEnum; + }; + }; + post: { + req: { + /** + * This is a required parameter + */ + parameter: Pageable; + /** + * This is an optional parameter + */ + requestBody?: ModelWithString; + }; + }; + }; + '/api/v{api-version}/descriptions/': { + post: { + req: { + /** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ + parameterWithBackticks?: unknown; + /** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ + parameterWithBreaks?: unknown; + /** + * Testing expression placeholders in string: ${expression} should work + */ + parameterWithExpressionPlaceholders?: unknown; + /** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ + parameterWithQuotes?: unknown; + /** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ + parameterWithReservedCharacters?: unknown; + /** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ + parameterWithSlashes?: unknown; + }; + }; + }; + '/api/v{api-version}/parameters/deprecated': { + post: { + req: { + /** + * This parameter is deprecated + * @deprecated + */ + parameter: DeprecatedModel | null; + }; + }; + }; + '/api/v{api-version}/requestBody/': { + post: { + req: { + /** + * A reusable request body + */ + foo?: ModelWithString; + /** + * This is a reusable parameter + */ + parameter?: string; + }; + }; + }; + '/api/v{api-version}/formData/': { + post: { + req: { + /** + * A reusable request body + */ + formData?: ModelWithString; + /** + * This is a reusable parameter + */ + parameter?: string; + }; + }; + }; + '/api/v{api-version}/defaults': { + get: { + req: { + /** + * This is a simple boolean with default value + */ + parameterBoolean?: boolean | null; + /** + * This is a simple enum with default value + */ + parameterEnum?: 'Success' | 'Warning' | 'Error'; + /** + * This is a simple model with default value + */ + parameterModel?: ModelWithString | null; + /** + * This is a simple number with default value + */ + parameterNumber?: number | null; + /** + * This is a simple string with default value + */ + parameterString?: string | null; + }; + }; + post: { + req: { + /** + * This is a simple boolean that is optional with default value + */ + parameterBoolean?: boolean; + /** + * This is a simple enum that is optional with default value + */ + parameterEnum?: 'Success' | 'Warning' | 'Error'; + /** + * This is a simple model that is optional with default value + */ + parameterModel?: ModelWithString; + /** + * This is a simple number that is optional with default value + */ + parameterNumber?: number; + /** + * This is a simple string that is optional with default value + */ + parameterString?: string; + }; + }; + put: { + req: { + /** + * This is a optional string with default + */ + parameterOptionalStringWithDefault?: string; + /** + * This is a optional string with empty default + */ + parameterOptionalStringWithEmptyDefault?: string; + /** + * This is a optional string with no default + */ + parameterOptionalStringWithNoDefault?: string; + /** + * This is a string that can be null with default + */ + parameterStringNullableWithDefault?: string | null; + /** + * This is a string that can be null with no default + */ + parameterStringNullableWithNoDefault?: string | null; + /** + * This is a string with default + */ + parameterStringWithDefault: string; + /** + * This is a string with empty default + */ + parameterStringWithEmptyDefault: string; + /** + * This is a string with no default + */ + parameterStringWithNoDefault: string; + }; + }; + }; + '/api/v{api-version}/no-content': { + get: { + res: { + /** + * Success + */ + 204: void; + }; + }; + }; + '/api/v{api-version}/multiple-tags/response-and-no-content': { + get: { + res: { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: void; + }; + }; + }; + '/api/v{api-version}/response': { + get: { + res: { + 200: ModelWithString; + }; + }; + post: { + res: { + /** + * Message for default response + */ + 200: ModelWithString; + }; + }; + put: { + res: { + /** + * Message for default response + */ + 200: ModelWithString; + /** + * Message for 201 response + */ + 201: ModelThatExtends; + /** + * Message for 202 response + */ + 202: ModelThatExtendsExtends; + }; + }; + }; + '/api/v{api-version}/multiple-tags/a': { + get: { + res: { + /** + * Success + */ + 204: void; + }; + }; + }; + '/api/v{api-version}/multiple-tags/b': { + get: { + res: { + /** + * Success + */ + 204: void; + }; + }; + }; + '/api/v{api-version}/collectionFormat': { + get: { + req: { + /** + * This is an array parameter that is sent as csv format (comma-separated values) + */ + parameterArrayCsv: Array<(string)> | null; + /** + * This is an array parameter that is sent as multi format (multiple parameter instances) + */ + parameterArrayMulti: Array<(string)> | null; + /** + * This is an array parameter that is sent as pipes format (pipe-separated values) + */ + parameterArrayPipes: Array<(string)> | null; + /** + * This is an array parameter that is sent as ssv format (space-separated values) + */ + parameterArraySsv: Array<(string)> | null; + /** + * This is an array parameter that is sent as tsv format (tab-separated values) + */ + parameterArrayTsv: Array<(string)> | null; + }; + }; + }; + '/api/v{api-version}/types': { + get: { + req: { + /** + * This is a number parameter + */ + id?: number; + /** + * This is an array parameter + */ + parameterArray: Array<(string)> | null; + /** + * This is a boolean parameter + */ + parameterBoolean: boolean | null; + /** + * This is a dictionary parameter + */ + parameterDictionary: Record | null; + /** + * This is an enum parameter + */ + parameterEnum: 'Success' | 'Warning' | 'Error' | null; + /** + * This is a number parameter + */ + parameterNumber: number; + /** + * This is an object parameter + */ + parameterObject: Record | null; + /** + * This is a string parameter + */ + parameterString: string | null; + }; + res: { + /** + * Response is a simple number + */ + 200: number; + /** + * Response is a simple string + */ + 201: string; + /** + * Response is a simple boolean + */ + 202: boolean; + /** + * Response is a simple object + */ + 203: Record; + }; + }; + }; + '/api/v{api-version}/upload': { + post: { + req: { + /** + * Supply a file reference for upload + */ + file: (Blob | File); + }; + res: { + 200: boolean; + }; + }; + }; + '/api/v{api-version}/file/{id}': { + get: { + req: { + id: string; + }; + res: { + /** + * Success + */ + 200: (Blob | File); + }; + }; + }; + '/api/v{api-version}/complex': { + get: { + req: { + /** + * Parameter containing object + */ + parameterObject: { + first?: { + second?: { + third?: string; + }; + }; + }; + /** + * Parameter containing reference + */ + parameterReference: ModelWithString; + }; + res: { + /** + * Successful response + */ + 200: Array; + }; + }; + }; + '/api/v{api-version}/complex/{id}': { + put: { + req: { + id: number; + requestBody?: { + readonly key: string | null; + name: string | null; + enabled?: boolean; + readonly type: 'Monkey' | 'Horse' | 'Bird'; + listOfModels?: Array | null; + listOfStrings?: Array<(string)> | null; + parameters: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; + readonly user?: { + readonly id?: number; + readonly name?: string | null; + }; + }; + }; + res: { + /** + * Success + */ + 200: ModelWithString; + }; + }; + }; + '/api/v{api-version}/multipart': { + post: { + req: { + formData?: { + content?: (Blob | File); + data?: ModelWithString | null; + }; + }; + }; + get: { + res: { + /** + * OK + */ + 200: { + file?: (Blob | File); + metadata?: { + foo?: string; + bar?: string; + }; + }; + }; + }; + }; + '/api/v{api-version}/header': { + post: { + res: { + /** + * Successful response + */ + 200: string; + }; + }; + }; + '/api/v{api-version}/error': { + post: { + req: { + /** + * Status code to return + */ + status: number; + }; + res: { + /** + * Custom message: Successful response + */ + 200: any; + }; + }; + }; + '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串': { + post: { + req: { + /** + * Dummy input param + */ + nonAsciiParamæøåÆøÅöôêÊ: number; + }; + res: { + /** + * Successful response + */ + 200: Array; + }; + }; + }; +}; \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/schemas.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/schemas.ts.snap new file mode 100644 index 000000000..d92d8fc63 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/schemas.ts.snap @@ -0,0 +1,1635 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export const $CommentWithBreaks = { + description: `Testing multiline comments in string: First line +Second line + +Fourth line`, + type: 'integer', +} as const; + +export const $CommentWithBackticks = { + description: 'Testing backticks in string: `backticks` and ```multiple backticks``` should work', + type: 'integer', +} as const; + +export const $CommentWithBackticksAndQuotes = { + description: `Testing backticks and quotes in string: \`backticks\`, 'quotes', "double quotes" and \`\`\`multiple backticks\`\`\` should work`, + type: 'integer', +} as const; + +export const $CommentWithSlashes = { + description: 'Testing slashes in string: \backwards\\ and /forwards/// should work', + type: 'integer', +} as const; + +export const $CommentWithExpressionPlaceholders = { + description: 'Testing expression placeholders in string: ${expression} should work', + type: 'integer', +} as const; + +export const $CommentWithQuotes = { + description: `Testing quotes in string: 'single quote''' and "double quotes""" should work`, + type: 'integer', +} as const; + +export const $CommentWithReservedCharacters = { + description: 'Testing reserved characters in string: /* inline */ and /** inline **/ should work', + type: 'integer', +} as const; + +export const $SimpleInteger = { + description: 'This is a simple number', + type: 'integer', +} as const; + +export const $SimpleBoolean = { + description: 'This is a simple boolean', + type: 'boolean', +} as const; + +export const $SimpleString = { + description: 'This is a simple string', + type: 'string', +} as const; + +export const $NonAsciiStringæøåÆØÅöôêÊ字符串 = { + description: 'A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串)', + type: 'string', +} as const; + +export const $SimpleFile = { + description: 'This is a simple file', + type: 'file', +} as const; + +export const $SimpleReference = { + description: 'This is a simple reference', + $ref: '#/components/schemas/ModelWithString', +} as const; + +export const $SimpleStringWithPattern = { + description: 'This is a simple string', + type: 'string', + nullable: true, + maxLength: 64, + pattern: '^[a-zA-Z0-9_]*$', +} as const; + +export const $EnumWithStrings = { + description: 'This is a simple enum with strings', + enum: ['Success', 'Warning', 'Error', "'Single Quote'", '"Double Quotes"', 'Non-ascii: øæåôöØÆÅÔÖ字符串'], +} as const; + +export const $EnumWithReplacedCharacters = { + enum: ["'Single Quote'", '"Double Quotes"', 'øæåôöØÆÅÔÖ字符串', 3.1, ''], + type: 'string', +} as const; + +export const $EnumWithNumbers = { + description: 'This is a simple enum with numbers', + enum: [1, 2, 3, 1.1, 1.2, 1.3, 100, 200, 300, -100, -200, -300, -1.1, -1.2, -1.3], + default: 200, +} as const; + +export const $EnumFromDescription = { + description: 'Success=1,Warning=2,Error=3', + type: 'number', +} as const; + +export const $EnumWithExtensions = { + description: 'This is a simple enum with numbers', + enum: [200, 400, 500], + 'x-enum-varnames': ['CUSTOM_SUCCESS', 'CUSTOM_WARNING', 'CUSTOM_ERROR'], + 'x-enum-descriptions': [ + 'Used when the status of something is successful', + 'Used when the status of something has a warning', + 'Used when the status of something has an error', + ], +} as const; + +export const $EnumWithXEnumNames = { + enum: [0, 1, 2], + 'x-enumNames': ['zero', 'one', 'two'], +} as const; + +export const $UPPER_SNAKE_ENUM = { + description: 'This is a simple enum with a non-PascalCase name.', + enum: ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], + 'x-enum-varnames': [0, 1, 2], + 'x-enum-descriptions': ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], +} as const; + +export const $ArrayWithNumbers = { + description: 'This is a simple array with numbers', + type: 'array', + items: { + type: 'integer', + }, +} as const; + +export const $ArrayWithBooleans = { + description: 'This is a simple array with booleans', + type: 'array', + items: { + type: 'boolean', + }, +} as const; + +export const $ArrayWithStrings = { + description: 'This is a simple array with strings', + type: 'array', + items: { + type: 'string', + }, + default: ['test'], +} as const; + +export const $ArrayWithReferences = { + description: 'This is a simple array with references', + type: 'array', + items: { + $ref: '#/components/schemas/ModelWithString', + }, +} as const; + +export const $ArrayWithArray = { + description: 'This is a simple array containing an array', + type: 'array', + items: { + type: 'array', + items: { + $ref: '#/components/schemas/ModelWithString', + }, + }, +} as const; + +export const $ArrayWithProperties = { + description: 'This is a simple array with properties', + type: 'array', + items: { + type: 'object', + properties: { + foo: { + type: 'string', + }, + bar: { + type: 'string', + }, + }, + }, +} as const; + +export const $ArrayWithAnyOfProperties = { + description: 'This is a simple array with any of properties', + type: 'array', + items: { + anyOf: [ + { + type: 'object', + properties: { + foo: { + type: 'string', + default: 'test', + }, + }, + }, + { + type: 'object', + properties: { + bar: { + type: 'string', + }, + }, + }, + ], + }, +} as const; + +export const $AnyOfAnyAndNull = { + type: 'object', + properties: { + data: { + anyOf: [ + {}, + { + type: 'null', + }, + ], + }, + }, +} as const; + +export const $AnyOfArrays = { + description: 'This is a simple array with any of properties', + type: 'object', + properties: { + results: { + items: { + anyOf: [ + { + type: 'object', + properties: { + foo: { + type: 'string', + }, + }, + }, + { + type: 'object', + properties: { + bar: { + type: 'string', + }, + }, + }, + ], + }, + type: 'array', + }, + }, +} as const; + +export const $DictionaryWithString = { + description: 'This is a string dictionary', + type: 'object', + additionalProperties: { + type: 'string', + }, +} as const; + +export const $DictionaryWithPropertiesAndAdditionalProperties = { + type: 'object', + properties: { + foo: { + type: 'string', + }, + }, + additionalProperties: { + type: 'string', + }, +} as const; + +export const $DictionaryWithReference = { + description: 'This is a string reference', + type: 'object', + additionalProperties: { + $ref: '#/components/schemas/ModelWithString', + }, +} as const; + +export const $DictionaryWithArray = { + description: 'This is a complex dictionary', + type: 'object', + additionalProperties: { + type: 'array', + items: { + $ref: '#/components/schemas/ModelWithString', + }, + }, +} as const; + +export const $DictionaryWithDictionary = { + description: 'This is a string dictionary', + type: 'object', + additionalProperties: { + type: 'object', + additionalProperties: { + type: 'string', + }, + }, +} as const; + +export const $DictionaryWithProperties = { + description: 'This is a complex dictionary', + type: 'object', + additionalProperties: { + type: 'object', + properties: { + foo: { + type: 'string', + }, + bar: { + type: 'string', + }, + }, + }, +} as const; + +export const $ModelWithInteger = { + description: 'This is a model with one number property', + type: 'object', + properties: { + prop: { + description: 'This is a simple number property', + type: 'integer', + }, + }, +} as const; + +export const $ModelWithBoolean = { + description: 'This is a model with one boolean property', + type: 'object', + properties: { + prop: { + description: 'This is a simple boolean property', + type: 'boolean', + }, + }, +} as const; + +export const $ModelWithString = { + description: 'This is a model with one string property', + type: 'object', + properties: { + prop: { + description: 'This is a simple string property', + type: 'string', + }, + }, +} as const; + +export const $Model_From_Zendesk = { + description: `\`Comment\` or \`VoiceComment\`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets)`, + type: 'string', +} as const; + +export const $ModelWithNullableString = { + description: 'This is a model with one string property', + type: 'object', + required: ['nullableRequiredProp1', 'nullableRequiredProp2'], + properties: { + nullableProp1: { + description: 'This is a simple string property', + type: 'string', + nullable: true, + }, + nullableRequiredProp1: { + description: 'This is a simple string property', + type: 'string', + nullable: true, + }, + nullableProp2: { + description: 'This is a simple string property', + type: ['string', 'null'], + }, + nullableRequiredProp2: { + description: 'This is a simple string property', + type: ['string', 'null'], + }, + 'foo_bar-enum': { + description: 'This is a simple enum with strings', + enum: ['Success', 'Warning', 'Error', 'ØÆÅ字符串'], + }, + }, +} as const; + +export const $ModelWithEnum = { + description: 'This is a model with one enum', + type: 'object', + properties: { + 'foo_bar-enum': { + description: 'This is a simple enum with strings', + enum: ['Success', 'Warning', 'Error', 'ØÆÅ字符串'], + }, + statusCode: { + description: 'These are the HTTP error code enums', + enum: ['100', '200 FOO', '300 FOO_BAR', '400 foo-bar', '500 foo.bar', '600 foo&bar'], + }, + bool: { + description: 'Simple boolean enum', + type: 'boolean', + enum: [true], + }, + }, +} as const; + +export const $ModelWithEnumWithHyphen = { + description: 'This is a model with one enum with escaped name', + type: 'object', + properties: { + 'foo-bar-baz-qux': { + type: 'string', + enum: ['3.0'], + title: 'Foo-Bar-Baz-Qux', + default: '3.0', + }, + }, +} as const; + +export const $ModelWithEnumFromDescription = { + description: 'This is a model with one enum', + type: 'object', + properties: { + test: { + type: 'integer', + description: 'Success=1,Warning=2,Error=3', + }, + }, +} as const; + +export const $ModelWithNestedEnums = { + description: 'This is a model with nested enums', + type: 'object', + properties: { + dictionaryWithEnum: { + type: 'object', + additionalProperties: { + enum: ['Success', 'Warning', 'Error'], + }, + }, + dictionaryWithEnumFromDescription: { + type: 'object', + additionalProperties: { + type: 'integer', + description: 'Success=1,Warning=2,Error=3', + }, + }, + arrayWithEnum: { + type: 'array', + items: { + enum: ['Success', 'Warning', 'Error'], + }, + }, + arrayWithDescription: { + type: 'array', + items: { + type: 'integer', + description: 'Success=1,Warning=2,Error=3', + }, + }, + 'foo_bar-enum': { + description: 'This is a simple enum with strings', + enum: ['Success', 'Warning', 'Error', 'ØÆÅ字符串'], + }, + }, +} as const; + +export const $ModelWithReference = { + description: 'This is a model with one property containing a reference', + type: 'object', + properties: { + prop: { + $ref: '#/components/schemas/ModelWithProperties', + }, + }, +} as const; + +export const $ModelWithArrayReadOnlyAndWriteOnly = { + description: 'This is a model with one property containing an array', + type: 'object', + properties: { + prop: { + type: 'array', + items: { + $ref: '#/components/schemas/ModelWithReadOnlyAndWriteOnly', + }, + }, + propWithFile: { + type: 'array', + items: { + type: 'file', + }, + }, + propWithNumber: { + type: 'array', + items: { + type: 'number', + }, + }, + }, +} as const; + +export const $ModelWithArray = { + description: 'This is a model with one property containing an array', + type: 'object', + properties: { + prop: { + type: 'array', + items: { + $ref: '#/components/schemas/ModelWithString', + }, + }, + propWithFile: { + type: 'array', + items: { + type: 'file', + }, + }, + propWithNumber: { + type: 'array', + items: { + type: 'number', + }, + }, + }, +} as const; + +export const $ModelWithDictionary = { + description: 'This is a model with one property containing a dictionary', + type: 'object', + properties: { + prop: { + type: 'object', + additionalProperties: { + type: 'string', + }, + }, + }, +} as const; + +export const $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', + }, + }, +} as const; + +export const $ModelWithCircularReference = { + description: 'This is a model with one property containing a circular reference', + type: 'object', + properties: { + prop: { + $ref: '#/components/schemas/ModelWithCircularReference', + }, + }, +} as const; + +export const $CompositionWithOneOf = { + description: "This is a model with one property with a 'one of' relationship", + type: 'object', + properties: { + propA: { + type: 'object', + oneOf: [ + { + $ref: '#/components/schemas/ModelWithString', + }, + { + $ref: '#/components/schemas/ModelWithEnum', + }, + { + $ref: '#/components/schemas/ModelWithArray', + }, + { + $ref: '#/components/schemas/ModelWithDictionary', + }, + ], + }, + }, +} as const; + +export const $CompositionWithOneOfAnonymous = { + description: "This is a model with one property with a 'one of' relationship where the options are not $ref", + type: 'object', + properties: { + propA: { + type: 'object', + oneOf: [ + { + description: 'Anonymous object type', + type: 'object', + properties: { + propA: { + type: 'string', + }, + }, + }, + { + description: 'Anonymous string type', + type: 'string', + }, + { + description: 'Anonymous integer type', + type: 'integer', + }, + ], + }, + }, +} as const; + +export const $ModelCircle = { + description: 'Circle', + type: 'object', + required: ['kind'], + properties: { + kind: { + type: 'string', + }, + radius: { + type: 'number', + }, + }, +} as const; + +export const $ModelSquare = { + description: 'Square', + type: 'object', + required: ['kind'], + properties: { + kind: { + type: 'string', + }, + sideLength: { + type: 'number', + }, + }, +} as const; + +export const $CompositionWithOneOfDiscriminator = { + description: "This is a model with one property with a 'one of' relationship where the options are not $ref", + type: 'object', + oneOf: [ + { + $ref: '#/components/schemas/ModelCircle', + }, + { + $ref: '#/components/schemas/ModelSquare', + }, + ], + discriminator: { + propertyName: 'kind', + mapping: { + circle: '#/components/schemas/ModelCircle', + square: '#/components/schemas/ModelSquare', + }, + }, +} as const; + +export const $CompositionWithAnyOf = { + description: "This is a model with one property with a 'any of' relationship", + type: 'object', + properties: { + propA: { + type: 'object', + anyOf: [ + { + $ref: '#/components/schemas/ModelWithString', + }, + { + $ref: '#/components/schemas/ModelWithEnum', + }, + { + $ref: '#/components/schemas/ModelWithArray', + }, + { + $ref: '#/components/schemas/ModelWithDictionary', + }, + ], + }, + }, +} as const; + +export const $CompositionWithAnyOfAnonymous = { + description: "This is a model with one property with a 'any of' relationship where the options are not $ref", + type: 'object', + properties: { + propA: { + type: 'object', + anyOf: [ + { + description: 'Anonymous object type', + type: 'object', + properties: { + propA: { + type: 'string', + }, + }, + }, + { + description: 'Anonymous string type', + type: 'string', + }, + { + description: 'Anonymous integer type', + type: 'integer', + }, + ], + }, + }, +} as const; + +export const $CompositionWithNestedAnyAndTypeNull = { + description: "This is a model with nested 'any of' property with a type null", + type: 'object', + properties: { + propA: { + type: 'object', + anyOf: [ + { + items: { + anyOf: [ + { + $ref: '#/components/schemas/ModelWithDictionary', + }, + { + type: 'null', + }, + ], + }, + type: 'array', + }, + { + items: { + anyOf: [ + { + $ref: '#/components/schemas/ModelWithArray', + }, + { + type: 'null', + }, + ], + }, + type: 'array', + }, + ], + }, + }, +} as const; + +export const $Enum1 = { + enum: ['Bird', 'Dog'], + type: 'string', +} as const; + +export const $ConstValue = { + type: 'string', + const: 'ConstValue', +} as const; + +export const $CompositionWithNestedAnyOfAndNull = { + description: "This is a model with one property with a 'any of' relationship where the options are not $ref", + type: 'object', + properties: { + propA: { + anyOf: [ + { + items: { + anyOf: [ + { + $ref: '#/components/schemas/Enum1', + }, + { + $ref: '#/components/schemas/ConstValue', + }, + ], + }, + type: 'array', + }, + { + type: 'null', + }, + ], + title: 'Scopes', + }, + }, +} as const; + +export const $CompositionWithOneOfAndNullable = { + description: "This is a model with one property with a 'one of' relationship", + type: 'object', + properties: { + propA: { + nullable: true, + type: 'object', + oneOf: [ + { + type: 'object', + properties: { + boolean: { + type: 'boolean', + }, + }, + }, + { + $ref: '#/components/schemas/ModelWithEnum', + }, + { + $ref: '#/components/schemas/ModelWithArray', + }, + { + $ref: '#/components/schemas/ModelWithDictionary', + }, + ], + }, + }, +} as const; + +export const $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', + }, + }, + ], + }, + }, +} as const; + +export const $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', + }, + }, + }, + ], + }, + }, +} as const; + +export const $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', + }, + ], + }, + }, + }, + ], + }, + }, +} as const; + +export const $CompositionWithAllOfAndNullable = { + description: "This is a model with one property with a 'all of' relationship", + type: 'object', + properties: { + propA: { + nullable: true, + type: 'object', + allOf: [ + { + type: 'object', + properties: { + boolean: { + type: 'boolean', + }, + }, + }, + { + $ref: '#/components/schemas/ModelWithEnum', + }, + { + $ref: '#/components/schemas/ModelWithArray', + }, + { + $ref: '#/components/schemas/ModelWithDictionary', + }, + ], + }, + }, +} as const; + +export const $CompositionWithAnyOfAndNullable = { + description: "This is a model with one property with a 'any of' relationship", + type: 'object', + properties: { + propA: { + nullable: true, + type: 'object', + anyOf: [ + { + type: 'object', + properties: { + boolean: { + type: 'boolean', + }, + }, + }, + { + $ref: '#/components/schemas/ModelWithEnum', + }, + { + $ref: '#/components/schemas/ModelWithArray', + }, + { + $ref: '#/components/schemas/ModelWithDictionary', + }, + ], + }, + }, +} as const; + +export const $CompositionBaseModel = { + description: 'This is a base model with two simple optional properties', + type: 'object', + properties: { + firstName: { + type: 'string', + }, + lastname: { + type: 'string', + }, + }, +} as const; + +export const $CompositionExtendedModel = { + description: 'This is a model that extends the base model', + type: 'object', + allOf: [ + { + $ref: '#/components/schemas/CompositionBaseModel', + }, + ], + properties: { + age: { + type: 'number', + }, + }, + required: ['firstName', 'lastname', 'age'], +} as const; + +export const $ModelWithProperties = { + description: 'This is a model with one nested property', + type: 'object', + required: ['required', 'requiredAndReadOnly', 'requiredAndNullable'], + properties: { + required: { + type: 'string', + }, + requiredAndReadOnly: { + type: 'string', + readOnly: true, + }, + requiredAndNullable: { + type: 'string', + nullable: true, + }, + string: { + type: 'string', + }, + number: { + type: 'number', + }, + boolean: { + type: 'boolean', + }, + reference: { + $ref: '#/components/schemas/ModelWithString', + }, + 'property with space': { + type: 'string', + }, + default: { + type: 'string', + }, + try: { + type: 'string', + }, + '@namespace.string': { + type: 'string', + readOnly: true, + }, + '@namespace.integer': { + type: 'integer', + readOnly: true, + }, + }, +} as const; + +export const $ModelWithNestedProperties = { + description: 'This is a model with one nested property', + type: 'object', + required: ['first'], + properties: { + first: { + type: 'object', + required: ['second'], + readOnly: true, + nullable: true, + properties: { + second: { + type: 'object', + required: ['third'], + readOnly: true, + nullable: true, + properties: { + third: { + type: 'string', + required: true, + readOnly: true, + nullable: true, + }, + }, + }, + }, + }, + }, +} as const; + +export const $ModelWithDuplicateProperties = { + description: 'This is a model with duplicated properties', + type: 'object', + properties: { + prop: { + $ref: '#/components/schemas/ModelWithString', + }, + }, +} as const; + +export const $ModelWithOrderedProperties = { + description: 'This is a model with ordered properties', + type: 'object', + properties: { + zebra: { + type: 'string', + }, + apple: { + type: 'string', + }, + hawaii: { + type: 'string', + }, + }, +} as const; + +export const $ModelWithDuplicateImports = { + description: 'This is a model with duplicated imports', + type: 'object', + properties: { + propA: { + $ref: '#/components/schemas/ModelWithString', + }, + propB: { + $ref: '#/components/schemas/ModelWithString', + }, + propC: { + $ref: '#/components/schemas/ModelWithString', + }, + }, +} as const; + +export const $ModelThatExtends = { + description: 'This is a model that extends another model', + type: 'object', + allOf: [ + { + $ref: '#/components/schemas/ModelWithString', + }, + { + type: 'object', + properties: { + propExtendsA: { + type: 'string', + }, + propExtendsB: { + $ref: '#/components/schemas/ModelWithString', + }, + }, + }, + ], +} as const; + +export const $ModelThatExtendsExtends = { + description: 'This is a model that extends another model', + type: 'object', + allOf: [ + { + $ref: '#/components/schemas/ModelWithString', + }, + { + $ref: '#/components/schemas/ModelThatExtends', + }, + { + type: 'object', + properties: { + propExtendsC: { + type: 'string', + }, + propExtendsD: { + $ref: '#/components/schemas/ModelWithString', + }, + }, + }, + ], +} as const; + +export const $ModelWithPattern = { + description: 'This is a model that contains a some patterns', + type: 'object', + required: ['key', 'name'], + properties: { + key: { + maxLength: 64, + pattern: '^[a-zA-Z0-9_]*$', + type: 'string', + }, + name: { + maxLength: 255, + type: 'string', + }, + enabled: { + type: 'boolean', + readOnly: true, + }, + modified: { + type: 'string', + format: 'date-time', + readOnly: true, + }, + id: { + type: 'string', + pattern: '^d{2}-d{3}-d{4}$', + }, + text: { + type: 'string', + pattern: '^w+$', + }, + patternWithSingleQuotes: { + type: 'string', + pattern: "^[a-zA-Z0-9']*$", + }, + patternWithNewline: { + type: 'string', + pattern: `aaa +bbb`, + }, + patternWithBacktick: { + type: 'string', + pattern: 'aaa`bbb', + }, + }, +} as const; + +export const $File = { + required: ['mime'], + type: 'object', + properties: { + id: { + title: 'Id', + type: 'string', + readOnly: true, + minLength: 1, + }, + updated_at: { + title: 'Updated at', + type: 'string', + format: 'date-time', + readOnly: true, + }, + created_at: { + title: 'Created at', + type: 'string', + format: 'date-time', + readOnly: true, + }, + mime: { + title: 'Mime', + type: 'string', + maxLength: 24, + minLength: 1, + }, + file: { + title: 'File', + type: 'string', + readOnly: true, + format: 'uri', + }, + }, +} as const; + +export const $default = { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, +} as const; + +export const $Pageable = { + type: 'object', + properties: { + page: { + minimum: 0, + type: 'integer', + format: 'int32', + default: 0, + }, + size: { + minimum: 1, + type: 'integer', + format: 'int32', + }, + sort: { + type: 'array', + items: { + type: 'string', + }, + }, + }, +} as const; + +export const $FreeFormObjectWithoutAdditionalProperties = { + description: 'This is a free-form object without additionalProperties.', + type: 'object', +} as const; + +export const $FreeFormObjectWithAdditionalPropertiesEqTrue = { + description: 'This is a free-form object with additionalProperties: true.', + type: 'object', + additionalProperties: true, +} as const; + +export const $FreeFormObjectWithAdditionalPropertiesEqEmptyObject = { + description: 'This is a free-form object with additionalProperties: {}.', + type: 'object', + additionalProperties: {}, +} as const; + +export const $ModelWithConst = { + type: 'object', + properties: { + String: { + const: 'String', + }, + number: { + const: 0, + }, + null: { + const: null, + }, + withType: { + type: 'string', + const: 'Some string', + }, + }, +} as const; + +export const $ModelWithAdditionalPropertiesEqTrue = { + description: 'This is a model with one property and additionalProperties: true', + type: 'object', + properties: { + prop: { + description: 'This is a simple string property', + type: 'string', + }, + }, + additionalProperties: true, +} as const; + +export const $NestedAnyOfArraysNullable = { + properties: { + nullableArray: { + anyOf: [ + { + items: { + anyOf: [ + { + type: 'string', + }, + { + type: 'boolean', + }, + ], + }, + type: 'array', + }, + { + type: 'null', + }, + ], + }, + }, + type: 'object', +} as const; + +export const $CompositionWithOneOfAndProperties = { + type: 'object', + oneOf: [ + { + type: 'object', + required: ['foo'], + properties: { + foo: { + $ref: '#/components/parameters/SimpleParameter', + }, + }, + additionalProperties: false, + }, + { + type: 'object', + required: ['bar'], + properties: { + bar: { + $ref: '#/components/schemas/NonAsciiString%C3%A6%C3%B8%C3%A5%C3%86%C3%98%C3%85%C3%B6%C3%B4%C3%AA%C3%8A%E5%AD%97%E7%AC%A6%E4%B8%B2', + }, + }, + additionalProperties: false, + }, + ], + required: ['baz', 'qux'], + properties: { + baz: { + type: 'integer', + format: 'uint16', + minimum: 0, + nullable: true, + }, + qux: { + type: 'integer', + format: 'uint8', + minimum: 0, + }, + }, +} as const; + +export const $NullableObject = { + type: 'object', + nullable: true, + description: 'An object that can be null', + properties: { + foo: { + type: 'string', + }, + }, + default: null, +} as const; + +export const $ModelWithNullableObject = { + type: 'object', + properties: { + data: { + $ref: '#/components/schemas/NullableObject', + }, + }, +} as const; + +export const $ModelWithOneOfEnum = { + oneOf: [ + { + type: 'object', + required: ['foo'], + properties: { + foo: { + type: 'string', + enum: ['Bar'], + }, + }, + }, + { + type: 'object', + required: ['foo'], + properties: { + foo: { + type: 'string', + enum: ['Baz'], + }, + }, + }, + { + type: 'object', + required: ['foo'], + properties: { + foo: { + type: 'string', + enum: ['Qux'], + }, + }, + }, + { + type: 'object', + required: ['content', 'foo'], + properties: { + content: { + type: 'string', + format: 'date-time', + }, + foo: { + type: 'string', + enum: ['Quux'], + }, + }, + }, + { + type: 'object', + required: ['content', 'foo'], + properties: { + content: { + type: 'array', + items: [ + { + type: 'string', + format: 'date-time', + }, + { + type: 'string', + }, + ], + maxItems: 2, + minItems: 2, + }, + foo: { + type: 'string', + enum: ['Corge'], + }, + }, + }, + ], +} as const; + +export const $ModelWithNestedArrayEnumsDataFoo = { + enum: ['foo', 'bar'], + type: 'string', +} as const; + +export const $ModelWithNestedArrayEnumsDataBar = { + enum: ['baz', 'qux'], + type: 'string', +} as const; + +export const $ModelWithNestedArrayEnumsData = { + type: 'object', + properties: { + foo: { + type: 'array', + items: { + $ref: '#/components/schemas/ModelWithNestedArrayEnumsDataFoo', + }, + }, + bar: { + type: 'array', + items: { + $ref: '#/components/schemas/ModelWithNestedArrayEnumsDataBar', + }, + }, + }, +} as const; + +export const $ModelWithNestedArrayEnums = { + type: 'object', + properties: { + array_strings: { + type: 'array', + items: { + type: 'string', + }, + }, + data: { + allOf: [ + { + $ref: '#/components/schemas/ModelWithNestedArrayEnumsData', + }, + ], + }, + }, +} as const; + +export const $ModelWithNestedCompositionEnums = { + type: 'object', + properties: { + foo: { + allOf: [ + { + $ref: '#/components/schemas/ModelWithNestedArrayEnumsDataFoo', + }, + ], + }, + }, +} as const; + +export const $ModelWithReadOnlyAndWriteOnly = { + type: 'object', + required: ['foo', 'bar', 'baz'], + properties: { + foo: { + type: 'string', + }, + bar: { + readOnly: true, + type: 'string', + }, + baz: { + type: 'string', + writeOnly: true, + }, + }, +} as const; + +export const $ModelWithConstantSizeArray = { + type: 'array', + items: { + type: 'number', + }, + minItems: 2, + maxItems: 2, +} as const; + +export const $ModelWithAnyOfConstantSizeArray = { + type: 'array', + items: { + oneOf: [ + { + type: 'number', + }, + { + type: 'string', + }, + ], + }, + minItems: 3, + maxItems: 3, +} as const; + +export const $ModelWithAnyOfConstantSizeArrayNullable = { + type: 'array', + items: { + oneOf: [ + { + type: 'number', + nullable: true, + }, + { + type: 'string', + }, + ], + }, + minItems: 3, + maxItems: 3, +} as const; + +export const $ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = { + type: 'array', + items: { + oneOf: [ + { + type: 'number', + }, + { + type: 'string', + }, + ], + }, + minItems: 2, + maxItems: 2, +} as const; + +export const $ModelWithAnyOfConstantSizeArrayAndIntersect = { + type: 'array', + items: { + allOf: [ + { + type: 'number', + }, + { + type: 'string', + }, + ], + }, + minItems: 2, + maxItems: 2, +} as const; + +export const $SimpleParameter = { + description: 'This is a reusable parameter', + name: 'parameter', + in: 'query', + required: false, + schema: { + type: 'string', + }, +} as const; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/services.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/services.ts.snap new file mode 100644 index 000000000..764e312db --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_javascript_preserved_name/services.ts.snap @@ -0,0 +1,867 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { CancelablePromise } from './core/CancelablePromise'; +import { OpenAPI } from './core/OpenAPI'; +import { request as __request } from './core/request'; +import type { $OpenApiTs } from './models'; + +export class DefaultService { + /** + * @throws ApiError + */ + public static serviceWithEmptyTag(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/no-tag', + }); + } + + /** + * @returns ModelWithReadOnlyAndWriteOnly + * @throws ApiError + */ + public static postServiceWithEmptyTag( + data: $OpenApiTs['/api/v{api-version}/no-tag']['post']['req'] + ): CancelablePromise<$OpenApiTs['/api/v{api-version}/no-tag']['post']['res'][200]> { + const { requestBody } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/no-tag', + body: requestBody, + mediaType: 'application/json', + }); + } +} + +export class SimpleService { + /** + * @returns Model_From_Zendesk Success + * @throws ApiError + */ + public static apiVVersionOdataControllerCount(): CancelablePromise< + $OpenApiTs['/api/v{api-version}/simple/$count']['get']['res'][200] + > { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/simple/$count', + }); + } + + /** + * @throws ApiError + */ + public static getCallWithoutParametersAndResponse(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/simple', + }); + } + + /** + * @throws ApiError + */ + public static putCallWithoutParametersAndResponse(): CancelablePromise { + return __request(OpenAPI, { + method: 'PUT', + url: '/api/v{api-version}/simple', + }); + } + + /** + * @throws ApiError + */ + public static postCallWithoutParametersAndResponse(): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/simple', + }); + } + + /** + * @throws ApiError + */ + public static deleteCallWithoutParametersAndResponse(): CancelablePromise { + return __request(OpenAPI, { + method: 'DELETE', + url: '/api/v{api-version}/simple', + }); + } + + /** + * @throws ApiError + */ + public static optionsCallWithoutParametersAndResponse(): CancelablePromise { + return __request(OpenAPI, { + method: 'OPTIONS', + url: '/api/v{api-version}/simple', + }); + } + + /** + * @throws ApiError + */ + public static headCallWithoutParametersAndResponse(): CancelablePromise { + return __request(OpenAPI, { + method: 'HEAD', + url: '/api/v{api-version}/simple', + }); + } + + /** + * @throws ApiError + */ + public static patchCallWithoutParametersAndResponse(): CancelablePromise { + return __request(OpenAPI, { + method: 'PATCH', + url: '/api/v{api-version}/simple', + }); + } +} + +export class ParametersService { + /** + * @throws ApiError + */ + public static deleteFoo( + data: $OpenApiTs['/api/v{api-version}/foo/{foo}/bar/{bar}']['delete']['req'] + ): CancelablePromise { + const { foo, bar } = data; + return __request(OpenAPI, { + method: 'DELETE', + url: '/api/v{api-version}/foo/{foo}/bar/{bar}', + path: { + foo, + bar, + }, + }); + } + + /** + * @throws ApiError + */ + public static callWithParameters( + data: $OpenApiTs['/api/v{api-version}/parameters/{parameterPath}']['post']['req'] + ): CancelablePromise { + const { + parameterHeader, + fooAllOfEnum, + parameterQuery, + parameterForm, + parameterCookie, + parameterPath, + requestBody, + fooRefEnum, + } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/parameters/{parameterPath}', + path: { + parameterPath, + }, + cookies: { + parameterCookie, + }, + headers: { + parameterHeader, + }, + query: { + foo_ref_enum: fooRefEnum, + foo_all_of_enum: fooAllOfEnum, + parameterQuery, + }, + formData: { + parameterForm, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + + /** + * @throws ApiError + */ + public static callWithWeirdParameterNames( + data: $OpenApiTs['/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}']['post']['req'] + ): CancelablePromise { + const { + parameterHeader, + parameterQuery, + parameterForm, + parameterCookie, + requestBody, + parameterPath1, + parameterPath2, + parameterPath3, + _default, + } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}', + path: { + 'parameter.path.1': parameterPath1, + 'parameter-path-2': parameterPath2, + 'PARAMETER-PATH-3': parameterPath3, + }, + cookies: { + 'PARAMETER-COOKIE': parameterCookie, + }, + headers: { + 'parameter.header': parameterHeader, + }, + query: { + default: _default, + 'parameter-query': parameterQuery, + }, + formData: { + parameter_form: parameterForm, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + + /** + * @throws ApiError + */ + public static getCallWithOptionalParam( + data: $OpenApiTs['/api/v{api-version}/parameters/']['get']['req'] + ): CancelablePromise { + const { requestBody, parameter } = data; + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/parameters/', + query: { + parameter, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + + /** + * @throws ApiError + */ + public static postCallWithOptionalParam( + data: $OpenApiTs['/api/v{api-version}/parameters/']['post']['req'] + ): CancelablePromise { + const { parameter, requestBody } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/parameters/', + query: { + parameter, + }, + body: requestBody, + mediaType: 'application/json', + }); + } +} + +export class DescriptionsService { + /** + * @throws ApiError + */ + public static callWithDescriptions( + data: $OpenApiTs['/api/v{api-version}/descriptions/']['post']['req'] = {} + ): CancelablePromise { + const { + parameterWithBreaks, + parameterWithBackticks, + parameterWithSlashes, + parameterWithExpressionPlaceholders, + parameterWithQuotes, + parameterWithReservedCharacters, + } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/descriptions/', + query: { + parameterWithBreaks, + parameterWithBackticks, + parameterWithSlashes, + parameterWithExpressionPlaceholders, + parameterWithQuotes, + parameterWithReservedCharacters, + }, + }); + } +} + +export class DeprecatedService { + /** + * @deprecated + * @throws ApiError + */ + public static deprecatedCall( + data: $OpenApiTs['/api/v{api-version}/parameters/deprecated']['post']['req'] + ): CancelablePromise { + const { parameter } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/parameters/deprecated', + headers: { + parameter, + }, + }); + } +} + +export class RequestBodyService { + /** + * @throws ApiError + */ + public static postApiRequestBody( + data: $OpenApiTs['/api/v{api-version}/requestBody/']['post']['req'] = {} + ): CancelablePromise { + const { parameter, foo } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/requestBody/', + query: { + parameter, + }, + body: foo, + mediaType: 'application/json', + }); + } +} + +export class FormDataService { + /** + * @throws ApiError + */ + public static postApiFormData( + data: $OpenApiTs['/api/v{api-version}/formData/']['post']['req'] = {} + ): CancelablePromise { + const { parameter, formData } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/formData/', + query: { + parameter, + }, + formData: formData, + mediaType: 'multipart/form-data', + }); + } +} + +export class DefaultsService { + /** + * @throws ApiError + */ + public static callWithDefaultParameters( + data: $OpenApiTs['/api/v{api-version}/defaults']['get']['req'] = {} + ): CancelablePromise { + const { parameterString, parameterNumber, parameterBoolean, parameterEnum, parameterModel } = data; + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/defaults', + query: { + parameterString, + parameterNumber, + parameterBoolean, + parameterEnum, + parameterModel, + }, + }); + } + + /** + * @throws ApiError + */ + public static callWithDefaultOptionalParameters( + data: $OpenApiTs['/api/v{api-version}/defaults']['post']['req'] = {} + ): CancelablePromise { + const { parameterString, parameterNumber, parameterBoolean, parameterEnum, parameterModel } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/defaults', + query: { + parameterString, + parameterNumber, + parameterBoolean, + parameterEnum, + parameterModel, + }, + }); + } + + /** + * @throws ApiError + */ + public static callToTestOrderOfParams( + data: $OpenApiTs['/api/v{api-version}/defaults']['put']['req'] + ): CancelablePromise { + const { + parameterStringWithNoDefault, + parameterOptionalStringWithDefault, + parameterOptionalStringWithEmptyDefault, + parameterOptionalStringWithNoDefault, + parameterStringWithDefault, + parameterStringWithEmptyDefault, + parameterStringNullableWithNoDefault, + parameterStringNullableWithDefault, + } = data; + return __request(OpenAPI, { + method: 'PUT', + url: '/api/v{api-version}/defaults', + query: { + parameterOptionalStringWithDefault, + parameterOptionalStringWithEmptyDefault, + parameterOptionalStringWithNoDefault, + parameterStringWithDefault, + parameterStringWithEmptyDefault, + parameterStringWithNoDefault, + parameterStringNullableWithNoDefault, + parameterStringNullableWithDefault, + }, + }); + } +} + +export class DuplicateService { + /** + * @throws ApiError + */ + public static duplicateName(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/duplicate', + }); + } + + /** + * @throws ApiError + */ + public static duplicateName1(): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/duplicate', + }); + } + + /** + * @throws ApiError + */ + public static duplicateName2(): CancelablePromise { + return __request(OpenAPI, { + method: 'PUT', + url: '/api/v{api-version}/duplicate', + }); + } + + /** + * @throws ApiError + */ + public static duplicateName3(): CancelablePromise { + return __request(OpenAPI, { + method: 'DELETE', + url: '/api/v{api-version}/duplicate', + }); + } +} + +export class NoContentService { + /** + * @returns void Success + * @throws ApiError + */ + public static callWithNoContentResponse(): CancelablePromise< + $OpenApiTs['/api/v{api-version}/no-content']['get']['res'][204] + > { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/no-content', + }); + } + + /** + * @returns number Response is a simple number + * @returns void Success + * @throws ApiError + */ + public static callWithResponseAndNoContentResponse(): CancelablePromise< + | $OpenApiTs['/api/v{api-version}/multiple-tags/response-and-no-content']['get']['res'][200] + | $OpenApiTs['/api/v{api-version}/multiple-tags/response-and-no-content']['get']['res'][204] + > { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/multiple-tags/response-and-no-content', + }); + } +} + +export class ResponseService { + /** + * @returns number Response is a simple number + * @returns void Success + * @throws ApiError + */ + public static callWithResponseAndNoContentResponse(): CancelablePromise< + | $OpenApiTs['/api/v{api-version}/multiple-tags/response-and-no-content']['get']['res'][200] + | $OpenApiTs['/api/v{api-version}/multiple-tags/response-and-no-content']['get']['res'][204] + > { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/multiple-tags/response-and-no-content', + }); + } + + /** + * @returns ModelWithString + * @throws ApiError + */ + public static callWithResponse(): CancelablePromise<$OpenApiTs['/api/v{api-version}/response']['get']['res'][200]> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/response', + }); + } + + /** + * @returns ModelWithString Message for default response + * @throws ApiError + */ + public static callWithDuplicateResponses(): CancelablePromise< + $OpenApiTs['/api/v{api-version}/response']['post']['res'][200] + > { + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/response', + errors: { + 500: `Message for 500 error`, + 501: `Message for 501 error`, + 502: `Message for 502 error`, + }, + }); + } + + /** + * @returns any Message for 200 response + * @returns ModelWithString Message for default response + * @returns ModelThatExtends Message for 201 response + * @returns ModelThatExtendsExtends Message for 202 response + * @throws ApiError + */ + public static callWithResponses(): CancelablePromise< + | $OpenApiTs['/api/v{api-version}/response']['put']['res'][200] + | $OpenApiTs['/api/v{api-version}/response']['put']['res'][200] + | $OpenApiTs['/api/v{api-version}/response']['put']['res'][201] + | $OpenApiTs['/api/v{api-version}/response']['put']['res'][202] + > { + return __request(OpenAPI, { + method: 'PUT', + url: '/api/v{api-version}/response', + errors: { + 500: `Message for 500 error`, + 501: `Message for 501 error`, + 502: `Message for 502 error`, + }, + }); + } +} + +export class MultipleTags1Service { + /** + * @returns void Success + * @throws ApiError + */ + public static dummyA(): CancelablePromise<$OpenApiTs['/api/v{api-version}/multiple-tags/a']['get']['res'][204]> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/multiple-tags/a', + }); + } + + /** + * @returns void Success + * @throws ApiError + */ + public static dummyB(): CancelablePromise<$OpenApiTs['/api/v{api-version}/multiple-tags/b']['get']['res'][204]> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/multiple-tags/b', + }); + } +} + +export class MultipleTags2Service { + /** + * @returns void Success + * @throws ApiError + */ + public static dummyA(): CancelablePromise<$OpenApiTs['/api/v{api-version}/multiple-tags/a']['get']['res'][204]> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/multiple-tags/a', + }); + } + + /** + * @returns void Success + * @throws ApiError + */ + public static dummyB(): CancelablePromise<$OpenApiTs['/api/v{api-version}/multiple-tags/b']['get']['res'][204]> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/multiple-tags/b', + }); + } +} + +export class MultipleTags3Service { + /** + * @returns void Success + * @throws ApiError + */ + public static dummyB(): CancelablePromise<$OpenApiTs['/api/v{api-version}/multiple-tags/b']['get']['res'][204]> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/multiple-tags/b', + }); + } +} + +export class CollectionFormatService { + /** + * @throws ApiError + */ + public static collectionFormat( + data: $OpenApiTs['/api/v{api-version}/collectionFormat']['get']['req'] + ): CancelablePromise { + const { parameterArrayCsv, parameterArraySsv, parameterArrayTsv, parameterArrayPipes, parameterArrayMulti } = + data; + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/collectionFormat', + query: { + parameterArrayCSV: parameterArrayCsv, + parameterArraySSV: parameterArraySsv, + parameterArrayTSV: parameterArrayTsv, + parameterArrayPipes, + parameterArrayMulti, + }, + }); + } +} + +export class TypesService { + /** + * @returns number Response is a simple number + * @returns string Response is a simple string + * @returns boolean Response is a simple boolean + * @returns unknown Response is a simple object + * @throws ApiError + */ + public static types( + data: $OpenApiTs['/api/v{api-version}/types']['get']['req'] + ): CancelablePromise< + | $OpenApiTs['/api/v{api-version}/types']['get']['res'][200] + | $OpenApiTs['/api/v{api-version}/types']['get']['res'][201] + | $OpenApiTs['/api/v{api-version}/types']['get']['res'][202] + | $OpenApiTs['/api/v{api-version}/types']['get']['res'][203] + > { + const { + parameterArray, + parameterDictionary, + parameterEnum, + parameterNumber, + parameterString, + parameterBoolean, + parameterObject, + id, + } = data; + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/types', + path: { + id, + }, + query: { + parameterNumber, + parameterString, + parameterBoolean, + parameterObject, + parameterArray, + parameterDictionary, + parameterEnum, + }, + }); + } +} + +export class UploadService { + /** + * @returns boolean + * @throws ApiError + */ + public static uploadFile( + data: $OpenApiTs['/api/v{api-version}/upload']['post']['req'] + ): CancelablePromise<$OpenApiTs['/api/v{api-version}/upload']['post']['res'][200]> { + const { file } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/upload', + formData: { + file, + }, + }); + } +} + +export class FileResponseService { + /** + * @returns binary Success + * @throws ApiError + */ + public static fileResponse( + data: $OpenApiTs['/api/v{api-version}/file/{id}']['get']['req'] + ): CancelablePromise<$OpenApiTs['/api/v{api-version}/file/{id}']['get']['res'][200]> { + const { id } = data; + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/file/{id}', + path: { + id, + }, + }); + } +} + +export class ComplexService { + /** + * @returns ModelWithString Successful response + * @throws ApiError + */ + public static complexTypes( + data: $OpenApiTs['/api/v{api-version}/complex']['get']['req'] + ): CancelablePromise<$OpenApiTs['/api/v{api-version}/complex']['get']['res'][200]> { + const { parameterObject, parameterReference } = data; + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/complex', + query: { + parameterObject, + parameterReference, + }, + errors: { + 400: `400 server error`, + 500: `500 server error`, + }, + }); + } + + /** + * @returns ModelWithString Success + * @throws ApiError + */ + public static complexParams( + data: $OpenApiTs['/api/v{api-version}/complex/{id}']['put']['req'] + ): CancelablePromise<$OpenApiTs['/api/v{api-version}/complex/{id}']['put']['res'][200]> { + const { id, requestBody } = data; + return __request(OpenAPI, { + method: 'PUT', + url: '/api/v{api-version}/complex/{id}', + path: { + id, + }, + body: requestBody, + mediaType: 'application/json-patch+json', + }); + } +} + +export class MultipartService { + /** + * @throws ApiError + */ + public static multipartRequest( + data: $OpenApiTs['/api/v{api-version}/multipart']['post']['req'] = {} + ): CancelablePromise { + const { formData } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/multipart', + formData: formData, + mediaType: 'multipart/form-data', + }); + } + + /** + * @returns any OK + * @throws ApiError + */ + public static multipartResponse(): CancelablePromise< + $OpenApiTs['/api/v{api-version}/multipart']['get']['res'][200] + > { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v{api-version}/multipart', + }); + } +} + +export class HeaderService { + /** + * @returns string Successful response + * @throws ApiError + */ + public static callWithResultFromHeader(): CancelablePromise< + $OpenApiTs['/api/v{api-version}/header']['post']['res'][200] + > { + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/header', + responseHeader: 'operation-location', + errors: { + 400: `400 server error`, + 500: `500 server error`, + }, + }); + } +} + +export class ErrorService { + /** + * @returns any Custom message: Successful response + * @throws ApiError + */ + public static testErrorCode( + data: $OpenApiTs['/api/v{api-version}/error']['post']['req'] + ): CancelablePromise<$OpenApiTs['/api/v{api-version}/error']['post']['res'][200]> { + const { status } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/error', + query: { + status, + }, + errors: { + 500: `Custom message: Internal Server Error`, + 501: `Custom message: Not Implemented`, + 502: `Custom message: Bad Gateway`, + 503: `Custom message: Service Unavailable`, + }, + }); + } +} + +export class NonAsciiÆøåÆøÅöôêÊService { + /** + * @returns NonAsciiStringæøåÆØÅöôêÊ字符串 Successful response + * @throws ApiError + */ + public static nonAsciiæøåÆøÅöôêÊ字符串( + data: $OpenApiTs['/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串']['post']['req'] + ): CancelablePromise<$OpenApiTs['/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串']['post']['res'][200]> { + const { nonAsciiParamæøåÆøÅöôêÊ } = data; + return __request(OpenAPI, { + method: 'POST', + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串', + query: { + nonAsciiParamæøåÆØÅöôêÊ: nonAsciiParamæøåÆøÅöôêÊ, + }, + }); + } +} diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_typescript/models.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_typescript/models.ts.snap index 20111fad2..3f98a8375 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_typescript/models.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_typescript/models.ts.snap @@ -143,6 +143,24 @@ export enum EnumWithXEnumNames { two = 2, } +/** + * This is a simple enum with a non-PascalCase name. + */ +export enum UPPER_SNAKE_ENUM { + /** + * UPPER_SNAKE_0 + */ + UPPER_SNAKE_0 = 'UPPER_SNAKE_0', + /** + * UPPER_SNAKE_1 + */ + UPPER_SNAKE_1 = 'UPPER_SNAKE_1', + /** + * UPPER_SNAKE_2 + */ + UPPER_SNAKE_2 = 'UPPER_SNAKE_2', +} + /** * This is a simple array with numbers */ diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_typescript/schemas.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_typescript/schemas.ts.snap index 01cc30888..d92d8fc63 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_typescript/schemas.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_enums_typescript/schemas.ts.snap @@ -113,6 +113,13 @@ export const $EnumWithXEnumNames = { 'x-enumNames': ['zero', 'one', 'two'], } as const; +export const $UPPER_SNAKE_ENUM = { + description: 'This is a simple enum with a non-PascalCase name.', + enum: ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], + 'x-enum-varnames': [0, 1, 2], + 'x-enum-descriptions': ['UPPER_SNAKE_0', 'UPPER_SNAKE_1', 'UPPER_SNAKE_2'], +} as const; + export const $ArrayWithNumbers = { description: 'This is a simple array with numbers', type: 'array', diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3_models/models.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3_models/models.ts.snap index b4fdd5a5c..8d7a5f404 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3_models/models.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3_models/models.ts.snap @@ -103,6 +103,11 @@ export type EnumWithExtensions = 200 | 400 | 500; export type EnumWithXEnumNames = 0 | 1 | 2; +/** + * This is a simple enum with a non-PascalCase name. + */ +export type UPPER_SNAKE_ENUM = 'UPPER_SNAKE_0' | 'UPPER_SNAKE_1' | 'UPPER_SNAKE_2'; + /** * This is a simple array with numbers */ diff --git a/packages/openapi-ts/test/index.spec.ts b/packages/openapi-ts/test/index.spec.ts index d0151a3ff..dfcf3f604 100644 --- a/packages/openapi-ts/test/index.spec.ts +++ b/packages/openapi-ts/test/index.spec.ts @@ -201,6 +201,21 @@ describe('OpenAPI v3', () => { description: 'generate TypeScript enums', name: 'v3_enums_typescript', }, + { + config: { + client: 'fetch', + enums: 'javascript-preserve-name', + exportCore: true, + exportModels: true, + exportServices: true, + input: '', + output: '', + schemas: true, + useOptions: true, + } as UserConfig, + description: 'generate javascript enums with names preserved', + name: 'v3_enums_javascript_preserved_name', + }, { config: { client: 'fetch', diff --git a/packages/openapi-ts/test/spec/v2.json b/packages/openapi-ts/test/spec/v2.json index d41e519a2..919f46847 100644 --- a/packages/openapi-ts/test/spec/v2.json +++ b/packages/openapi-ts/test/spec/v2.json @@ -964,6 +964,12 @@ "Used when the status of something has an error" ] }, + "UPPER_SNAKE_ENUM": { + "description": "This is a simple enum with a non-PascalCase name.", + "enum": ["UPPER_SNAKE_0", "UPPER_SNAKE_1", "UPPER_SNAKE_2"], + "x-enum-varnames": [0, 1, 2], + "x-enum-descriptions": ["UPPER_SNAKE_0", "UPPER_SNAKE_1", "UPPER_SNAKE_2"] + }, "ArrayWithNumbers": { "description": "This is a simple array with numbers", "type": "array", diff --git a/packages/openapi-ts/test/spec/v3.json b/packages/openapi-ts/test/spec/v3.json index 7773a095f..bdbac1efb 100644 --- a/packages/openapi-ts/test/spec/v3.json +++ b/packages/openapi-ts/test/spec/v3.json @@ -1713,6 +1713,12 @@ "enum": [0, 1, 2], "x-enumNames": ["zero", "one", "two"] }, + "UPPER_SNAKE_ENUM": { + "description": "This is a simple enum with a non-PascalCase name.", + "enum": ["UPPER_SNAKE_0", "UPPER_SNAKE_1", "UPPER_SNAKE_2"], + "x-enum-varnames": [0, 1, 2], + "x-enum-descriptions": ["UPPER_SNAKE_0", "UPPER_SNAKE_1", "UPPER_SNAKE_2"] + }, "ArrayWithNumbers": { "description": "This is a simple array with numbers", "type": "array",