openapi-ts-request 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +147 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +31 -0
- package/dist/generator/config.d.ts +29 -0
- package/dist/generator/config.js +57 -0
- package/dist/generator/file.d.ts +2 -0
- package/dist/generator/file.js +36 -0
- package/dist/generator/mockGenarator.d.ts +6 -0
- package/dist/generator/mockGenarator.js +202 -0
- package/dist/generator/serviceGenarator.d.ts +31 -0
- package/dist/generator/serviceGenarator.js +640 -0
- package/dist/generator/type.d.ts +34 -0
- package/dist/generator/type.js +2 -0
- package/dist/generator/util.d.ts +18 -0
- package/dist/generator/util.js +314 -0
- package/dist/index.d.ts +125 -0
- package/dist/index.js +32 -0
- package/dist/log.d.ts +2 -0
- package/dist/log.js +6 -0
- package/dist/parser-mock/index.d.ts +19 -0
- package/dist/parser-mock/index.js +223 -0
- package/dist/parser-mock/primitives.d.ts +1 -0
- package/dist/parser-mock/primitives.js +33 -0
- package/dist/parser-mock/util.d.ts +90 -0
- package/dist/parser-mock/util.js +46 -0
- package/dist/type.d.ts +72 -0
- package/dist/type.js +30 -0
- package/dist/util.d.ts +2 -0
- package/dist/util.js +77 -0
- package/package.json +81 -0
- package/prettier.config.cjs +9 -0
- package/templates/displayEnumLabel.njk +10 -0
- package/templates/interface.njk +54 -0
- package/templates/serviceController.njk +168 -0
- package/templates/serviceIndex.njk +13 -0
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const glob_1 = require("glob");
|
|
6
|
+
const lodash_1 = require("lodash");
|
|
7
|
+
const nunjucks_1 = tslib_1.__importDefault(require("nunjucks"));
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const rimraf_1 = require("rimraf");
|
|
10
|
+
const log_1 = tslib_1.__importDefault(require("../log"));
|
|
11
|
+
const type_1 = require("../type");
|
|
12
|
+
const config_1 = require("./config");
|
|
13
|
+
const file_1 = require("./file");
|
|
14
|
+
const util_1 = require("./util");
|
|
15
|
+
class ServiceGenerator {
|
|
16
|
+
constructor(config, openAPIData) {
|
|
17
|
+
var _a, _b;
|
|
18
|
+
this.apiData = {};
|
|
19
|
+
this.classNameList = [];
|
|
20
|
+
this.finalPath = '';
|
|
21
|
+
this.config = Object.assign({ templatesFolder: (0, path_1.join)(__dirname, '../../', 'templates') }, config);
|
|
22
|
+
const hookCustomFileNames = ((_a = this.config.hook) === null || _a === void 0 ? void 0 : _a.customFileNames) || util_1.getDefaultFileTag;
|
|
23
|
+
if ((_b = this.config.hook) === null || _b === void 0 ? void 0 : _b.afterOpenApiDataInited) {
|
|
24
|
+
this.openAPIData =
|
|
25
|
+
this.config.hook.afterOpenApiDataInited(openAPIData) || openAPIData;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.openAPIData = openAPIData;
|
|
29
|
+
}
|
|
30
|
+
// 用 tag 分组 paths, { [tag]: [pathMap, pathMap] }
|
|
31
|
+
(0, lodash_1.keys)(this.openAPIData.paths).forEach((pathKey) => {
|
|
32
|
+
const pathItem = this.openAPIData.paths[pathKey];
|
|
33
|
+
(0, lodash_1.forEach)(config_1.methods, (method) => {
|
|
34
|
+
const operationObject = pathItem[method];
|
|
35
|
+
if (!operationObject) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let tags = hookCustomFileNames(operationObject, pathKey, method);
|
|
39
|
+
if (!tags) {
|
|
40
|
+
tags = (0, util_1.getDefaultFileTag)(operationObject, pathKey);
|
|
41
|
+
}
|
|
42
|
+
tags.forEach((tag) => {
|
|
43
|
+
var _a;
|
|
44
|
+
// 筛选出 tags 关联的paths
|
|
45
|
+
if (!(0, lodash_1.isEmpty)((_a = this.config) === null || _a === void 0 ? void 0 : _a.allowedTags) &&
|
|
46
|
+
!(0, lodash_1.includes)(this.config.allowedTags, tag.toLowerCase())) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const tagKey = this.config.isCamelCase
|
|
50
|
+
? (0, lodash_1.camelCase)((0, util_1.resolveTypeName)(tag))
|
|
51
|
+
: (0, util_1.resolveTypeName)(tag);
|
|
52
|
+
if (!this.apiData[tagKey]) {
|
|
53
|
+
this.apiData[tagKey] = [];
|
|
54
|
+
}
|
|
55
|
+
this.apiData[tagKey].push(Object.assign({ path: pathKey, method }, operationObject));
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
genFile() {
|
|
61
|
+
try {
|
|
62
|
+
(0, glob_1.globSync)(`${this.config.serversPath}/**/*`)
|
|
63
|
+
.filter((item) => !item.includes('_deperated'))
|
|
64
|
+
.forEach((item) => {
|
|
65
|
+
(0, rimraf_1.sync)(item);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
(0, log_1.default)(`🚥 api 生成失败: ${error}`);
|
|
70
|
+
}
|
|
71
|
+
// 处理重复的 typeName
|
|
72
|
+
const interfaceTPConfigs = this.getInterfaceTPConfigs();
|
|
73
|
+
(0, util_1.handleDuplicateTypeNames)(interfaceTPConfigs);
|
|
74
|
+
// 生成 ts 类型声明
|
|
75
|
+
this.genFileFromTemplate(`${config_1.interfaceFileName}.ts`, config_1.TypescriptFileType.interface, {
|
|
76
|
+
nullable: this.config.nullable,
|
|
77
|
+
list: interfaceTPConfigs,
|
|
78
|
+
});
|
|
79
|
+
// 生成枚举翻译
|
|
80
|
+
this.genFileFromTemplate(`${config_1.displayEnumLabelFileName}.ts`, config_1.TypescriptFileType.displayEnumLabel, {
|
|
81
|
+
list: (0, lodash_1.filter)(interfaceTPConfigs, (item) => item.isEnum),
|
|
82
|
+
namespace: this.config.namespace,
|
|
83
|
+
interfaceFileName: config_1.interfaceFileName,
|
|
84
|
+
});
|
|
85
|
+
const prettierError = [];
|
|
86
|
+
// 生成 service controller 文件
|
|
87
|
+
this.getServiceTPConfigs().forEach((tp) => {
|
|
88
|
+
const hasError = this.genFileFromTemplate((0, util_1.getFinalFileName)(`${tp.className}.ts`), config_1.TypescriptFileType.serviceController, Object.assign({ namespace: this.config.namespace, requestOptionsType: this.config.requestOptionsType, requestImportStatement: this.config.requestImportStatement, interfaceFileName: config_1.interfaceFileName }, tp));
|
|
89
|
+
prettierError.push(hasError);
|
|
90
|
+
});
|
|
91
|
+
if (prettierError.includes(true)) {
|
|
92
|
+
(0, log_1.default)('🚥 格式化失败,请检查 service controller 文件内可能存在的语法错误');
|
|
93
|
+
}
|
|
94
|
+
// 生成 service index 文件
|
|
95
|
+
this.genFileFromTemplate(`${config_1.serviceEntryFileName}.ts`, config_1.TypescriptFileType.serviceIndex, {
|
|
96
|
+
list: this.classNameList,
|
|
97
|
+
namespace: this.config.namespace,
|
|
98
|
+
interfaceFileName: config_1.interfaceFileName,
|
|
99
|
+
});
|
|
100
|
+
// 打印日志
|
|
101
|
+
(0, log_1.default)('✅ 成功生成 api 文件');
|
|
102
|
+
}
|
|
103
|
+
getInterfaceTPConfigs() {
|
|
104
|
+
var _a;
|
|
105
|
+
const schemas = (_a = this.openAPIData.components) === null || _a === void 0 ? void 0 : _a.schemas;
|
|
106
|
+
const lastTypes = [];
|
|
107
|
+
// 强行替换掉请求参数params的类型,生成方法对应的 xxxxParams 类型
|
|
108
|
+
(0, lodash_1.keys)(this.openAPIData.paths).forEach((pathKey) => {
|
|
109
|
+
const pathItem = this.openAPIData.paths[pathKey];
|
|
110
|
+
(0, lodash_1.forEach)(config_1.methods, (method) => {
|
|
111
|
+
var _a, _b, _c, _d;
|
|
112
|
+
const operationObject = pathItem[method];
|
|
113
|
+
if (!operationObject) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// 筛选出 pathItem 包含的 $ref 对应的schema
|
|
117
|
+
if (!(0, lodash_1.isEmpty)((_a = this.config) === null || _a === void 0 ? void 0 : _a.allowedTags) &&
|
|
118
|
+
!(0, lodash_1.isEmpty)(operationObject.tags)) {
|
|
119
|
+
if (!(0, lodash_1.isEmpty)((0, lodash_1.intersection)(this.config.allowedTags, (0, lodash_1.map)(operationObject.tags, (tag) => tag.toLowerCase())))) {
|
|
120
|
+
(0, util_1.markAllowSchema)(JSON.stringify(pathItem), schemas);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
operationObject.parameters = (_b = operationObject.parameters) === null || _b === void 0 ? void 0 : _b.filter((item) => (item === null || item === void 0 ? void 0 : item.in) !== `${config_1.parametersInsEnum.header}`);
|
|
127
|
+
const props = [];
|
|
128
|
+
(_c = operationObject.parameters) === null || _c === void 0 ? void 0 : _c.forEach((parameter) => {
|
|
129
|
+
var _a;
|
|
130
|
+
props.push({
|
|
131
|
+
name: parameter.name,
|
|
132
|
+
desc: (_a = parameter.description) !== null && _a !== void 0 ? _a : '',
|
|
133
|
+
required: parameter.required || false,
|
|
134
|
+
type: this.getType(parameter.schema),
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
// parameters may be in path
|
|
138
|
+
(_d = pathItem.parameters) === null || _d === void 0 ? void 0 : _d.forEach((parameter) => {
|
|
139
|
+
var _a;
|
|
140
|
+
props.push({
|
|
141
|
+
name: parameter.name,
|
|
142
|
+
desc: (_a = parameter.description) !== null && _a !== void 0 ? _a : '',
|
|
143
|
+
required: parameter.required,
|
|
144
|
+
type: this.getType(parameter.schema),
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
if (props.length > 0) {
|
|
148
|
+
lastTypes.push({
|
|
149
|
+
typeName: this.getTypeName(Object.assign(Object.assign({}, operationObject), { method, path: pathKey })),
|
|
150
|
+
type: 'Record<string, any>',
|
|
151
|
+
props: [props],
|
|
152
|
+
isEnum: false,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
(0, lodash_1.keys)(schemas).forEach((schemaKey) => {
|
|
158
|
+
const schema = schemas[schemaKey];
|
|
159
|
+
const result = this.resolveObject(schema);
|
|
160
|
+
const getDefinesType = () => {
|
|
161
|
+
if (result === null || result === void 0 ? void 0 : result.type) {
|
|
162
|
+
return schema.type === 'object'
|
|
163
|
+
? type_1.SchemaObjectType.object
|
|
164
|
+
: config_1.numberEnum.includes(result.type)
|
|
165
|
+
? type_1.SchemaObjectType.number
|
|
166
|
+
: result.type;
|
|
167
|
+
}
|
|
168
|
+
return 'Record<string, any>';
|
|
169
|
+
};
|
|
170
|
+
// 解析 props 属性中的枚举
|
|
171
|
+
if ((0, lodash_1.isArray)(result.props) && result.props.length > 0) {
|
|
172
|
+
(0, lodash_1.forEach)(result.props[0], (item) => {
|
|
173
|
+
if (item.enum) {
|
|
174
|
+
const enumObj = this.resolveEnumObject(item);
|
|
175
|
+
lastTypes.push({
|
|
176
|
+
typeName: `${(0, lodash_1.upperFirst)(item.name)}Enum`,
|
|
177
|
+
type: enumObj.type,
|
|
178
|
+
props: [],
|
|
179
|
+
isEnum: enumObj.isEnum,
|
|
180
|
+
displayLabelFuncName: (0, lodash_1.camelCase)(`display-${item.name}-Enum`),
|
|
181
|
+
enumLabelType: enumObj.enumLabelType,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if ((0, lodash_1.isEmpty)(this.config.allowedTags) ||
|
|
187
|
+
schema.isAllowed) {
|
|
188
|
+
const isEnum = result.isEnum;
|
|
189
|
+
const typeName = (0, util_1.resolveTypeName)(schemaKey);
|
|
190
|
+
lastTypes.push({
|
|
191
|
+
typeName,
|
|
192
|
+
type: getDefinesType(),
|
|
193
|
+
props: (result.props || []),
|
|
194
|
+
isEnum,
|
|
195
|
+
displayLabelFuncName: isEnum
|
|
196
|
+
? (0, lodash_1.camelCase)(`display-${typeName}-Enum`)
|
|
197
|
+
: '',
|
|
198
|
+
enumLabelType: isEnum ? result.enumLabelType : '',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return lastTypes === null || lastTypes === void 0 ? void 0 : lastTypes.sort((a, b) => a.typeName.localeCompare(b.typeName)); // typeName排序
|
|
203
|
+
}
|
|
204
|
+
getServiceTPConfigs() {
|
|
205
|
+
return (0, lodash_1.keys)(this.apiData)
|
|
206
|
+
.map((tag, index) => {
|
|
207
|
+
var _a, _b;
|
|
208
|
+
// functionName tag 级别防重
|
|
209
|
+
const tmpFunctionRD = {};
|
|
210
|
+
const genParams = this.apiData[tag]
|
|
211
|
+
.filter((api) =>
|
|
212
|
+
// 暂不支持变量, path 需要普通前缀请使用例如: apiPrefix: "`api`", path 需要变量前缀请使用例如: apiPrefix: "api"
|
|
213
|
+
!api.path.includes('${'))
|
|
214
|
+
.map((api) => {
|
|
215
|
+
var _a, _b, _c, _d, _e, _f;
|
|
216
|
+
const newApi = api;
|
|
217
|
+
try {
|
|
218
|
+
const params = this.getParamsTP(newApi.parameters, newApi.path) || {};
|
|
219
|
+
const body = this.getBodyTP(newApi.requestBody);
|
|
220
|
+
const response = this.getResponseTP(newApi.responses);
|
|
221
|
+
const file = this.getFileTP(newApi.requestBody);
|
|
222
|
+
let formData = false;
|
|
223
|
+
if (((_a = body === null || body === void 0 ? void 0 : body.mediaType) === null || _a === void 0 ? void 0 : _a.includes('form-data')) || file) {
|
|
224
|
+
formData = true;
|
|
225
|
+
}
|
|
226
|
+
let functionName = this.getFuncationName(newApi);
|
|
227
|
+
if (functionName && tmpFunctionRD[functionName]) {
|
|
228
|
+
functionName = `${functionName}_${(tmpFunctionRD[functionName] += 1)}`;
|
|
229
|
+
}
|
|
230
|
+
else if (functionName) {
|
|
231
|
+
tmpFunctionRD[functionName] = 1;
|
|
232
|
+
}
|
|
233
|
+
let formattedPath = newApi.path.replace(/:([^/]*)|{([^}]*)}/gi, (_, str, str2) => `$\{${str || str2}}`);
|
|
234
|
+
// 为 path 中的 params 添加 alias
|
|
235
|
+
const escapedPathParams = (0, lodash_1.map)(params.path, (item, index) => (Object.assign(Object.assign({}, item), { alias: `param${index}` })));
|
|
236
|
+
if (escapedPathParams.length) {
|
|
237
|
+
escapedPathParams.forEach((param) => {
|
|
238
|
+
formattedPath = formattedPath.replace(`$\{${param.name}}`, `$\{${param.alias}}`);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const finalParams = escapedPathParams && escapedPathParams.length
|
|
242
|
+
? Object.assign(Object.assign({}, params), { path: escapedPathParams }) : params;
|
|
243
|
+
// 处理 query 中的复杂对象
|
|
244
|
+
if (finalParams === null || finalParams === void 0 ? void 0 : finalParams.query) {
|
|
245
|
+
finalParams.query = finalParams.query.map((item) => (Object.assign(Object.assign({}, item), { isComplexType: item.isObject })));
|
|
246
|
+
}
|
|
247
|
+
// 处理 api path 前缀
|
|
248
|
+
const getPrefixPath = () => {
|
|
249
|
+
if (!this.config.apiPrefix) {
|
|
250
|
+
return formattedPath;
|
|
251
|
+
}
|
|
252
|
+
// 静态 apiPrefix
|
|
253
|
+
const prefix = (0, lodash_1.isFunction)(this.config.apiPrefix)
|
|
254
|
+
? `${this.config.apiPrefix({
|
|
255
|
+
path: formattedPath,
|
|
256
|
+
method: newApi.method,
|
|
257
|
+
namespace: tag,
|
|
258
|
+
functionName,
|
|
259
|
+
})}`.trim()
|
|
260
|
+
: this.config.apiPrefix.trim();
|
|
261
|
+
if (!prefix) {
|
|
262
|
+
return formattedPath;
|
|
263
|
+
}
|
|
264
|
+
if (prefix.startsWith("'") ||
|
|
265
|
+
prefix.startsWith('"') ||
|
|
266
|
+
prefix.startsWith('`')) {
|
|
267
|
+
const finalPrefix = prefix.slice(1, prefix.length - 1);
|
|
268
|
+
if (formattedPath.startsWith(finalPrefix) ||
|
|
269
|
+
formattedPath.startsWith(`/${finalPrefix}`)) {
|
|
270
|
+
return formattedPath;
|
|
271
|
+
}
|
|
272
|
+
return `${finalPrefix}${formattedPath}`;
|
|
273
|
+
}
|
|
274
|
+
// prefix 变量
|
|
275
|
+
return `$\{${prefix}}${formattedPath}`;
|
|
276
|
+
};
|
|
277
|
+
return Object.assign(Object.assign({}, newApi), { functionName: this.config.isCamelCase
|
|
278
|
+
? (0, lodash_1.camelCase)(functionName)
|
|
279
|
+
: functionName, typeName: this.getTypeName(newApi), path: getPrefixPath(), pathInComment: formattedPath.replace(/\*/g, '*'), hasPathVariables: formattedPath.includes('{'), hasApiPrefix: !!this.config.apiPrefix, method: newApi.method,
|
|
280
|
+
// 如果 functionName 和 summary 相同,则不显示 summary
|
|
281
|
+
desc: functionName === newApi.summary
|
|
282
|
+
? newApi.description
|
|
283
|
+
: [
|
|
284
|
+
newApi.summary,
|
|
285
|
+
newApi.description,
|
|
286
|
+
((_c = (_b = newApi.responses) === null || _b === void 0 ? void 0 : _b.default) === null || _c === void 0 ? void 0 : _c.description)
|
|
287
|
+
? `返回值: ${((_d = newApi.responses) === null || _d === void 0 ? void 0 : _d.default).description}`
|
|
288
|
+
: '',
|
|
289
|
+
]
|
|
290
|
+
.filter((s) => s)
|
|
291
|
+
.join(' '), hasHeader: !!(params === null || params === void 0 ? void 0 : params.header) || !!(body === null || body === void 0 ? void 0 : body.mediaType), params: finalParams, hasParams: Boolean((0, lodash_1.keys)(finalParams).length), options: ((_f = (_e = this.config.hook) === null || _e === void 0 ? void 0 : _e.customOptionsDefaultValue) === null || _f === void 0 ? void 0 : _f.call(_e, newApi)) || {}, body,
|
|
292
|
+
file, hasFormData: formData, response });
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
console.error('[GenSDK] gen service param error:', error);
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
// 排序下,防止git乱
|
|
300
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
301
|
+
const fileName = (0, util_1.replaceDot)(tag) || `api${index}`;
|
|
302
|
+
let className = fileName;
|
|
303
|
+
if ((_a = this.config.hook) === null || _a === void 0 ? void 0 : _a.customClassName) {
|
|
304
|
+
className = this.config.hook.customClassName(tag);
|
|
305
|
+
}
|
|
306
|
+
if (genParams.length) {
|
|
307
|
+
this.classNameList.push({
|
|
308
|
+
fileName: className,
|
|
309
|
+
controllerName: className,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
genType: 'ts',
|
|
314
|
+
className,
|
|
315
|
+
instanceName: `${(_b = fileName[0]) === null || _b === void 0 ? void 0 : _b.toLowerCase()}${fileName.slice(1)}`,
|
|
316
|
+
list: genParams,
|
|
317
|
+
};
|
|
318
|
+
})
|
|
319
|
+
.filter((item) => { var _a; return !!((_a = item === null || item === void 0 ? void 0 : item.list) === null || _a === void 0 ? void 0 : _a.length); });
|
|
320
|
+
}
|
|
321
|
+
genFileFromTemplate(fileName, type, params) {
|
|
322
|
+
try {
|
|
323
|
+
const template = this.getTemplate(type);
|
|
324
|
+
// 设置输出不转义
|
|
325
|
+
nunjucks_1.default.configure({
|
|
326
|
+
autoescape: false,
|
|
327
|
+
});
|
|
328
|
+
return (0, file_1.writeFile)(this.config.serversPath, fileName, nunjucks_1.default.renderString(template, Object.assign({ ddisableTypeCheck: false }, params)));
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
console.error('[GenSDK] file gen fail:', fileName, 'type:', type);
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
getTemplate(type) {
|
|
336
|
+
return (0, fs_1.readFileSync)((0, path_1.join)(this.config.templatesFolder, `${type}.njk`), 'utf8');
|
|
337
|
+
}
|
|
338
|
+
getFuncationName(data) {
|
|
339
|
+
// 获取路径相同部分
|
|
340
|
+
const pathBasePrefix = (0, util_1.getBasePrefix)((0, lodash_1.keys)(this.openAPIData.paths));
|
|
341
|
+
return this.config.hook && this.config.hook.customFunctionName
|
|
342
|
+
? this.config.hook.customFunctionName(data)
|
|
343
|
+
: data.operationId
|
|
344
|
+
? (0, util_1.resolveFunctionName)((0, util_1.stripDot)(data.operationId), data.method)
|
|
345
|
+
: data.method + (0, util_1.genDefaultFunctionName)(data.path, pathBasePrefix);
|
|
346
|
+
}
|
|
347
|
+
getType(schemaObject, namespace) {
|
|
348
|
+
var _a, _b;
|
|
349
|
+
const customTypeHookFunc = (_a = this.config.hook) === null || _a === void 0 ? void 0 : _a.customType;
|
|
350
|
+
const schemas = (_b = this.openAPIData.components) === null || _b === void 0 ? void 0 : _b.schemas;
|
|
351
|
+
if (customTypeHookFunc) {
|
|
352
|
+
const type = customTypeHookFunc({
|
|
353
|
+
schemaObject,
|
|
354
|
+
namespace,
|
|
355
|
+
schemas,
|
|
356
|
+
originGetType: util_1.getDefaultType,
|
|
357
|
+
});
|
|
358
|
+
if (typeof type === 'string') {
|
|
359
|
+
return type;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return (0, util_1.getDefaultType)(schemaObject, namespace, schemas);
|
|
363
|
+
}
|
|
364
|
+
getTypeName(data) {
|
|
365
|
+
var _a, _b, _c;
|
|
366
|
+
const namespace = this.config.namespace ? `${this.config.namespace}.` : '';
|
|
367
|
+
const typeName = ((_c = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.hook) === null || _b === void 0 ? void 0 : _b.customTypeName) === null || _c === void 0 ? void 0 : _c.call(_b, data)) || this.getFuncationName(data);
|
|
368
|
+
return (0, util_1.resolveTypeName)(`${namespace}${typeName !== null && typeName !== void 0 ? typeName : data.operationId}Params`);
|
|
369
|
+
}
|
|
370
|
+
getBodyTP(requestBody) {
|
|
371
|
+
const reqBody = this.resolveRefObject(requestBody);
|
|
372
|
+
if ((0, lodash_1.isEmpty)(reqBody)) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
const reqContent = reqBody.content;
|
|
376
|
+
if (!(0, lodash_1.isObject)(reqContent)) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
let mediaType = (0, lodash_1.keys)(reqContent)[0];
|
|
380
|
+
const schema = reqContent[mediaType].schema || config_1.DEFAULT_SCHEMA;
|
|
381
|
+
if (mediaType === '*/*') {
|
|
382
|
+
mediaType = '';
|
|
383
|
+
}
|
|
384
|
+
// 如果 requestBody 有 required 属性,则正常展示;如果没有,默认非必填
|
|
385
|
+
const required = typeof (requestBody === null || requestBody === void 0 ? void 0 : requestBody.required) === 'boolean' ? requestBody.required : false;
|
|
386
|
+
if (schema.type === 'object' && schema.properties) {
|
|
387
|
+
const propertiesList = (0, lodash_1.keys)(schema.properties)
|
|
388
|
+
.map((propertyKey) => {
|
|
389
|
+
var _a, _b;
|
|
390
|
+
const propertyObj = schema.properties[propertyKey];
|
|
391
|
+
if (propertyObj &&
|
|
392
|
+
![type_1.SchemaObjectFormat.binary, type_1.SchemaObjectFormat.base64].includes(propertyObj.format) &&
|
|
393
|
+
!(0, util_1.isBinaryArraySchemaObject)(propertyObj)) {
|
|
394
|
+
// 测试了很多用例,很少有用例走到这里
|
|
395
|
+
return {
|
|
396
|
+
key: propertyKey,
|
|
397
|
+
schema: Object.assign(Object.assign({}, propertyObj), { type: this.getType(propertyObj, this.config.namespace), required: (_b = (_a = schema.required) === null || _a === void 0 ? void 0 : _a.includes(propertyKey)) !== null && _b !== void 0 ? _b : false }),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
return null;
|
|
401
|
+
})
|
|
402
|
+
.filter((p) => p);
|
|
403
|
+
return Object.assign(Object.assign({ mediaType }, schema), { required,
|
|
404
|
+
propertiesList });
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
mediaType,
|
|
408
|
+
required,
|
|
409
|
+
type: this.getType(schema, this.config.namespace),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
getFileTP(requestBody) {
|
|
413
|
+
var _a;
|
|
414
|
+
const reqBody = this.resolveRefObject(requestBody);
|
|
415
|
+
if ((_a = reqBody === null || reqBody === void 0 ? void 0 : reqBody.content) === null || _a === void 0 ? void 0 : _a['multipart/form-data']) {
|
|
416
|
+
const ret = this.resolveFileTP(reqBody.content['multipart/form-data'].schema);
|
|
417
|
+
return ret.length > 0 ? ret : null;
|
|
418
|
+
}
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
resolveFileTP(obj) {
|
|
422
|
+
var _a;
|
|
423
|
+
let ret = [];
|
|
424
|
+
const resolved = this.resolveObject(obj);
|
|
425
|
+
const props = (((_a = resolved.props) === null || _a === void 0 ? void 0 : _a.length) > 0 &&
|
|
426
|
+
resolved.props[0].filter((p) => p.format === 'binary' ||
|
|
427
|
+
p.format === 'base64' ||
|
|
428
|
+
(0, util_1.isBinaryArraySchemaObject)(p))) ||
|
|
429
|
+
[];
|
|
430
|
+
if (props.length > 0) {
|
|
431
|
+
ret = props.map((p) => {
|
|
432
|
+
// 这里 p.type 是自定义type, 注意别混淆
|
|
433
|
+
return {
|
|
434
|
+
title: p.name,
|
|
435
|
+
multiple: p.type === `${type_1.SchemaObjectType.array}` ||
|
|
436
|
+
p.type === `${type_1.SchemaObjectType.stringArray}`,
|
|
437
|
+
};
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
if (resolved.type)
|
|
441
|
+
ret = [...ret, ...this.resolveFileTP(resolved.type)];
|
|
442
|
+
return ret;
|
|
443
|
+
}
|
|
444
|
+
getResponseTP(responses = {}) {
|
|
445
|
+
var _a;
|
|
446
|
+
const { components } = this.openAPIData;
|
|
447
|
+
const response = responses &&
|
|
448
|
+
this.resolveRefObject(responses.default || responses['200'] || responses['201']);
|
|
449
|
+
const defaultResponse = {
|
|
450
|
+
mediaType: '*/*',
|
|
451
|
+
type: 'any',
|
|
452
|
+
};
|
|
453
|
+
if (!response) {
|
|
454
|
+
return defaultResponse;
|
|
455
|
+
}
|
|
456
|
+
const resContent = response.content;
|
|
457
|
+
const resContentMediaTypes = (0, lodash_1.keys)(resContent);
|
|
458
|
+
const mediaType = resContentMediaTypes.includes('application/json')
|
|
459
|
+
? 'application/json'
|
|
460
|
+
: resContentMediaTypes[0]; // 优先使用 application/json
|
|
461
|
+
if (!(0, lodash_1.isObject)(resContent) || !mediaType) {
|
|
462
|
+
return defaultResponse;
|
|
463
|
+
}
|
|
464
|
+
let schema = (resContent[mediaType].schema ||
|
|
465
|
+
config_1.DEFAULT_SCHEMA);
|
|
466
|
+
if ((0, util_1.isReferenceObject)(schema)) {
|
|
467
|
+
const refPaths = schema.$ref.split('/');
|
|
468
|
+
const refName = refPaths[refPaths.length - 1];
|
|
469
|
+
const childrenSchema = components.schemas[refName];
|
|
470
|
+
if ((0, util_1.isNonArraySchemaObject)(childrenSchema) && this.config.dataFields) {
|
|
471
|
+
schema = (((_a = this.config.dataFields
|
|
472
|
+
.map((field) => childrenSchema.properties[field])
|
|
473
|
+
.filter(Boolean)) === null || _a === void 0 ? void 0 : _a[0]) ||
|
|
474
|
+
resContent[mediaType].schema ||
|
|
475
|
+
config_1.DEFAULT_SCHEMA);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if ((0, util_1.isSchemaObject)(schema)) {
|
|
479
|
+
(0, lodash_1.keys)(schema.properties).map((fieldName) => {
|
|
480
|
+
var _a, _b;
|
|
481
|
+
schema.properties[fieldName]['required'] =
|
|
482
|
+
(_b = (_a = schema.required) === null || _a === void 0 ? void 0 : _a.includes(fieldName)) !== null && _b !== void 0 ? _b : false;
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
mediaType,
|
|
487
|
+
type: this.getType(schema, this.config.namespace),
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
getParamsTP(parameters = [], path = null) {
|
|
491
|
+
const templateParams = {};
|
|
492
|
+
if (parameters === null || parameters === void 0 ? void 0 : parameters.length) {
|
|
493
|
+
(0, lodash_1.forEach)(config_1.parametersIn, (source) => {
|
|
494
|
+
const params = parameters
|
|
495
|
+
.map((p) => this.resolveRefObject(p))
|
|
496
|
+
.filter((p) => p.in === source)
|
|
497
|
+
.map((p) => {
|
|
498
|
+
var _a, _b, _c, _d;
|
|
499
|
+
const isDirectObject = (((_a = p.schema) === null || _a === void 0 ? void 0 : _a.type) ||
|
|
500
|
+
p.type) === 'object';
|
|
501
|
+
const refList = (((_b = p.schema) === null || _b === void 0 ? void 0 : _b.$ref) ||
|
|
502
|
+
p.$ref ||
|
|
503
|
+
'').split('/');
|
|
504
|
+
const ref = refList[refList.length - 1];
|
|
505
|
+
const deRefObj = (0, lodash_1.entries)((_c = this.openAPIData.components) === null || _c === void 0 ? void 0 : _c.schemas).find(([k]) => k === ref) || [];
|
|
506
|
+
const isRefObject = ((_d = deRefObj[1]) === null || _d === void 0 ? void 0 : _d.type) === 'object';
|
|
507
|
+
return Object.assign(Object.assign({}, p), { isObject: isDirectObject || isRefObject, type: this.getType(p.schema || config_1.DEFAULT_SCHEMA, this.config.namespace) });
|
|
508
|
+
});
|
|
509
|
+
if (params.length) {
|
|
510
|
+
templateParams[source] = params;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
if (path && path.length > 0) {
|
|
515
|
+
const regex = /\{(\w+)\}/g;
|
|
516
|
+
templateParams.path = templateParams.path || [];
|
|
517
|
+
let match = null;
|
|
518
|
+
while ((match = regex.exec(path))) {
|
|
519
|
+
if (!templateParams.path.some((p) => p.name === match[1])) {
|
|
520
|
+
templateParams.path.push(Object.assign(Object.assign({}, config_1.DEFAULT_PATH_PARAM), { name: match[1] }));
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// 如果 path 没有内容,则将删除 path 参数,避免影响后续的 hasParams 判断
|
|
524
|
+
if (!templateParams.path.length)
|
|
525
|
+
delete templateParams.path;
|
|
526
|
+
}
|
|
527
|
+
return templateParams;
|
|
528
|
+
}
|
|
529
|
+
resolveObject(schemaObject) {
|
|
530
|
+
// 不使用 schemaObject: ISchemaObject = {}
|
|
531
|
+
schemaObject = schemaObject !== null && schemaObject !== void 0 ? schemaObject : {};
|
|
532
|
+
// 引用类型
|
|
533
|
+
if ((0, util_1.isReferenceObject)(schemaObject)) {
|
|
534
|
+
return this.resolveRefObject(schemaObject);
|
|
535
|
+
}
|
|
536
|
+
// 枚举类型
|
|
537
|
+
if (schemaObject.enum) {
|
|
538
|
+
return this.resolveEnumObject(schemaObject);
|
|
539
|
+
}
|
|
540
|
+
// 继承类型
|
|
541
|
+
if (schemaObject.allOf && schemaObject.allOf.length) {
|
|
542
|
+
return this.resolveAllOfObject(schemaObject);
|
|
543
|
+
}
|
|
544
|
+
// 对象类型
|
|
545
|
+
if (schemaObject.properties) {
|
|
546
|
+
return this.resolveProperties(schemaObject);
|
|
547
|
+
}
|
|
548
|
+
// 数组类型
|
|
549
|
+
if ((0, util_1.isArraySchemaObject)(schemaObject)) {
|
|
550
|
+
return this.resolveArray(schemaObject);
|
|
551
|
+
}
|
|
552
|
+
return schemaObject;
|
|
553
|
+
}
|
|
554
|
+
resolveArray(schemaObject) {
|
|
555
|
+
if ((0, util_1.isReferenceObject)(schemaObject.items)) {
|
|
556
|
+
const refPaths = schemaObject.items.$ref.split('/');
|
|
557
|
+
return {
|
|
558
|
+
type: `${refPaths[refPaths.length - 1]}[]`,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
// 这里需要解析出具体属性,但由于 parser 层还不确定,所以暂时先返回 any[]
|
|
562
|
+
return { type: 'any[]' };
|
|
563
|
+
}
|
|
564
|
+
resolveProperties(schemaObject) {
|
|
565
|
+
return {
|
|
566
|
+
props: [this.getProps(schemaObject)],
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
resolveEnumObject(schemaObject) {
|
|
570
|
+
const enumArray = schemaObject.enum;
|
|
571
|
+
const enumStr = `{${(0, lodash_1.map)(enumArray, (value) => `${value}="${value}"`).join(',')}}`;
|
|
572
|
+
let enumLabelTypeStr = '';
|
|
573
|
+
// 翻译枚举
|
|
574
|
+
if (schemaObject['x-enum-varnames'] && schemaObject['x-enum-comments']) {
|
|
575
|
+
enumLabelTypeStr = `{${(0, lodash_1.map)(enumArray, (value, index) => {
|
|
576
|
+
const enumKey = schemaObject['x-enum-varnames'][index];
|
|
577
|
+
return `${value}:"${schemaObject['x-enum-comments'][enumKey]}"`;
|
|
578
|
+
}).join(',')}}`;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
enumLabelTypeStr = `{${(0, lodash_1.map)(enumArray, (value) => `${value}:"${value}"`).join(',')}}`;
|
|
582
|
+
}
|
|
583
|
+
return {
|
|
584
|
+
isEnum: true,
|
|
585
|
+
type: Array.isArray(enumArray) ? enumStr : 'string',
|
|
586
|
+
enumLabelType: enumLabelTypeStr,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
resolveAllOfObject(schemaObject) {
|
|
590
|
+
const props = (0, lodash_1.map)(schemaObject.allOf, (item) => {
|
|
591
|
+
return (0, util_1.isReferenceObject)(item)
|
|
592
|
+
? [Object.assign(Object.assign({}, item), { type: this.getType(item) })]
|
|
593
|
+
: this.getProps(item);
|
|
594
|
+
});
|
|
595
|
+
if (schemaObject.properties) {
|
|
596
|
+
const extProps = this.getProps(schemaObject);
|
|
597
|
+
return { props: [...props, extProps] };
|
|
598
|
+
}
|
|
599
|
+
return { props };
|
|
600
|
+
}
|
|
601
|
+
// 获取 TS 类型的属性列表
|
|
602
|
+
getProps(schemaObject) {
|
|
603
|
+
var _a;
|
|
604
|
+
const requiredPropKeys = (_a = schemaObject === null || schemaObject === void 0 ? void 0 : schemaObject.required) !== null && _a !== void 0 ? _a : false;
|
|
605
|
+
const properties = schemaObject.properties;
|
|
606
|
+
return (0, lodash_1.keys)(properties).map((propKey) => {
|
|
607
|
+
const schema = ((properties === null || properties === void 0 ? void 0 : properties[propKey]) || config_1.DEFAULT_SCHEMA);
|
|
608
|
+
// 剔除属性键值中的特殊符号,因为函数入参变量存在特殊符号会导致解析文件失败
|
|
609
|
+
// eslint-disable-next-line no-useless-escape
|
|
610
|
+
propKey = propKey.replace(/[\[|\]]/g, '');
|
|
611
|
+
// 复用 schema 部分字段
|
|
612
|
+
return Object.assign(Object.assign({}, schema), { name: propKey, type: this.getType(schema), desc: [schema.title, schema.description]
|
|
613
|
+
.filter((item) => item)
|
|
614
|
+
.join(' '),
|
|
615
|
+
// 如果没有 required 信息,默认全部是非必填
|
|
616
|
+
required: requiredPropKeys
|
|
617
|
+
? requiredPropKeys.some((key) => key === propKey)
|
|
618
|
+
: false });
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
resolveRefObject(refObject) {
|
|
622
|
+
var _a, _b;
|
|
623
|
+
if (!(0, util_1.isReferenceObject)(refObject)) {
|
|
624
|
+
return refObject;
|
|
625
|
+
}
|
|
626
|
+
// 测试了很多用例,很少有用例走到这里
|
|
627
|
+
const refPaths = refObject.$ref.split('/');
|
|
628
|
+
if (refPaths[0] === '#') {
|
|
629
|
+
const schema = (_b = (_a = this.openAPIData.components) === null || _a === void 0 ? void 0 : _a.schemas) === null || _b === void 0 ? void 0 : _b[refPaths[refPaths.length - 1]];
|
|
630
|
+
if (!schema) {
|
|
631
|
+
throw new Error(`[GenSDK] Data Error! Notfoud: ${refObject.$ref}`);
|
|
632
|
+
}
|
|
633
|
+
return Object.assign(Object.assign({}, (this.resolveRefObject(schema) || {})), { type: (0, util_1.isReferenceObject)(schema)
|
|
634
|
+
? this.resolveRefObject(schema).type
|
|
635
|
+
: schema.type });
|
|
636
|
+
}
|
|
637
|
+
return refObject;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
exports.default = ServiceGenerator;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { OperationObject, ParameterObject, SchemaObject } from '../type';
|
|
2
|
+
import { TypescriptFileType } from './config';
|
|
3
|
+
export type ITypescriptFileType = keyof typeof TypescriptFileType;
|
|
4
|
+
export interface APIDataType extends OperationObject {
|
|
5
|
+
path: string;
|
|
6
|
+
method: string;
|
|
7
|
+
}
|
|
8
|
+
export type TagAPIDataType = Record<string, APIDataType[]>;
|
|
9
|
+
export interface ControllerType {
|
|
10
|
+
fileName: string;
|
|
11
|
+
controllerName: string;
|
|
12
|
+
}
|
|
13
|
+
export interface IPropBasicObject {
|
|
14
|
+
name: string;
|
|
15
|
+
desc: string;
|
|
16
|
+
required?: boolean;
|
|
17
|
+
type: string;
|
|
18
|
+
}
|
|
19
|
+
export type IPropObject = IPropBasicObject & Partial<Omit<SchemaObject, 'type' | 'required'>>;
|
|
20
|
+
export interface ITypeItem {
|
|
21
|
+
typeName: string;
|
|
22
|
+
type: string | boolean | SchemaObject;
|
|
23
|
+
props: IPropObject[][];
|
|
24
|
+
isEnum: boolean;
|
|
25
|
+
displayLabelFuncName?: string;
|
|
26
|
+
enumLabelType?: string;
|
|
27
|
+
}
|
|
28
|
+
export type ICustomSchemaObject = SchemaObject & {
|
|
29
|
+
isAllowed?: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type ICustomParameterObject = ParameterObject & {
|
|
32
|
+
isObject: boolean;
|
|
33
|
+
type: string;
|
|
34
|
+
};
|