From 12624c4e426b417e50e5f19665fd1b529b3d0b6a Mon Sep 17 00:00:00 2001 From: Stijn Van Hulle Date: Wed, 18 Oct 2023 14:37:50 +0200 Subject: [PATCH 1/3] feat: `pathParamsType` to override behaviour when calling a generated get/post/put function --- docs/plugins/swagger-client/index.md | 13 +++++++++++-- .../gen/clients/axios/petService/deletePet.ts | 2 +- .../gen/clients/axios/petService/getPetById.ts | 2 +- .../axios/petService/updatePetWithForm.ts | 2 +- .../gen/clients/axios/petService/uploadFile.ts | 2 +- .../gen/clients/axios/petsService/createPets.ts | 2 +- .../gen/clients/axios/userService/deleteUser.ts | 2 +- .../clients/axios/userService/getUserByName.ts | 2 +- .../gen/clients/axios/userService/updateUser.ts | 2 +- packages/core/src/utils/FunctionParams.ts | 8 +++----- .../src/builders/ClientBuilder.tsx | 5 +++-- .../src/generators/OperationGenerator.ts | 7 +++++-- packages/swagger-client/src/plugin.ts | 3 ++- packages/swagger-client/src/types.ts | 12 ++++++++++++ packages/swagger/src/utils/getParams.ts | 16 +++++++++++++++- 15 files changed, 59 insertions(+), 21 deletions(-) diff --git a/docs/plugins/swagger-client/index.md b/docs/plugins/swagger-client/index.md index 05825a786..b3a4a9bce 100644 --- a/docs/plugins/swagger-client/index.md +++ b/docs/plugins/swagger-client/index.md @@ -84,8 +84,17 @@ Default: `"@kubb/swagger-client/client"` ### dataReturnType ReturnType that needs to be used when calling client(). -`Data` will return ResponseConfig[data].
-`Full` will return ResponseConfig. +`data` will return ResponseConfig[data].
+`full` will return ResponseConfig. + +Type: `string`
+Default: `"data"` + +### pathParamsType +How to pass your pathParams. + +`object` will return the pathParams as an object.
+`inline` will return the pathParams as comma separated params. Type: `string`
Default: `"data"` diff --git a/examples/advanced/src/gen/clients/axios/petService/deletePet.ts b/examples/advanced/src/gen/clients/axios/petService/deletePet.ts index b565ac2cd..245a1718a 100644 --- a/examples/advanced/src/gen/clients/axios/petService/deletePet.ts +++ b/examples/advanced/src/gen/clients/axios/petService/deletePet.ts @@ -8,7 +8,7 @@ import type { DeletePetMutationResponse, DeletePetPathParams, DeletePetHeaderPar * @link /pet/:petId */ export async function deletePet( - petId: DeletePetPathParams['petId'], + { petId }: DeletePetPathParams, headers?: DeletePetHeaderParams, options: Partial[0]> = {}, ): Promise> { diff --git a/examples/advanced/src/gen/clients/axios/petService/getPetById.ts b/examples/advanced/src/gen/clients/axios/petService/getPetById.ts index 1da3be835..298d3c1df 100644 --- a/examples/advanced/src/gen/clients/axios/petService/getPetById.ts +++ b/examples/advanced/src/gen/clients/axios/petService/getPetById.ts @@ -8,7 +8,7 @@ import type { GetPetByIdQueryResponse, GetPetByIdPathParams } from '../../../mod * @link /pet/:petId */ export async function getPetById( - petId: GetPetByIdPathParams['petId'], + { petId }: GetPetByIdPathParams, options: Partial[0]> = {}, ): Promise> { return client({ diff --git a/examples/advanced/src/gen/clients/axios/petService/updatePetWithForm.ts b/examples/advanced/src/gen/clients/axios/petService/updatePetWithForm.ts index 127795fc9..ed7869d7d 100644 --- a/examples/advanced/src/gen/clients/axios/petService/updatePetWithForm.ts +++ b/examples/advanced/src/gen/clients/axios/petService/updatePetWithForm.ts @@ -11,7 +11,7 @@ import type { * @link /pet/:petId */ export async function updatePetWithForm( - petId: UpdatePetWithFormPathParams['petId'], + { petId }: UpdatePetWithFormPathParams, params?: UpdatePetWithFormQueryParams, options: Partial[0]> = {}, ): Promise> { diff --git a/examples/advanced/src/gen/clients/axios/petService/uploadFile.ts b/examples/advanced/src/gen/clients/axios/petService/uploadFile.ts index f93269143..b556ca242 100644 --- a/examples/advanced/src/gen/clients/axios/petService/uploadFile.ts +++ b/examples/advanced/src/gen/clients/axios/petService/uploadFile.ts @@ -12,7 +12,7 @@ import type { * @link /pet/:petId/uploadImage */ export async function uploadFile( - petId: UploadFilePathParams['petId'], + { petId }: UploadFilePathParams, data?: TVariables, params?: UploadFileQueryParams, options: Partial[0]> = {}, diff --git a/examples/advanced/src/gen/clients/axios/petsService/createPets.ts b/examples/advanced/src/gen/clients/axios/petsService/createPets.ts index 75cba40aa..89f3a1018 100644 --- a/examples/advanced/src/gen/clients/axios/petsService/createPets.ts +++ b/examples/advanced/src/gen/clients/axios/petsService/createPets.ts @@ -13,7 +13,7 @@ import type { * @link /pets/:uuid */ export async function createPets( - uuid: CreatePetsPathParams['uuid'], + { uuid }: CreatePetsPathParams, data: TVariables, headers: CreatePetsHeaderParams, params?: CreatePetsQueryParams, diff --git a/examples/advanced/src/gen/clients/axios/userService/deleteUser.ts b/examples/advanced/src/gen/clients/axios/userService/deleteUser.ts index 79b09898b..7b054fd3c 100644 --- a/examples/advanced/src/gen/clients/axios/userService/deleteUser.ts +++ b/examples/advanced/src/gen/clients/axios/userService/deleteUser.ts @@ -8,7 +8,7 @@ import type { DeleteUserMutationResponse, DeleteUserPathParams } from '../../../ * @link /user/:username */ export async function deleteUser( - username: DeleteUserPathParams['username'], + { username }: DeleteUserPathParams, options: Partial[0]> = {}, ): Promise> { return client({ diff --git a/examples/advanced/src/gen/clients/axios/userService/getUserByName.ts b/examples/advanced/src/gen/clients/axios/userService/getUserByName.ts index 973209a7b..cf8652868 100644 --- a/examples/advanced/src/gen/clients/axios/userService/getUserByName.ts +++ b/examples/advanced/src/gen/clients/axios/userService/getUserByName.ts @@ -7,7 +7,7 @@ import type { GetUserByNameQueryResponse, GetUserByNamePathParams } from '../../ * @link /user/:username */ export async function getUserByName( - username: GetUserByNamePathParams['username'], + { username }: GetUserByNamePathParams, options: Partial[0]> = {}, ): Promise> { return client({ diff --git a/examples/advanced/src/gen/clients/axios/userService/updateUser.ts b/examples/advanced/src/gen/clients/axios/userService/updateUser.ts index 57481660f..f5b28c078 100644 --- a/examples/advanced/src/gen/clients/axios/userService/updateUser.ts +++ b/examples/advanced/src/gen/clients/axios/userService/updateUser.ts @@ -8,7 +8,7 @@ import type { UpdateUserMutationRequest, UpdateUserMutationResponse, UpdateUserP * @link /user/:username */ export async function updateUser( - username: UpdateUserPathParams['username'], + { username }: UpdateUserPathParams, data?: TVariables, options: Partial[0]> = {}, ): Promise> { diff --git a/packages/core/src/utils/FunctionParams.ts b/packages/core/src/utils/FunctionParams.ts index a45fc6f03..4554ed166 100644 --- a/packages/core/src/utils/FunctionParams.ts +++ b/packages/core/src/utils/FunctionParams.ts @@ -65,16 +65,14 @@ export class FunctionParams { return acc } - const parameterName = camelCase(name, { delimiter: '', transform: camelCaseTransformMerge }) - if (type) { if (required) { - acc.push(`${parameterName}: ${type}${rest.default ? ` = ${rest.default}` : ''}`) + acc.push(`${name}: ${type}${rest.default ? ` = ${rest.default}` : ''}`) } else { - acc.push(`${parameterName}?: ${type}`) + acc.push(`${name}?: ${type}`) } } else { - acc.push(`${parameterName}`) + acc.push(`${name}`) } return acc diff --git a/packages/swagger-client/src/builders/ClientBuilder.tsx b/packages/swagger-client/src/builders/ClientBuilder.tsx index c80c4992b..4285a206d 100644 --- a/packages/swagger-client/src/builders/ClientBuilder.tsx +++ b/packages/swagger-client/src/builders/ClientBuilder.tsx @@ -15,6 +15,7 @@ import type { AppMeta, Options as PluginOptions } from '../types.ts' type Config = { pluginManager: PluginManager dataReturnType: PluginOptions['dataReturnType'] + pathParamsType: PluginOptions['pathParamsType'] operation: Operation schemas: OperationSchemas clientPath?: KubbFile.OptionalPath @@ -25,7 +26,7 @@ type ClientResult = { Component: React.ElementType } export class ClientBuilder extends OasBuilder { private get client(): ClientResult { - const { operation, schemas, dataReturnType } = this.config + const { operation, schemas, dataReturnType, pathParamsType } = this.config const comments = getComments(operation) const method = operation.method @@ -42,7 +43,7 @@ export class ClientBuilder extends OasBuilder { clientGenerics.add([{ type: 'TData' }, { type: 'TVariables', enabled: !!schemas.request?.name }]) params.add([ - ...getASTParams(schemas.pathParams, { typed: true }), + ...getASTParams(schemas.pathParams, { typed: true, asObject: pathParamsType === 'object' }), { name: 'data', type: 'TVariables', diff --git a/packages/swagger-client/src/generators/OperationGenerator.ts b/packages/swagger-client/src/generators/OperationGenerator.ts index 27dbd9654..86cb21715 100644 --- a/packages/swagger-client/src/generators/OperationGenerator.ts +++ b/packages/swagger-client/src/generators/OperationGenerator.ts @@ -13,6 +13,7 @@ type Options = { clientPath?: KubbFile.OptionalPath clientImportPath?: KubbFile.OptionalPath dataReturnType: PluginOptions['dataReturnType'] + pathParamsType: PluginOptions['pathParamsType'] oas: Oas contentType?: ContentType skipBy: SkipBy[] @@ -78,7 +79,7 @@ export class OperationGenerator extends Generator { } async get(operation: Operation, schemas: OperationSchemas): Promise | null> { - const { pluginManager, oas, clientPath, clientImportPath, dataReturnType } = this.options + const { pluginManager, oas, clientPath, clientImportPath, dataReturnType, pathParamsType } = this.options const clientBuilder = new ClientBuilder(oas).configure({ pluginManager, @@ -87,6 +88,7 @@ export class OperationGenerator extends Generator { dataReturnType, clientPath, clientImportPath, + pathParamsType, }) const file = clientBuilder.render().file @@ -107,7 +109,7 @@ export class OperationGenerator extends Generator { } async post(operation: Operation, schemas: OperationSchemas): Promise | null> { - const { pluginManager, oas, clientPath, clientImportPath, dataReturnType } = this.options + const { pluginManager, oas, clientPath, clientImportPath, dataReturnType, pathParamsType } = this.options const clientBuilder = new ClientBuilder(oas).configure({ pluginManager, @@ -116,6 +118,7 @@ export class OperationGenerator extends Generator { dataReturnType, clientPath, clientImportPath, + pathParamsType, }) const file = clientBuilder.render().file diff --git a/packages/swagger-client/src/plugin.ts b/packages/swagger-client/src/plugin.ts index 3970387b2..c947dd64f 100644 --- a/packages/swagger-client/src/plugin.ts +++ b/packages/swagger-client/src/plugin.ts @@ -14,7 +14,7 @@ import type { FileMeta, PluginOptions } from './types.ts' export const pluginName: PluginOptions['name'] = 'swagger-client' as const export const definePlugin = createPlugin((options) => { - const { output = 'clients', groupBy, skipBy = [], transformers = {}, dataReturnType = 'data' } = options + const { output = 'clients', groupBy, skipBy = [], transformers = {}, dataReturnType = 'data', pathParamsType = 'inline' } = options const template = groupBy?.output ? groupBy.output : `${output}/{{tag}}Controller` let pluginsOptions: [SwaggerPluginOptions] @@ -65,6 +65,7 @@ export const definePlugin = createPlugin((options) => { dataReturnType, clientPath, clientImportPath: options.clientImportPath, + pathParamsType, oas, skipBy, resolvePath: (params) => this.resolvePath({ pluginName, ...params }), diff --git a/packages/swagger-client/src/types.ts b/packages/swagger-client/src/types.ts index 8ff3cca25..750b27c7f 100644 --- a/packages/swagger-client/src/types.ts +++ b/packages/swagger-client/src/types.ts @@ -79,6 +79,18 @@ export type Options = { * @private */ dataReturnType?: 'data' | 'full' + /** + * Experimental + * + * How to pass your pathParams. + * + * `object` will return the pathParams as an object. + * + * `inline` will return the pathParams as comma separated params. + * @default `'inline'` + * @private + */ + pathParamsType?: 'object' | 'inline' transformers?: { /** * Override the name of the client that is getting generated, this will also override the name of the file. diff --git a/packages/swagger/src/utils/getParams.ts b/packages/swagger/src/utils/getParams.ts index b90bb71b1..5a8b7a72b 100644 --- a/packages/swagger/src/utils/getParams.ts +++ b/packages/swagger/src/utils/getParams.ts @@ -7,11 +7,25 @@ import type { OperationSchema } from '../types.ts' export function getASTParams( operationSchema: OperationSchema | undefined, - { typed = false, override }: { typed?: boolean; override?: (data: FunctionParamsAST) => FunctionParamsAST } = {}, + { typed = false, override, asObject = false }: { typed?: boolean; asObject?: boolean; override?: (data: FunctionParamsAST) => FunctionParamsAST } = {}, ): FunctionParamsAST[] { if (!operationSchema || !operationSchema.schema.properties || !operationSchema.name) { return [] } + + if (asObject) { + return [ + { + name: `{ ${getASTParams(operationSchema, { typed: true }) + .map((item) => item.name) + .join(', ')} }`, + type: operationSchema?.name, + enabled: !!operationSchema?.name, + required: !!operationSchema?.schema.required?.length, + }, + ] + } + return Object.entries(operationSchema.schema.properties).map(([name, schema]) => { const isParam = isParameterObject(schema) const data: FunctionParamsAST = { name, required: isParam ? schema.required : undefined, type: typed ? `${operationSchema.name}["${name}"]` : undefined } From a435b1e470246cfd91088f47f593641b09c7639c Mon Sep 17 00:00:00 2001 From: Stijn Van Hulle Date: Wed, 18 Oct 2023 18:47:41 +0200 Subject: [PATCH 2/3] chore: pathParamsType example --- examples/advanced/configs/kubb.config.ts | 1 + packages/core/src/utils/FunctionParams.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/advanced/configs/kubb.config.ts b/examples/advanced/configs/kubb.config.ts index d674d14fc..a2540d889 100644 --- a/examples/advanced/configs/kubb.config.ts +++ b/examples/advanced/configs/kubb.config.ts @@ -77,6 +77,7 @@ export default defineConfig(async () => { groupBy: { type: 'tag', output: './clients/axios/{{tag}}Service' }, clientImportPath: '../../../../axios-client.ts', dataReturnType: 'full', + pathParamsType: 'object', }, ], [ diff --git a/packages/core/src/utils/FunctionParams.ts b/packages/core/src/utils/FunctionParams.ts index a45fc6f03..147c9a1a6 100644 --- a/packages/core/src/utils/FunctionParams.ts +++ b/packages/core/src/utils/FunctionParams.ts @@ -64,8 +64,8 @@ export class FunctionParams { return acc } - - const parameterName = camelCase(name, { delimiter: '', transform: camelCaseTransformMerge }) + // TODO check whey we still need the camelcase here + const parameterName = name.startsWith('{') ? name : camelCase(name, { delimiter: '', transform: camelCaseTransformMerge }) if (type) { if (required) { From 2da4c7a79384d6466ffa00d7552c2e1487756b48 Mon Sep 17 00:00:00 2001 From: Stijn Van Hulle Date: Wed, 18 Oct 2023 18:53:49 +0200 Subject: [PATCH 3/3] test: asObject for getParamsAST --- packages/swagger/src/utils/getParams.test.ts | 21 ++++++++++++++++++++ packages/swagger/src/utils/getParams.ts | 8 ++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/swagger/src/utils/getParams.test.ts b/packages/swagger/src/utils/getParams.test.ts index fe71ffc65..b25c4f3f7 100644 --- a/packages/swagger/src/utils/getParams.test.ts +++ b/packages/swagger/src/utils/getParams.test.ts @@ -49,4 +49,25 @@ describe('getParams', () => { ).toString(), ).toBe('name: Pet["name"], age: Pet["age"]') }) + + test('if operation returns a string with typed parameters and as an object', () => { + expect( + getParams( + { + name: 'Pet', + schema: { + properties: { + name: { + type: 'string', + }, + age: { + type: 'number', + }, + }, + }, + }, + { asObject: true }, + ).toString(), + ).toBe('{ name, age }: Pet') + }) }) diff --git a/packages/swagger/src/utils/getParams.ts b/packages/swagger/src/utils/getParams.ts index 5a8b7a72b..2af9d8673 100644 --- a/packages/swagger/src/utils/getParams.ts +++ b/packages/swagger/src/utils/getParams.ts @@ -16,12 +16,12 @@ export function getASTParams( if (asObject) { return [ { - name: `{ ${getASTParams(operationSchema, { typed: true }) + name: `{ ${getASTParams(operationSchema) .map((item) => item.name) .join(', ')} }`, type: operationSchema?.name, enabled: !!operationSchema?.name, - required: !!operationSchema?.schema.required?.length, + required: true, }, ] } @@ -41,9 +41,9 @@ type GetParamsResult = { // TODO convert to class together with `createFunctionParams` and `getASTParams` export function getParams( operationSchema: OperationSchema | undefined, - { typed = false, override }: { typed?: boolean; override?: (data: FunctionParamsAST) => FunctionParamsAST } = {}, + { typed = false, override, asObject = false }: { typed?: boolean; asObject?: boolean; override?: (data: FunctionParamsAST) => FunctionParamsAST } = {}, ): GetParamsResult { - const ast = getASTParams(operationSchema, { typed, override }) + const ast = getASTParams(operationSchema, { typed, override, asObject }) const functionParams = new FunctionParams() functionParams.add(ast)