From 6e6bd4de3d7b143fc647dff0c61c63c91ba09480 Mon Sep 17 00:00:00 2001 From: josh-hemphill Date: Thu, 6 Feb 2025 15:53:05 -0800 Subject: [PATCH 01/11] feat: add @pinia-colada/sdk plugin --- .../sdk/__tests__/plugin.test.ts | 300 ++++++++++++++++++ .../src/plugins/@pinia-colada/sdk/config.ts | 21 ++ .../src/plugins/@pinia-colada/sdk/index.ts | 2 + .../src/plugins/@pinia-colada/sdk/plugin.ts | 284 +++++++++++++++++ .../src/plugins/@pinia-colada/sdk/types.d.ts | 51 +++ packages/openapi-ts/src/plugins/types.d.ts | 3 +- 6 files changed, 660 insertions(+), 1 deletion(-) create mode 100644 packages/openapi-ts/src/plugins/@pinia-colada/sdk/__tests__/plugin.test.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia-colada/sdk/config.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia-colada/sdk/index.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia-colada/sdk/plugin.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia-colada/sdk/types.d.ts diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/__tests__/plugin.test.ts b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/__tests__/plugin.test.ts new file mode 100644 index 0000000000..aaace885b1 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/__tests__/plugin.test.ts @@ -0,0 +1,300 @@ +import { describe, expect, it, type Mock, vi } from 'vitest'; + +import { TypeScriptFile } from '../../../../generate/files'; +import type { IR } from '../../../../ir/types'; +import type { Plugin } from '../../../types'; +import { handler } from '../plugin'; +import type { Config } from '../types'; + +describe('@pinia-colada/sdk plugin', () => { + // Test setup helper to create a mock context and operation + const createMockContext = () => { + const file = new TypeScriptFile({ dir: '/', name: 'test.ts' }); + const files = new Map([['/test.ts', file]]); + + const subscribe = vi.fn() as Mock; + const fileGetter = vi.fn().mockReturnValue(file); + + return { + createFile: vi.fn().mockReturnValue(file), + file: fileGetter, + files, + subscribe, + } as unknown as IR.Context; + }; + + // Test: Query Generation + describe('query generation', () => { + it('generates a query function for GET operations', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + name: '@pinia-colada/sdk', + output: '', + }; + + const operation: IR.OperationObject = { + deprecated: false, + description: 'Retrieves a user by their unique identifier', + id: 'getUser', + method: 'get', + path: '/users/{id}', + summary: 'Get user by ID', + tags: ['users'], + } as IR.OperationObject; + + handler({ context, plugin }); + + // Verify subscription was created + expect(context.subscribe).toHaveBeenCalledWith( + 'operation', + expect.any(Function), + ); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify file content contains expected query definition + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain('export const useGetUserQuery'); + expect(content).toContain('defineQuery'); + expect(content).toContain('@pinia/colada'); + }); + }); + + // Test: Mutation Generation + describe('mutation generation', () => { + it('generates a mutation function for POST operations', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + name: '@pinia-colada/sdk', + output: '', + }; + + const operation: IR.OperationObject = { + deprecated: false, + id: 'createUser', + method: 'post', + path: '/users', + summary: 'Create new user', + tags: ['users'], + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify file content contains expected mutation definition + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain('export const useCreateUserMutation'); + expect(content).toContain('defineMutation'); + }); + }); + + // Test: File Grouping + describe('file grouping', () => { + it('groups operations by tag when groupByTag is true', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + groupByTag: true, + name: '@pinia-colada/sdk', + output: '', + }; + + const operation: IR.OperationObject = { + id: 'getUser', + method: 'get', + path: '/users/{id}', + tags: ['users'], + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify file was created with tag-based path + expect(context.createFile).toHaveBeenCalledWith( + expect.objectContaining({ + path: expect.stringContaining('/users'), + }), + ); + }); + }); + + // Test: Hook Customization + describe('hook customization', () => { + it('respects onQuery hook to skip query generation', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + hooks: { + onQuery: vi.fn().mockReturnValue(false), + }, + name: '@pinia-colada/sdk', + output: '', + }; + + const operation: IR.OperationObject = { + id: 'getUser', + method: 'get', + path: '/users/{id}', + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify hook was called + expect(plugin.hooks!.onQuery).toHaveBeenCalledWith(operation); + + // Verify no query was generated + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).not.toContain('useGetUserQuery'); + }); + + it('uses custom query key from getQueryKey hook', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + hooks: { + getQueryKey: vi.fn().mockReturnValue(['custom', 'key']), + }, + name: '@pinia-colada/sdk', + output: '', + }; + + const operation: IR.OperationObject = { + id: 'getUser', + method: 'get', + path: '/users/{id}', + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify hook was called + expect(plugin.hooks!.getQueryKey).toHaveBeenCalledWith(operation); + + // Verify custom key was used + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain(`[\n 'custom',\n 'key'\n ]`); + }); + }); + + // Test: Operation ID Formatting + describe('operation id formatting', () => { + it('converts operation ids to camelCase', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + name: '@pinia-colada/sdk', + output: '', + }; + + const operations: IR.OperationObject[] = [ + { + id: 'get_user_profile', + method: 'get', + path: '/users/{id}/profile', + tags: ['users'], + } as IR.OperationObject, + { + id: 'UPDATE-USER-SETTINGS', + method: 'put', + path: '/users/{id}/settings', + tags: ['users'], + } as IR.OperationObject, + { + id: 'delete.user.account', + method: 'delete', + path: '/users/{id}', + tags: ['users'], + } as IR.OperationObject, + ]; + + handler({ context, plugin }); + + // Test each operation + operations.forEach((operation) => { + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + }); + + // Verify file content contains properly camelCased function names + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain('useGetUserProfileQuery'); + expect(content).toContain('useUpdateUserSettingsMutation'); + expect(content).toContain('useDeleteUserAccountQuery'); + }); + }); + + // Test: Operation Type Detection + describe('operation type detection', () => { + it('respects isQuery hook override', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + hooks: { + isQuery: vi.fn().mockReturnValue(true), + }, + name: '@pinia-colada/sdk', + output: '', + }; + + // Test POST operation that would normally be a mutation + const operation: IR.OperationObject = { + body: { + mediaType: 'application/json', + schema: {}, + }, + id: 'createUser', + method: 'post', + path: '/users', + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify hook was called + expect(plugin.hooks!.isQuery).toHaveBeenCalledWith(operation); + + // Verify operation was treated as a query despite being POST + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain('useCreateUserQuery'); + expect(content).not.toContain('useCreateUserMutation'); + }); + }); +}); diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/config.ts b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/config.ts new file mode 100644 index 0000000000..f2be0a4039 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/config.ts @@ -0,0 +1,21 @@ +import type { Plugin } from '../../types'; +import { handler } from './plugin'; +import type { Config } from './types'; + +export const defaultConfig: Plugin.Config = { + _dependencies: ['@hey-api/typescript'], + _handler: handler, + exportFromIndex: true, + name: '@pinia-colada/sdk', + output: 'queries', +}; + +/** + * Type helper for `@pinia-colada` plugin, returns {@link Plugin.Config} object + */ +export const defineConfig: Plugin.DefineConfig = ( + config?: Partial, +) => ({ + ...defaultConfig, + ...config, +}); diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/index.ts b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/index.ts new file mode 100644 index 0000000000..3a85a1be8a --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/index.ts @@ -0,0 +1,2 @@ +export { defaultConfig, defineConfig } from './config'; +export type { Config } from './types'; diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/plugin.ts b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/plugin.ts new file mode 100644 index 0000000000..9b68e04e6e --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/plugin.ts @@ -0,0 +1,284 @@ +import { compiler } from '../../../compiler'; +import type { TypeScriptFile } from '../../../generate/files'; +import type { IR } from '../../../ir/types'; +import { escapeComment } from '../../../utils/escape'; +import { stringCase } from '../../../utils/stringCase'; +import { + importIdentifierData, + importIdentifierError, + importIdentifierResponse, +} from '../../@hey-api/typescript/ref'; +import type { Plugin } from '../../types'; +import type { Config } from './types'; + +const createQueryFunction = ({ + context, + operation, + plugin, +}: { + context: IR.Context; + operation: IR.OperationObject; + plugin: Plugin.Instance; +}) => { + const file = context.file({ id: plugin.name })!; + + // Allow hooks to customize or skip query generation + if (plugin.hooks?.onQuery && plugin.hooks.onQuery(operation) === false) { + return; + } + + // Get query key from hooks or generate default + const queryKey = plugin.hooks?.getQueryKey?.(operation) ?? [ + operation.tags?.[0] || 'default', + operation.id, + ]; + + // Import response type for type parameters + const identifierResponse = importIdentifierResponse({ + context, + file, + operation, + }); + const identifierError = importIdentifierError({ context, file, operation }); + + const queryFn = compiler.arrowFunction({ + async: true, + parameters: [], + statements: [ + compiler.returnStatement({ + expression: compiler.callExpression({ + functionName: 'client', + parameters: [ + compiler.objectExpression({ + obj: [ + { + key: 'method', + value: operation.method, + }, + { + key: 'url', + value: operation.path, + }, + ], + }), + ], + }), + }), + ], + }); + + const node = compiler.constVariable({ + comment: [ + operation.deprecated && '@deprecated', + operation.summary && escapeComment(operation.summary), + operation.description && escapeComment(operation.description), + ], + exportConst: true, + expression: compiler.callExpression({ + functionName: 'defineQuery', + parameters: [ + compiler.objectExpression({ + obj: [ + { + key: 'key', + value: compiler.arrayLiteralExpression({ + elements: queryKey.map((k: string) => compiler.ots.string(k)), + }), + }, + { + key: 'query', + value: queryFn, + }, + ], + }), + ], + types: [ + compiler.typeReferenceNode({ + typeName: identifierResponse?.name || 'unknown', + }), + compiler.typeReferenceNode({ + typeName: identifierError?.name || 'unknown', + }), + ], + }), + name: `use${stringCase({ case: 'PascalCase', value: operation.id })}Query`, + }); + + file.import({ + module: '@pinia/colada', + name: 'defineQuery', + }); + + file.add(node); +}; + +const createMutationFunction = ({ + context, + operation, + plugin, +}: { + context: IR.Context; + operation: IR.OperationObject; + plugin: Plugin.Instance; +}) => { + const file = context.file({ id: plugin.name })!; + + // Allow hooks to customize or skip mutation generation + if ( + plugin.hooks?.onMutation && + plugin.hooks.onMutation(operation) === false + ) { + return; + } + + // Import response type for type parameters + const identifierResponse = importIdentifierResponse({ + context, + file, + operation, + }); + const identifierError = importIdentifierError({ context, file, operation }); + const identifierData = importIdentifierData({ context, file, operation }); + + const mutationFn = compiler.arrowFunction({ + async: true, + parameters: [ + { + name: 'data', + }, + ], + statements: [ + compiler.returnStatement({ + expression: compiler.callExpression({ + functionName: 'client', + parameters: [ + compiler.objectExpression({ + obj: [ + { + key: 'method', + value: operation.method, + }, + { + key: 'url', + value: operation.path, + }, + { + key: 'data', + shorthand: true, + value: compiler.identifier({ text: 'data' }), + }, + ], + }), + ], + }), + }), + ], + }); + + const node = compiler.constVariable({ + comment: [ + operation.deprecated && '@deprecated', + operation.summary && escapeComment(operation.summary), + operation.description && escapeComment(operation.description), + ], + exportConst: true, + expression: compiler.callExpression({ + functionName: 'defineMutation', + parameters: [ + compiler.objectExpression({ + obj: [ + { + key: 'mutation', + value: mutationFn, + }, + ], + }), + ], + types: [ + compiler.typeReferenceNode({ + typeName: identifierData?.name || 'unknown', + }), + compiler.typeReferenceNode({ + typeName: identifierResponse?.name || 'unknown', + }), + compiler.typeReferenceNode({ + typeName: identifierError?.name || 'unknown', + }), + ], + }), + name: `use${stringCase({ case: 'PascalCase', value: operation.id })}Mutation`, + }); + + file.import({ + module: '@pinia/colada', + name: 'defineMutation', + }); + + file.add(node); +}; + +export const handler: Plugin.Handler = ({ + context, + plugin, +}: { + context: IR.Context; + plugin: Plugin.Instance; +}) => { + // Create files based on grouping strategy + const files = new Map(); + + const getFile = (tag: string) => { + if (!plugin.groupByTag) { + return context.file({ id: plugin.name })!; + } + + const fileId = `${plugin.name}/${tag}`; + if (!files.has(fileId)) { + files.set( + fileId, + context.createFile({ + exportFromIndex: plugin.exportFromIndex, + id: fileId, + path: `${plugin.output}/${tag}`, + }), + ); + } + return files.get(fileId)!; + }; + + const isQuery = (operation: IR.OperationObject): boolean => { + // 1. Check for hook override + const hookResult = plugin.hooks?.isQuery?.(operation); + if (hookResult !== undefined) { + return hookResult; + } + + // 2. Check for explicit override + // TODO: parser - add support for extensions in operation object to enhance mutability detection + /* if (operation.extensions?.['x-readonly'] !== undefined) { + return operation.extensions['x-readonly']; + } */ + + // 3. Use method as primary signal + if (['get', 'delete'].includes(operation.method)) { + return true; + } + + // 4. Consider body presence as secondary signal + // If method is not GET/DELETE but also has no body schema, likely a query + return !operation.body?.schema; + }; + + context.subscribe( + 'operation', + ({ operation }: { operation: IR.OperationObject }) => { + getFile(operation.tags?.[0] || 'default'); + + if (isQuery(operation)) { + createQueryFunction({ context, operation, plugin }); + } else { + createMutationFunction({ context, operation, plugin }); + } + }, + ); +}; diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/types.d.ts b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/types.d.ts new file mode 100644 index 0000000000..6e7b1fb2fe --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/types.d.ts @@ -0,0 +1,51 @@ +import type { IR } from '../../../ir/types'; +import type { Plugin } from '../../types'; + +export interface QueryHooks { + /** + * Customize the query key for a specific operation + */ + getQueryKey?: (operation: IR.OperationObject) => string[]; + + /** + * Override the default query detection logic for an operation. + * Return true to force treat as query, false to force treat as mutation. + * Return undefined to use default detection logic. + */ + isQuery?: (operation: IR.OperationObject) => boolean | undefined; + + /** + * Called when a mutation is created. Return false to skip generating this mutation. + */ + onMutation?: (operation: IR.OperationObject) => boolean | void; + + /** + * Called when a query is created. Return false to skip generating this query. + */ + onQuery?: (operation: IR.OperationObject) => boolean | void; +} + +export interface Config extends Plugin.Name<'@pinia-colada/sdk'> { + /** + * Export queries/mutations from index? + * @default true + */ + exportFromIndex?: boolean; + + /** + * Group queries by tag into separate files? + * @default false + */ + groupByTag?: boolean; + + /** + * Hooks to customize query/mutation generation + */ + hooks?: QueryHooks; + + /** + * Name of the generated file. + * @default 'queries' + */ + output?: string; +} diff --git a/packages/openapi-ts/src/plugins/types.d.ts b/packages/openapi-ts/src/plugins/types.d.ts index 396b33364b..2bd12f4969 100644 --- a/packages/openapi-ts/src/plugins/types.d.ts +++ b/packages/openapi-ts/src/plugins/types.d.ts @@ -13,6 +13,7 @@ export type PluginNames = | '@hey-api/sdk' | '@hey-api/transformers' | '@hey-api/typescript' + | '@pinia-colada/sdk' | '@tanstack/angular-query-experimental' | '@tanstack/react-query' | '@tanstack/solid-query' @@ -77,7 +78,7 @@ export namespace Plugin { export type Config = Config & Meta & { _handler: Plugin.Handler; - _handlerLegacy: Plugin.LegacyHandler; + _handlerLegacy?: Plugin.LegacyHandler; exportFromIndex?: boolean; }; From e3e92e8ed665847238e26dd6ba46f2599c3bcee7 Mon Sep 17 00:00:00 2001 From: josh-hemphill Date: Sun, 23 Mar 2025 08:01:54 -0700 Subject: [PATCH 02/11] updates --- .../sdk/__tests__/plugin.test.ts | 300 --- .../src/plugins/@pinia-colada/sdk/index.ts | 2 - .../src/plugins/@pinia-colada/sdk/plugin.ts | 284 --- .../src/plugins/@pinia-colada/sdk/types.d.ts | 51 - .../@pinia/colada/__tests__/plugin.test.ts | 433 ++++ .../sdk => @pinia/colada}/config.ts | 18 +- .../src/plugins/@pinia/colada/index.ts | 13 + .../src/plugins/@pinia/colada/mutation.ts | 134 ++ .../src/plugins/@pinia/colada/plugin.ts | 89 + .../src/plugins/@pinia/colada/query.ts | 132 ++ .../src/plugins/@pinia/colada/types.d.ts | 106 + .../src/plugins/@pinia/colada/utils.ts | 234 ++ packages/openapi-ts/src/plugins/index.ts | 7 + packages/openapi-ts/src/plugins/types.d.ts | 4 +- .../default/@pinia/colada/default.gen.ts | 421 ++++ .../plugins/@pinia/colada/default/index.ts | 2 + .../@pinia/colada/default/types.gen.ts | 1102 ++++++++++ .../default/@pinia/colada/default.gen.ts | 578 +++++ .../plugins/@pinia/colada/default/index.ts | 2 + .../@pinia/colada/default/types.gen.ts | 1933 ++++++++++++++++ .../plugins/@pinia/colada/default/index.ts | 2 + .../@pinia/colada/default/types.gen.ts | 1943 +++++++++++++++++ packages/openapi-ts/test/plugins.test.ts | 19 + 23 files changed, 7168 insertions(+), 641 deletions(-) delete mode 100644 packages/openapi-ts/src/plugins/@pinia-colada/sdk/__tests__/plugin.test.ts delete mode 100644 packages/openapi-ts/src/plugins/@pinia-colada/sdk/index.ts delete mode 100644 packages/openapi-ts/src/plugins/@pinia-colada/sdk/plugin.ts delete mode 100644 packages/openapi-ts/src/plugins/@pinia-colada/sdk/types.d.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia/colada/__tests__/plugin.test.ts rename packages/openapi-ts/src/plugins/{@pinia-colada/sdk => @pinia/colada}/config.ts (55%) create mode 100644 packages/openapi-ts/src/plugins/@pinia/colada/index.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia/colada/query.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts create mode 100644 packages/openapi-ts/src/plugins/@pinia/colada/utils.ts create mode 100644 packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/@pinia/colada/default.gen.ts create mode 100644 packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/index.ts create mode 100644 packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/types.gen.ts create mode 100644 packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/@pinia/colada/default.gen.ts create mode 100644 packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/index.ts create mode 100644 packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/types.gen.ts create mode 100644 packages/openapi-ts/test/__snapshots__/3.1.x/plugins/@pinia/colada/default/index.ts create mode 100644 packages/openapi-ts/test/__snapshots__/3.1.x/plugins/@pinia/colada/default/types.gen.ts diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/__tests__/plugin.test.ts b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/__tests__/plugin.test.ts deleted file mode 100644 index aaace885b1..0000000000 --- a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/__tests__/plugin.test.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { describe, expect, it, type Mock, vi } from 'vitest'; - -import { TypeScriptFile } from '../../../../generate/files'; -import type { IR } from '../../../../ir/types'; -import type { Plugin } from '../../../types'; -import { handler } from '../plugin'; -import type { Config } from '../types'; - -describe('@pinia-colada/sdk plugin', () => { - // Test setup helper to create a mock context and operation - const createMockContext = () => { - const file = new TypeScriptFile({ dir: '/', name: 'test.ts' }); - const files = new Map([['/test.ts', file]]); - - const subscribe = vi.fn() as Mock; - const fileGetter = vi.fn().mockReturnValue(file); - - return { - createFile: vi.fn().mockReturnValue(file), - file: fileGetter, - files, - subscribe, - } as unknown as IR.Context; - }; - - // Test: Query Generation - describe('query generation', () => { - it('generates a query function for GET operations', () => { - const context = createMockContext(); - const plugin: Plugin.Instance = { - exportFromIndex: false, - name: '@pinia-colada/sdk', - output: '', - }; - - const operation: IR.OperationObject = { - deprecated: false, - description: 'Retrieves a user by their unique identifier', - id: 'getUser', - method: 'get', - path: '/users/{id}', - summary: 'Get user by ID', - tags: ['users'], - } as IR.OperationObject; - - handler({ context, plugin }); - - // Verify subscription was created - expect(context.subscribe).toHaveBeenCalledWith( - 'operation', - expect.any(Function), - ); - - // Trigger the subscription callback - const callback = (context.subscribe as Mock).mock.calls[0][1]; - callback({ operation }); - - // Verify file content contains expected query definition - const file = context.file({ id: plugin.name }); - if (!file) throw new Error('File not found'); - - const content = file.toString(); - expect(content).toContain('export const useGetUserQuery'); - expect(content).toContain('defineQuery'); - expect(content).toContain('@pinia/colada'); - }); - }); - - // Test: Mutation Generation - describe('mutation generation', () => { - it('generates a mutation function for POST operations', () => { - const context = createMockContext(); - const plugin: Plugin.Instance = { - exportFromIndex: false, - name: '@pinia-colada/sdk', - output: '', - }; - - const operation: IR.OperationObject = { - deprecated: false, - id: 'createUser', - method: 'post', - path: '/users', - summary: 'Create new user', - tags: ['users'], - } as IR.OperationObject; - - handler({ context, plugin }); - - // Trigger the subscription callback - const callback = (context.subscribe as Mock).mock.calls[0][1]; - callback({ operation }); - - // Verify file content contains expected mutation definition - const file = context.file({ id: plugin.name }); - if (!file) throw new Error('File not found'); - - const content = file.toString(); - expect(content).toContain('export const useCreateUserMutation'); - expect(content).toContain('defineMutation'); - }); - }); - - // Test: File Grouping - describe('file grouping', () => { - it('groups operations by tag when groupByTag is true', () => { - const context = createMockContext(); - const plugin: Plugin.Instance = { - exportFromIndex: false, - groupByTag: true, - name: '@pinia-colada/sdk', - output: '', - }; - - const operation: IR.OperationObject = { - id: 'getUser', - method: 'get', - path: '/users/{id}', - tags: ['users'], - } as IR.OperationObject; - - handler({ context, plugin }); - - // Trigger the subscription callback - const callback = (context.subscribe as Mock).mock.calls[0][1]; - callback({ operation }); - - // Verify file was created with tag-based path - expect(context.createFile).toHaveBeenCalledWith( - expect.objectContaining({ - path: expect.stringContaining('/users'), - }), - ); - }); - }); - - // Test: Hook Customization - describe('hook customization', () => { - it('respects onQuery hook to skip query generation', () => { - const context = createMockContext(); - const plugin: Plugin.Instance = { - exportFromIndex: false, - hooks: { - onQuery: vi.fn().mockReturnValue(false), - }, - name: '@pinia-colada/sdk', - output: '', - }; - - const operation: IR.OperationObject = { - id: 'getUser', - method: 'get', - path: '/users/{id}', - } as IR.OperationObject; - - handler({ context, plugin }); - - // Trigger the subscription callback - const callback = (context.subscribe as Mock).mock.calls[0][1]; - callback({ operation }); - - // Verify hook was called - expect(plugin.hooks!.onQuery).toHaveBeenCalledWith(operation); - - // Verify no query was generated - const file = context.file({ id: plugin.name }); - if (!file) throw new Error('File not found'); - - const content = file.toString(); - expect(content).not.toContain('useGetUserQuery'); - }); - - it('uses custom query key from getQueryKey hook', () => { - const context = createMockContext(); - const plugin: Plugin.Instance = { - exportFromIndex: false, - hooks: { - getQueryKey: vi.fn().mockReturnValue(['custom', 'key']), - }, - name: '@pinia-colada/sdk', - output: '', - }; - - const operation: IR.OperationObject = { - id: 'getUser', - method: 'get', - path: '/users/{id}', - } as IR.OperationObject; - - handler({ context, plugin }); - - // Trigger the subscription callback - const callback = (context.subscribe as Mock).mock.calls[0][1]; - callback({ operation }); - - // Verify hook was called - expect(plugin.hooks!.getQueryKey).toHaveBeenCalledWith(operation); - - // Verify custom key was used - const file = context.file({ id: plugin.name }); - if (!file) throw new Error('File not found'); - - const content = file.toString(); - expect(content).toContain(`[\n 'custom',\n 'key'\n ]`); - }); - }); - - // Test: Operation ID Formatting - describe('operation id formatting', () => { - it('converts operation ids to camelCase', () => { - const context = createMockContext(); - const plugin: Plugin.Instance = { - exportFromIndex: false, - name: '@pinia-colada/sdk', - output: '', - }; - - const operations: IR.OperationObject[] = [ - { - id: 'get_user_profile', - method: 'get', - path: '/users/{id}/profile', - tags: ['users'], - } as IR.OperationObject, - { - id: 'UPDATE-USER-SETTINGS', - method: 'put', - path: '/users/{id}/settings', - tags: ['users'], - } as IR.OperationObject, - { - id: 'delete.user.account', - method: 'delete', - path: '/users/{id}', - tags: ['users'], - } as IR.OperationObject, - ]; - - handler({ context, plugin }); - - // Test each operation - operations.forEach((operation) => { - const callback = (context.subscribe as Mock).mock.calls[0][1]; - callback({ operation }); - }); - - // Verify file content contains properly camelCased function names - const file = context.file({ id: plugin.name }); - if (!file) throw new Error('File not found'); - - const content = file.toString(); - expect(content).toContain('useGetUserProfileQuery'); - expect(content).toContain('useUpdateUserSettingsMutation'); - expect(content).toContain('useDeleteUserAccountQuery'); - }); - }); - - // Test: Operation Type Detection - describe('operation type detection', () => { - it('respects isQuery hook override', () => { - const context = createMockContext(); - const plugin: Plugin.Instance = { - exportFromIndex: false, - hooks: { - isQuery: vi.fn().mockReturnValue(true), - }, - name: '@pinia-colada/sdk', - output: '', - }; - - // Test POST operation that would normally be a mutation - const operation: IR.OperationObject = { - body: { - mediaType: 'application/json', - schema: {}, - }, - id: 'createUser', - method: 'post', - path: '/users', - } as IR.OperationObject; - - handler({ context, plugin }); - - // Trigger the subscription callback - const callback = (context.subscribe as Mock).mock.calls[0][1]; - callback({ operation }); - - // Verify hook was called - expect(plugin.hooks!.isQuery).toHaveBeenCalledWith(operation); - - // Verify operation was treated as a query despite being POST - const file = context.file({ id: plugin.name }); - if (!file) throw new Error('File not found'); - - const content = file.toString(); - expect(content).toContain('useCreateUserQuery'); - expect(content).not.toContain('useCreateUserMutation'); - }); - }); -}); diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/index.ts b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/index.ts deleted file mode 100644 index 3a85a1be8a..0000000000 --- a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { defaultConfig, defineConfig } from './config'; -export type { Config } from './types'; diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/plugin.ts b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/plugin.ts deleted file mode 100644 index 9b68e04e6e..0000000000 --- a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/plugin.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { compiler } from '../../../compiler'; -import type { TypeScriptFile } from '../../../generate/files'; -import type { IR } from '../../../ir/types'; -import { escapeComment } from '../../../utils/escape'; -import { stringCase } from '../../../utils/stringCase'; -import { - importIdentifierData, - importIdentifierError, - importIdentifierResponse, -} from '../../@hey-api/typescript/ref'; -import type { Plugin } from '../../types'; -import type { Config } from './types'; - -const createQueryFunction = ({ - context, - operation, - plugin, -}: { - context: IR.Context; - operation: IR.OperationObject; - plugin: Plugin.Instance; -}) => { - const file = context.file({ id: plugin.name })!; - - // Allow hooks to customize or skip query generation - if (plugin.hooks?.onQuery && plugin.hooks.onQuery(operation) === false) { - return; - } - - // Get query key from hooks or generate default - const queryKey = plugin.hooks?.getQueryKey?.(operation) ?? [ - operation.tags?.[0] || 'default', - operation.id, - ]; - - // Import response type for type parameters - const identifierResponse = importIdentifierResponse({ - context, - file, - operation, - }); - const identifierError = importIdentifierError({ context, file, operation }); - - const queryFn = compiler.arrowFunction({ - async: true, - parameters: [], - statements: [ - compiler.returnStatement({ - expression: compiler.callExpression({ - functionName: 'client', - parameters: [ - compiler.objectExpression({ - obj: [ - { - key: 'method', - value: operation.method, - }, - { - key: 'url', - value: operation.path, - }, - ], - }), - ], - }), - }), - ], - }); - - const node = compiler.constVariable({ - comment: [ - operation.deprecated && '@deprecated', - operation.summary && escapeComment(operation.summary), - operation.description && escapeComment(operation.description), - ], - exportConst: true, - expression: compiler.callExpression({ - functionName: 'defineQuery', - parameters: [ - compiler.objectExpression({ - obj: [ - { - key: 'key', - value: compiler.arrayLiteralExpression({ - elements: queryKey.map((k: string) => compiler.ots.string(k)), - }), - }, - { - key: 'query', - value: queryFn, - }, - ], - }), - ], - types: [ - compiler.typeReferenceNode({ - typeName: identifierResponse?.name || 'unknown', - }), - compiler.typeReferenceNode({ - typeName: identifierError?.name || 'unknown', - }), - ], - }), - name: `use${stringCase({ case: 'PascalCase', value: operation.id })}Query`, - }); - - file.import({ - module: '@pinia/colada', - name: 'defineQuery', - }); - - file.add(node); -}; - -const createMutationFunction = ({ - context, - operation, - plugin, -}: { - context: IR.Context; - operation: IR.OperationObject; - plugin: Plugin.Instance; -}) => { - const file = context.file({ id: plugin.name })!; - - // Allow hooks to customize or skip mutation generation - if ( - plugin.hooks?.onMutation && - plugin.hooks.onMutation(operation) === false - ) { - return; - } - - // Import response type for type parameters - const identifierResponse = importIdentifierResponse({ - context, - file, - operation, - }); - const identifierError = importIdentifierError({ context, file, operation }); - const identifierData = importIdentifierData({ context, file, operation }); - - const mutationFn = compiler.arrowFunction({ - async: true, - parameters: [ - { - name: 'data', - }, - ], - statements: [ - compiler.returnStatement({ - expression: compiler.callExpression({ - functionName: 'client', - parameters: [ - compiler.objectExpression({ - obj: [ - { - key: 'method', - value: operation.method, - }, - { - key: 'url', - value: operation.path, - }, - { - key: 'data', - shorthand: true, - value: compiler.identifier({ text: 'data' }), - }, - ], - }), - ], - }), - }), - ], - }); - - const node = compiler.constVariable({ - comment: [ - operation.deprecated && '@deprecated', - operation.summary && escapeComment(operation.summary), - operation.description && escapeComment(operation.description), - ], - exportConst: true, - expression: compiler.callExpression({ - functionName: 'defineMutation', - parameters: [ - compiler.objectExpression({ - obj: [ - { - key: 'mutation', - value: mutationFn, - }, - ], - }), - ], - types: [ - compiler.typeReferenceNode({ - typeName: identifierData?.name || 'unknown', - }), - compiler.typeReferenceNode({ - typeName: identifierResponse?.name || 'unknown', - }), - compiler.typeReferenceNode({ - typeName: identifierError?.name || 'unknown', - }), - ], - }), - name: `use${stringCase({ case: 'PascalCase', value: operation.id })}Mutation`, - }); - - file.import({ - module: '@pinia/colada', - name: 'defineMutation', - }); - - file.add(node); -}; - -export const handler: Plugin.Handler = ({ - context, - plugin, -}: { - context: IR.Context; - plugin: Plugin.Instance; -}) => { - // Create files based on grouping strategy - const files = new Map(); - - const getFile = (tag: string) => { - if (!plugin.groupByTag) { - return context.file({ id: plugin.name })!; - } - - const fileId = `${plugin.name}/${tag}`; - if (!files.has(fileId)) { - files.set( - fileId, - context.createFile({ - exportFromIndex: plugin.exportFromIndex, - id: fileId, - path: `${plugin.output}/${tag}`, - }), - ); - } - return files.get(fileId)!; - }; - - const isQuery = (operation: IR.OperationObject): boolean => { - // 1. Check for hook override - const hookResult = plugin.hooks?.isQuery?.(operation); - if (hookResult !== undefined) { - return hookResult; - } - - // 2. Check for explicit override - // TODO: parser - add support for extensions in operation object to enhance mutability detection - /* if (operation.extensions?.['x-readonly'] !== undefined) { - return operation.extensions['x-readonly']; - } */ - - // 3. Use method as primary signal - if (['get', 'delete'].includes(operation.method)) { - return true; - } - - // 4. Consider body presence as secondary signal - // If method is not GET/DELETE but also has no body schema, likely a query - return !operation.body?.schema; - }; - - context.subscribe( - 'operation', - ({ operation }: { operation: IR.OperationObject }) => { - getFile(operation.tags?.[0] || 'default'); - - if (isQuery(operation)) { - createQueryFunction({ context, operation, plugin }); - } else { - createMutationFunction({ context, operation, plugin }); - } - }, - ); -}; diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/types.d.ts b/packages/openapi-ts/src/plugins/@pinia-colada/sdk/types.d.ts deleted file mode 100644 index 6e7b1fb2fe..0000000000 --- a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/types.d.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { IR } from '../../../ir/types'; -import type { Plugin } from '../../types'; - -export interface QueryHooks { - /** - * Customize the query key for a specific operation - */ - getQueryKey?: (operation: IR.OperationObject) => string[]; - - /** - * Override the default query detection logic for an operation. - * Return true to force treat as query, false to force treat as mutation. - * Return undefined to use default detection logic. - */ - isQuery?: (operation: IR.OperationObject) => boolean | undefined; - - /** - * Called when a mutation is created. Return false to skip generating this mutation. - */ - onMutation?: (operation: IR.OperationObject) => boolean | void; - - /** - * Called when a query is created. Return false to skip generating this query. - */ - onQuery?: (operation: IR.OperationObject) => boolean | void; -} - -export interface Config extends Plugin.Name<'@pinia-colada/sdk'> { - /** - * Export queries/mutations from index? - * @default true - */ - exportFromIndex?: boolean; - - /** - * Group queries by tag into separate files? - * @default false - */ - groupByTag?: boolean; - - /** - * Hooks to customize query/mutation generation - */ - hooks?: QueryHooks; - - /** - * Name of the generated file. - * @default 'queries' - */ - output?: string; -} diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/__tests__/plugin.test.ts b/packages/openapi-ts/src/plugins/@pinia/colada/__tests__/plugin.test.ts new file mode 100644 index 0000000000..ac686df23b --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/__tests__/plugin.test.ts @@ -0,0 +1,433 @@ +import { describe, expect, it, type Mock, vi } from 'vitest'; + +import { TypeScriptFile } from '../../../../generate/files'; +import type { IR } from '../../../../ir/types'; +import type { Plugin } from '../../../types'; +import { handler } from '../plugin'; +import type { Config } from '../types'; + +describe('@pinia/colada plugin', () => { + // Test setup helper to create a mock context and operation + const createMockContext = () => { + const file = new TypeScriptFile({ dir: '/', name: 'test.ts' }); + const files = new Map([['/test.ts', file]]); + + const subscribe = vi.fn() as Mock; + const fileGetter = vi.fn().mockReturnValue(file); + + return { + createFile: vi.fn().mockReturnValue(file), + file: fileGetter, + files, + subscribe, + } as unknown as IR.Context; + }; + + const basicBody = { + mediaType: 'application/json', + schema: { + properties: { + name: { type: 'string' } as IR.SchemaObject, + }, + required: ['name'], + type: 'object', + } as IR.SchemaObject, + }; + + // Test: Query Generation + describe('query generation', () => { + it('generates a query function for GET operations', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + name: '@pinia/colada', + output: '', + }; + + const operation: IR.OperationObject = { + deprecated: false, + description: 'Retrieves a user by their unique identifier', + id: 'getUser', + method: 'get', + path: '/users/{id}', + summary: 'Get user by ID', + tags: ['users'], + } as IR.OperationObject; + + handler({ context, plugin }); + + // Verify subscription was created + expect(context.subscribe).toHaveBeenCalledWith( + 'operation', + expect.any(Function), + ); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify file content contains expected query definition + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain('export const useGetUserQuery'); + expect(content).toContain('defineQuery'); + expect(content).toContain('@pinia/colada'); + }); + }); + + // Test: Mutation Generation + describe('mutation generation', () => { + it('generates a mutation function for POST operations with body', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + name: '@pinia/colada', + output: '', + }; + + const operation: IR.OperationObject = { + body: basicBody, + deprecated: false, + id: 'createUser', + method: 'post', + path: '/users', + summary: 'Create new user', + tags: ['users'], + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify file content contains expected mutation definition + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain('export const useCreateUserMutation'); + expect(content).toContain('defineMutation'); + }); + }); + + // Test: File Grouping + describe('file grouping', () => { + it('groups operations by tag when groupByTag is true', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + groupByTag: true, + name: '@pinia/colada', + output: '', + }; + + const operation: IR.OperationObject = { + id: 'getUser', + method: 'get', + path: '/users/{id}', + tags: ['users'], + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify file was created with tag-based path + expect(context.createFile).toHaveBeenCalledWith( + expect.objectContaining({ + path: expect.stringContaining('/users'), + }), + ); + }); + }); + + // Test: Hook Customization + // TODO: parser - add support for hooks + /* describe('hook customization', () => { + it('respects onQuery hook to skip query generation', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + hooks: { + onQuery: vi.fn().mockReturnValue(false), + }, + name: '@pinia/colada', + output: '', + }; + + const operation: IR.OperationObject = { + id: 'getUser', + method: 'get', + path: '/users/{id}', + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify hook was called + expect(plugin.hooks!.onQuery).toHaveBeenCalledWith(operation); + + // Verify no query was generated + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).not.toContain('useGetUserQuery'); + }); + + it('uses custom query key from getQueryKey hook', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + hooks: { + getQueryKey: vi.fn().mockReturnValue(['custom', 'key']), + }, + name: '@pinia/colada', + output: '', + }; + + const operation: IR.OperationObject = { + id: 'getUser', + method: 'get', + path: '/users/{id}', + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify hook was called + expect(plugin.hooks!.getQueryKey).toHaveBeenCalledWith(operation); + + // Verify custom key was used + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain(`[\n 'custom',\n 'key'\n ]`); + }); + }); */ + + // Test: Operation ID Formatting + describe('operation id formatting', () => { + it('converts operation ids to camelCase', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + name: '@pinia/colada', + output: '', + }; + + const operations: IR.OperationObject[] = [ + { + id: 'get_user_profile', + method: 'get', + path: '/users/{id}/profile', + tags: ['users'], + } as IR.OperationObject, + { + body: basicBody, + id: 'UPDATE-USER-SETTINGS', + method: 'put', + path: '/users/{id}/settings', + tags: ['users'], + } as IR.OperationObject, + { + id: 'delete.user.account', + method: 'delete', + path: '/users/{id}', + tags: ['users'], + } as IR.OperationObject, + ]; + + handler({ context, plugin }); + + // Test each operation + operations.forEach((operation) => { + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + }); + + // Verify file content contains properly camelCased function names + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain('useGetUserProfileQuery'); + expect(content).toContain('useUpdateUserSettingsMutation'); + expect(content).toContain('useDeleteUserAccountQuery'); + }); + }); + + // Test: Operation Type Detection + // TODO: parser - add support for hooks + /* describe('operation type detection', () => { + it('respects isQuery hook override', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + hooks: { + isQuery: vi.fn().mockReturnValue(true), + }, + name: '@pinia/colada', + output: '', + }; + + // Test POST operation that would normally be a mutation + const operation: IR.OperationObject = { + body: { + mediaType: 'application/json', + schema: {}, + }, + id: 'createUser', + method: 'post', + path: '/users', + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Verify hook was called + expect(plugin.hooks!.isQuery).toHaveBeenCalledWith(operation); + + // Verify operation was treated as a query despite being POST + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + + const content = file.toString(); + expect(content).toContain('useCreateUserQuery'); + expect(content).not.toContain('useCreateUserMutation'); + }); + }); */ + + // Test: Code Generation Snapshots + describe('code generation snapshots', () => { + it('generates properly typed query with response and error types', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + name: '@pinia/colada', + output: '', + }; + + const operation: IR.OperationObject = { + deprecated: false, + description: 'Get user profile data', + id: 'getUserProfile', + method: 'get', + path: '/users/{id}/profile', + responses: { + '200': { + mediaType: 'application/json', + schema: { + properties: { + email: { type: 'string' } as IR.SchemaObject, + id: { type: 'number' } as IR.SchemaObject, + name: { type: 'string' } as IR.SchemaObject, + }, + type: 'object', + } as IR.SchemaObject, + } as IR.ResponseObject, + '404': { + mediaType: 'application/json', + schema: { + properties: { + message: { type: 'string' } as IR.SchemaObject, + }, + type: 'object', + } as IR.SchemaObject, + } as IR.ResponseObject, + } as IR.ResponsesObject, + summary: 'Get user profile', + tags: ['users'], + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Get generated code + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + expect(file.toString()).toMatchSnapshot('query-with-types'); + }); + + it('generates properly typed query with hook overrides', () => { + const context = createMockContext(); + const plugin: Plugin.Instance = { + exportFromIndex: false, + name: '@pinia/colada', + onMutation: vi.fn().mockReturnValue(true), + onQuery: vi.fn().mockReturnValue(true), + output: '', + resolveQuery: vi.fn().mockReturnValue(true), + resolveQueryKey: vi.fn().mockReturnValue(['users', 'search']), + }; + + const operation: IR.OperationObject = { + body: { + mediaType: 'application/json', + schema: { + properties: { + query: { type: 'string' } as IR.SchemaObject, + }, + type: 'object', + } as IR.SchemaObject, + type: 'json', + }, + deprecated: false, + description: 'Search users with POST body', + id: 'searchUsers', + method: 'post', + path: '/users/search', + responses: { + '200': { + mediaType: 'application/json', + schema: { + items: [ + { + properties: { + id: { type: 'number' } as IR.SchemaObject, + name: { type: 'string' } as IR.SchemaObject, + }, + type: 'object', + } as IR.SchemaObject, + ], + type: 'array', + } as IR.SchemaObject, + } as IR.ResponseObject, + } as IR.ResponsesObject, + summary: 'Search users', + tags: ['users'], + } as IR.OperationObject; + + handler({ context, plugin }); + + // Trigger the subscription callback + const callback = (context.subscribe as Mock).mock.calls[0][1]; + callback({ operation }); + + // Get generated code + const file = context.file({ id: plugin.name }); + if (!file) throw new Error('File not found'); + expect(file.toString()).toMatchSnapshot( + 'typed-query-with-hook-overrides', + ); + }); + }); +}); diff --git a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/config.ts b/packages/openapi-ts/src/plugins/@pinia/colada/config.ts similarity index 55% rename from packages/openapi-ts/src/plugins/@pinia-colada/sdk/config.ts rename to packages/openapi-ts/src/plugins/@pinia/colada/config.ts index f2be0a4039..0307d4cc49 100644 --- a/packages/openapi-ts/src/plugins/@pinia-colada/sdk/config.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/config.ts @@ -2,12 +2,26 @@ import type { Plugin } from '../../types'; import { handler } from './plugin'; import type { Config } from './types'; +export const PluginName = '@pinia/colada'; + +/** + * Default options for the Pinia Colada plugin + */ export const defaultConfig: Plugin.Config = { _dependencies: ['@hey-api/typescript'], _handler: handler, + enableCaching: false, + enablePaginationOnKey: undefined, + errorHandling: 'specific', exportFromIndex: true, - name: '@pinia-colada/sdk', - output: 'queries', + groupByTag: false, + importPath: PluginName, + includeTypes: true, + name: PluginName, + output: PluginName, + resolveQuery: undefined, + resolveQueryKey: undefined, + useInfiniteQueries: false, }; /** diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/index.ts b/packages/openapi-ts/src/plugins/@pinia/colada/index.ts new file mode 100644 index 0000000000..98f39ae1fc --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/index.ts @@ -0,0 +1,13 @@ +import { PluginName } from './config'; +import { handler } from './plugin.js'; +export { defaultConfig, defineConfig } from './config'; +export type { Config } from './types'; + +/** + * Pinia Colada plugin for generating query and mutation functions + * that work with @pinia/colada + */ +export default { + handler, + name: PluginName, +}; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts b/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts new file mode 100644 index 0000000000..2e6303be91 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts @@ -0,0 +1,134 @@ +import { compiler } from '../../../compiler'; +import type { TypeScriptFile } from '../../../generate/files'; +import type { IR } from '../../../ir/types'; +import { escapeComment } from '../../../utils/escape'; +import { + importIdentifierData, + importIdentifierError, + importIdentifierResponse, +} from '../../@hey-api/typescript/ref'; +import type { Plugin } from '../../types'; +import type { Config } from './types'; +import { createParamTypes, generateFunctionName } from './utils'; + +/** + * Creates a mutation function for an operation + */ +export const createMutationFunction = ({ + context, + file, + operation, + plugin, +}: { + context: IR.Context; + file: TypeScriptFile; + operation: IR.OperationObject; + plugin: Plugin.Instance; +}) => { + // Allow hooks to customize or skip mutation generation + if (plugin?.onMutation && plugin.onMutation(operation) === false) { + return; + } + + // Import necessary functions and types + file.import({ + module: '@pinia/colada', + name: 'defineMutation', + }); + + // Import response/error/data types for type parameters + const identifierResponse = importIdentifierResponse({ + context, + file, + operation, + }); + const identifierError = importIdentifierError({ context, file, operation }); + const identifierData = importIdentifierData({ context, file, operation }); + + // Get parameter types + const paramTypes = createParamTypes({ context, operation, plugin }); + + const mutationOptions = [ + { + key: 'mutation', + value: compiler.arrowFunction({ + async: true, + parameters: [ + { + name: 'data', + type: identifierData?.name + ? compiler.typeReferenceNode({ + typeName: identifierData.name, + }) + : undefined, + }, + ], + statements: [ + compiler.returnStatement({ + expression: compiler.callExpression({ + functionName: 'client', + parameters: [ + compiler.objectExpression({ + obj: [ + { + key: 'method', + value: operation.method, + }, + { + key: 'url', + value: operation.path, + }, + // Only include data if it's a valid body parameter + operation.body + ? { + key: 'data', + shorthand: true, + value: compiler.identifier({ text: 'data' }), + } + : null, + ...paramTypes.map(({ key }) => ({ + key, + value: compiler.identifier({ text: key }), + })), + ].filter(Boolean), + }), + ], + }), + }), + ], + }), + }, + ]; + + const node = compiler.constVariable({ + comment: [ + operation.deprecated && '@deprecated', + operation.summary && escapeComment(operation.summary), + operation.description && escapeComment(operation.description), + ].filter(Boolean), + exportConst: true, + expression: compiler.callExpression({ + functionName: 'defineMutation', + parameters: [ + compiler.objectExpression({ + multiLine: true, + obj: mutationOptions, + }), + ], + types: [ + compiler.typeReferenceNode({ + typeName: identifierData?.name || 'unknown', + }), + compiler.typeReferenceNode({ + typeName: identifierResponse?.name || 'unknown', + }), + compiler.typeReferenceNode({ + typeName: identifierError?.name || 'unknown', + }), + ], + }), + name: generateFunctionName(operation, false), + }); + + file.add(node); +}; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts new file mode 100644 index 0000000000..bc01754976 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts @@ -0,0 +1,89 @@ +import type { IR } from '../../../ir/types'; +import type { Plugin } from '../../types'; +import { createMutationFunction } from './mutation'; +import { createQueryFunction } from './query'; +import type { Config } from './types'; +import { isQuery } from './utils'; + +/** + * Main handler for the Pinia Colada plugin + */ +export const handler: Plugin.Handler = ({ + context, + plugin, +}: { + context: IR.Context; + plugin: Plugin.Instance; +}) => { + const { enableCaching, exportFromIndex, groupByTag, name, output } = plugin; + + // Create default configuration if not provided + if (enableCaching && plugin.defaultCacheTime === undefined) { + plugin.defaultCacheTime = 300000; // 5 minutes + } + + if (enableCaching && plugin.defaultStaleTime === undefined) { + plugin.defaultStaleTime = 0; // No stale time by default + } + + if (!groupByTag) { + context.createFile({ + exportFromIndex, + id: name, + path: output, + }); + } + + // Create files based on grouping strategy + const getFile = (tag: string) => { + if (!groupByTag) { + return ( + context.file({ id: name }) ?? + context.createFile({ + exportFromIndex, + id: name, + path: output, + }) + ); + } + + const fileId = `${name}/${tag}`; + return ( + context.file({ id: fileId }) ?? + context.createFile({ + exportFromIndex, + id: fileId, + path: `${output}/${tag}`, + }) + ); + }; + + // Process each operation as it's discovered + context.subscribe( + 'operation', + ({ operation }: { operation: IR.OperationObject }) => { + const file = getFile(operation.tags?.[0] || 'default'); + + // Determine if the operation should be a query or mutation + if (isQuery(operation, plugin)) { + createQueryFunction({ context, file, operation, plugin }); + } else { + createMutationFunction({ context, file, operation, plugin }); + } + }, + ); + + // Clean up and finalize after processing + context.subscribe('after', () => { + // Add client import to all generated files + Object.entries(context.files).forEach(([fileId, file]) => { + if (fileId.startsWith(name)) { + // Make sure we have a client import + file.import({ + module: '@pinia/colada/client', + name: 'client', + }); + } + }); + }); +}; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/query.ts b/packages/openapi-ts/src/plugins/@pinia/colada/query.ts new file mode 100644 index 0000000000..05ed7c3c92 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/query.ts @@ -0,0 +1,132 @@ +import { compiler } from '../../../compiler'; +import type { TypeScriptFile } from '../../../generate/files'; +import type { IR } from '../../../ir/types'; +import { escapeComment } from '../../../utils/escape'; +import { + importIdentifierError, + importIdentifierResponse, +} from '../../@hey-api/typescript/ref'; +import type { Plugin } from '../../types'; +import type { Config } from './types'; +import { + createParamTypes, + generateCacheConfig, + generateFunctionName, +} from './utils'; + +/** + * Creates a query function for an operation + */ +export const createQueryFunction = ({ + context, + file, + operation, + plugin, +}: { + context: IR.Context; + file: TypeScriptFile; + operation: IR.OperationObject; + plugin: Plugin.Instance; +}) => { + // Allow hooks to customize or skip query generation + if (plugin?.onQuery && plugin.onQuery(operation) === false) { + return; + } + + // Import necessary functions and types + file.import({ + module: '@pinia/colada', + name: 'defineQuery', + }); + + // Import response type for type parameters + const identifierResponse = importIdentifierResponse({ + context, + file, + operation, + }); + const identifierError = importIdentifierError({ context, file, operation }); + + // Get query key from hooks or generate default + const queryKey = plugin?.resolveQueryKey?.(operation) ?? [ + operation.tags?.[0] || 'default', + operation.id, + ]; + + // Get parameter types + const paramTypes = createParamTypes({ context, operation, plugin }); + + // Generate cache configuration + const cacheConfig = generateCacheConfig(operation, plugin); + + const queryOptions = [ + { + key: 'key', + value: compiler.arrayLiteralExpression({ + elements: queryKey.map((k: string) => compiler.ots.string(k)), + }), + }, + { + key: 'query', + value: compiler.arrowFunction({ + async: true, + parameters: [], + statements: [ + compiler.returnStatement({ + expression: compiler.callExpression({ + functionName: 'client', + parameters: [ + compiler.objectExpression({ + obj: [ + { + key: 'method', + value: operation.method, + }, + { + key: 'url', + value: operation.path, + }, + ...paramTypes.map(({ key }) => ({ + key, + value: compiler.identifier({ text: key }), + })), + ], + }), + ], + }), + }), + ], + }), + }, + ...cacheConfig, + ]; + + const node = compiler.constVariable({ + comment: [ + operation.deprecated && '@deprecated', + operation.summary && escapeComment(operation.summary), + operation.description && escapeComment(operation.description), + ].filter(Boolean), + exportConst: true, + expression: compiler.callExpression({ + functionName: 'defineQuery', + parameters: [ + compiler.objectExpression({ + multiLine: true, + obj: queryOptions, + }), + ], + types: [ + compiler.typeReferenceNode({ + typeName: identifierResponse?.name || 'unknown', + }), + compiler.typeReferenceNode({ + typeName: identifierError?.name || 'unknown', + }), + ], + }), + name: generateFunctionName(operation, true), + }); + + file.add(node); +}; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts b/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts new file mode 100644 index 0000000000..d8687846c4 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts @@ -0,0 +1,106 @@ +import type { IR } from '../../../ir/types'; +import type { Plugin } from '../../types'; + +export interface Config extends Plugin.Name<'@pinia/colada'> { + /** + * Default cache time for queries in milliseconds. + * @default 300000 (5 minutes) + */ + defaultCacheTime?: number; + + /** + * Default stale time for queries in milliseconds. + * @default 0 (no stale time) + */ + defaultStaleTime?: number; + + /** + * Enable caching for queries. + * @default false + */ + enableCaching?: boolean; + + /** + * Enable pagination support on this key when found in the query parameters or body. + * @default undefined + */ + enablePaginationOnKey?: string; + + /** + * How to handle error responses. + * 'unified' - Unified error type for all errors + * 'specific' - Specific error types per operation + * @default 'specific' + */ + errorHandling?: 'unified' | 'specific'; + + /** + * Export types from index barrel file. + * @default false + */ + exportFromIndex?: boolean; + + /** + * Group operations by tag into separate files. + * @default false + */ + groupByTag?: boolean; + + /** + * Import path for the plugin. + * @default '@pinia/colada' + */ + importPath?: string; + + /** + * Include types in the generated files. + * @default true + */ + includeTypes?: boolean; + + /** + * Plugin name. + * @default '@pinia/colada' + */ + name: string; + + /** + * Custom hook to customize or skip mutation generation. + * Return false to skip generating a mutation for this operation. + * @default undefined + */ + onMutation?: (operation: IR.OperationObject) => boolean | undefined; + + /** + * Custom hook to customize or skip query generation. + * Return false to skip generating a query for this operation. + * @default undefined + */ + onQuery?: (operation: IR.OperationObject) => boolean | undefined; + + /** + * Plugin output path. + * @default '@pinia/colada' + */ + output: string; + + /** + * Custom hook that determines if an operation should be a query or not. + * Return true to force query, false to force mutation, undefined to use default logic. + * @default undefined + */ + resolveQuery?: (operation: IR.OperationObject) => boolean | undefined; + + /** + * Custom hook to resolve query key. + * Default is [operation.tags?.[0] || 'default', operation.id] + * @default undefined + */ + resolveQueryKey?: (operation: IR.OperationObject) => Array; + + /** + * Use infinite queries. + * @default false + */ + useInfiniteQueries?: boolean; +} diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts b/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts new file mode 100644 index 0000000000..6334a05afe --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts @@ -0,0 +1,234 @@ +import type ts from 'typescript'; + +import { compiler } from '../../../compiler'; +import type { IR } from '../../../ir/types'; +import { stringCase } from '../../../utils/stringCase'; +import { schemaToType } from '../../@hey-api/typescript/plugin'; +import type { Plugin } from '../../types'; +import type { Config } from './types'; + +/** + * Creates parameter types from operation parameters + */ +export const createParamTypes = ({ + context, + operation, + plugin, +}: { + context: IR.Context; + operation: IR.OperationObject; + plugin: Plugin.Instance; +}) => { + const paramTypes: Array<{ + initialValue?: string; + key: string; + required: boolean; + type: ts.TypeNode; + }> = []; + + // Path parameters + if (operation.parameters?.path) { + const pathParams = Object.entries(operation.parameters.path).map( + ([name, param]) => ({ + key: name, + required: param.required ?? false, + type: schemaToType({ + context, + plugin: plugin as any, // Type assertion to work around type mismatch + schema: param.schema, + }), + }), + ); + if (pathParams.length > 0) { + paramTypes.push({ + key: 'path', + required: pathParams.some((p) => p.required), + type: compiler.typeInterfaceNode({ + properties: pathParams.map(({ key, required, type }) => ({ + isRequired: required, + name: key, + type, + })), + useLegacyResolution: false, + }), + }); + } + } + + // Query parameters + if (operation.parameters?.query) { + const queryParams = Object.entries(operation.parameters.query).map( + ([name, param]) => ({ + key: name, + required: param.required ?? false, + type: schemaToType({ + context, + plugin: plugin as any, // Type assertion to work around type mismatch + schema: param.schema, + }), + }), + ); + if (queryParams.length > 0) { + paramTypes.push({ + key: 'query', + required: queryParams.some((p) => p.required), + type: compiler.typeInterfaceNode({ + properties: queryParams.map(({ key, required, type }) => ({ + isRequired: required, + name: key, + type, + })), + useLegacyResolution: false, + }), + }); + } + } + + // Header parameters + if (operation.parameters?.header) { + const headerParams = Object.entries(operation.parameters.header).map( + ([name, param]) => ({ + key: name, + required: param.required ?? false, + type: schemaToType({ + context, + plugin: plugin as any, // Type assertion to work around type mismatch + schema: param.schema, + }), + }), + ); + if (headerParams.length > 0) { + paramTypes.push({ + key: 'headers', + required: headerParams.some((p) => p.required), + type: compiler.typeInterfaceNode({ + properties: headerParams.map(({ key, required, type }) => ({ + isRequired: required, + name: key, + type, + })), + useLegacyResolution: false, + }), + }); + } + } + + // Cookie parameters + if (operation.parameters?.cookie) { + const cookieParams = Object.entries(operation.parameters.cookie).map( + ([name, param]) => ({ + key: name, + required: param.required ?? false, + type: schemaToType({ + context, + plugin: plugin as any, // Type assertion to work around type mismatch + schema: param.schema, + }), + }), + ); + if (cookieParams.length > 0) { + paramTypes.push({ + key: 'cookies', + required: cookieParams.some((p) => p.required), + type: compiler.typeInterfaceNode({ + properties: cookieParams.map(({ key, required, type }) => ({ + isRequired: required, + name: key, + type, + })), + useLegacyResolution: false, + }), + }); + } + } + + return paramTypes; +}; + +/** + * Determines if an operation should be a query or mutation + */ +export const isQuery = ( + operation: IR.OperationObject, + plugin: Plugin.Instance, +): boolean => { + // 1. Check for hook override + const hookResult = plugin?.resolveQuery?.(operation); + if (hookResult !== undefined) { + return hookResult; + } + + // 2. Use method as primary signal + if (['get', 'head', 'options'].includes(operation.method)) { + return true; + } + + // 3. Consider body presence as secondary signal + // If method is not GET/HEAD/OPTIONS but also has no body schema, likely a query + return !operation.body?.schema; +}; + +/** + * Generates the cache configuration object for a query + */ +export const generateCacheConfig = ( + operation: IR.OperationObject, + plugin: Plugin.Instance, +) => { + const obj: Array<{ + key: string; + value: any; + }> = []; + + // Use default stale time if specified in config + if (plugin.defaultStaleTime !== undefined) { + obj.push({ + key: 'staleTime', + value: plugin.defaultStaleTime, + }); + } + + // Use default cache time if specified in config + if (plugin.defaultCacheTime !== undefined) { + obj.push({ + key: 'gcTime', + value: plugin.defaultCacheTime, + }); + } + + // Add pagination config if enabled and operation has pagination parameters + if ( + plugin.enablePaginationOnKey && + hasPagination(operation, plugin.enablePaginationOnKey) + ) { + obj.push({ + key: 'infinite', + value: true, + }); + } + + return obj; +}; + +/** + * Checks if operation has pagination parameters + */ +export const hasPagination = ( + operation: IR.OperationObject, + paginationParam: string, +): boolean => + // Check if operation has pagination parameter + !!operation.parameters?.query?.[paginationParam] || + !!operation.body?.pagination; + +/** + * Generates the function name for an operation + */ +export const generateFunctionName = ( + operation: IR.OperationObject, + isQueryType: boolean, +): string => { + const prefix = isQueryType ? 'use' : 'use'; + const suffix = isQueryType ? 'Query' : 'Mutation'; + return `${prefix}${stringCase({ case: 'PascalCase', value: operation.id })}${suffix}`; +}; diff --git a/packages/openapi-ts/src/plugins/index.ts b/packages/openapi-ts/src/plugins/index.ts index d194d87c95..587bd05e27 100644 --- a/packages/openapi-ts/src/plugins/index.ts +++ b/packages/openapi-ts/src/plugins/index.ts @@ -50,6 +50,10 @@ import { type Config as HeyApiTypeScript, defaultConfig as heyApiTypeScript, } from './@hey-api/typescript'; +import { + type Config as PiniaColadaSdk, + defaultConfig as piniaColadaSdk, +} from './@pinia/colada'; import { type Config as TanStackAngularQueryExperimental, defaultConfig as tanStackAngularQueryExperimental, @@ -91,6 +95,7 @@ export type UserPlugins = | Plugin.UserConfig | Plugin.UserConfig | Plugin.UserConfig + | Plugin.UserConfig | Plugin.UserConfig | Plugin.UserConfig | Plugin.UserConfig @@ -116,6 +121,7 @@ export type ClientPlugins = | Plugin.Config | Plugin.Config | Plugin.Config + | Plugin.Config | Plugin.Config | Plugin.Config | Plugin.Config @@ -133,6 +139,7 @@ export const defaultPluginConfigs: DefaultPluginConfigs = { '@hey-api/sdk': heyApiSdk, '@hey-api/transformers': heyApiTransformers, '@hey-api/typescript': heyApiTypeScript, + '@pinia/colada': piniaColadaSdk, '@tanstack/angular-query-experimental': tanStackAngularQueryExperimental, '@tanstack/react-query': tanStackReactQuery, '@tanstack/solid-query': tanStackSolidQuery, diff --git a/packages/openapi-ts/src/plugins/types.d.ts b/packages/openapi-ts/src/plugins/types.d.ts index 932bf2aea2..accce1dc75 100644 --- a/packages/openapi-ts/src/plugins/types.d.ts +++ b/packages/openapi-ts/src/plugins/types.d.ts @@ -27,7 +27,7 @@ export type PluginNames = | '@hey-api/sdk' | '@hey-api/transformers' | '@hey-api/typescript' - | '@pinia-colada/sdk' + | '@pinia/colada' | '@tanstack/angular-query-experimental' | '@tanstack/react-query' | '@tanstack/solid-query' @@ -84,7 +84,7 @@ export type DefaultPluginConfigs = { [K in PluginNames]: BaseConfig & Meta & { _handler: Plugin.Handler>>; - _handlerLegacy: Plugin.LegacyHandler>>; + _handlerLegacy?: Plugin.LegacyHandler>>; }; }; diff --git a/packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/@pinia/colada/default.gen.ts b/packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/@pinia/colada/default.gen.ts new file mode 100644 index 0000000000..7540e0d471 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/@pinia/colada/default.gen.ts @@ -0,0 +1,421 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { defineQuery, defineMutation } from '@pinia/colada'; +import type { CallWithResponseAndNoContentResponseResponse, CallWithResponseResponse, CallWithDuplicateResponsesResponse, CallWithDuplicateResponsesError, CallWithResponsesResponse, CallWithResponsesError, TypesResponse, ComplexTypesResponse, NonAsciiæøåÆøÅöôêÊ字符串Response, PostApiVbyApiVersionBodyData, PostApiVbyApiVersionBodyResponse, PostApiVbyApiVersionBodyError } from '../../types.gen'; + +export const useServiceWithEmptyTagQuery = defineQuery({ + key: [ + 'default', + 'serviceWithEmptyTag' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/no-tag' + }); + } +}); + +export const useDeleteCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'deleteCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'delete', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const useGetCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'getCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const useHeadCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'headCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'head', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const useOptionsCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'optionsCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'options', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const usePatchCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'patchCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'patch', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const usePostCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'postCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const usePutCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'putCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'put', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const useCallWithDescriptionsQuery = defineQuery({ + key: [ + 'Descriptions', + 'callWithDescriptions' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/descriptions/' + }); + } +}); + +export const useCallWithParametersQuery = defineQuery({ + key: [ + 'Parameters', + 'callWithParameters' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/parameters/{parameterPath}' + }); + } +}); + +export const useCallWithWeirdParameterNamesQuery = defineQuery({ + key: [ + 'Parameters', + 'callWithWeirdParameterNames' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}' + }); + } +}); + +export const useCallWithDefaultParametersQuery = defineQuery({ + key: [ + 'Defaults', + 'callWithDefaultParameters' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/defaults' + }); + } +}); + +export const useCallWithDefaultOptionalParametersQuery = defineQuery({ + key: [ + 'Defaults', + 'callWithDefaultOptionalParameters' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/defaults' + }); + } +}); + +export const useCallToTestOrderOfParamsQuery = defineQuery({ + key: [ + 'Defaults', + 'callToTestOrderOfParams' + ], + query: async () => { + return client({ + method: 'put', + url: '/api/v{api-version}/defaults' + }); + } +}); + +export const useDuplicateNameQuery = defineQuery({ + key: [ + 'Duplicate', + 'duplicateName' + ], + query: async () => { + return client({ + method: 'delete', + url: '/api/v{api-version}/duplicate' + }); + } +}); + +export const useDuplicateName2Query = defineQuery({ + key: [ + 'Duplicate', + 'duplicateName2' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/duplicate' + }); + } +}); + +export const useDuplicateName3Query = defineQuery({ + key: [ + 'Duplicate', + 'duplicateName3' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/duplicate' + }); + } +}); + +export const useDuplicateName4Query = defineQuery({ + key: [ + 'Duplicate', + 'duplicateName4' + ], + query: async () => { + return client({ + method: 'put', + url: '/api/v{api-version}/duplicate' + }); + } +}); + +export const useCallWithNoContentResponseQuery = defineQuery({ + key: [ + 'NoContent', + 'callWithNoContentResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/no-content' + }); + } +}); + +export const useCallWithResponseAndNoContentResponseQuery = defineQuery({ + key: [ + 'Response', + 'callWithResponseAndNoContentResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/multiple-tags/response-and-no-content' + }); + } +}); + +export const useDummyAQuery = defineQuery({ + key: [ + 'MultipleTags1', + 'dummyA' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/multiple-tags/a' + }); + } +}); + +export const useDummyBQuery = defineQuery({ + key: [ + 'MultipleTags1', + 'dummyB' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/multiple-tags/b' + }); + } +}); + +export const useCallWithResponseQuery = defineQuery({ + key: [ + 'Response', + 'callWithResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/response' + }); + } +}); + +export const useCallWithDuplicateResponsesQuery = defineQuery({ + key: [ + 'Response', + 'callWithDuplicateResponses' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/response' + }); + } +}); + +export const useCallWithResponsesQuery = defineQuery({ + key: [ + 'Response', + 'callWithResponses' + ], + query: async () => { + return client({ + method: 'put', + url: '/api/v{api-version}/response' + }); + } +}); + +export const useCollectionFormatQuery = defineQuery({ + key: [ + 'CollectionFormat', + 'collectionFormat' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/collectionFormat' + }); + } +}); + +export const useTypesQuery = defineQuery({ + key: [ + 'Types', + 'types' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/types' + }); + } +}); + +export const useComplexTypesQuery = defineQuery({ + key: [ + 'Complex', + 'complexTypes' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/complex' + }); + } +}); + +export const useCallWithResultFromHeaderQuery = defineQuery({ + key: [ + 'Header', + 'callWithResultFromHeader' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/header' + }); + } +}); + +export const useTestErrorCodeQuery = defineQuery({ + key: [ + 'Error', + 'testErrorCode' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/error' + }); + } +}); + +export const useNonAsciiæøåÆøÅöôêÊ字符串Query = defineQuery({ + key: [ + 'Non-Ascii-æøåÆØÅöôêÊ', + 'nonAsciiæøåÆøÅöôêÊ字符串' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串' + }); + } +}); + +/** + * Body should not be unknown + * Body should not be unknown + */ +export const usePostApiVbyApiVersionBodyMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'post', + url: '/api/v{api-version}/body', + data + }); + } +}); \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/index.ts b/packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/index.ts new file mode 100644 index 0000000000..56bade120a --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/index.ts @@ -0,0 +1,2 @@ +// This file is auto-generated by @hey-api/openapi-ts +export * from './types.gen'; \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/types.gen.ts b/packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/types.gen.ts new file mode 100644 index 0000000000..c14c396515 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/2.0.x/plugins/@pinia/colada/default/types.gen.ts @@ -0,0 +1,1102 @@ +// 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; + +export type SimpleReference = ModelWithString; + +/** + * This is a simple string + */ +export type SimpleStringWithPattern = string; + +/** + * This is a simple enum with strings + */ +export type EnumWithStrings = 'Success' | 'Warning' | 'Error' | "'Single Quote'" | '"Double Quotes"' | 'Non-ascii: øæåôöØÆÅÔÖ字符串'; + +/** + * 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; + +/** + * Success=1,Warning=2,Error=3 + */ +export type EnumFromDescription = number; + +/** + * This is a simple enum with numbers + */ +export type EnumWithExtensions = 200 | 400 | 500; + +/** + * This is a simple array with numbers + */ +export type ArrayWithNumbers = Array; + +/** + * This is a simple array with booleans + */ +export type ArrayWithBooleans = Array; + +/** + * This is a simple array with strings + */ +export type ArrayWithStrings = Array; + +/** + * 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 string dictionary + */ +export type DictionaryWithString = { + [key: string]: string; +}; + +/** + * This is a string reference + */ +export type DictionaryWithReference = { + [key: string]: ModelWithString; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithArray = { + [key: string]: Array; +}; + +/** + * This is a string dictionary + */ +export type DictionaryWithDictionary = { + [key: string]: { + [key: string]: string; + }; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithProperties = { + [key: string]: { + foo?: string; + bar?: string; + }; +}; + +/** + * This is a type-only model that defines Date as a string + */ +export type _Date = string; + +/** + * 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; +}; + +/** + * This is a model with one string property + */ +export type ModelWithStringError = { + /** + * This is a simple string property + */ + prop?: string; +}; + +/** + * This is a model with one string property + */ +export type ModelWithNullableString = { + /** + * This is a simple string property + */ + nullableProp?: string | null; + /** + * This is a simple string property + */ + nullableRequiredProp: string | null; +}; + +/** + * This is a model with one enum + */ +export type ModelWithEnum = { + /** + * This is a simple enum with strings + */ + test?: '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?: true; +}; + +/** + * 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?: { + [key: string]: 'Success' | 'Warning' | 'Error'; + }; + dictionaryWithEnumFromDescription?: { + [key: string]: number; + }; + arrayWithEnum?: Array<'Success' | 'Warning' | 'Error'>; + arrayWithDescription?: Array; +}; + +/** + * 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 ModelWithArray = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing a dictionary + */ +export type ModelWithDictionary = { + prop?: { + [key: string]: string; + }; +}; + +/** + * This is a model with one property containing a circular reference + */ +export type ModelWithCircularReference = { + prop?: ModelWithCircularReference; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithProperties = { + required: string; + readonly requiredAndReadOnly: string; + 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; + }; + }; +}; + +/** + * 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; +}; + +export type Default = { + name?: string; +}; + +/** + * 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 ParameterActivityParams = { + description?: string; + graduate_id?: number; + organization_id?: number; + parent_activity?: number; + post_id?: number; +}; + +export type ResponsePostActivityResponse = { + description?: string; + graduate_id?: number; + organization_id?: number; + parent_activity_id?: number; + post_id?: number; +}; + +export type FailureFailure = { + error?: string; + message?: string; + reference_code?: string; +}; + +export type ServiceWithEmptyTagData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no-tag'; +}; + +export type DeleteCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type GetCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type HeadCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type OptionsCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PatchCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PostCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PutCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type CallWithDescriptionsData = { + body?: never; + path?: never; + query?: { + /** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ + parameterWithBreaks?: string; + /** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ + parameterWithBackticks?: string; + /** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ + parameterWithSlashes?: string; + /** + * Testing expression placeholders in string: ${expression} should work + */ + parameterWithExpressionPlaceholders?: string; + /** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ + parameterWithQuotes?: string; + /** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ + parameterWithReservedCharacters?: string; + }; + url: '/api/v{api-version}/descriptions/'; +}; + +export type CallWithParametersData = { + body?: never; + headers: { + /** + * This is the parameter that goes into the header + */ + parameterHeader: string; + }; + path: { + /** + * This is the parameter that goes into the path + */ + parameterPath: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query: { + /** + * This is the parameter that goes into the query params + */ + parameterQuery: string; + }; + url: '/api/v{api-version}/parameters/{parameterPath}'; +}; + +export type CallWithWeirdParameterNamesData = { + body?: never; + headers: { + /** + * This is the parameter that goes into the request header + */ + 'parameter.header': string; + }; + path: { + /** + * This is the parameter that goes into the path + */ + 'parameter.path.1'?: string; + /** + * This is the parameter that goes into the path + */ + 'parameter-path-2'?: string; + /** + * This is the parameter that goes into the path + */ + 'PARAMETER-PATH-3'?: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query: { + /** + * This is the parameter with a reserved keyword + */ + default?: string; + /** + * This is the parameter that goes into the request query params + */ + 'parameter-query': string; + }; + url: '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}'; +}; + +export type CallWithDefaultParametersData = { + body?: never; + path?: never; + query: { + /** + * This is a simple string with default value + */ + parameterString: string; + /** + * This is a simple number with default value + */ + parameterNumber: number; + /** + * This is a simple boolean with default value + */ + parameterBoolean: boolean; + /** + * This is a simple enum with default value + */ + parameterEnum: 'Success' | 'Warning' | 'Error'; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallWithDefaultOptionalParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string that is optional with default value + */ + parameterString?: string; + /** + * This is a simple number that is optional with default value + */ + parameterNumber?: number; + /** + * 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'; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallToTestOrderOfParamsData = { + body?: never; + path?: never; + query: { + /** + * 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 with default + */ + parameterStringWithDefault: string; + /** + * This is a string with empty default + */ + parameterStringWithEmptyDefault: string; + /** + * This is a string with no default + */ + parameterStringWithNoDefault: string; + /** + * This is a string that can be null with no default + */ + parameterStringNullableWithNoDefault?: string | null; + /** + * This is a string that can be null with default + */ + parameterStringNullableWithDefault?: string | null; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type DuplicateNameData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName2Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName3Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName4Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type CallWithNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no-content'; +}; + +export type CallWithNoContentResponseResponses = { + /** + * Success + */ + 204: unknown; +}; + +export type CallWithResponseAndNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/response-and-no-content'; +}; + +export type CallWithResponseAndNoContentResponseResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: unknown; +}; + +export type CallWithResponseAndNoContentResponseResponse = CallWithResponseAndNoContentResponseResponses[keyof CallWithResponseAndNoContentResponseResponses]; + +export type DummyAData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/a'; +}; + +export type DummyAResponses = { + /** + * Success + */ + 204: unknown; +}; + +export type DummyBData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/b'; +}; + +export type DummyBResponses = { + /** + * Success + */ + 204: unknown; +}; + +export type CallWithResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponseResponses = { + /** + * Message for default response + */ + default: ModelWithString; +}; + +export type CallWithResponseResponse = CallWithResponseResponses[keyof CallWithResponseResponses]; + +export type CallWithDuplicateResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithDuplicateResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for default response + */ + default: ModelWithString; +}; + +export type CallWithDuplicateResponsesError = CallWithDuplicateResponsesErrors[keyof CallWithDuplicateResponsesErrors]; + +export type CallWithDuplicateResponsesResponses = { + /** + * Message for 201 response + */ + 201: ModelWithString; + /** + * Message for 202 response + */ + 202: ModelWithString; +}; + +export type CallWithDuplicateResponsesResponse = CallWithDuplicateResponsesResponses[keyof CallWithDuplicateResponsesResponses]; + +export type CallWithResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for default response + */ + default: ModelWithString; +}; + +export type CallWithResponsesError = CallWithResponsesErrors[keyof CallWithResponsesErrors]; + +export type CallWithResponsesResponses = { + /** + * Message for 200 response + */ + 200: { + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; + readonly value?: Array; + }; + /** + * Message for 201 response + */ + 201: ModelThatExtends; + /** + * Message for 202 response + */ + 202: ModelThatExtendsExtends; +}; + +export type CallWithResponsesResponse = CallWithResponsesResponses[keyof CallWithResponsesResponses]; + +export type CollectionFormatData = { + body?: never; + path?: never; + query: { + /** + * This is an array parameter that is sent as csv format (comma-separated values) + */ + parameterArrayCSV: Array; + /** + * This is an array parameter that is sent as ssv format (space-separated values) + */ + parameterArraySSV: Array; + /** + * This is an array parameter that is sent as tsv format (tab-separated values) + */ + parameterArrayTSV: Array; + /** + * This is an array parameter that is sent as pipes format (pipe-separated values) + */ + parameterArrayPipes: Array; + /** + * This is an array parameter that is sent as multi format (multiple parameter instances) + */ + parameterArrayMulti: Array; + }; + url: '/api/v{api-version}/collectionFormat'; +}; + +export type TypesData = { + body?: never; + path?: { + /** + * This is a number parameter + */ + id?: number; + }; + query: { + /** + * This is a number parameter + */ + parameterNumber: number; + /** + * This is a string parameter + */ + parameterString: string; + /** + * This is a boolean parameter + */ + parameterBoolean: boolean; + /** + * This is an object parameter + */ + parameterObject: { + [key: string]: unknown; + }; + /** + * This is an array parameter + */ + parameterArray: Array; + /** + * This is a dictionary parameter + */ + parameterDictionary: { + [key: string]: unknown; + }; + /** + * This is an enum parameter + */ + parameterEnum: 'Success' | 'Warning' | 'Error'; + }; + url: '/api/v{api-version}/types'; +}; + +export type TypesResponses = { + /** + * 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: { + [key: string]: unknown; + }; +}; + +export type TypesResponse = TypesResponses[keyof TypesResponses]; + +export type ComplexTypesData = { + body?: never; + path?: never; + query: { + /** + * Parameter containing object + */ + parameterObject: { + first?: { + second?: { + third?: string; + }; + }; + }; + }; + url: '/api/v{api-version}/complex'; +}; + +export type ComplexTypesErrors = { + /** + * 400 server error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type ComplexTypesResponses = { + /** + * Successful response + */ + 200: Array; +}; + +export type ComplexTypesResponse = ComplexTypesResponses[keyof ComplexTypesResponses]; + +export type CallWithResultFromHeaderData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/header'; +}; + +export type CallWithResultFromHeaderErrors = { + /** + * 400 server error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type CallWithResultFromHeaderResponses = { + /** + * Successful response + */ + 200: unknown; +}; + +export type TestErrorCodeData = { + body?: never; + path?: never; + query: { + /** + * Status code to return + */ + status: string; + }; + url: '/api/v{api-version}/error'; +}; + +export type TestErrorCodeErrors = { + /** + * Custom message: Internal Server Error + */ + 500: unknown; + /** + * Custom message: Not Implemented + */ + 501: unknown; + /** + * Custom message: Bad Gateway + */ + 502: unknown; + /** + * Custom message: Service Unavailable + */ + 503: unknown; +}; + +export type TestErrorCodeResponses = { + /** + * Custom message: Successful response + */ + 200: unknown; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Data = { + body?: never; + path?: never; + query: { + /** + * Dummy input param + */ + nonAsciiParamæøåÆØÅöôêÊ: number; + }; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Responses = { + /** + * Successful response + */ + 200: NonAsciiStringæøåÆøÅöôêÊ字符串; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Response = NonAsciiæøåÆøÅöôêÊ字符串Responses[keyof NonAsciiæøåÆøÅöôêÊ字符串Responses]; + +export type PostApiVbyApiVersionBodyData = { + /** + * Body should not be unknown + */ + body: ParameterActivityParams; + path?: never; + query?: never; + url: '/api/v{api-version}/body'; +}; + +export type PostApiVbyApiVersionBodyErrors = { + /** + * Bad Request + */ + 400: FailureFailure; + /** + * Internal Server Error + */ + 500: FailureFailure; +}; + +export type PostApiVbyApiVersionBodyError = PostApiVbyApiVersionBodyErrors[keyof PostApiVbyApiVersionBodyErrors]; + +export type PostApiVbyApiVersionBodyResponses = { + /** + * OK + */ + 200: ResponsePostActivityResponse; +}; + +export type PostApiVbyApiVersionBodyResponse = PostApiVbyApiVersionBodyResponses[keyof PostApiVbyApiVersionBodyResponses]; + +export type ClientOptions = { + baseUrl: 'http://localhost:3000/base' | (string & {}); +}; \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/@pinia/colada/default.gen.ts b/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/@pinia/colada/default.gen.ts new file mode 100644 index 0000000000..8a05c1d905 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/@pinia/colada/default.gen.ts @@ -0,0 +1,578 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { defineQuery, defineMutation } from '@pinia/colada'; +import type { ImportData, ImportResponse, ApiVVersionODataControllerCountResponse, GetApiVbyApiVersionSimpleOperationResponse, GetApiVbyApiVersionSimpleOperationError, CallWithParametersData, CallWithWeirdParameterNamesData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponse, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithNoContentResponseResponse, CallWithResponseAndNoContentResponseResponse, DummyAResponse, DummyBResponse, CallWithResponseResponse, CallWithDuplicateResponsesResponse, CallWithDuplicateResponsesError, CallWithResponsesResponse, CallWithResponsesError, TypesResponse, UploadFileData, UploadFileResponse, FileResponseResponse, ComplexTypesResponse, MultipartResponseResponse, MultipartRequestData, ComplexParamsData, ComplexParamsResponse, NonAsciiæøåÆøÅöôêÊ字符串Response, PutWithFormUrlEncodedData } from '../../types.gen'; + +export const useExportQuery = defineQuery({ + key: [ + 'default', + 'export' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/no-tag' + }); + } +}); + +export const useImportMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'post', + url: '/api/v{api-version}/no-tag', + data + }); + } +}); + +export const useApiVVersionODataControllerCountQuery = defineQuery({ + key: [ + 'Simple', + 'apiVVersionODataControllerCount' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/simple/$count' + }); + } +}); + +export const useGetApiVbyApiVersionSimpleOperationQuery = defineQuery({ + key: [ + 'default', + 'getApiVbyApiVersionSimpleOperation' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/simple:operation' + }); + } +}); + +export const useDeleteCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'deleteCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'delete', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const useGetCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'getCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const useHeadCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'headCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'head', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const useOptionsCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'optionsCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'options', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const usePatchCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'patchCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'patch', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const usePostCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'postCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const usePutCallWithoutParametersAndResponseQuery = defineQuery({ + key: [ + 'Simple', + 'putCallWithoutParametersAndResponse' + ], + query: async () => { + return client({ + method: 'put', + url: '/api/v{api-version}/simple' + }); + } +}); + +export const useDeleteFooQuery = defineQuery({ + key: [ + 'Parameters', + 'deleteFoo' + ], + query: async () => { + return client({ + method: 'delete', + url: '/api/v{api-version}/foo/{foo_param}/bar/{BarParam}' + }); + } +}); + +export const useCallWithDescriptionsQuery = defineQuery({ + key: [ + 'Descriptions', + 'callWithDescriptions' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/descriptions' + }); + } +}); + +/** + * @deprecated + */ +export const useDeprecatedCallQuery = defineQuery({ + key: [ + 'Deprecated', + 'deprecatedCall' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/parameters/deprecated' + }); + } +}); + +export const useCallWithParametersMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'post', + url: '/api/v{api-version}/parameters/{parameterPath}', + data + }); + } +}); + +export const useCallWithWeirdParameterNamesMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'post', + url: '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}', + data + }); + } +}); + +export const useGetCallWithOptionalParamQuery = defineQuery({ + key: [ + 'Parameters', + 'getCallWithOptionalParam' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/parameters' + }); + } +}); + +export const usePostCallWithOptionalParamMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'post', + url: '/api/v{api-version}/parameters', + data + }); + } +}); + +export const usePostApiVbyApiVersionRequestBodyMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'post', + url: '/api/v{api-version}/requestBody', + data + }); + } +}); + +export const usePostApiVbyApiVersionFormDataMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'post', + url: '/api/v{api-version}/formData', + data + }); + } +}); + +export const useCallWithDefaultParametersQuery = defineQuery({ + key: [ + 'Defaults', + 'callWithDefaultParameters' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/defaults' + }); + } +}); + +export const useCallWithDefaultOptionalParametersQuery = defineQuery({ + key: [ + 'Defaults', + 'callWithDefaultOptionalParameters' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/defaults' + }); + } +}); + +export const useCallToTestOrderOfParamsQuery = defineQuery({ + key: [ + 'Defaults', + 'callToTestOrderOfParams' + ], + query: async () => { + return client({ + method: 'put', + url: '/api/v{api-version}/defaults' + }); + } +}); + +export const useDuplicateNameQuery = defineQuery({ + key: [ + 'Duplicate', + 'duplicateName' + ], + query: async () => { + return client({ + method: 'delete', + url: '/api/v{api-version}/duplicate' + }); + } +}); + +export const useDuplicateName2Query = defineQuery({ + key: [ + 'Duplicate', + 'duplicateName2' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/duplicate' + }); + } +}); + +export const useDuplicateName3Query = defineQuery({ + key: [ + 'Duplicate', + 'duplicateName3' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/duplicate' + }); + } +}); + +export const useDuplicateName4Query = defineQuery({ + key: [ + 'Duplicate', + 'duplicateName4' + ], + query: async () => { + return client({ + method: 'put', + url: '/api/v{api-version}/duplicate' + }); + } +}); + +export const useCallWithNoContentResponseQuery = defineQuery({ + key: [ + 'NoContent', + 'callWithNoContentResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/no-content' + }); + } +}); + +export const useCallWithResponseAndNoContentResponseQuery = defineQuery({ + key: [ + 'Response', + 'callWithResponseAndNoContentResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/multiple-tags/response-and-no-content' + }); + } +}); + +export const useDummyAQuery = defineQuery({ + key: [ + 'MultipleTags1', + 'dummyA' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/multiple-tags/a' + }); + } +}); + +export const useDummyBQuery = defineQuery({ + key: [ + 'MultipleTags1', + 'dummyB' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/multiple-tags/b' + }); + } +}); + +export const useCallWithResponseQuery = defineQuery({ + key: [ + 'Response', + 'callWithResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/response' + }); + } +}); + +export const useCallWithDuplicateResponsesQuery = defineQuery({ + key: [ + 'Response', + 'callWithDuplicateResponses' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/response' + }); + } +}); + +export const useCallWithResponsesQuery = defineQuery({ + key: [ + 'Response', + 'callWithResponses' + ], + query: async () => { + return client({ + method: 'put', + url: '/api/v{api-version}/response' + }); + } +}); + +export const useCollectionFormatQuery = defineQuery({ + key: [ + 'CollectionFormat', + 'collectionFormat' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/collectionFormat' + }); + } +}); + +export const useTypesQuery = defineQuery({ + key: [ + 'Types', + 'types' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/types' + }); + } +}); + +export const useUploadFileMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'post', + url: '/api/v{api-version}/upload', + data + }); + } +}); + +export const useFileResponseQuery = defineQuery({ + key: [ + 'FileResponse', + 'fileResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/file/{id}' + }); + } +}); + +export const useComplexTypesQuery = defineQuery({ + key: [ + 'Complex', + 'complexTypes' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/complex' + }); + } +}); + +export const useMultipartResponseQuery = defineQuery({ + key: [ + 'multipart', + 'multipartResponse' + ], + query: async () => { + return client({ + method: 'get', + url: '/api/v{api-version}/multipart' + }); + } +}); + +export const useMultipartRequestMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'post', + url: '/api/v{api-version}/multipart', + data + }); + } +}); + +export const useComplexParamsMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'put', + url: '/api/v{api-version}/complex/{id}', + data + }); + } +}); + +export const useCallWithResultFromHeaderQuery = defineQuery({ + key: [ + 'Header', + 'callWithResultFromHeader' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/header' + }); + } +}); + +export const useTestErrorCodeQuery = defineQuery({ + key: [ + 'Error', + 'testErrorCode' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/error' + }); + } +}); + +export const useNonAsciiæøåÆøÅöôêÊ字符串Query = defineQuery({ + key: [ + 'Non-Ascii-æøåÆØÅöôêÊ', + 'nonAsciiæøåÆøÅöôêÊ字符串' + ], + query: async () => { + return client({ + method: 'post', + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串' + }); + } +}); + +/** + * Login User + */ +export const usePutWithFormUrlEncodedMutation = defineMutation({ + mutation: async (data) => { + return client({ + method: 'put', + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串', + data + }); + } +}); \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/index.ts b/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/index.ts new file mode 100644 index 0000000000..56bade120a --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/index.ts @@ -0,0 +1,2 @@ +// This file is auto-generated by @hey-api/openapi-ts +export * from './types.gen'; \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/types.gen.ts b/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/types.gen.ts new file mode 100644 index 0000000000..4674bd3f74 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.0.x/plugins/@pinia/colada/default/types.gen.ts @@ -0,0 +1,1933 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * Model with number-only name + */ +export type _400 = string; + +/** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ +export type CamelCaseCommentWithBreaks = number; + +/** + * 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 type EnumWithReplacedCharacters = "'Single Quote'" | '"Double Quotes"' | 'øæåôöØÆÅÔÖ字符串' | 3.1 | ''; + +/** + * 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; + +/** + * Success=1,Warning=2,Error=3 + */ +export type EnumFromDescription = number; + +/** + * This is a simple enum with numbers + */ +export type EnumWithExtensions = 200 | 400 | 500; + +export type EnumWithXEnumNames = 0 | 1 | 2; + +/** + * This is a simple array with numbers + */ +export type ArrayWithNumbers = Array; + +/** + * This is a simple array with booleans + */ +export type ArrayWithBooleans = Array; + +/** + * This is a simple array with strings + */ +export type ArrayWithStrings = Array; + +/** + * 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<{ + '16x16'?: CamelCaseCommentWithBreaks; + bar?: string; +}>; + +/** + * This is a simple array with any of properties + */ +export type ArrayWithAnyOfProperties = Array<{ + foo?: string; +} | { + bar?: string; +}>; + +export type AnyOfAnyAndNull = { + data?: unknown; +}; + +/** + * 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 = { + [key: string]: string; +}; + +export type DictionaryWithPropertiesAndAdditionalProperties = { + foo?: number; + bar?: boolean; + [key: string]: string | number | boolean | undefined; +}; + +/** + * This is a string reference + */ +export type DictionaryWithReference = { + [key: string]: ModelWithString; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithArray = { + [key: string]: Array; +}; + +/** + * This is a string dictionary + */ +export type DictionaryWithDictionary = { + [key: string]: { + [key: string]: string; + }; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithProperties = { + [key: string]: { + foo?: string; + bar?: string; + }; +}; + +/** + * 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; +}; + +/** + * This is a model with one string property + */ +export type ModelWithStringError = { + /** + * 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 ModelFromZendesk = 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' | 'ØÆÅ字符串'; +}; + +/** + * 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?: true; +}; + +/** + * This is a model with one enum with escaped name + */ +export type ModelWithEnumWithHyphen = { + 'foo-bar-baz-qux'?: '3.0'; +}; + +/** + * 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?: { + [key: string]: 'Success' | 'Warning' | 'Error'; + }; + dictionaryWithEnumFromDescription?: { + [key: string]: number; + }; + arrayWithEnum?: Array<'Success' | 'Warning' | 'Error'>; + arrayWithDescription?: Array; + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; +}; + +/** + * 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; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArray = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing a dictionary + */ +export type ModelWithDictionary = { + prop?: { + [key: string]: string; + }; +}; + +/** + * 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: string; + radius?: number; +}; + +/** + * Square + */ +export type ModelSquare = { + kind: string; + sideLength?: number; +}; + +/** + * This is a model with one property with a 'one of' relationship where the options are not $ref + */ +export type CompositionWithOneOfDiscriminator = ({ + kind?: 'circle'; +} & ModelCircle) | ({ + kind?: 'square'; +} & 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 | Array; +}; + +export type _3eNum1Период = 'Bird' | 'Dog'; + +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<_3eNum1Период | 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 | { + [key: string]: number; + }; +}; + +/** + * This is a model that contains a dictionary of simple arrays within composition + */ +export type CompositionWithOneOfAndSimpleArrayDictionary = { + propA?: boolean | { + [key: string]: Array; + }; +}; + +/** + * This is a model that contains a dictionary of complex arrays (composited) within composition + */ +export type CompositionWithOneOfAndComplexArrayDictionary = { + propA?: boolean | { + [key: string]: Array; + }; +}; + +/** + * 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 & { + age: number; + firstName: string; + lastname: string; +}; + +/** + * 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; +}; + +/** + * This is a free-form object without additionalProperties. + */ +export type FreeFormObjectWithoutAdditionalProperties = { + [key: string]: unknown; +}; + +/** + * This is a free-form object with additionalProperties: true. + */ +export type FreeFormObjectWithAdditionalPropertiesEqTrue = { + [key: string]: unknown; +}; + +/** + * This is a free-form object with additionalProperties: {}. + */ +export type FreeFormObjectWithAdditionalPropertiesEqEmptyObject = {}; + +export type ModelWithConst = { + String?: 'String'; + number?: 0; + null?: unknown; + 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 | string | undefined; +}; + +export type NestedAnyOfArraysNullable = { + nullableArray?: Array | null; +}; + +export type CompositionWithOneOfAndProperties = ({ + foo: SimpleParameter; +} | { + bar: NonAsciiStringæøåÆøÅöôêÊ字符串; +}) & { + baz: number | null; + qux: number; +}; + +/** + * An object that can be null + */ +export type NullableObject = { + foo?: string; +} | null; + +/** + * Some % character + */ +export type CharactersInDescription = string; + +export type ModelWithNullableObject = { + data?: NullableObject; +}; + +export type ModelWithOneOfEnum = { + foo: 'Bar'; +} | { + foo: 'Baz'; +} | { + foo: 'Qux'; +} | { + content: string; + foo: 'Quux'; +} | { + content: [ + string, + string + ]; + foo: 'Corge'; +}; + +export type ModelWithNestedArrayEnumsDataFoo = 'foo' | 'bar'; + +export type ModelWithNestedArrayEnumsDataBar = 'baz' | 'qux'; + +export type ModelWithNestedArrayEnumsData = { + foo?: Array; + bar?: Array; +}; + +export type ModelWithNestedArrayEnums = { + array_strings?: Array; + 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 ModelWithPrefixItemsConstantSizeArray = Array; + +export type ModelWithAnyOfConstantSizeArrayNullable = [ + number | null | string, + number | null | string, + number | null | string +]; + +export type ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = [ + number | Import, + number | Import +]; + +export type ModelWithAnyOfConstantSizeArrayAndIntersect = [ + number & string, + number & string +]; + +export type ModelWithNumericEnumUnion = { + /** + * Период + */ + value?: -10 | -1 | 0 | 1 | 3 | 6 | 12; +}; + +/** + * Some description with `back ticks` + */ +export type ModelWithBackticksInDescription = { + /** + * The template `that` should be used for parsing and importing the contents of the CSV file. + * + *

There is one placeholder currently supported:

  • ${x} - refers to the n-th column in the CSV file, e.g. ${1}, ${2}, ...)

Example of a correct JSON template:

+ *
+     * [
+     * {
+     * "resourceType": "Asset",
+     * "identifier": {
+     * "name": "${1}",
+     * "domain": {
+     * "name": "${2}",
+     * "community": {
+     * "name": "Some Community"
+     * }
+     * }
+     * },
+     * "attributes" : {
+     * "00000000-0000-0000-0000-000000003115" : [ {
+     * "value" : "${3}"
+     * } ],
+     * "00000000-0000-0000-0000-000000000222" : [ {
+     * "value" : "${4}"
+     * } ]
+     * }
+     * }
+     * ]
+     * 
+ */ + template?: string; +}; + +export type ModelWithOneOfAndProperties = (SimpleParameter | NonAsciiStringæøåÆøÅöôêÊ字符串) & { + baz: number | null; + qux: number; +}; + +/** + * Model used to test deduplication strategy (unused) + */ +export type ParameterSimpleParameterUnused = string; + +/** + * Model used to test deduplication strategy + */ +export type PostServiceWithEmptyTagResponse = string; + +/** + * Model used to test deduplication strategy + */ +export type PostServiceWithEmptyTagResponse2 = string; + +/** + * Model used to test deduplication strategy + */ +export type DeleteFooData = string; + +/** + * Model used to test deduplication strategy + */ +export type DeleteFooData2 = string; + +/** + * Model with restricted keyword name + */ +export type Import = string; + +export type SchemaWithFormRestrictedKeys = { + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + object?: { + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + }; + array?: Array<{ + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + }>; +}; + +/** + * This schema was giving PascalCase transformations a hard time + */ +export type IoK8sApimachineryPkgApisMetaV1DeleteOptions = { + /** + * Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned. + */ + preconditions?: IoK8sApimachineryPkgApisMetaV1Preconditions; +}; + +/** + * This schema was giving PascalCase transformations a hard time + */ +export type IoK8sApimachineryPkgApisMetaV1Preconditions = { + /** + * Specifies the target ResourceVersion + */ + resourceVersion?: string; + /** + * Specifies the target UID. + */ + uid?: string; +}; + +export type AdditionalPropertiesUnknownIssue = { + [key: string]: string | number; +}; + +export type AdditionalPropertiesUnknownIssue2 = { + [key: string]: string | number; +}; + +export type AdditionalPropertiesUnknownIssue3 = string & { + entries: { + [key: string]: AdditionalPropertiesUnknownIssue; + }; +}; + +export type AdditionalPropertiesIntegerIssue = { + value: number; + [key: string]: number; +}; + +export type OneOfAllOfIssue = ((ConstValue | GenericSchemaDuplicateIssue1SystemBoolean) & _3eNum1Период) | GenericSchemaDuplicateIssue1SystemString; + +export type GenericSchemaDuplicateIssue1SystemBoolean = { + item?: boolean; + error?: string | null; + readonly hasError?: boolean; + data?: { + [key: string]: never; + }; +}; + +export type GenericSchemaDuplicateIssue1SystemString = { + item?: string | null; + error?: string | null; + readonly hasError?: boolean; +}; + +/** + * This is a reusable parameter + */ +export type SimpleParameter = string; + +/** + * Parameter with illegal characters + */ +export type XFooBar = ModelWithString; + +export type SimpleRequestBody = ModelWithString; + +export type SimpleFormData = ModelWithString; + +export type ExportData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no-tag'; +}; + +export type ImportData = { + body: ModelWithReadOnlyAndWriteOnly | ModelWithArrayReadOnlyAndWriteOnly; + path?: never; + query?: never; + url: '/api/v{api-version}/no-tag'; +}; + +export type ImportResponses = { + /** + * Success + */ + 200: ModelFromZendesk; + /** + * Default success response + */ + default: ModelWithReadOnlyAndWriteOnly; +}; + +export type ImportResponse = ImportResponses[keyof ImportResponses]; + +export type ApiVVersionODataControllerCountData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple/$count'; +}; + +export type ApiVVersionODataControllerCountResponses = { + /** + * Success + */ + 200: ModelFromZendesk; +}; + +export type ApiVVersionODataControllerCountResponse = ApiVVersionODataControllerCountResponses[keyof ApiVVersionODataControllerCountResponses]; + +export type GetApiVbyApiVersionSimpleOperationData = { + body?: never; + path: { + /** + * foo in method + */ + foo_param: string; + }; + query?: never; + url: '/api/v{api-version}/simple:operation'; +}; + +export type GetApiVbyApiVersionSimpleOperationErrors = { + /** + * Default error response + */ + default: ModelWithBoolean; +}; + +export type GetApiVbyApiVersionSimpleOperationError = GetApiVbyApiVersionSimpleOperationErrors[keyof GetApiVbyApiVersionSimpleOperationErrors]; + +export type GetApiVbyApiVersionSimpleOperationResponses = { + /** + * Response is a simple number + */ + 200: number; +}; + +export type GetApiVbyApiVersionSimpleOperationResponse = GetApiVbyApiVersionSimpleOperationResponses[keyof GetApiVbyApiVersionSimpleOperationResponses]; + +export type DeleteCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type GetCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type HeadCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type OptionsCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PatchCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PostCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PutCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type DeleteFooData3 = { + body?: never; + headers: { + /** + * Parameter with illegal characters + */ + 'x-Foo-Bar': ModelWithString; + }; + path: { + /** + * foo in method + */ + foo_param: string; + /** + * bar in method + */ + BarParam: string; + }; + query?: never; + url: '/api/v{api-version}/foo/{foo_param}/bar/{BarParam}'; +}; + +export type CallWithDescriptionsData = { + body?: never; + path?: never; + query?: { + /** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ + parameterWithBreaks?: string; + /** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ + parameterWithBackticks?: string; + /** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ + parameterWithSlashes?: string; + /** + * Testing expression placeholders in string: ${expression} should work + */ + parameterWithExpressionPlaceholders?: string; + /** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ + parameterWithQuotes?: string; + /** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ + parameterWithReservedCharacters?: string; + }; + url: '/api/v{api-version}/descriptions'; +}; + +export type DeprecatedCallData = { + body?: never; + headers: { + /** + * This parameter is deprecated + * @deprecated + */ + parameter: DeprecatedModel | null; + }; + path?: never; + query?: never; + url: '/api/v{api-version}/parameters/deprecated'; +}; + +export type CallWithParametersData = { + /** + * This is the parameter that goes into the body + */ + body: { + [key: string]: unknown; + } | null; + headers: { + /** + * This is the parameter that goes into the header + */ + parameterHeader: string | null; + }; + path: { + /** + * This is the parameter that goes into the path + */ + parameterPath: string | null; + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query: { + foo_ref_enum?: ModelWithNestedArrayEnumsDataFoo; + foo_all_of_enum: ModelWithNestedArrayEnumsDataFoo; + /** + * This is the parameter that goes into the query params + */ + cursor: string | null; + }; + url: '/api/v{api-version}/parameters/{parameterPath}'; +}; + +export type CallWithWeirdParameterNamesData = { + /** + * This is the parameter that goes into the body + */ + body: ModelWithString | null; + headers: { + /** + * This is the parameter that goes into the request header + */ + 'parameter.header': string | null; + }; + path: { + /** + * This is the parameter that goes into the path + */ + 'parameter.path.1'?: string; + /** + * This is the parameter that goes into the path + */ + 'parameter-path-2'?: string; + /** + * This is the parameter that goes into the path + */ + 'PARAMETER-PATH-3'?: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query: { + /** + * This is the parameter with a reserved keyword + */ + default?: string; + /** + * This is the parameter that goes into the request query params + */ + 'parameter-query': string | null; + }; + url: '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}'; +}; + +export type GetCallWithOptionalParamData = { + /** + * This is a required parameter + */ + body: ModelWithOneOfEnum; + path?: never; + query?: { + /** + * This is an optional parameter + */ + page?: number; + }; + url: '/api/v{api-version}/parameters'; +}; + +export type PostCallWithOptionalParamData = { + /** + * This is an optional parameter + */ + body?: { + offset?: number | null; + }; + path?: never; + query: { + /** + * This is a required parameter + */ + parameter: Pageable; + }; + url: '/api/v{api-version}/parameters'; +}; + +export type PostCallWithOptionalParamResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: void; +}; + +export type PostCallWithOptionalParamResponse = PostCallWithOptionalParamResponses[keyof PostCallWithOptionalParamResponses]; + +export type PostApiVbyApiVersionRequestBodyData = { + /** + * A reusable request body + */ + body?: SimpleRequestBody; + path?: never; + query?: { + /** + * This is a reusable parameter + */ + parameter?: string; + }; + url: '/api/v{api-version}/requestBody'; +}; + +export type PostApiVbyApiVersionFormDataData = { + /** + * A reusable request body + */ + body?: SimpleFormData; + path?: never; + query?: { + /** + * This is a reusable parameter + */ + parameter?: string; + }; + url: '/api/v{api-version}/formData'; +}; + +export type CallWithDefaultParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string with default value + */ + parameterString?: string | null; + /** + * This is a simple number with default value + */ + parameterNumber?: number | null; + /** + * 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; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallWithDefaultOptionalParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string that is optional with default value + */ + parameterString?: string; + /** + * This is a simple number that is optional with default value + */ + parameterNumber?: number; + /** + * 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; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallToTestOrderOfParamsData = { + body?: never; + path?: never; + query: { + /** + * 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 with default + */ + parameterStringWithDefault: string; + /** + * This is a string with empty default + */ + parameterStringWithEmptyDefault: string; + /** + * This is a string with no default + */ + parameterStringWithNoDefault: string; + /** + * This is a string that can be null with no default + */ + parameterStringNullableWithNoDefault?: string | null; + /** + * This is a string that can be null with default + */ + parameterStringNullableWithDefault?: string | null; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type DuplicateNameData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName2Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName3Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName4Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type CallWithNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no-content'; +}; + +export type CallWithNoContentResponseResponses = { + /** + * Success + */ + 204: void; +}; + +export type CallWithNoContentResponseResponse = CallWithNoContentResponseResponses[keyof CallWithNoContentResponseResponses]; + +export type CallWithResponseAndNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/response-and-no-content'; +}; + +export type CallWithResponseAndNoContentResponseResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: void; +}; + +export type CallWithResponseAndNoContentResponseResponse = CallWithResponseAndNoContentResponseResponses[keyof CallWithResponseAndNoContentResponseResponses]; + +export type DummyAData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/a'; +}; + +export type DummyAResponses = { + 200: _400; +}; + +export type DummyAResponse = DummyAResponses[keyof DummyAResponses]; + +export type DummyBData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/b'; +}; + +export type DummyBResponses = { + /** + * Success + */ + 204: void; +}; + +export type DummyBResponse = DummyBResponses[keyof DummyBResponses]; + +export type CallWithResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponseResponses = { + default: Import; +}; + +export type CallWithResponseResponse = CallWithResponseResponses[keyof CallWithResponseResponses]; + +export type CallWithDuplicateResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithDuplicateResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for 4XX errors + */ + '4XX': DictionaryWithArray; + /** + * Default error response + */ + default: ModelWithBoolean; +}; + +export type CallWithDuplicateResponsesError = CallWithDuplicateResponsesErrors[keyof CallWithDuplicateResponsesErrors]; + +export type CallWithDuplicateResponsesResponses = { + /** + * Message for 200 response + */ + 200: ModelWithBoolean & ModelWithInteger; + /** + * Message for 201 response + */ + 201: ModelWithString; + /** + * Message for 202 response + */ + 202: ModelWithString; +}; + +export type CallWithDuplicateResponsesResponse = CallWithDuplicateResponsesResponses[keyof CallWithDuplicateResponsesResponses]; + +export type CallWithResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for default response + */ + default: ModelWithStringError; +}; + +export type CallWithResponsesError = CallWithResponsesErrors[keyof CallWithResponsesErrors]; + +export type CallWithResponsesResponses = { + /** + * Message for 200 response + */ + 200: { + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; + readonly value?: Array; + }; + /** + * Message for 201 response + */ + 201: ModelThatExtends; + /** + * Message for 202 response + */ + 202: ModelThatExtendsExtends; +}; + +export type CallWithResponsesResponse = CallWithResponsesResponses[keyof CallWithResponsesResponses]; + +export type CollectionFormatData = { + body?: never; + path?: never; + query: { + /** + * This is an array parameter that is sent as csv format (comma-separated values) + */ + parameterArrayCSV: Array | null; + /** + * This is an array parameter that is sent as ssv format (space-separated values) + */ + parameterArraySSV: Array | null; + /** + * This is an array parameter that is sent as tsv format (tab-separated values) + */ + parameterArrayTSV: Array | null; + /** + * This is an array parameter that is sent as pipes format (pipe-separated values) + */ + parameterArrayPipes: Array | null; + /** + * This is an array parameter that is sent as multi format (multiple parameter instances) + */ + parameterArrayMulti: Array | null; + }; + url: '/api/v{api-version}/collectionFormat'; +}; + +export type TypesData = { + body?: never; + path?: { + /** + * This is a number parameter + */ + id?: number; + }; + query: { + /** + * This is a number parameter + */ + parameterNumber: number; + /** + * This is a string parameter + */ + parameterString: string | null; + /** + * This is a boolean parameter + */ + parameterBoolean: boolean | null; + /** + * This is an object parameter + */ + parameterObject: { + [key: string]: unknown; + } | null; + /** + * This is an array parameter + */ + parameterArray: Array | null; + /** + * This is a dictionary parameter + */ + parameterDictionary: { + [key: string]: unknown; + } | null; + /** + * This is an enum parameter + */ + parameterEnum: 'Success' | 'Warning' | 'Error'; + }; + url: '/api/v{api-version}/types'; +}; + +export type TypesResponses = { + /** + * 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: { + [key: string]: unknown; + }; +}; + +export type TypesResponse = TypesResponses[keyof TypesResponses]; + +export type UploadFileData = { + body: Blob | File; + path: { + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query?: never; + url: '/api/v{api-version}/upload'; +}; + +export type UploadFileResponses = { + 200: boolean; +}; + +export type UploadFileResponse = UploadFileResponses[keyof UploadFileResponses]; + +export type FileResponseData = { + body?: never; + path: { + id: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query?: never; + url: '/api/v{api-version}/file/{id}'; +}; + +export type FileResponseResponses = { + /** + * Success + */ + 200: Blob | File; +}; + +export type FileResponseResponse = FileResponseResponses[keyof FileResponseResponses]; + +export type ComplexTypesData = { + body?: never; + path?: never; + query: { + /** + * Parameter containing object + */ + parameterObject: { + first?: { + second?: { + third?: string; + }; + }; + }; + /** + * Parameter containing reference + */ + parameterReference: ModelWithString; + }; + url: '/api/v{api-version}/complex'; +}; + +export type ComplexTypesErrors = { + /** + * 400 `server` error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type ComplexTypesResponses = { + /** + * Successful response + */ + 200: Array; +}; + +export type ComplexTypesResponse = ComplexTypesResponses[keyof ComplexTypesResponses]; + +export type MultipartResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multipart'; +}; + +export type MultipartResponseResponses = { + /** + * OK + */ + 200: { + file?: Blob | File; + metadata?: { + foo?: string; + bar?: string; + }; + }; +}; + +export type MultipartResponseResponse = MultipartResponseResponses[keyof MultipartResponseResponses]; + +export type MultipartRequestData = { + body?: { + content?: Blob | File; + data?: ModelWithString | null; + }; + path?: never; + query?: never; + url: '/api/v{api-version}/multipart'; +}; + +export type ComplexParamsData = { + body?: { + readonly key: string | null; + name: string | null; + enabled?: boolean; + type: 'Monkey' | 'Horse' | 'Bird'; + listOfModels?: Array | null; + listOfStrings?: Array | null; + parameters: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; + readonly user?: { + readonly id?: number; + readonly name?: string | null; + }; + }; + path: { + id: number; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query?: never; + url: '/api/v{api-version}/complex/{id}'; +}; + +export type ComplexParamsResponses = { + /** + * Success + */ + 200: ModelWithString; +}; + +export type ComplexParamsResponse = ComplexParamsResponses[keyof ComplexParamsResponses]; + +export type CallWithResultFromHeaderData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/header'; +}; + +export type CallWithResultFromHeaderErrors = { + /** + * 400 server error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type CallWithResultFromHeaderResponses = { + /** + * Successful response + */ + 200: unknown; +}; + +export type TestErrorCodeData = { + body?: never; + path?: never; + query: { + /** + * Status code to return + */ + status: number; + }; + url: '/api/v{api-version}/error'; +}; + +export type TestErrorCodeErrors = { + /** + * Custom message: Internal Server Error + */ + 500: unknown; + /** + * Custom message: Not Implemented + */ + 501: unknown; + /** + * Custom message: Bad Gateway + */ + 502: unknown; + /** + * Custom message: Service Unavailable + */ + 503: unknown; +}; + +export type TestErrorCodeResponses = { + /** + * Custom message: Successful response + */ + 200: unknown; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Data = { + body?: never; + path?: never; + query: { + /** + * Dummy input param + */ + nonAsciiParamæøåÆØÅöôêÊ: number; + }; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Responses = { + /** + * Successful response + */ + 200: Array; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Response = NonAsciiæøåÆøÅöôêÊ字符串Responses[keyof NonAsciiæøåÆøÅöôêÊ字符串Responses]; + +export type PutWithFormUrlEncodedData = { + body: ArrayWithStrings; + path?: never; + query?: never; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; + +export type ClientOptions = { + baseUrl: 'http://localhost:3000/base' | (string & {}); +}; \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/@pinia/colada/default/index.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/@pinia/colada/default/index.ts new file mode 100644 index 0000000000..56bade120a --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/@pinia/colada/default/index.ts @@ -0,0 +1,2 @@ +// This file is auto-generated by @hey-api/openapi-ts +export * from './types.gen'; \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/@pinia/colada/default/types.gen.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/@pinia/colada/default/types.gen.ts new file mode 100644 index 0000000000..af811982d3 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/plugins/@pinia/colada/default/types.gen.ts @@ -0,0 +1,1943 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * Model with number-only name + */ +export type _400 = string; + +/** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ +export type CamelCaseCommentWithBreaks = number; + +/** + * 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 type EnumWithReplacedCharacters = "'Single Quote'" | '"Double Quotes"' | 'øæåôöØÆÅÔÖ字符串' | 3.1 | ''; + +/** + * 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; + +/** + * Success=1,Warning=2,Error=3 + */ +export type EnumFromDescription = number; + +/** + * This is a simple enum with numbers + */ +export type EnumWithExtensions = 200 | 400 | 500; + +export type EnumWithXEnumNames = 0 | 1 | 2; + +/** + * This is a simple array with numbers + */ +export type ArrayWithNumbers = Array; + +/** + * This is a simple array with booleans + */ +export type ArrayWithBooleans = Array; + +/** + * This is a simple array with strings + */ +export type ArrayWithStrings = Array; + +/** + * 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<{ + '16x16'?: CamelCaseCommentWithBreaks; + 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 = { + [key: string]: string; +}; + +export type DictionaryWithPropertiesAndAdditionalProperties = { + foo?: number; + bar?: boolean; + [key: string]: string | number | boolean | undefined; +}; + +/** + * This is a string reference + */ +export type DictionaryWithReference = { + [key: string]: ModelWithString; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithArray = { + [key: string]: Array; +}; + +/** + * This is a string dictionary + */ +export type DictionaryWithDictionary = { + [key: string]: { + [key: string]: string; + }; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithProperties = { + [key: string]: { + foo?: string; + bar?: string; + }; +}; + +/** + * 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; +}; + +/** + * This is a model with one string property + */ +export type ModelWithStringError = { + /** + * 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 ModelFromZendesk = 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' | 'ØÆÅ字符串'; +}; + +/** + * 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?: true; +}; + +/** + * This is a model with one enum with escaped name + */ +export type ModelWithEnumWithHyphen = { + 'foo-bar-baz-qux'?: '3.0'; +}; + +/** + * 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?: { + [key: string]: 'Success' | 'Warning' | 'Error'; + }; + dictionaryWithEnumFromDescription?: { + [key: string]: number; + }; + arrayWithEnum?: Array<'Success' | 'Warning' | 'Error'>; + arrayWithDescription?: Array; + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; +}; + +/** + * 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; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArray = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing a dictionary + */ +export type ModelWithDictionary = { + prop?: { + [key: string]: string; + }; +}; + +/** + * 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: string; + radius?: number; +}; + +/** + * Square + */ +export type ModelSquare = { + kind: string; + sideLength?: number; +}; + +/** + * This is a model with one property with a 'one of' relationship where the options are not $ref + */ +export type CompositionWithOneOfDiscriminator = ({ + kind?: 'circle'; +} & ModelCircle) | ({ + kind?: 'square'; +} & 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 | Array; +}; + +export type _3eNum1Период = 'Bird' | 'Dog'; + +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<_3eNum1Период | 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 | { + [key: string]: number; + }; +}; + +/** + * This is a model that contains a dictionary of simple arrays within composition + */ +export type CompositionWithOneOfAndSimpleArrayDictionary = { + propA?: boolean | { + [key: string]: Array; + }; +}; + +/** + * This is a model that contains a dictionary of complex arrays (composited) within composition + */ +export type CompositionWithOneOfAndComplexArrayDictionary = { + propA?: boolean | { + [key: string]: Array; + }; +}; + +/** + * 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 & { + age: number; + firstName: string; + lastname: string; +}; + +/** + * 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; +}; + +/** + * This is a free-form object without additionalProperties. + */ +export type FreeFormObjectWithoutAdditionalProperties = { + [key: string]: unknown; +}; + +/** + * This is a free-form object with additionalProperties: true. + */ +export type FreeFormObjectWithAdditionalPropertiesEqTrue = { + [key: string]: unknown; +}; + +/** + * This is a free-form object with additionalProperties: {}. + */ +export type FreeFormObjectWithAdditionalPropertiesEqEmptyObject = {}; + +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 | string | undefined; +}; + +export type NestedAnyOfArraysNullable = { + nullableArray?: Array | null; +}; + +export type CompositionWithOneOfAndProperties = ({ + foo: SimpleParameter; +} | { + bar: NonAsciiStringæøåÆøÅöôêÊ字符串; +}) & { + baz: number | null; + qux: number; +}; + +/** + * An object that can be null + */ +export type NullableObject = { + foo?: string; +} | null; + +/** + * Some % character + */ +export type CharactersInDescription = string; + +export type ModelWithNullableObject = { + data?: NullableObject; +}; + +export type ModelWithOneOfEnum = { + foo: 'Bar'; +} | { + foo: 'Baz'; +} | { + foo: 'Qux'; +} | { + content: string; + foo: 'Quux'; +} | { + content: [ + string, + string + ]; + foo: 'Corge'; +}; + +export type ModelWithNestedArrayEnumsDataFoo = 'foo' | 'bar'; + +export type ModelWithNestedArrayEnumsDataBar = 'baz' | 'qux'; + +export type ModelWithNestedArrayEnumsData = { + foo?: Array; + bar?: Array; +}; + +export type ModelWithNestedArrayEnums = { + array_strings?: Array; + 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 ModelWithPrefixItemsConstantSizeArray = [ + ModelWithInteger, + number | string, + string +]; + +export type ModelWithAnyOfConstantSizeArrayNullable = [ + number | null | string, + number | null | string, + number | null | string +]; + +export type ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = [ + number | Import, + number | Import +]; + +export type ModelWithAnyOfConstantSizeArrayAndIntersect = [ + number & string, + number & string +]; + +export type ModelWithNumericEnumUnion = { + /** + * Период + */ + value?: -10 | -1 | 0 | 1 | 3 | 6 | 12; +}; + +/** + * Some description with `back ticks` + */ +export type ModelWithBackticksInDescription = { + /** + * The template `that` should be used for parsing and importing the contents of the CSV file. + * + *

There is one placeholder currently supported:

  • ${x} - refers to the n-th column in the CSV file, e.g. ${1}, ${2}, ...)

Example of a correct JSON template:

+ *
+     * [
+     * {
+     * "resourceType": "Asset",
+     * "identifier": {
+     * "name": "${1}",
+     * "domain": {
+     * "name": "${2}",
+     * "community": {
+     * "name": "Some Community"
+     * }
+     * }
+     * },
+     * "attributes" : {
+     * "00000000-0000-0000-0000-000000003115" : [ {
+     * "value" : "${3}"
+     * } ],
+     * "00000000-0000-0000-0000-000000000222" : [ {
+     * "value" : "${4}"
+     * } ]
+     * }
+     * }
+     * ]
+     * 
+ */ + template?: string; +}; + +export type ModelWithOneOfAndProperties = (SimpleParameter | NonAsciiStringæøåÆøÅöôêÊ字符串) & { + baz: number | null; + qux: number; +}; + +/** + * Model used to test deduplication strategy (unused) + */ +export type ParameterSimpleParameterUnused = string; + +/** + * Model used to test deduplication strategy + */ +export type PostServiceWithEmptyTagResponse = string; + +/** + * Model used to test deduplication strategy + */ +export type PostServiceWithEmptyTagResponse2 = string; + +/** + * Model used to test deduplication strategy + */ +export type DeleteFooData = string; + +/** + * Model used to test deduplication strategy + */ +export type DeleteFooData2 = string; + +/** + * Model with restricted keyword name + */ +export type Import = string; + +export type SchemaWithFormRestrictedKeys = { + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + object?: { + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + }; + array?: Array<{ + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + }>; +}; + +/** + * This schema was giving PascalCase transformations a hard time + */ +export type IoK8sApimachineryPkgApisMetaV1DeleteOptions = { + /** + * Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned. + */ + preconditions?: IoK8sApimachineryPkgApisMetaV1Preconditions; +}; + +/** + * This schema was giving PascalCase transformations a hard time + */ +export type IoK8sApimachineryPkgApisMetaV1Preconditions = { + /** + * Specifies the target ResourceVersion + */ + resourceVersion?: string; + /** + * Specifies the target UID. + */ + uid?: string; +}; + +export type AdditionalPropertiesUnknownIssue = { + [key: string]: string | number; +}; + +export type AdditionalPropertiesUnknownIssue2 = { + [key: string]: string | number; +}; + +export type AdditionalPropertiesUnknownIssue3 = string & { + entries: { + [key: string]: AdditionalPropertiesUnknownIssue; + }; +}; + +export type AdditionalPropertiesIntegerIssue = { + value: number; + [key: string]: number; +}; + +export type OneOfAllOfIssue = ((ConstValue | GenericSchemaDuplicateIssue1SystemBoolean) & _3eNum1Период) | GenericSchemaDuplicateIssue1SystemString; + +export type GenericSchemaDuplicateIssue1SystemBoolean = { + item?: boolean; + error?: string | null; + readonly hasError?: boolean; + data?: { + [key: string]: never; + }; +}; + +export type GenericSchemaDuplicateIssue1SystemString = { + item?: string | null; + error?: string | null; + readonly hasError?: boolean; +}; + +/** + * This is a reusable parameter + */ +export type SimpleParameter = string; + +/** + * Parameter with illegal characters + */ +export type XFooBar = ModelWithString; + +/** + * A reusable request body + */ +export type SimpleRequestBody = ModelWithString; + +/** + * A reusable request body + */ +export type SimpleFormData = ModelWithString; + +export type ExportData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no-tag'; +}; + +export type ImportData = { + body: ModelWithReadOnlyAndWriteOnly | ModelWithArrayReadOnlyAndWriteOnly; + path?: never; + query?: never; + url: '/api/v{api-version}/no-tag'; +}; + +export type ImportResponses = { + /** + * Success + */ + 200: ModelFromZendesk; + /** + * Default success response + */ + default: ModelWithReadOnlyAndWriteOnly; +}; + +export type ImportResponse = ImportResponses[keyof ImportResponses]; + +export type ApiVVersionODataControllerCountData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple/$count'; +}; + +export type ApiVVersionODataControllerCountResponses = { + /** + * Success + */ + 200: ModelFromZendesk; +}; + +export type ApiVVersionODataControllerCountResponse = ApiVVersionODataControllerCountResponses[keyof ApiVVersionODataControllerCountResponses]; + +export type GetApiVbyApiVersionSimpleOperationData = { + body?: never; + path: { + /** + * foo in method + */ + foo_param: string; + }; + query?: never; + url: '/api/v{api-version}/simple:operation'; +}; + +export type GetApiVbyApiVersionSimpleOperationErrors = { + /** + * Default error response + */ + default: ModelWithBoolean; +}; + +export type GetApiVbyApiVersionSimpleOperationError = GetApiVbyApiVersionSimpleOperationErrors[keyof GetApiVbyApiVersionSimpleOperationErrors]; + +export type GetApiVbyApiVersionSimpleOperationResponses = { + /** + * Response is a simple number + */ + 200: number; +}; + +export type GetApiVbyApiVersionSimpleOperationResponse = GetApiVbyApiVersionSimpleOperationResponses[keyof GetApiVbyApiVersionSimpleOperationResponses]; + +export type DeleteCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type GetCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type HeadCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type OptionsCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PatchCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PostCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PutCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type DeleteFooData3 = { + body?: never; + headers: { + /** + * Parameter with illegal characters + */ + 'x-Foo-Bar': ModelWithString; + }; + path: { + /** + * foo in method + */ + foo_param: string; + /** + * bar in method + */ + BarParam: string; + }; + query?: never; + url: '/api/v{api-version}/foo/{foo_param}/bar/{BarParam}'; +}; + +export type CallWithDescriptionsData = { + body?: never; + path?: never; + query?: { + /** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ + parameterWithBreaks?: string; + /** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ + parameterWithBackticks?: string; + /** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ + parameterWithSlashes?: string; + /** + * Testing expression placeholders in string: ${expression} should work + */ + parameterWithExpressionPlaceholders?: string; + /** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ + parameterWithQuotes?: string; + /** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ + parameterWithReservedCharacters?: string; + }; + url: '/api/v{api-version}/descriptions'; +}; + +export type DeprecatedCallData = { + body?: never; + headers: { + /** + * This parameter is deprecated + * @deprecated + */ + parameter: DeprecatedModel | null; + }; + path?: never; + query?: never; + url: '/api/v{api-version}/parameters/deprecated'; +}; + +export type CallWithParametersData = { + /** + * This is the parameter that goes into the body + */ + body: { + [key: string]: unknown; + } | null; + headers: { + /** + * This is the parameter that goes into the header + */ + parameterHeader: string | null; + }; + path: { + /** + * This is the parameter that goes into the path + */ + parameterPath: string | null; + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query: { + foo_ref_enum?: ModelWithNestedArrayEnumsDataFoo; + foo_all_of_enum: ModelWithNestedArrayEnumsDataFoo; + /** + * This is the parameter that goes into the query params + */ + cursor: string | null; + }; + url: '/api/v{api-version}/parameters/{parameterPath}'; +}; + +export type CallWithWeirdParameterNamesData = { + /** + * This is the parameter that goes into the body + */ + body: ModelWithString | null; + headers: { + /** + * This is the parameter that goes into the request header + */ + 'parameter.header': string | null; + }; + path: { + /** + * This is the parameter that goes into the path + */ + 'parameter.path.1'?: string; + /** + * This is the parameter that goes into the path + */ + 'parameter-path-2'?: string; + /** + * This is the parameter that goes into the path + */ + 'PARAMETER-PATH-3'?: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query: { + /** + * This is the parameter with a reserved keyword + */ + default?: string; + /** + * This is the parameter that goes into the request query params + */ + 'parameter-query': string | null; + }; + url: '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}'; +}; + +export type GetCallWithOptionalParamData = { + /** + * This is a required parameter + */ + body: ModelWithOneOfEnum; + path?: never; + query?: { + /** + * This is an optional parameter + */ + page?: number; + }; + url: '/api/v{api-version}/parameters'; +}; + +export type PostCallWithOptionalParamData = { + /** + * This is an optional parameter + */ + body?: { + offset?: number | null; + }; + path?: never; + query: { + /** + * This is a required parameter + */ + parameter: Pageable; + }; + url: '/api/v{api-version}/parameters'; +}; + +export type PostCallWithOptionalParamResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: void; +}; + +export type PostCallWithOptionalParamResponse = PostCallWithOptionalParamResponses[keyof PostCallWithOptionalParamResponses]; + +export type PostApiVbyApiVersionRequestBodyData = { + /** + * A reusable request body + */ + body?: SimpleRequestBody; + path?: never; + query?: { + /** + * This is a reusable parameter + */ + parameter?: string; + }; + url: '/api/v{api-version}/requestBody'; +}; + +export type PostApiVbyApiVersionFormDataData = { + /** + * A reusable request body + */ + body?: SimpleFormData; + path?: never; + query?: { + /** + * This is a reusable parameter + */ + parameter?: string; + }; + url: '/api/v{api-version}/formData'; +}; + +export type CallWithDefaultParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string with default value + */ + parameterString?: string | null; + /** + * This is a simple number with default value + */ + parameterNumber?: number | null; + /** + * 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; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallWithDefaultOptionalParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string that is optional with default value + */ + parameterString?: string; + /** + * This is a simple number that is optional with default value + */ + parameterNumber?: number; + /** + * 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; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallToTestOrderOfParamsData = { + body?: never; + path?: never; + query: { + /** + * 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 with default + */ + parameterStringWithDefault: string; + /** + * This is a string with empty default + */ + parameterStringWithEmptyDefault: string; + /** + * This is a string with no default + */ + parameterStringWithNoDefault: string; + /** + * This is a string that can be null with no default + */ + parameterStringNullableWithNoDefault?: string | null; + /** + * This is a string that can be null with default + */ + parameterStringNullableWithDefault?: string | null; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type DuplicateNameData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName2Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName3Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName4Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type CallWithNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no-content'; +}; + +export type CallWithNoContentResponseResponses = { + /** + * Success + */ + 204: void; +}; + +export type CallWithNoContentResponseResponse = CallWithNoContentResponseResponses[keyof CallWithNoContentResponseResponses]; + +export type CallWithResponseAndNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/response-and-no-content'; +}; + +export type CallWithResponseAndNoContentResponseResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: void; +}; + +export type CallWithResponseAndNoContentResponseResponse = CallWithResponseAndNoContentResponseResponses[keyof CallWithResponseAndNoContentResponseResponses]; + +export type DummyAData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/a'; +}; + +export type DummyAResponses = { + 200: _400; +}; + +export type DummyAResponse = DummyAResponses[keyof DummyAResponses]; + +export type DummyBData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/b'; +}; + +export type DummyBResponses = { + /** + * Success + */ + 204: void; +}; + +export type DummyBResponse = DummyBResponses[keyof DummyBResponses]; + +export type CallWithResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponseResponses = { + default: Import; +}; + +export type CallWithResponseResponse = CallWithResponseResponses[keyof CallWithResponseResponses]; + +export type CallWithDuplicateResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithDuplicateResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for 4XX errors + */ + '4XX': DictionaryWithArray; + /** + * Default error response + */ + default: ModelWithBoolean; +}; + +export type CallWithDuplicateResponsesError = CallWithDuplicateResponsesErrors[keyof CallWithDuplicateResponsesErrors]; + +export type CallWithDuplicateResponsesResponses = { + /** + * Message for 200 response + */ + 200: ModelWithBoolean & ModelWithInteger; + /** + * Message for 201 response + */ + 201: ModelWithString; + /** + * Message for 202 response + */ + 202: ModelWithString; +}; + +export type CallWithDuplicateResponsesResponse = CallWithDuplicateResponsesResponses[keyof CallWithDuplicateResponsesResponses]; + +export type CallWithResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for default response + */ + default: ModelWithStringError; +}; + +export type CallWithResponsesError = CallWithResponsesErrors[keyof CallWithResponsesErrors]; + +export type CallWithResponsesResponses = { + /** + * Message for 200 response + */ + 200: { + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; + readonly value?: Array; + }; + /** + * Message for 201 response + */ + 201: ModelThatExtends; + /** + * Message for 202 response + */ + 202: ModelThatExtendsExtends; +}; + +export type CallWithResponsesResponse = CallWithResponsesResponses[keyof CallWithResponsesResponses]; + +export type CollectionFormatData = { + body?: never; + path?: never; + query: { + /** + * This is an array parameter that is sent as csv format (comma-separated values) + */ + parameterArrayCSV: Array | null; + /** + * This is an array parameter that is sent as ssv format (space-separated values) + */ + parameterArraySSV: Array | null; + /** + * This is an array parameter that is sent as tsv format (tab-separated values) + */ + parameterArrayTSV: Array | null; + /** + * This is an array parameter that is sent as pipes format (pipe-separated values) + */ + parameterArrayPipes: Array | null; + /** + * This is an array parameter that is sent as multi format (multiple parameter instances) + */ + parameterArrayMulti: Array | null; + }; + url: '/api/v{api-version}/collectionFormat'; +}; + +export type TypesData = { + body?: never; + path?: { + /** + * This is a number parameter + */ + id?: number; + }; + query: { + /** + * This is a number parameter + */ + parameterNumber: number; + /** + * This is a string parameter + */ + parameterString: string | null; + /** + * This is a boolean parameter + */ + parameterBoolean: boolean | null; + /** + * This is an object parameter + */ + parameterObject: { + [key: string]: unknown; + } | null; + /** + * This is an array parameter + */ + parameterArray: Array | null; + /** + * This is a dictionary parameter + */ + parameterDictionary: { + [key: string]: unknown; + } | null; + /** + * This is an enum parameter + */ + parameterEnum: 'Success' | 'Warning' | 'Error' | null; + }; + url: '/api/v{api-version}/types'; +}; + +export type TypesResponses = { + /** + * 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: { + [key: string]: unknown; + }; +}; + +export type TypesResponse = TypesResponses[keyof TypesResponses]; + +export type UploadFileData = { + body: Blob | File; + path: { + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query?: never; + url: '/api/v{api-version}/upload'; +}; + +export type UploadFileResponses = { + 200: boolean; +}; + +export type UploadFileResponse = UploadFileResponses[keyof UploadFileResponses]; + +export type FileResponseData = { + body?: never; + path: { + id: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query?: never; + url: '/api/v{api-version}/file/{id}'; +}; + +export type FileResponseResponses = { + /** + * Success + */ + 200: Blob | File; +}; + +export type FileResponseResponse = FileResponseResponses[keyof FileResponseResponses]; + +export type ComplexTypesData = { + body?: never; + path?: never; + query: { + /** + * Parameter containing object + */ + parameterObject: { + first?: { + second?: { + third?: string; + }; + }; + }; + /** + * Parameter containing reference + */ + parameterReference: ModelWithString; + }; + url: '/api/v{api-version}/complex'; +}; + +export type ComplexTypesErrors = { + /** + * 400 `server` error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type ComplexTypesResponses = { + /** + * Successful response + */ + 200: Array; +}; + +export type ComplexTypesResponse = ComplexTypesResponses[keyof ComplexTypesResponses]; + +export type MultipartResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multipart'; +}; + +export type MultipartResponseResponses = { + /** + * OK + */ + 200: { + file?: Blob | File; + metadata?: { + foo?: string; + bar?: string; + }; + }; +}; + +export type MultipartResponseResponse = MultipartResponseResponses[keyof MultipartResponseResponses]; + +export type MultipartRequestData = { + body?: { + content?: Blob | File; + data?: ModelWithString | null; + }; + path?: never; + query?: never; + url: '/api/v{api-version}/multipart'; +}; + +export type ComplexParamsData = { + body?: { + readonly key: string | null; + name: string | null; + enabled?: boolean; + type: 'Monkey' | 'Horse' | 'Bird'; + listOfModels?: Array | null; + listOfStrings?: Array | null; + parameters: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; + readonly user?: { + readonly id?: number; + readonly name?: string | null; + }; + }; + path: { + id: number; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query?: never; + url: '/api/v{api-version}/complex/{id}'; +}; + +export type ComplexParamsResponses = { + /** + * Success + */ + 200: ModelWithString; +}; + +export type ComplexParamsResponse = ComplexParamsResponses[keyof ComplexParamsResponses]; + +export type CallWithResultFromHeaderData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/header'; +}; + +export type CallWithResultFromHeaderErrors = { + /** + * 400 server error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type CallWithResultFromHeaderResponses = { + /** + * Successful response + */ + 200: unknown; +}; + +export type TestErrorCodeData = { + body?: never; + path?: never; + query: { + /** + * Status code to return + */ + status: number; + }; + url: '/api/v{api-version}/error'; +}; + +export type TestErrorCodeErrors = { + /** + * Custom message: Internal Server Error + */ + 500: unknown; + /** + * Custom message: Not Implemented + */ + 501: unknown; + /** + * Custom message: Bad Gateway + */ + 502: unknown; + /** + * Custom message: Service Unavailable + */ + 503: unknown; +}; + +export type TestErrorCodeResponses = { + /** + * Custom message: Successful response + */ + 200: unknown; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Data = { + body?: never; + path?: never; + query: { + /** + * Dummy input param + */ + nonAsciiParamæøåÆØÅöôêÊ: number; + }; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Responses = { + /** + * Successful response + */ + 200: Array; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Response = NonAsciiæøåÆøÅöôêÊ字符串Responses[keyof NonAsciiæøåÆøÅöôêÊ字符串Responses]; + +export type PutWithFormUrlEncodedData = { + body: ArrayWithStrings; + path?: never; + query?: never; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; + +export type ClientOptions = { + baseUrl: 'http://localhost:3000/base' | (string & {}); +}; \ No newline at end of file diff --git a/packages/openapi-ts/test/plugins.test.ts b/packages/openapi-ts/test/plugins.test.ts index a4edf0a412..f472b47b32 100644 --- a/packages/openapi-ts/test/plugins.test.ts +++ b/packages/openapi-ts/test/plugins.test.ts @@ -199,6 +199,25 @@ for (const version of versions) { description: 'generate Fetch API client with TanStack Vue Query plugin using class-based SDKs', }, + { + config: createConfig({ + output: 'default', + plugins: ['@pinia/colada', '@hey-api/client-fetch'], + }), + description: 'generate with plugin Pinia Colada', + }, + { + config: createConfig({ + output: 'asFileSdk', + plugins: [ + { + groupByTag: true, + name: '@pinia/colada', + }, + ], + }), + description: 'generate with plugin Pinia Colada using file-based SDK', + }, { config: createConfig({ output: 'default', From ec975c83d5536de23d4c9c8ca85535945e1638f4 Mon Sep 17 00:00:00 2001 From: josh-hemphill Date: Tue, 1 Apr 2025 21:55:08 -0700 Subject: [PATCH 03/11] reworked --- .../src/plugins/@pinia/colada/config.ts | 2 ++ .../src/plugins/@pinia/colada/mutation.ts | 7 ++++++- .../src/plugins/@pinia/colada/query.ts | 7 ++++++- .../src/plugins/@pinia/colada/types.d.ts | 13 +++++++++++++ .../src/plugins/@pinia/colada/utils.ts | 16 +++++++++++++--- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/config.ts b/packages/openapi-ts/src/plugins/@pinia/colada/config.ts index 0307d4cc49..a92c40d2e5 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/config.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/config.ts @@ -19,8 +19,10 @@ export const defaultConfig: Plugin.Config = { includeTypes: true, name: PluginName, output: PluginName, + prefixUse: true, resolveQuery: undefined, resolveQueryKey: undefined, + suffixQueryMutation: true, useInfiniteQueries: false, }; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts b/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts index 2e6303be91..e5d5a2a362 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts @@ -127,7 +127,12 @@ export const createMutationFunction = ({ }), ], }), - name: generateFunctionName(operation, false), + name: generateFunctionName( + operation, + false, + plugin.prefixUse, + plugin.suffixQueryMutation, + ), }); file.add(node); diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/query.ts b/packages/openapi-ts/src/plugins/@pinia/colada/query.ts index 05ed7c3c92..8fbc965257 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/query.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/query.ts @@ -125,7 +125,12 @@ export const createQueryFunction = ({ }), ], }), - name: generateFunctionName(operation, true), + name: generateFunctionName( + operation, + true, + plugin.prefixUse, + plugin.suffixQueryMutation, + ), }); file.add(node); diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts b/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts index d8687846c4..2986782d04 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts @@ -84,6 +84,12 @@ export interface Config extends Plugin.Name<'@pinia/colada'> { */ output: string; + /** + * Whether to prefix generated function names with 'use'. + * @default true + */ + prefixUse?: boolean; + /** * Custom hook that determines if an operation should be a query or not. * Return true to force query, false to force mutation, undefined to use default logic. @@ -98,6 +104,13 @@ export interface Config extends Plugin.Name<'@pinia/colada'> { */ resolveQueryKey?: (operation: IR.OperationObject) => Array; + /** + * Whether to suffix generated function names with 'Query' or 'Mutation' to indicate the type + * of Pinia Colada operation that is used under the hood. + * @default true + */ + suffixQueryMutation?: boolean; + /** * Use infinite queries. * @default false diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts b/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts index 6334a05afe..0b15be399a 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts @@ -227,8 +227,18 @@ export const hasPagination = ( export const generateFunctionName = ( operation: IR.OperationObject, isQueryType: boolean, + prefixUse: boolean = true, + suffixQueryMutation: boolean = true, ): string => { - const prefix = isQueryType ? 'use' : 'use'; - const suffix = isQueryType ? 'Query' : 'Mutation'; - return `${prefix}${stringCase({ case: 'PascalCase', value: operation.id })}${suffix}`; + const operationPascalCase = stringCase({ + case: 'PascalCase', + value: operation.id, + }); + const prefix = prefixUse ? 'use' : ''; + const suffix = suffixQueryMutation + ? isQueryType + ? 'Query' + : 'Mutation' + : ''; + return `${prefix}${operationPascalCase}${suffix}`; }; From 4c0875b149defeb4d0e28e95c8ae2cdbb130761d Mon Sep 17 00:00:00 2001 From: josh-hemphill Date: Sun, 6 Apr 2025 03:02:32 -0700 Subject: [PATCH 04/11] Merge remote-tracking branch 'upstream/main' --- .github/workflows/ci.yml | 2 +- .github/workflows/coverage.yml | 2 +- .github/workflows/release.yml | 4 +- README.md | 29 +- docs/.vitepress/theme/Layout.vue | 12 +- docs/.vitepress/theme/custom.css | 28 +- docs/about.md | 2 +- docs/contributing.md | 2 +- docs/index.md | 37 +- docs/openapi-ts/clients.md | 2 +- docs/openapi-ts/clients/axios.md | 2 +- docs/openapi-ts/clients/legacy.md | 6 +- docs/openapi-ts/clients/next-js.md | 32 +- docs/openapi-ts/clients/nuxt.md | 6 +- docs/openapi-ts/get-started.md | 12 +- docs/openapi-ts/integrations.md | 4 +- docs/openapi-ts/plugins.md | 2 + docs/openapi-ts/plugins/ajv.md | 2 +- docs/openapi-ts/plugins/arktype.md | 2 +- docs/openapi-ts/plugins/express.md | 2 +- docs/openapi-ts/plugins/faker.md | 2 +- docs/openapi-ts/plugins/fastify.md | 2 +- docs/openapi-ts/plugins/hono.md | 2 +- docs/openapi-ts/plugins/joi.md | 2 +- docs/openapi-ts/plugins/koa.md | 2 +- docs/openapi-ts/plugins/msw.md | 2 +- docs/openapi-ts/plugins/nest.md | 2 +- docs/openapi-ts/plugins/pinia-colada.md | 2 +- docs/openapi-ts/plugins/superstruct.md | 2 +- docs/openapi-ts/plugins/swr.md | 2 +- docs/openapi-ts/plugins/valibot.md | 2 +- docs/openapi-ts/plugins/zod.md | 2 +- docs/openapi-ts/plugins/zustand.md | 2 +- examples/openapi-ts-axios/package.json | 4 +- examples/openapi-ts-fastify/CHANGELOG.md | 7 + examples/openapi-ts-fastify/package.json | 2 +- examples/openapi-ts-fetch/CHANGELOG.md | 7 + examples/openapi-ts-fetch/package.json | 4 +- .../openapi-ts-next/src/client/client.gen.ts | 6 +- .../openapi-ts-next/src/client/sdk.gen.ts | 67 +- .../openapi-ts-next/src/client/types.gen.ts | 186 +++- examples/openapi-ts-nuxt/package.json | 2 +- examples/openapi-ts-sample/CHANGELOG.md | 7 + examples/openapi-ts-sample/package.json | 4 +- .../CHANGELOG.md | 7 + .../package.json | 2 +- .../CHANGELOG.md | 7 + .../package.json | 4 +- .../CHANGELOG.md | 7 + .../package.json | 4 +- .../CHANGELOG.md | 7 + .../openapi-ts-tanstack-vue-query/README.md | 8 +- .../package.json | 4 +- packages/client-axios/README.md | 8 +- packages/client-axios/package.json | 2 +- packages/client-fetch/CHANGELOG.md | 6 + packages/client-fetch/README.md | 8 +- packages/client-fetch/package.json | 2 +- packages/client-fetch/src/types.ts | 7 + packages/client-next/README.md | 8 +- packages/client-nuxt/README.md | 8 +- packages/client-nuxt/package.json | 2 +- packages/nuxt/README.md | 8 +- packages/nuxt/package.json | 2 +- packages/openapi-ts-tests/package.json | 2 +- packages/openapi-ts-tests/test/2.0.x.test.ts | 49 +- packages/openapi-ts-tests/test/3.0.x.test.ts | 49 +- packages/openapi-ts-tests/test/3.1.x.test.ts | 49 +- .../2.0.x/exclude-deprecated/index.ts | 2 + .../2.0.x/exclude-deprecated/types.gen.ts | 21 + .../3.0.x/exclude-deprecated/index.ts | 2 + .../3.0.x/exclude-deprecated/types.gen.ts | 21 + .../transformers.gen.ts | 3 + .../transformers-any-of-null/types.gen.ts | 1 + .../client-fetch/bundle/client/index.d.cts | 7 + .../client-fetch/bundle/client/index.d.ts | 7 + .../3.1.x/exclude-deprecated/index.ts | 2 + .../3.1.x/exclude-deprecated/types.gen.ts | 21 + .../transformers.gen.ts | 3 + .../transformers-any-of-null/types.gen.ts | 1 + .../test/openapi-ts.config.ts | 7 +- .../test/spec/2.0.x/exclude-deprecated.yaml | 38 + .../test/spec/3.0.x/exclude-deprecated.yaml | 35 + .../spec/3.0.x/transformers-any-of-null.json | 12 +- .../test/spec/3.1.x/exclude-deprecated.yaml | 35 + .../spec/3.1.x/transformers-any-of-null.json | 11 +- packages/openapi-ts/CHANGELOG.md | 20 + packages/openapi-ts/README.md | 29 +- packages/openapi-ts/package.json | 4 +- .../src/ir/__tests__/pagination.test.ts | 187 +++- packages/openapi-ts/src/ir/operation.ts | 43 +- .../src/openApi/2.0.x/parser/index.ts | 46 +- .../src/openApi/3.0.x/parser/index.ts | 67 +- .../src/openApi/3.1.x/parser/index.ts | 67 +- .../src/openApi/shared/utils/filter.ts | 79 +- .../plugins/@hey-api/transformers/plugin.ts | 34 +- packages/openapi-ts/src/types/config.d.ts | 19 +- packages/vite-plugin/README.md | 8 +- packages/vite-plugin/package.json | 2 +- pnpm-lock.yaml | 988 ++++++++++-------- 100 files changed, 1743 insertions(+), 851 deletions(-) create mode 100644 packages/openapi-ts-tests/test/__snapshots__/2.0.x/exclude-deprecated/index.ts create mode 100644 packages/openapi-ts-tests/test/__snapshots__/2.0.x/exclude-deprecated/types.gen.ts create mode 100644 packages/openapi-ts-tests/test/__snapshots__/3.0.x/exclude-deprecated/index.ts create mode 100644 packages/openapi-ts-tests/test/__snapshots__/3.0.x/exclude-deprecated/types.gen.ts create mode 100644 packages/openapi-ts-tests/test/__snapshots__/3.1.x/exclude-deprecated/index.ts create mode 100644 packages/openapi-ts-tests/test/__snapshots__/3.1.x/exclude-deprecated/types.gen.ts create mode 100644 packages/openapi-ts-tests/test/spec/2.0.x/exclude-deprecated.yaml create mode 100644 packages/openapi-ts-tests/test/spec/3.0.x/exclude-deprecated.yaml create mode 100644 packages/openapi-ts-tests/test/spec/3.1.x/exclude-deprecated.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a6bf42adc..6cdb48b7d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: with: fetch-depth: 0 - - uses: pnpm/action-setup@v4.0.0 + - uses: pnpm/action-setup@v4.1.0 - uses: actions/setup-node@v4 with: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 536280400e..7f1bf44075 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/checkout@v4.2.2 - - uses: pnpm/action-setup@v4.0.0 + - uses: pnpm/action-setup@v4.1.0 - uses: actions/setup-node@v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9be2f29109..613a93a948 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/checkout@v4.2.2 - - uses: pnpm/action-setup@v4.0.0 + - uses: pnpm/action-setup@v4.1.0 - uses: actions/setup-node@v4 with: @@ -35,7 +35,7 @@ jobs: run: pnpm build - name: Create Release Pull Request - uses: changesets/action@v1.4.9 + uses: changesets/action@v1.4.10 with: publish: pnpm changeset publish version: pnpm changeset version diff --git a/README.md b/README.md index b08d63a5af..4f8dbbac0d 100644 --- a/README.md +++ b/README.md @@ -30,17 +30,19 @@ ## Features -- works with CLI, Node.js 18+, or npx -- supports OpenAPI 2.0, 3.0, and 3.1 specifications -- supports JSON and YAML input files -- generates TypeScript interfaces and SDKs -- Fetch API, Axios, Nuxt, Angular, Node.js, and XHR clients available +- runs in CLI, Node.js 18+, or npx +- works with OpenAPI 2.0, 3.0, and 3.1 +- customizable types and SDKs +- clients for your runtime (Fetch API, Axios, Next.js, Nuxt, etc.) - plugin ecosystem to reduce third-party boilerplate -- [platform](https://heyapi.dev/openapi-ts/integrations) for automating codegen builds +- custom plugins and custom clients +- [integration](https://heyapi.dev/openapi-ts/integrations) with Hey API platform -## Platform +## Dashboard -Our platform for OpenAPI specifications is now available. Automatically update your code when the APIs it depends on change. [Find out more](https://heyapi.dev/openapi-ts/integrations). +Hey API is an ecosystem of products helping you build better APIs. Superpower your codegen and APIs with our platform. + +[Sign In](https://app.heyapi.dev) to Hey API platform. ## Sponsors @@ -177,7 +179,14 @@ Output is the next thing to define. It can be either a string pointing to the de Clients are responsible for sending the actual HTTP requests. Using clients is not required, but you must add a client to `plugins` if you're generating SDKs (enabled by default). -You can learn more on the [Clients](https://heyapi.dev/openapi-ts/clients) page. +### Native Clients + +- [`@hey-api/client-fetch`](https://heyapi.dev/openapi-ts/clients/fetch) +- [`@hey-api/client-axios`](https://heyapi.dev/openapi-ts/clients/axios) +- [`@hey-api/client-next`](https://heyapi.dev/openapi-ts/clients/next-js) +- [`@hey-api/client-nuxt`](https://heyapi.dev/openapi-ts/clients/nuxt) + +Don't see your client? [Build your own](https://heyapi.dev/openapi-ts/clients/custom) or let us know your interest by [opening an issue](https://github.com/hey-api/openapi-ts/issues). ## Plugins @@ -222,6 +231,8 @@ The following plugins are planned but not in development yet. You can help us pr - [Yup](https://heyapi.dev/openapi-ts/plugins/yup) - [Zustand](https://heyapi.dev/openapi-ts/plugins/zustand) +Don't see your plugin? [Build your own](https://heyapi.dev/openapi-ts/plugins/custom) or let us know your interest by [opening an issue](https://github.com/hey-api/openapi-ts/issues). + ## Migration Guides [OpenAPI Typescript Codegen](https://heyapi.dev/openapi-ts/migrating#openapi-typescript-codegen) diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue index d0f170ec58..87ec830cbf 100644 --- a/docs/.vitepress/theme/Layout.vue +++ b/docs/.vitepress/theme/Layout.vue @@ -8,11 +8,13 @@ const { Layout } = DefaultTheme;