Skip to content

Commit fcd8540

Browse files
committed
feat: 添加looseInputNumber配置,支持将number类型解析为number | string
1 parent 175b23d commit fcd8540

File tree

7 files changed

+108
-20
lines changed

7 files changed

+108
-20
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,20 @@ await client.user.getUsersById(opts);
232232

233233
**注意**:如果operationId字段不存在,则仍以`method+uri`的规则生成
234234

235+
### looseInputNumber
236+
237+
类型:`boolean`<br>
238+
默认值:`false`
239+
240+
开启后,`query``params`对象中的属性类型,`number`会被解析为`number | string`。<br>
241+
不管是否开启都不会对请求造成影响,因为最终都会拼接到请求链接上变成一段完整的uri。
242+
243+
```typescript
244+
// http://host:port/users/1?tag=234
245+
openapi.getUsersById({ params: { id: 1 }, query: { tag: 234 } });
246+
openapi.getUsersById({ params: { id: '1' }, query: { tag: '234' } });
247+
```
248+
235249
### onDocumentLoaded
236250

237251
类型:`(docs: Document) => Document | void`

src/base-openapi-client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import type { OpenapiClientAdapter, Methods } from './lib/adapter';
22
import { utils } from './utils';
33

44
export namespace string {
5+
/**
6+
* '12345'
7+
*/
8+
export type Number = string;
59
export type BigInt = string;
610
/**
711
* 2020-02-12T07:20:50.52Z

src/define-config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ export interface OpenapiClientConfig {
7777
* - 如果配置了项目名,默认值:`./src/openapi/${projectName}.ts`
7878
*/
7979
outputFile?: string;
80+
/**
81+
* 开启后,`query`和`params`对象的属性类型中,`number`会被解析为`number | string`。默认值:`false`
82+
*
83+
* 不管是否开启都不会对请求造成影响,因为最终都会拼接到请求链接上变成一段完整的uri。
84+
*
85+
* ```typescript
86+
* // http://host:port/users/1?tag=234
87+
* openapi.getUsersById({ params: { id: 1 }, query: { tag: 234 } });
88+
* openapi.getUsersById({ params: { id: '1' }, query: { tag: '234' } });
89+
* ```
90+
*/
91+
looseInputNumber?: boolean;
8092
}
8193

8294
export type DefineConfigOptions =

src/lib/document-to-meta.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type Metas = Record<
2828
export const documentToMeta = (
2929
docs: OpenAPIV3.Document,
3030
rpcName: OpenapiClientConfig['rpcName'],
31+
looseInputNumber?: boolean,
3132
) => {
3233
const metas: Metas = {
3334
get: [],
@@ -49,8 +50,8 @@ export const documentToMeta = (
4950
? methodItem.operationId
5051
: `${method}_${uri.replaceAll(/{(.+?)}/g, '_by_$1')}`,
5152
),
52-
query: parseParameters(docs, pathItem, methodItem, 'query'),
53-
params: parseParameters(docs, pathItem, methodItem, 'path'),
53+
query: parseParameters(docs, pathItem, methodItem, 'query', looseInputNumber),
54+
params: parseParameters(docs, pathItem, methodItem, 'path', looseInputNumber),
5455
...parseRequestBody(docs, methodItem),
5556
...parseResponse(docs, methodItem),
5657
deprecated: methodItem.deprecated,

src/lib/generate-template.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@ import { pickContentTypes } from './template';
99

1010
export const generateTemplate = async (
1111
docs: OpenAPIV3.Document,
12-
config: Pick<OpenapiClientConfig, 'projectName' | 'classMode' | 'rpcName'>,
12+
config: Pick<
13+
OpenapiClientConfig,
14+
'projectName' | 'classMode' | 'rpcName' | 'looseInputNumber'
15+
>,
1316
) => {
14-
const { projectName = '', classMode = 'rest', rpcName = 'method+uri' } = config;
17+
const {
18+
projectName = '',
19+
classMode = 'rest',
20+
rpcName = 'method+uri',
21+
looseInputNumber = false,
22+
} = config;
1523
const className = `OpenapiClient${upperFirst(camelCase(projectName))}`;
16-
const metas = documentToMeta(docs, rpcName);
24+
const metas = documentToMeta(docs, rpcName, looseInputNumber);
1725

1826
const classTpl =
1927
classMode === 'rest'

src/lib/parse-parameters.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const parseParameters = (
88
pathItem: OpenAPIV3.PathItemObject,
99
methodItem: OpenAPIV3.OperationObject,
1010
key: string,
11+
looseInputNumber?: boolean,
1112
): { optional: boolean; types: [string] | [] } => {
1213
const parameters = (methodItem.parameters || [])
1314
.concat(pathItem.parameters || [])
@@ -20,7 +21,14 @@ export const parseParameters = (
2021
return `${generateComments(parameter)}${parameter.name}${parameter.required ? '' : '?'}: ${parseSchema(docs, parameter.schema)}
2122
`;
2223
})
23-
.filter(Boolean);
24+
.filter(Boolean)
25+
.map((type) => {
26+
if (!looseInputNumber) return type;
27+
return type
28+
.replaceAll('(number)', '(number | string.Number)')
29+
.replaceAll('(number | null)', '(number | string.Number | null)');
30+
});
31+
2432
return {
2533
optional: parameters.every((parameter) => !parameter.required),
2634
types: types.length ? [`{ ${types.join(';\n')} }`] : [],

test/lib/parse-parameters.test.ts

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const docs = getBasicDocument({
3535
},
3636
{ $ref: '#/components/parameters/refA' },
3737
{ $ref: '#/components/parameters/refB' },
38+
{ $ref: '#/components/parameters/refC' },
3839
],
3940
},
4041
},
@@ -54,12 +55,18 @@ docs.components = {
5455
required: true,
5556
schema: { type: 'integer' },
5657
},
58+
refC: {
59+
name: 'ref_c',
60+
in: 'path',
61+
required: true,
62+
schema: { type: 'integer', nullable: true },
63+
},
5764
},
5865
};
5966

