Skip to content

Commit 7f53175

Browse files
Merge pull request #488 from openapi-ui/fix/#486
fix: fix parse parameters ref error #486
2 parents c2c94e4 + 386378c commit 7f53175

File tree

5 files changed

+284
-17
lines changed

5 files changed

+284
-17
lines changed

.changeset/tricky-pants-remain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openapi-ts-request': patch
3+
---
4+
5+
fix: fix parse parameters ref error #486

src/generator/serviceGenarator.ts

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -501,28 +501,41 @@ export default class ServiceGenerator {
501501
markAllowedSchema(JSON.stringify(pathItem), this.openAPIData);
502502

503503
operationObject.parameters = operationObject.parameters?.filter(
504-
(item: ParameterObject) => item?.in !== `${parametersInsEnum.header}`
504+
(item: ParameterObject | ReferenceObject) => {
505+
const parameter = this.resolveParameterRef(item);
506+
return parameter?.in !== `${parametersInsEnum.header}`;
507+
}
505508
);
506509
const props = [] as IPropObject[];
507510

508-
operationObject.parameters?.forEach((parameter: ParameterObject) => {
509-
props.push({
510-
name: parameter.name,
511-
desc: (parameter.description ?? '').replace(lineBreakReg, ''),
512-
required: parameter.required || false,
513-
type: this.getType(parameter.schema),
514-
});
515-
});
511+
operationObject.parameters?.forEach(
512+
(param: ParameterObject | ReferenceObject) => {
513+
const parameter = this.resolveParameterRef(param);
514+
if (parameter) {
515+
props.push({
516+
name: parameter.name,
517+
desc: (parameter.description ?? '').replace(lineBreakReg, ''),
518+
required: parameter.required || false,
519+
type: this.getType(parameter.schema),
520+
});
521+
}
522+
}
523+
);
516524

517525
// parameters may be in path
518-
pathItem.parameters?.forEach((parameter: ParameterObject) => {
519-
props.push({
520-
name: parameter.name,
521-
desc: (parameter.description ?? '').replace(lineBreakReg, ''),
522-
required: parameter.required,
523-
type: this.getType(parameter.schema),
524-
});
525-
});
526+
pathItem.parameters?.forEach(
527+
(param: ParameterObject | ReferenceObject) => {
528+
const parameter = this.resolveParameterRef(param);
529+
if (parameter) {
530+
props.push({
531+
name: parameter.name,
532+
desc: (parameter.description ?? '').replace(lineBreakReg, ''),
533+
required: parameter.required,
534+
type: this.getType(parameter.schema),
535+
});
536+
}
537+
}
538+
);
526539

527540
const typeName = this.getFunctionParamsTypeName({
528541
...operationObject,
@@ -1468,6 +1481,22 @@ export default class ServiceGenerator {
14681481
});
14691482
}
14701483

1484+
private resolveParameterRef(
1485+
param: ParameterObject | ReferenceObject
1486+
): ParameterObject | null {
1487+
if (!isReferenceObject(param)) {
1488+
return param;
1489+
}
1490+
1491+
// 解析 $ref 引用,从 components.parameters 中获取实际定义
1492+
const refName = getLastRefName(param.$ref);
1493+
const parameter = this.openAPIData.components?.parameters?.[
1494+
refName
1495+
] as ParameterObject;
1496+
1497+
return parameter || null;
1498+
}
1499+
14711500
private resolveRefObject<T>(refObject: ReferenceObject | T): T {
14721501
if (!isReferenceObject(refObject)) {
14731502
return refObject;
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/* eslint-disable */
2+
// @ts-ignore
3+
export * from './types';
4+
5+
export * from './products';
6+
/* eslint-disable */
7+
// @ts-ignore
8+
import { request } from 'axios';
9+
10+
import * as API from './types';
11+
12+
/** Get product list Get paginated product list with search, filter, and sort support GET /products */
13+
export function productsUsingGet({
14+
params,
15+
options,
16+
}: {
17+
// 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
18+
params: API.ProductsUsingGetParams;
19+
options?: { [key: string]: unknown };
20+
}) {
21+
return request<
22+
API.BaseResponse & {
23+
data?: API.ProductListResponse;
24+
}
25+
>('/products', {
26+
method: 'GET',
27+
params: {
28+
// page has a default value: 1
29+
page: '1',
30+
// pageSize has a default value: 10
31+
pageSize: '10',
32+
33+
...params,
34+
},
35+
...(options || {}),
36+
});
37+
}
38+
/* eslint-disable */
39+
// @ts-ignore
40+
41+
export type BaseResponse = {
42+
/** Response status code */
43+
code?: number;
44+
/** Response message */
45+
message?: string;
46+
/** Response data */
47+
data?: Record<string, unknown>;
48+
};
49+
50+
export type PaginatedResponse = {
51+
/** Data list */
52+
list?: Record<string, unknown>[];
53+
/** Total records */
54+
total?: number;
55+
/** Current page */
56+
page?: number;
57+
};
58+
59+
export type Product = {
60+
/** Product ID */
61+
productId?: string;
62+
/** Product name */
63+
name?: string;
64+
/** Product description */
65+
description?: string;
66+
};
67+
68+
export type ProductListResponse =
69+
// #/components/schemas/PaginatedResponse
70+
PaginatedResponse & {
71+
list?: Product[];
72+
};
73+
74+
export type ProductsUsingGetParams = {
75+
/** Page number */
76+
page?: number;
77+
/** Page size */
78+
pageSize?: number;
79+
/** Search keyword */
80+
keyword?: string;
81+
/** Product category ID */
82+
categoryId?: string;
83+
};
84+
85+
export type ProductsUsingGetResponses = {
86+
/**
87+
* Get successful
88+
*/
89+
200: BaseResponse & {
90+
data?: ProductListResponse;
91+
};
92+
};

test/common.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,4 +484,17 @@ export async function ${api.functionName}(${api.body ? `data: ${api.body.type}`
484484
readGeneratedFiles('./apis/openapi-anonymous-response')
485485
).resolves.toMatchFileSnapshot(getSnapshotDir(ctx));
486486
});
487+
488+
it('测试解析 components.parameters 中的 $ref 引用', async (ctx) => {
489+
await openAPI.generateService({
490+
schemaPath: join(
491+
import.meta.dirname,
492+
'./example-files/openapi-components-parameters.yaml'
493+
),
494+
serversPath: './apis/components-parameters',
495+
});
496+
await expect(
497+
readGeneratedFiles('./apis/components-parameters')
498+
).resolves.toMatchFileSnapshot(getSnapshotDir(ctx));
499+
});
487500
});
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
openapi: 3.0.3
2+
servers:
3+
- url: http://localhost:3000/v1
4+
description: 开发环境
5+
6+
security:
7+
- BearerAuth: []
8+
9+
paths:
10+
/products:
11+
get:
12+
tags:
13+
- Products
14+
summary: Get product list
15+
description: Get paginated product list with search, filter, and sort support
16+
parameters:
17+
- $ref: '#/components/parameters/PageParam'
18+
- $ref: '#/components/parameters/PageSizeParam'
19+
- $ref: '#/components/parameters/KeywordParam'
20+
- name: categoryId
21+
in: query
22+
description: Product category ID
23+
schema:
24+
type: string
25+
example: cat_001
26+
responses:
27+
'200':
28+
description: Get successful
29+
content:
30+
application/json:
31+
schema:
32+
allOf:
33+
- $ref: '#/components/schemas/BaseResponse'
34+
- type: object
35+
properties:
36+
data:
37+
$ref: '#/components/schemas/ProductListResponse'
38+
39+
components:
40+
schemas:
41+
# Base response format
42+
BaseResponse:
43+
type: object
44+
properties:
45+
code:
46+
type: integer
47+
description: Response status code
48+
example: 200
49+
message:
50+
type: string
51+
description: Response message
52+
example: Operation successful
53+
data:
54+
type: object
55+
description: Response data
56+
57+
ProductListResponse:
58+
allOf:
59+
- $ref: '#/components/schemas/PaginatedResponse'
60+
- type: object
61+
properties:
62+
list:
63+
type: array
64+
items:
65+
$ref: '#/components/schemas/Product'
66+
67+
PaginatedResponse:
68+
type: object
69+
properties:
70+
list:
71+
type: array
72+
description: Data list
73+
items:
74+
type: object
75+
total:
76+
type: integer
77+
description: Total records
78+
example: 100
79+
page:
80+
type: integer
81+
description: Current page
82+
example: 1
83+
84+
Product:
85+
type: object
86+
properties:
87+
productId:
88+
type: string
89+
description: Product ID
90+
example: prod_001
91+
name:
92+
type: string
93+
description: Product name
94+
example: Classic Pearl Milk Tea
95+
description:
96+
type: string
97+
description: Product description
98+
example: 'Rich milk tea with chewy pearls, a classic that never goes out of style'
99+
100+
parameters:
101+
# Pagination parameters
102+
PageParam:
103+
name: page
104+
in: query
105+
description: Page number
106+
schema:
107+
type: integer
108+
minimum: 1
109+
default: 1
110+
example: 1
111+
112+
PageSizeParam:
113+
name: pageSize
114+
in: query
115+
description: Page size
116+
schema:
117+
type: integer
118+
minimum: 1
119+
maximum: 100
120+
default: 10
121+
example: 10
122+
KeywordParam:
123+
name: keyword
124+
in: query
125+
description: Search keyword
126+
schema:
127+
type: string
128+
example: Pearl Milk Tea

0 commit comments

Comments
 (0)