diff --git a/packages/rtk-query-codegen-openapi/src/codegen.ts b/packages/rtk-query-codegen-openapi/src/codegen.ts index 9ac4bd2274..6ea573807a 100644 --- a/packages/rtk-query-codegen-openapi/src/codegen.ts +++ b/packages/rtk-query-codegen-openapi/src/codegen.ts @@ -119,6 +119,7 @@ export function generateEndpointDefinition({ endpointBuilder = defaultEndpointBuilder, extraEndpointsProps, tags, + tagOverrides, }: { operationName: string; type: 'query' | 'mutation'; @@ -127,14 +128,37 @@ export function generateEndpointDefinition({ queryFn: ts.Expression; endpointBuilder?: ts.Identifier; extraEndpointsProps: ObjectPropertyDefinitions; - tags: string[]; + tags?: string[]; + tagOverrides?: { providesTags?: string[]; invalidatesTags?: string[] }; }) { const objectProperties = generateObjectProperties({ query: queryFn, ...extraEndpointsProps }); - if (tags.length > 0) { + const providesTags = + tagOverrides && 'providesTags' in tagOverrides + ? tagOverrides.providesTags + : type === 'query' + ? tags + : undefined; + const invalidatesTags = + tagOverrides && 'invalidatesTags' in tagOverrides + ? tagOverrides.invalidatesTags + : type === 'mutation' + ? tags + : undefined; + + if (providesTags !== undefined) { + objectProperties.push( + factory.createPropertyAssignment( + factory.createIdentifier('providesTags'), + factory.createArrayLiteralExpression(providesTags.map((tag) => factory.createStringLiteral(tag)), false) + ) + ); + } + + if (invalidatesTags !== undefined) { objectProperties.push( factory.createPropertyAssignment( - factory.createIdentifier(type === 'query' ? 'providesTags' : 'invalidatesTags'), - factory.createArrayLiteralExpression(tags.map((tag) => factory.createStringLiteral(tag), false)) + factory.createIdentifier('invalidatesTags'), + factory.createArrayLiteralExpression(invalidatesTags.map((tag) => factory.createStringLiteral(tag)), false) ) ); } diff --git a/packages/rtk-query-codegen-openapi/src/generate.ts b/packages/rtk-query-codegen-openapi/src/generate.ts index 789457e9ef..5fa0554132 100644 --- a/packages/rtk-query-codegen-openapi/src/generate.ts +++ b/packages/rtk-query-codegen-openapi/src/generate.ts @@ -253,7 +253,7 @@ export async function generateApi( operation: { responses, requestBody }, } = operationDefinition; const operationName = getOperationName({ verb, path, operation }); - const tags = tag ? getTags({ verb, pathItem }) : []; + const tags = tag ? getTags({ verb, pathItem }) : undefined; const isQuery = testIsQuery(verb, overrides); const returnsJson = apiGen.getResponseType(responses) === 'json'; @@ -408,6 +408,14 @@ export async function generateApi( ).name ); + const tagOverrides = + overrides && (overrides.providesTags !== undefined || overrides.invalidatesTags !== undefined) + ? { + ...(overrides.providesTags !== undefined ? { providesTags: overrides.providesTags } : {}), + ...(overrides.invalidatesTags !== undefined ? { invalidatesTags: overrides.invalidatesTags } : {}), + } + : undefined; + return generateEndpointDefinition({ operationName: operationNameSuffix ? capitalize(operationName + operationNameSuffix) : operationName, type: isQuery ? 'query' : 'mutation', @@ -425,6 +433,7 @@ export async function generateApi( ? generateQueryEndpointProps({ operationDefinition }) : generateMutationEndpointProps({ operationDefinition }), tags, + tagOverrides, }); } diff --git a/packages/rtk-query-codegen-openapi/src/types.ts b/packages/rtk-query-codegen-openapi/src/types.ts index f72dcece35..ce79a61ba2 100644 --- a/packages/rtk-query-codegen-openapi/src/types.ts +++ b/packages/rtk-query-codegen-openapi/src/types.ts @@ -154,6 +154,8 @@ export type EndpointOverrides = { } & AtLeastOneKey<{ type: 'mutation' | 'query'; parameterFilter: ParameterMatcher; + providesTags: string[]; + invalidatesTags: string[]; }>; export type ConfigFile = diff --git a/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts b/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts index bb823e913e..e45a21e85a 100644 --- a/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts +++ b/packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts @@ -172,6 +172,98 @@ describe('endpoint overrides', () => { expect(api).not.toMatch(/headers: {/); expect(api).toMatchSnapshot('should remove all parameters except for findPetsByStatus'); }); + + it('should override generated tags', async () => { + const api = await generateEndpoints({ + unionUndefined: true, + tag: true, + apiFile: './fixtures/emptyApi.ts', + schemaFile: resolve(__dirname, 'fixtures/petstore.json'), + filterEndpoints: ['getPetById', 'deletePet'], + endpointOverrides: [ + { + pattern: 'getPetById', + providesTags: ['CustomQueryTag'], + }, + { + pattern: 'deletePet', + invalidatesTags: [], + }, + ], + }); + + expect(api).toMatch(/getPetById: build\.query[\s\S]*providesTags: \["CustomQueryTag"\]/); + expect(api).not.toMatch(/getPetById: build\.query[\s\S]*providesTags: \["pet"\]/); + expect(api).toMatch(/deletePet: build\.mutation[\s\S]*invalidatesTags: \[\]/); + expect(api).not.toMatch(/deletePet: build\.mutation[\s\S]*invalidatesTags: \["pet"\]/); + }); + + it('should allow tag overrides when tag generation is disabled', async () => { + const api = await generateEndpoints({ + unionUndefined: true, + apiFile: './fixtures/emptyApi.ts', + schemaFile: resolve(__dirname, 'fixtures/petstore.json'), + filterEndpoints: ['getPetById', 'deletePet'], + endpointOverrides: [ + { + pattern: 'getPetById', + providesTags: ['ManualProvides'], + }, + { + pattern: 'deletePet', + invalidatesTags: ['ManualInvalidates'], + }, + ], + }); + + expect(api).toMatch(/getPetById: build\.query[\s\S]*providesTags: \["ManualProvides"\]/); + expect(api).toMatch(/deletePet: build\.mutation[\s\S]*invalidatesTags: \["ManualInvalidates"\]/); + expect(api).not.toMatch(/providesTags: \[\]/); + expect(api).not.toMatch(/invalidatesTags: \[\]/); + }); + + it('allows overriding tags regardless of inferred endpoint type', async () => { + const api = await generateEndpoints({ + unionUndefined: true, + apiFile: './fixtures/emptyApi.ts', + schemaFile: resolve(__dirname, 'fixtures/petstore.json'), + filterEndpoints: 'loginUser', + endpointOverrides: [ + { + pattern: 'loginUser', + type: 'mutation', + providesTags: ['LoginStatus'], + }, + ], + }); + + expect(api).toMatch(/loginUser: build\.mutation/); + expect(api).toMatch(/providesTags: \["LoginStatus"\]/); + expect(api).not.toMatch(/invalidatesTags:/); + }); + + it('allows overriding both providesTags and invalidatesTags simultaneously', async () => { + const api = await generateEndpoints({ + unionUndefined: true, + tag: true, + apiFile: './fixtures/emptyApi.ts', + schemaFile: resolve(__dirname, 'fixtures/petstore.json'), + filterEndpoints: 'findPetsByStatus', + endpointOverrides: [ + { + pattern: 'findPetsByStatus', + providesTags: ['CustomProvide'], + invalidatesTags: ['CustomInvalidate'], + }, + ], + }); + + expect(api).toMatch(/findPetsByStatus: build\.query/); + expect(api).toMatch(/providesTags: \["CustomProvide"\]/); + expect(api).toMatch(/invalidatesTags: \["CustomInvalidate"\]/); + expect(api).not.toMatch(/providesTags: \["pet"\]/); + expect(api).not.toMatch(/invalidatesTags: \["pet"\]/); + }); }); describe('option encodePathParams', () => {