Skip to content

Commit

Permalink
Merge pull request #506 from kubb-project/feat/pathParamsType
Browse files Browse the repository at this point in the history
feat: `pathParamsType` to override behaviour when calling a generated get/post/put function
  • Loading branch information
stijnvanhulle authored Oct 18, 2023
2 parents a08cd67 + 2da4c7a commit f6ad26a
Show file tree
Hide file tree
Showing 17 changed files with 83 additions and 21 deletions.
13 changes: 11 additions & 2 deletions docs/plugins/swagger-client/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,17 @@ Default: `"@kubb/swagger-client/client"`
### dataReturnType <Badge type="warning" text="experimental" />
ReturnType that needs to be used when calling client().

`Data` will return ResponseConfig[data]. <br/>
`Full` will return ResponseConfig.
`data` will return ResponseConfig[data]. <br/>
`full` will return ResponseConfig.

Type: `string` <br/>
Default: `"data"`

### pathParamsType <Badge type="warning" text="experimental" />
How to pass your pathParams.

`object` will return the pathParams as an object. <br/>
`inline` will return the pathParams as comma separated params.

Type: `string` <br/>
Default: `"data"`
Expand Down
1 change: 1 addition & 0 deletions examples/advanced/configs/kubb.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export default defineConfig(async () => {
groupBy: { type: 'tag', output: './clients/axios/{{tag}}Service' },
clientImportPath: '../../../../axios-client.ts',
dataReturnType: 'full',
pathParamsType: 'object',
},
],
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { DeletePetMutationResponse, DeletePetPathParams, DeletePetHeaderPar
* @link /pet/:petId
*/
export async function deletePet<TData = DeletePetMutationResponse>(
petId: DeletePetPathParams['petId'],
{ petId }: DeletePetPathParams,
headers?: DeletePetHeaderParams,
options: Partial<Parameters<typeof client>[0]> = {},
): Promise<ResponseConfig<TData>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { GetPetByIdQueryResponse, GetPetByIdPathParams } from '../../../mod
* @link /pet/:petId
*/
export async function getPetById<TData = GetPetByIdQueryResponse>(
petId: GetPetByIdPathParams['petId'],
{ petId }: GetPetByIdPathParams,
options: Partial<Parameters<typeof client>[0]> = {},
): Promise<ResponseConfig<TData>> {
return client<TData>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
* @link /pet/:petId
*/
export async function updatePetWithForm<TData = UpdatePetWithFormMutationResponse>(
petId: UpdatePetWithFormPathParams['petId'],
{ petId }: UpdatePetWithFormPathParams,
params?: UpdatePetWithFormQueryParams,
options: Partial<Parameters<typeof client>[0]> = {},
): Promise<ResponseConfig<TData>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
* @link /pet/:petId/uploadImage
*/
export async function uploadFile<TData = UploadFileMutationResponse, TVariables = UploadFileMutationRequest>(
petId: UploadFilePathParams['petId'],
{ petId }: UploadFilePathParams,
data?: TVariables,
params?: UploadFileQueryParams,
options: Partial<Parameters<typeof client>[0]> = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
* @link /pets/:uuid
*/
export async function createPets<TData = CreatePetsMutationResponse, TVariables = CreatePetsMutationRequest>(
uuid: CreatePetsPathParams['uuid'],
{ uuid }: CreatePetsPathParams,
data: TVariables,
headers: CreatePetsHeaderParams,
params?: CreatePetsQueryParams,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { DeleteUserMutationResponse, DeleteUserPathParams } from '../../../
* @link /user/:username
*/
export async function deleteUser<TData = DeleteUserMutationResponse>(
username: DeleteUserPathParams['username'],
{ username }: DeleteUserPathParams,
options: Partial<Parameters<typeof client>[0]> = {},
): Promise<ResponseConfig<TData>> {
return client<TData>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { GetUserByNameQueryResponse, GetUserByNamePathParams } from '../../
* @link /user/:username
*/
export async function getUserByName<TData = GetUserByNameQueryResponse>(
username: GetUserByNamePathParams['username'],
{ username }: GetUserByNamePathParams,
options: Partial<Parameters<typeof client>[0]> = {},
): Promise<ResponseConfig<TData>> {
return client<TData>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { UpdateUserMutationRequest, UpdateUserMutationResponse, UpdateUserP
* @link /user/:username
*/
export async function updateUser<TData = UpdateUserMutationResponse, TVariables = UpdateUserMutationRequest>(
username: UpdateUserPathParams['username'],
{ username }: UpdateUserPathParams,
data?: TVariables,
options: Partial<Parameters<typeof client>[0]> = {},
): Promise<ResponseConfig<TData>> {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/utils/FunctionParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
5 changes: 3 additions & 2 deletions packages/swagger-client/src/builders/ClientBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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?: PluginOptions['client']
Expand All @@ -25,7 +26,7 @@ type ClientResult = { Component: React.ElementType }

export class ClientBuilder extends OasBuilder<Config> {
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
Expand All @@ -42,7 +43,7 @@ export class ClientBuilder extends OasBuilder<Config> {
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',
Expand Down
7 changes: 5 additions & 2 deletions packages/swagger-client/src/generators/OperationGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { FileMeta, Options as PluginOptions } from '../types.ts'

type Options = {
clientPath?: PluginOptions['client']
pathParamsType: PluginOptions['pathParamsType']
clientImportPath?: PluginOptions['clientImportPath']
dataReturnType: NonNullable<PluginOptions['dataReturnType']>
}
Expand Down Expand Up @@ -62,7 +63,7 @@ export class OperationGenerator extends Generator<Options> {
}

async get(operation: Operation, schemas: OperationSchemas): Promise<KubbFile.File<FileMeta> | 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,
Expand All @@ -71,6 +72,7 @@ export class OperationGenerator extends Generator<Options> {
dataReturnType,
clientPath,
clientImportPath,
pathParamsType,
})
const file = clientBuilder.render().file

Expand All @@ -91,7 +93,7 @@ export class OperationGenerator extends Generator<Options> {
}

async post(operation: Operation, schemas: OperationSchemas): Promise<KubbFile.File<FileMeta> | 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,
Expand All @@ -100,6 +102,7 @@ export class OperationGenerator extends Generator<Options> {
dataReturnType,
clientPath,
clientImportPath,
pathParamsType,
})
const file = clientBuilder.render().file

Expand Down
5 changes: 3 additions & 2 deletions packages/swagger-client/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { FileMeta, PluginOptions } from './types.ts'
export const pluginName: PluginOptions['name'] = 'swagger-client' as const

export const definePlugin = createPlugin<PluginOptions>((options) => {
const { output = 'clients', groupBy, skipBy = [], transformers = {}, dataReturnType = 'data' } = options
const { output = 'clients', groupBy, skipBy = [], transformers = {}, clientImportPath, dataReturnType = 'data', pathParamsType = 'inline' } = options
const template = groupBy?.output ? groupBy.output : `${output}/{{tag}}Controller`
let pluginsOptions: [SwaggerPluginOptions]

Expand Down Expand Up @@ -65,7 +65,8 @@ export const definePlugin = createPlugin<PluginOptions>((options) => {
contentType: swaggerPlugin.api.contentType,
dataReturnType,
clientPath,
clientImportPath: options.clientImportPath,
clientImportPath,
pathParamsType,
skipBy,
})

Expand Down
12 changes: 12 additions & 0 deletions packages/swagger-client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions packages/swagger/src/utils/getParams.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
})
20 changes: 17 additions & 3 deletions packages/swagger/src/utils/getParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
.map((item) => item.name)
.join(', ')} }`,
type: operationSchema?.name,
enabled: !!operationSchema?.name,
required: true,
},
]
}

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 }
Expand All @@ -27,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)

Expand Down

1 comment on commit f6ad26a

@vercel
Copy link

@vercel vercel bot commented on f6ad26a Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

kubb – ./

kubb-kubb.vercel.app
kubb-git-main-kubb.vercel.app
kubb.dev
www.kubb.dev

Please sign in to comment.