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.
@@ -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, '&#42;'), 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
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });