diff --git a/packages/rtk-query-codegen-openapi/src/generate.ts b/packages/rtk-query-codegen-openapi/src/generate.ts index 123c0433c9..646d3a72c3 100644 --- a/packages/rtk-query-codegen-openapi/src/generate.ts +++ b/packages/rtk-query-codegen-openapi/src/generate.ts @@ -262,7 +262,24 @@ export async function generateApi( const isPureSnakeCase = /^[a-zA-Z][\\w]*$/.test(param.name); const camelCaseName = camelCase(param.name); - const name = isPureSnakeCase && !allNames.includes(camelCaseName) ? camelCaseName : param.name; + let name = isPureSnakeCase && !allNames.includes(camelCaseName) ? camelCaseName : param.name; + + if (name in queryArg) { + let duplicatedNewName = `${queryArg[name].param?.in}_${name}` + duplicatedNewName = isPureSnakeCase && !allNames.includes(duplicatedNewName) ? camelCase(duplicatedNewName) : duplicatedNewName + while (duplicatedNewName in queryArg) { + duplicatedNewName = '_' + duplicatedNewName; + } + queryArg[duplicatedNewName] = queryArg[name] + queryArg[duplicatedNewName].name = duplicatedNewName + delete queryArg[name] + + name = `${param.in}_${name}` + name = isPureSnakeCase && !allNames.includes(name) ? camelCase(name) : name; + } + while (name in queryArg) { + name = '_' + name; + } queryArg[name] = { origin: 'param', @@ -479,6 +496,7 @@ type QueryArgDefinition = { originalName: string; type: ts.TypeNode; required?: boolean; + param?: OpenAPIV3.ParameterObject } & ( | { origin: 'param'; diff --git a/packages/rtk-query-codegen-openapi/test/__snapshots__/generateEndpoints.test.ts.snap b/packages/rtk-query-codegen-openapi/test/__snapshots__/generateEndpoints.test.ts.snap index 1f01cf2155..6d7e9e5fb7 100644 --- a/packages/rtk-query-codegen-openapi/test/__snapshots__/generateEndpoints.test.ts.snap +++ b/packages/rtk-query-codegen-openapi/test/__snapshots__/generateEndpoints.test.ts.snap @@ -263,6 +263,39 @@ export type User = { `; +exports[`duplicate parameter names must be prefixed with a path or query prefix 1`] = ` +import { api } from './fixtures/emptyApi'; +const injectedRtkApi = api.injectEndpoints({ + endpoints: (build) => ({ + patchApiV1ListByItemId: build.mutation({ + query: (queryArg) => ({ + url: \`/api/v1/list/\${queryArg['item.id']}\`, + method: 'PATCH', + }), + }), + patchApiV2BySomeName: build.mutation({ + query: (queryArg) => ({ + url: \`/api/v2/\${queryArg.pathSomeName}\`, + method: 'PATCH', + params: { some_name: queryArg.querySomeName }, + }), + }), + }), + overrideExisting: false, +}); +export { injectedRtkApi as enhancedApi }; +export type PatchApiV1ListByItemIdApiResponse = /** status 200 A successful response. */ string; +export type PatchApiV1ListByItemIdApiArg = { + 'item.id': string; +}; +export type PatchApiV2BySomeNameApiResponse = /** status 200 A successful response. */ string; +export type PatchApiV2BySomeNameApiArg = { + pathSomeName: string; + querySomeName: string; +}; + +`; + exports[`endpoint filtering: should only have endpoints loginUser, placeOrder, getOrderById, deleteOrder 1`] = ` import { api } from './fixtures/emptyApi'; const injectedRtkApi = api.injectEndpoints({ @@ -434,6 +467,13 @@ const injectedRtkApi = api.injectEndpoints({ method: 'PATCH', }), }), + patchApiV2BySomeName: build.mutation({ + query: (queryArg) => ({ + url: \`/api/v2/\${queryArg.pathSomeName}\`, + method: 'PATCH', + params: { some_name: queryArg.querySomeName }, + }), + }), }), overrideExisting: false, }); @@ -442,6 +482,11 @@ export type PatchApiV1ListByItemIdApiResponse = /** status 200 A successful resp export type PatchApiV1ListByItemIdApiArg = { 'item.id': string; }; +export type PatchApiV2BySomeNameApiResponse = /** status 200 A successful response. */ string; +export type PatchApiV2BySomeNameApiArg = { + pathSomeName: string; + querySomeName: string; +}; `; diff --git a/packages/rtk-query-codegen-openapi/test/fixtures/params.json b/packages/rtk-query-codegen-openapi/test/fixtures/params.json index 5a97d8984e..8b078860cf 100644 --- a/packages/rtk-query-codegen-openapi/test/fixtures/params.json +++ b/packages/rtk-query-codegen-openapi/test/fixtures/params.json @@ -27,6 +27,32 @@ } ] } + }, + "/api/v2/{some_name}": { + "patch": { + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "string" + } + } + }, + "parameters": [ + { + "name": "some_name", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "some_name", + "in": "query", + "required": true, + "type": "string" + } + ] + } } } } diff --git a/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts b/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts index 01585dc291..51ed80db41 100644 --- a/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts +++ b/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts @@ -229,6 +229,18 @@ test('should use brackets in a querystring urls arg, when the arg contains full expect(api).toMatchSnapshot(); }); +test('duplicate parameter names must be prefixed with a path or query prefix', async () => { + const api = await generateEndpoints({ + unionUndefined: true, + apiFile: './fixtures/emptyApi.ts', + schemaFile: resolve(__dirname, 'fixtures/params.json'), + }); + // eslint-disable-next-line no-template-curly-in-string + expect(api).toContain('pathSomeName: string'); + expect(api).toContain('querySomeName: string'); + expect(api).toMatchSnapshot(); +}); + test('apiImport builds correct `import` statement', async () => { const api = await generateEndpoints({ unionUndefined: true,