6067
test('解析查询字符串', () => {
61-
const result = parseParameters(docs, docs.paths['/']!, docs.paths['/']!.get!, 'query');
62-
expect(result).toMatchInlineSnapshot(`
68+
expect(parseParameters(docs, docs.paths['/']!, docs.paths['/']!.get!, 'query', false))
69+
.toMatchInlineSnapshot(`
6370
{
6471
"optional": false,
6572
"types": [
@@ -74,21 +81,55 @@ test('解析查询字符串', () => {
7481
],
7582
}
7683
`);
84+
85+
expect(parseParameters(docs, docs.paths['/']!, docs.paths['/']!.get!, 'query', true))
86+
.toMatchInlineSnapshot(`
87+
{
88+
"optional": false,
89+
"types": [
90+
"{ foo: (number | string.Number)
91+
;
92+
bar?: (number | string.Number)
93+
;
94+
ref_a?: (number | string.Number)
95+
;
96+
bazz: (string)
97+
}",
98+
],
99+
}
100+
`);
77101
});
78102

79103
test('解析路径参数', () => {
80-
const result = parseParameters(docs, docs.paths['/']!, docs.paths['/']!.get!, 'path');
81-
expect(result).toMatchInlineSnapshot(`
82-
{
83-
"optional": false,
84-
"types": [
85-
"{ id: (number)
86-
;
87-
ref_b: (number)
88-
}",
89-
],
90-
}
91-
`);
104+
expect(parseParameters(docs, docs.paths['/']!, docs.paths['/']!.get!, 'path', false))
105+
.toMatchInlineSnapshot(`
106+
{
107+
"optional": false,
108+
"types": [
109+
"{ id: (number)
110+
;
111+
ref_b: (number)
112+
;
113+
ref_c: (number | null)
114+
}",
115+
],
116+
}
117+
`);
118+
119+
expect(parseParameters(docs, docs.paths['/']!, docs.paths['/']!.get!, 'path', true))
120+
.toMatchInlineSnapshot(`
121+
{
122+
"optional": false,
123+
"types": [
124+
"{ id: (number | string.Number)
125+
;
126+
ref_b: (number | string.Number)
127+
;
128+
ref_c: (number | string.Number | null)
129+
}",
130+
],
131+
}
132+
`);
92133
});
93134

94135
test('未找到参数则变成可选', () => {

0 commit comments

Comments
 (0)