Skip to content

Commit b953515

Browse files
committed
fix(parameter): pathパラメータを優先しつつ入力順を保持するよう同名パラメータの解決ロジックを修正
1 parent d223459 commit b953515

50 files changed

Lines changed: 5626 additions & 5322 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/internal/OpenApiTools/components/Parameter.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,28 @@ export const generatePropertySignatureObject = (
100100
};
101101
};
102102

103+
/**
104+
* パラメータの `in` プロパティを返す。
105+
*
106+
* inline パラメータはそのまま `in` を返す。
107+
* `$ref` 参照の場合はローカル参照を解決して store から実体を取得する。
108+
* store に存在しない場合(リモート参照など)は `undefined` を返す。
109+
*/
110+
const resolveParameterIn = (store: Walker.Store, parameter: OpenApi.Parameter | OpenApi.Reference): string | undefined => {
111+
if (!Guard.isReference(parameter)) {
112+
return parameter.in;
113+
}
114+
const localRef = Reference.generateLocalReference(parameter);
115+
if (!localRef) {
116+
return undefined;
117+
}
118+
try {
119+
return store.getParameter(localRef.path).in;
120+
} catch {
121+
return undefined;
122+
}
123+
};
124+
103125
export const generatePropertySignatures = (
104126
entryPoint: string,
105127
currentPoint: string,
@@ -109,16 +131,11 @@ export const generatePropertySignatures = (
109131
context: ToTypeNode.Context,
110132
converterContext: ConverterContext.Types,
111133
): string[] => {
112-
// Path parameters must be processed last so they win over same-named query/header
113-
// parameters when building the TypeScript interface (path params are always required).
114-
const sorted = [...parameters].sort((a, b): number => {
115-
const aIsPath = !Guard.isReference(a) && a.in === "path";
116-
const bIsPath = !Guard.isReference(b) && b.in === "path";
117-
if (aIsPath && !bIsPath) return 1;
118-
if (!aIsPath && bIsPath) return -1;
119-
return 0;
120-
});
121-
const typeElementMap = sorted.reduce<Record<string, string>>((all, parameter) => {
134+
// 入力順を維持しながら、同名パラメータが存在する場合は path パラメータを優先する。
135+
// Map はキーの挿入順を保持するため、既存キーへの set() は値のみ更新し順序は変わらない。
136+
// これにより OpenAPI spec の記述順を崩さず、path パラメータの required が保たれる。
137+
const typeElementMap = new Map<string, string>();
138+
for (const parameter of parameters) {
122139
const { name, typeElement } = generatePropertySignatureObject(
123140
entryPoint,
124141
currentPoint,
@@ -128,9 +145,12 @@ export const generatePropertySignatures = (
128145
context,
129146
converterContext,
130147
);
131-
return { ...all, [name]: typeElement };
132-
}, {});
133-
return Object.values(typeElementMap);
148+
const isPath = resolveParameterIn(store, parameter) === "path";
149+
if (!typeElementMap.has(name) || isPath) {
150+
typeElementMap.set(name, typeElement);
151+
}
152+
}
153+
return [...typeElementMap.values()];
134154
};
135155

136156
export const generateInterface = (

test/__tests__/class/__snapshots__/argo-rollout/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Generated by @himenon/openapi-typescript-code-generator v2.0.4
2+
// Generated by @himenon/openapi-typescript-code-generator
33
//
44
// OpenApi : 3.0.0
55
//

test/__tests__/class/__snapshots__/cloudflare/client.ts

Lines changed: 181 additions & 181 deletions
Large diffs are not rendered by default.

test/__tests__/class/__snapshots__/format.domain/code.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Generated by @himenon/openapi-typescript-code-generator v2.0.4
2+
// Generated by @himenon/openapi-typescript-code-generator
33
//
44
// OpenApi : 3.1.0
55
//

test/__tests__/class/__snapshots__/kubernetes/client-v1.18.5.ts

Lines changed: 1833 additions & 1833 deletions
Large diffs are not rendered by default.

test/__tests__/class/__snapshots__/kubernetes/client-v1.28.6.ts

Lines changed: 1048 additions & 1048 deletions
Large diffs are not rendered by default.

test/__tests__/class/__snapshots__/mulit-type-test.domain/apiClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Generated by @himenon/openapi-typescript-code-generator v2.0.4
2+
// Generated by @himenon/openapi-typescript-code-generator
33
//
44
// OpenApi : 3.0.1
55
//

test/__tests__/class/__snapshots__/mulit-type-test.domain/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Generated by @himenon/openapi-typescript-code-generator v2.0.4
2+
// Generated by @himenon/openapi-typescript-code-generator
33
//
44
// OpenApi : 3.0.1
55
//

test/__tests__/class/__snapshots__/parameter/path-parameter.json

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,94 @@
224224
}
225225
}
226226
},
227+
{
228+
"operationId": "getPodProxyWithPath",
229+
"convertedParams": {
230+
"escapedOperationId": "getPodProxyWithPath",
231+
"argumentParamsTypeDeclaration": "Params$getPodProxyWithPath",
232+
"functionName": "getPodProxyWithPath",
233+
"requestContentTypeName": "RequestContentType$getPodProxyWithPath",
234+
"responseContentTypeName": "ResponseContentType$getPodProxyWithPath",
235+
"parameterName": "Parameter$getPodProxyWithPath",
236+
"requestBodyName": "RequestBody$getPodProxyWithPath",
237+
"hasRequestBody": false,
238+
"hasParameter": true,
239+
"pickedParameters": [
240+
{
241+
"name": "name",
242+
"in": "path"
243+
},
244+
{
245+
"name": "path",
246+
"in": "path",
247+
"required": true
248+
},
249+
{
250+
"name": "path",
251+
"in": "query"
252+
}
253+
],
254+
"requestContentTypes": [],
255+
"responseSuccessNames": [
256+
"Response$getPodProxyWithPath$Status$200"
257+
],
258+
"responseFirstSuccessName": "Response$getPodProxyWithPath$Status$200",
259+
"has2OrMoreSuccessNames": false,
260+
"responseErrorNames": [],
261+
"has2OrMoreRequestContentTypes": false,
262+
"successResponseContentTypes": [
263+
"application/json"
264+
],
265+
"successResponseFirstContentType": "application/json",
266+
"has2OrMoreSuccessResponseContentTypes": false,
267+
"hasAdditionalHeaders": false,
268+
"hasQueryParameters": true
269+
},
270+
"operationParams": {
271+
"httpMethod": "get",
272+
"requestUri": "/pods/{name}/proxy/{path}",
273+
"comment": "$ref パラメータで同名の pathパラメータと queryパラメータが競合するケース",
274+
"deprecated": false,
275+
"parameters": [
276+
{
277+
"in": "path",
278+
"name": "name",
279+
"schema": {
280+
"type": "string"
281+
}
282+
},
283+
{
284+
"in": "path",
285+
"name": "path",
286+
"required": true,
287+
"description": "URL パスの一部として使用されるパスパラメータ",
288+
"schema": {
289+
"type": "string"
290+
}
291+
},
292+
{
293+
"in": "query",
294+
"name": "path",
295+
"description": "クエリパラメータとして使用される path(省略可能)",
296+
"schema": {
297+
"type": "string"
298+
}
299+
}
300+
],
301+
"responses": {
302+
"200": {
303+
"description": "OK",
304+
"content": {
305+
"application/json": {
306+
"schema": {
307+
"type": "string"
308+
}
309+
}
310+
}
311+
}
312+
}
313+
}
314+
},
227315
{
228316
"operationId": "getUserPost",
229317
"convertedParams": {

test/__tests__/class/__snapshots__/split/apiClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Generated by @himenon/openapi-typescript-code-generator v2.0.4
2+
// Generated by @himenon/openapi-typescript-code-generator
33
//
44
// OpenApi : 3.1.0
55
//
@@ -45,8 +45,8 @@ export interface Response$getReferenceItems$Status$200 {
4545
};
4646
}
4747
export interface Parameter$searchBook {
48-
"from.publishedAt"?: number;
4948
"book.name": string;
49+
"from.publishedAt"?: number;
5050
}
5151
export interface Response$searchBook$Status$200 {
5252
"application/json": {

0 commit comments

Comments
 (0)