Skip to content

Commit e2a2b58

Browse files
authored
[OpenAPI codegen] Use schema.title for body params (#2813)
* Use schema.title for body params * Add test case for title as body param name --------- Co-authored-by: Georg Wicke-Arndt <[email protected]>
1 parent 63f9ef5 commit e2a2b58

File tree

4 files changed

+157
-51
lines changed

4 files changed

+157
-51
lines changed

packages/rtk-query-codegen-openapi/src/generate.ts

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,13 @@ export async function generateApi(
168168
...apiGen.enumAliases,
169169
...(hooks
170170
? [
171-
generateReactHooks({
172-
exportName: generatedApiName,
173-
operationDefinitions,
174-
endpointOverrides,
175-
config: hooks,
176-
}),
177-
]
171+
generateReactHooks({
172+
exportName: generatedApiName,
173+
operationDefinitions,
174+
endpointOverrides,
175+
config: hooks,
176+
}),
177+
]
178178
: []),
179179
],
180180
factory.createToken(ts.SyntaxKind.EndOfFileToken),
@@ -294,7 +294,7 @@ export async function generateApi(
294294
const body = apiGen.resolve(requestBody);
295295
const schema = apiGen.getSchemaFromContent(body.content);
296296
const type = apiGen.getTypeFromSchema(schema);
297-
const schemaName = camelCase((type as any).name || getReferenceName(schema) || 'body');
297+
const schemaName = camelCase((type as any).name || getReferenceName(schema) || ("title" in schema && schema.title) || 'body');
298298
const name = generateName(schemaName in queryArg ? 'body' : schemaName, 'body');
299299

300300
queryArg[name] = {
@@ -328,19 +328,19 @@ export async function generateApi(
328328
? isFlatArg
329329
? withQueryComment({ ...queryArgValues[0].type }, queryArgValues[0], false)
330330
: factory.createTypeLiteralNode(
331-
queryArgValues.map((def) =>
332-
withQueryComment(
333-
factory.createPropertySignature(
334-
undefined,
335-
propertyName(def.name),
336-
createQuestionToken(!def.required),
337-
def.type
338-
),
339-
def,
340-
true
341-
)
331+
queryArgValues.map((def) =>
332+
withQueryComment(
333+
factory.createPropertySignature(
334+
undefined,
335+
propertyName(def.name),
336+
createQuestionToken(!def.required),
337+
def.type
338+
),
339+
def,
340+
true
342341
)
343342
)
343+
)
344344
: factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)
345345
)
346346
).name
@@ -384,18 +384,18 @@ export async function generateApi(
384384
return parameters.length === 0
385385
? undefined
386386
: factory.createPropertyAssignment(
387-
factory.createIdentifier(propertyName),
388-
factory.createObjectLiteralExpression(
389-
parameters.map(
390-
(param) =>
391-
createPropertyAssignment(
392-
param.originalName,
393-
isFlatArg ? rootObject : accessProperty(rootObject, param.name)
394-
),
395-
true
396-
)
387+
factory.createIdentifier(propertyName),
388+
factory.createObjectLiteralExpression(
389+
parameters.map(
390+
(param) =>
391+
createPropertyAssignment(
392+
param.originalName,
393+
isFlatArg ? rootObject : accessProperty(rootObject, param.name)
394+
),
395+
true
397396
)
398-
);
397+
)
398+
);
399399
}
400400

401401
return factory.createArrowFunction(
@@ -416,17 +416,17 @@ export async function generateApi(
416416
isQuery && verb.toUpperCase() === 'GET'
417417
? undefined
418418
: factory.createPropertyAssignment(
419-
factory.createIdentifier('method'),
420-
factory.createStringLiteral(verb.toUpperCase())
421-
),
419+
factory.createIdentifier('method'),
420+
factory.createStringLiteral(verb.toUpperCase())
421+
),
422422
bodyParameter === undefined
423423
? undefined
424424
: factory.createPropertyAssignment(
425-
factory.createIdentifier('body'),
426-
isFlatArg
427-
? rootObject
428-
: factory.createPropertyAccessExpression(rootObject, factory.createIdentifier(bodyParameter.name))
429-
),
425+
factory.createIdentifier('body'),
426+
isFlatArg
427+
? rootObject
428+
: factory.createPropertyAccessExpression(rootObject, factory.createIdentifier(bodyParameter.name))
429+
),
430430
createObjectLiteralProperty(pickParams('cookie'), 'cookies'),
431431
createObjectLiteralProperty(pickParams('header'), 'headers'),
432432
createObjectLiteralProperty(pickParams('query'), 'params'),
@@ -438,12 +438,12 @@ export async function generateApi(
438438
}
439439

440440
// eslint-disable-next-line no-empty-pattern
441-
function generateQueryEndpointProps({}: { operationDefinition: OperationDefinition }): ObjectPropertyDefinitions {
441+
function generateQueryEndpointProps({ }: { operationDefinition: OperationDefinition }): ObjectPropertyDefinitions {
442442
return {}; /* TODO needs implementation - skip for now */
443443
}
444444

445445
// eslint-disable-next-line no-empty-pattern
446-
function generateMutationEndpointProps({}: { operationDefinition: OperationDefinition }): ObjectPropertyDefinitions {
446+
function generateMutationEndpointProps({ }: { operationDefinition: OperationDefinition }): ObjectPropertyDefinitions {
447447
return {}; /* TODO needs implementation - skip for now */
448448
}
449449
}
@@ -473,16 +473,16 @@ function generatePathExpression(
473473

474474
return expressions.length
475475
? factory.createTemplateExpression(
476-
factory.createTemplateHead(head),
477-
expressions.map(([prop, literal], index) =>
478-
factory.createTemplateSpan(
479-
isFlatArg ? rootObject : accessProperty(rootObject, prop),
480-
index === expressions.length - 1
481-
? factory.createTemplateTail(literal)
482-
: factory.createTemplateMiddle(literal)
483-
)
476+
factory.createTemplateHead(head),
477+
expressions.map(([prop, literal], index) =>
478+
factory.createTemplateSpan(
479+
isFlatArg ? rootObject : accessProperty(rootObject, prop),
480+
index === expressions.length - 1
481+
? factory.createTemplateTail(literal)
482+
: factory.createTemplateMiddle(literal)
484483
)
485484
)
485+
)
486486
: factory.createNoSubstitutionTemplateLiteral(head);
487487
}
488488

@@ -493,13 +493,13 @@ type QueryArgDefinition = {
493493
required?: boolean;
494494
param?: OpenAPIV3.ParameterObject;
495495
} & (
496-
| {
496+
| {
497497
origin: 'param';
498498
param: OpenAPIV3.ParameterObject;
499499
}
500-
| {
500+
| {
501501
origin: 'body';
502502
body: OpenAPIV3.RequestBodyObject;
503503
}
504-
);
504+
);
505505
type QueryArgDefinitions = Record<string, QueryArgDefinition>;

packages/rtk-query-codegen-openapi/test/__snapshots__/generateEndpoints.test.ts.snap

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,40 @@ export type LoginUserApiArg = {
383383

384384
`;
385385

386+
exports[`falls back to the \`title\` parameter for the body parameter name when no other name is available 1`] = `
387+
import { api } from 'fixtures/emptyApi';
388+
const injectedRtkApi = api.injectEndpoints({
389+
endpoints: (build) => ({
390+
postV1Export: build.mutation<PostV1ExportApiResponse, PostV1ExportApiArg>({
391+
query: (queryArg) => ({
392+
url: \`/v1/export\`,
393+
method: 'POST',
394+
body: queryArg.exportedEntityIds,
395+
}),
396+
}),
397+
postV1Import: build.mutation<PostV1ImportApiResponse, PostV1ImportApiArg>({
398+
query: (queryArg) => ({
399+
url: \`/v1/import\`,
400+
method: 'POST',
401+
body: queryArg.rawData,
402+
}),
403+
}),
404+
}),
405+
overrideExisting: false,
406+
});
407+
export { injectedRtkApi as enhancedApi };
408+
export type PostV1ExportApiResponse = unknown;
409+
export type PostV1ExportApiArg = {
410+
exportedEntityIds: IdList;
411+
};
412+
export type PostV1ImportApiResponse = unknown;
413+
export type PostV1ImportApiArg = {
414+
rawData: string;
415+
};
416+
export type IdList = number[];
417+
418+
`;
419+
386420
exports[`hooks generation uses overrides: should generate an \`useLoginMutation\` mutation hook 1`] = `
387421
import { api } from './fixtures/emptyApi';
388422
const injectedRtkApi = api.injectEndpoints({
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"openapi": "3.0.2",
3+
"info": {
4+
"title": "Title as param name",
5+
"version": "0.1"
6+
},
7+
"paths": {
8+
"/v1/export": {
9+
"post": {
10+
"requestBody": {
11+
"content": {
12+
"application/json": {
13+
"schema": {
14+
"title": "exportedEntityIds",
15+
"anyOf": [
16+
{
17+
"$ref": "#/components/schemas/IdList"
18+
}
19+
]
20+
}
21+
}
22+
}
23+
},
24+
"responses": {
25+
"200": {
26+
"description": "OK"
27+
}
28+
}
29+
}
30+
},
31+
"/v1/import": {
32+
"post": {
33+
"requestBody": {
34+
"content": {
35+
"application/json": {
36+
"schema": {
37+
"title": "rawData",
38+
"type": "string"
39+
}
40+
}
41+
}
42+
},
43+
"responses": {
44+
"200": {
45+
"description": "OK"
46+
}
47+
}
48+
}
49+
}
50+
},
51+
"components": {
52+
"schemas": {
53+
"IdList": {
54+
"type": "array",
55+
"items": {
56+
"type": "number"
57+
}
58+
}
59+
}
60+
}
61+
}

packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,17 @@ it('supports granular hooks generation with only mutations', async () => {
198198
expect(api).toMatchSnapshot();
199199
});
200200

201+
it('falls back to the `title` parameter for the body parameter name when no other name is available', async () => {
202+
const api = await generateEndpoints({
203+
apiFile: 'fixtures/emptyApi.ts',
204+
schemaFile: resolve(__dirname, 'fixtures/title-as-param-name.json'),
205+
});
206+
expect(api).not.toContain('queryArg.body');
207+
expect(api).toContain('queryArg.exportedEntityIds');
208+
expect(api).toContain('queryArg.rawData');
209+
expect(api).toMatchSnapshot();
210+
});
211+
201212
test('hooks generation uses overrides', async () => {
202213
const api = await generateEndpoints({
203214
unionUndefined: true,

0 commit comments

Comments
 (0)