@midwayjs/swagger 3.17.2 → 3.17.3

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.
@@ -13,5 +13,6 @@ exports.swagger = {
13
13
  ? render_1.renderSwaggerUIDist
14
14
  : render_1.renderSwaggerUIRemote,
15
15
  swaggerUIRenderOptions: {},
16
+ isGenerateTagForController: true,
16
17
  };
17
18
  //# sourceMappingURL=config.default.js.map
@@ -95,6 +95,19 @@ class DocumentBuilder {
95
95
  externalDocs,
96
96
  });
97
97
  }
98
+ else {
99
+ // update description and externalDocs
100
+ tags.forEach(tag => {
101
+ if (tag.name === name) {
102
+ if (description) {
103
+ tag.description = description;
104
+ }
105
+ if (externalDocs) {
106
+ tag.externalDocs = externalDocs;
107
+ }
108
+ }
109
+ });
110
+ }
98
111
  this.document.tags = tags;
99
112
  return this;
100
113
  }
@@ -373,6 +373,10 @@ export interface SwaggerOptions {
373
373
  * 自定义路由过滤器
374
374
  */
375
375
  routerFilter?: (url: string, options: RouterOption) => boolean;
376
+ /**
377
+ * Weather to generate the Tag for controller
378
+ */
379
+ isGenerateTagForController?: boolean;
376
380
  }
377
381
  export interface MixDecoratorMetadata {
378
382
  key: string;
@@ -1,5 +1,6 @@
1
1
  import { RouterOption } from '@midwayjs/core';
2
2
  import { Type } from './interfaces';
3
+ import { DocumentBuilder } from './documentBuilder';
3
4
  export declare class SwaggerExplorer {
4
5
  private swaggerConfig;
5
6
  private documentBuilder;
@@ -8,18 +9,13 @@ export declare class SwaggerExplorer {
8
9
  addGlobalPrefix(globalPrefix: string): void;
9
10
  scanApp(): void;
10
11
  getData(): Omit<import("./interfaces").OpenAPIObject, "paths">;
12
+ getDocumentBuilder(): DocumentBuilder;
11
13
  protected generatePath(target: Type): void;
12
14
  /**
13
15
  * 构造 router 提取方法
14
16
  */
15
17
  private generateRouteMethod;
16
18
  getOperationId(controllerKey: string, webRouter: RouterOption): string;
17
- /**
18
- * 提取参数
19
- * @param params
20
- * @param p
21
- */
22
- protected parseFromParamsToP(paramMeta: any, p: any): void;
23
19
  /**
24
20
  * 解析 ApiExtraModel
25
21
  * @param clzz
@@ -96,13 +96,18 @@ let SwaggerExplorer = class SwaggerExplorer {
96
96
  getData() {
97
97
  return this.documentBuilder.build();
98
98
  }
99
+ getDocumentBuilder() {
100
+ return this.documentBuilder;
101
+ }
99
102
  generatePath(target) {
103
+ var _a;
100
104
  // 获取控制器元数据
101
105
  const excludeClassMeta = (0, core_1.getClassMetadata)(constants_1.DECORATORS.API_EXCLUDE_CONTROLLER, target);
102
106
  if (excludeClassMeta && excludeClassMeta.disable) {
103
107
  // 如果存在需要排除的控制器,则直接返回
104
108
  return;
105
109
  }
110
+ const isGenerateTagForController = (_a = this.swaggerConfig.isGenerateTagForController) !== null && _a !== void 0 ? _a : true;
106
111
  // 解析额外的模型
107
112
  this.parseExtraModel(target);
108
113
  const metaForClass = (0, core_1.getClassMetadata)(constants_1.DECORATORS_CLASS_METADATA, target) || [];
@@ -118,33 +123,35 @@ let SwaggerExplorer = class SwaggerExplorer {
118
123
  const controllerTags = [];
119
124
  // 如果存在标签,则将其添加到文档构建器中
120
125
  if (tags.length > 0) {
121
- for (const t of tags) {
122
- // 这里 metadata => string[]
123
- strTags = strTags.concat(t.metadata);
124
- controllerTags.push(Array.isArray(t.metadata) ? [t.metadata] : t.metadata);
125
- // this.documentBuilder.addTag(t.metadata);
126
- }
126
+ strTags = parseTags(tags);
127
+ strTags.forEach(tag => {
128
+ addTag(tag, controllerTags);
129
+ });
127
130
  }
128
131
  else {
129
- // 如果不存在标签,则根据控制器选项生成标签
130
- const tag = { name: '', description: '' };
131
- if (prefix !== '/') {
132
- tag.name =
133
- (controllerOption === null || controllerOption === void 0 ? void 0 : controllerOption.routerOptions.tagName) ||
134
- (/^\//.test(prefix) ? prefix.split('/')[1] : prefix);
135
- tag.description =
136
- (controllerOption === null || controllerOption === void 0 ? void 0 : controllerOption.routerOptions.description) || tag.name;
132
+ if (isGenerateTagForController) {
133
+ // 如果不存在标签,则根据控制器选项生成标签
134
+ const tag = { name: '', description: '' };
135
+ if (prefix !== '/') {
136
+ tag.name =
137
+ (controllerOption === null || controllerOption === void 0 ? void 0 : controllerOption.routerOptions.tagName) ||
138
+ (/^\//.test(prefix) ? prefix.split('/')[1] : prefix);
139
+ tag.description =
140
+ (controllerOption === null || controllerOption === void 0 ? void 0 : controllerOption.routerOptions.description) || tag.name;
141
+ }
142
+ else {
143
+ tag.name = controllerOption === null || controllerOption === void 0 ? void 0 : controllerOption.routerOptions.tagName;
144
+ tag.description =
145
+ (controllerOption === null || controllerOption === void 0 ? void 0 : controllerOption.routerOptions.description) || tag.name;
146
+ }
147
+ // 如果标签名存在,则将其添加到文档构建器中
148
+ if (tag.name) {
149
+ strTags.push(tag.name);
150
+ addTag([tag.name, tag.description], controllerTags);
151
+ }
137
152
  }
138
153
  else {
139
- tag.name = controllerOption === null || controllerOption === void 0 ? void 0 : controllerOption.routerOptions.tagName;
140
- tag.description =
141
- (controllerOption === null || controllerOption === void 0 ? void 0 : controllerOption.routerOptions.description) || tag.name;
142
- }
143
- // 如果标签名存在,则将其添加到文档构建器中
144
- if (tag.name) {
145
- strTags.push(tag.name);
146
- controllerTags.push([tag.name, tag.description]);
147
- // this.documentBuilder.addTag(tag.name, tag.description);
154
+ // 否则不添加标签
148
155
  }
149
156
  }
150
157
  // 获取路由信息
@@ -198,6 +205,12 @@ let SwaggerExplorer = class SwaggerExplorer {
198
205
  if (paths[url][webRouter.requestMethod].tags.length === 0) {
199
206
  paths[url][webRouter.requestMethod].tags = strTags;
200
207
  }
208
+ else {
209
+ // 如果 tags 不在全局中,则添加
210
+ paths[url][webRouter.requestMethod].tags.forEach(tag => {
211
+ addTag(tag, controllerTags);
212
+ });
213
+ }
201
214
  // 过滤出扩展信息
202
215
  const exts = metaForMethods.filter(item => item.key === constants_1.DECORATORS.API_EXTENSION &&
203
216
  item.propertyName === webRouter.method);
@@ -242,9 +255,12 @@ let SwaggerExplorer = class SwaggerExplorer {
242
255
  * 构造 router 提取方法
243
256
  */
244
257
  generateRouteMethod(url, webRouter, paths, metaForMethods, routerArgs, headers, target) {
245
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
258
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
246
259
  const operMeta = metaForMethods.filter(item => item.key === constants_1.DECORATORS.API_OPERATION &&
247
260
  item.propertyName === webRouter.method)[0];
261
+ const routerTagsMeta = metaForMethods.filter(item => item.key === constants_1.DECORATORS.API_TAGS &&
262
+ item.propertyName === webRouter.method);
263
+ const routerTags = parseTags(routerTagsMeta);
248
264
  let opts = paths[url];
249
265
  if (!opts) {
250
266
  opts = {};
@@ -255,11 +271,11 @@ let SwaggerExplorer = class SwaggerExplorer {
255
271
  description: getNotEmptyValue((_b = operMeta === null || operMeta === void 0 ? void 0 : operMeta.metadata) === null || _b === void 0 ? void 0 : _b.description, webRouter.description),
256
272
  operationId: ((_c = operMeta === null || operMeta === void 0 ? void 0 : operMeta.metadata) === null || _c === void 0 ? void 0 : _c.operationId) ||
257
273
  this.getOperationId(target.name, webRouter),
258
- tags: ((_d = operMeta === null || operMeta === void 0 ? void 0 : operMeta.metadata) === null || _d === void 0 ? void 0 : _d.tags) || [],
274
+ tags: routerTags.length ? routerTags : (_e = (_d = operMeta === null || operMeta === void 0 ? void 0 : operMeta.metadata) === null || _d === void 0 ? void 0 : _d.tags) !== null && _e !== void 0 ? _e : [],
259
275
  };
260
- if (((_e = operMeta === null || operMeta === void 0 ? void 0 : operMeta.metadata) === null || _e === void 0 ? void 0 : _e.deprecated) != null) {
276
+ if (((_f = operMeta === null || operMeta === void 0 ? void 0 : operMeta.metadata) === null || _f === void 0 ? void 0 : _f.deprecated) != null) {
261
277
  opts[webRouter.requestMethod].deprecated =
262
- !!((_f = operMeta === null || operMeta === void 0 ? void 0 : operMeta.metadata) === null || _f === void 0 ? void 0 : _f.deprecated);
278
+ !!((_g = operMeta === null || operMeta === void 0 ? void 0 : operMeta.metadata) === null || _g === void 0 ? void 0 : _g.deprecated);
263
279
  }
264
280
  /**
265
281
  * [{"key":"web:router_param","parameterIndex":1,"propertyName":"create","metadata":{"type":2}},
@@ -283,7 +299,7 @@ let SwaggerExplorer = class SwaggerExplorer {
283
299
  param.metadata.schema['type'] = param.metadata.type;
284
300
  delete param.metadata.type;
285
301
  }
286
- if (param.metadata.isArray !== undefined) {
302
+ if (param.metadata.isArray) {
287
303
  param.metadata.schema['items'] = {
288
304
  type: param.metadata.schema['type'],
289
305
  };
@@ -307,9 +323,9 @@ let SwaggerExplorer = class SwaggerExplorer {
307
323
  parameters.push(p);
308
324
  }
309
325
  else if (p.in === 'body') {
310
- p.content = (_g = p.content) !== null && _g !== void 0 ? _g : {};
326
+ p.content = (_h = p.content) !== null && _h !== void 0 ? _h : {};
311
327
  if (Object.keys(p.content).length === 0) {
312
- p.content[p.contentType || 'application/json'] = (_h = p.content[p.contentType || 'application/json']) !== null && _h !== void 0 ? _h : {
328
+ p.content[p.contentType || 'application/json'] = (_j = p.content[p.contentType || 'application/json']) !== null && _j !== void 0 ? _j : {
313
329
  schema: p.schema,
314
330
  };
315
331
  }
@@ -319,20 +335,20 @@ let SwaggerExplorer = class SwaggerExplorer {
319
335
  }
320
336
  // if requestBody is already set, skip
321
337
  opts[webRouter.requestMethod].requestBody =
322
- (_j = opts[webRouter.requestMethod].requestBody) !== null && _j !== void 0 ? _j : {};
338
+ (_k = opts[webRouter.requestMethod].requestBody) !== null && _k !== void 0 ? _k : {};
323
339
  opts[webRouter.requestMethod].requestBody.description =
324
- (_k = opts[webRouter.requestMethod].requestBody.description) !== null && _k !== void 0 ? _k : p.description;
340
+ (_l = opts[webRouter.requestMethod].requestBody.description) !== null && _l !== void 0 ? _l : p.description;
325
341
  opts[webRouter.requestMethod].requestBody.content =
326
- (_l = opts[webRouter.requestMethod].requestBody.content) !== null && _l !== void 0 ? _l : p.content;
342
+ (_m = opts[webRouter.requestMethod].requestBody.content) !== null && _m !== void 0 ? _m : p.content;
327
343
  opts[webRouter.requestMethod].requestBody.required =
328
- (_m = opts[webRouter.requestMethod].requestBody.required) !== null && _m !== void 0 ? _m : p.required;
344
+ (_o = opts[webRouter.requestMethod].requestBody.required) !== null && _o !== void 0 ? _o : p.required;
329
345
  }
330
346
  }
331
347
  for (const arg of args) {
332
348
  const currentType = types[arg.parameterIndex];
333
349
  const p = {
334
- name: (_o = arg === null || arg === void 0 ? void 0 : arg.metadata) === null || _o === void 0 ? void 0 : _o.propertyData,
335
- in: convertTypeToString((_p = arg.metadata) === null || _p === void 0 ? void 0 : _p.type),
350
+ name: (_p = arg === null || arg === void 0 ? void 0 : arg.metadata) === null || _p === void 0 ? void 0 : _p.propertyData,
351
+ in: convertTypeToString((_q = arg.metadata) === null || _q === void 0 ? void 0 : _q.type),
336
352
  required: false,
337
353
  };
338
354
  const existsParam = parameters.find(item => {
@@ -374,7 +390,7 @@ let SwaggerExplorer = class SwaggerExplorer {
374
390
  continue;
375
391
  }
376
392
  p.schema = {
377
- type: convertSchemaType((_q = currentType === null || currentType === void 0 ? void 0 : currentType.name) !== null && _q !== void 0 ? _q : currentType),
393
+ type: convertSchemaType((_r = currentType === null || currentType === void 0 ? void 0 : currentType.name) !== null && _r !== void 0 ? _r : currentType),
378
394
  };
379
395
  }
380
396
  }
@@ -387,7 +403,7 @@ let SwaggerExplorer = class SwaggerExplorer {
387
403
  continue;
388
404
  }
389
405
  // 这里兼容一下 @File()、@Files()、@Fields() 装饰器
390
- if (((_r = arg.metadata) === null || _r === void 0 ? void 0 : _r.type) === core_1.RouteParamTypes.FILESSTREAM) {
406
+ if (((_s = arg.metadata) === null || _s === void 0 ? void 0 : _s.type) === core_1.RouteParamTypes.FILESSTREAM) {
391
407
  p.content = {};
392
408
  p.content[_1.BodyContentType.Multipart] = {
393
409
  schema: {
@@ -405,7 +421,7 @@ let SwaggerExplorer = class SwaggerExplorer {
405
421
  },
406
422
  };
407
423
  }
408
- else if (((_s = arg.metadata) === null || _s === void 0 ? void 0 : _s.type) === core_1.RouteParamTypes.FILESTREAM) {
424
+ else if (((_t = arg.metadata) === null || _t === void 0 ? void 0 : _t.type) === core_1.RouteParamTypes.FILESTREAM) {
409
425
  p.content = {};
410
426
  p.content[_1.BodyContentType.Multipart] = {
411
427
  schema: {
@@ -435,7 +451,7 @@ let SwaggerExplorer = class SwaggerExplorer {
435
451
  p.content = {
436
452
  'text/plain': {
437
453
  schema: {
438
- type: convertSchemaType((_t = currentType === null || currentType === void 0 ? void 0 : currentType.name) !== null && _t !== void 0 ? _t : currentType),
454
+ type: convertSchemaType((_u = currentType === null || currentType === void 0 ? void 0 : currentType.name) !== null && _u !== void 0 ? _u : currentType),
439
455
  },
440
456
  },
441
457
  };
@@ -527,96 +543,6 @@ let SwaggerExplorer = class SwaggerExplorer {
527
543
  getOperationId(controllerKey, webRouter) {
528
544
  return this.operationIdFactory(controllerKey, webRouter);
529
545
  }
530
- /**
531
- * 提取参数
532
- * @param params
533
- * @param p
534
- */
535
- parseFromParamsToP(paramMeta, p) {
536
- var _a, _b, _c;
537
- if (paramMeta) {
538
- const param = paramMeta.metadata;
539
- if (param) {
540
- p.description = param.description;
541
- if (!p.name && param.name) {
542
- p.name = param.name;
543
- }
544
- if (param.in === 'query') {
545
- p.allowEmptyValue = param.allowEmptyValue || false;
546
- }
547
- if (typeof param.example !== undefined) {
548
- p.example = param.example;
549
- }
550
- if (param.examples) {
551
- p.examples = param.examples;
552
- }
553
- if (param.deprecated) {
554
- p.deprecated = param.deprecated;
555
- }
556
- if (param.contentType) {
557
- p.contentType = param.contentType;
558
- }
559
- p.in = (_a = param === null || param === void 0 ? void 0 : param.in) !== null && _a !== void 0 ? _a : p.in;
560
- p.required = (_b = param === null || param === void 0 ? void 0 : param.required) !== null && _b !== void 0 ? _b : p.required;
561
- if (p.in === 'query') {
562
- p.style = 'form';
563
- }
564
- else if (p.in === 'path' || p.in === 'header') {
565
- p.style = 'simple';
566
- }
567
- else if (p.in === 'cookie') {
568
- p.style = 'form';
569
- }
570
- p.explode = p.style === 'form';
571
- // response content
572
- if (param === null || param === void 0 ? void 0 : param.content) {
573
- p.content = param === null || param === void 0 ? void 0 : param.content;
574
- }
575
- if (param.schema) {
576
- p.schema = param.schema;
577
- }
578
- else {
579
- if (param.type) {
580
- if (core_1.Types.isClass(param.type)) {
581
- this.parseClzz(param.type);
582
- p.schema = {
583
- $ref: '#/components/schemas/' + param.type.name,
584
- };
585
- }
586
- if (param.isArray) {
587
- let ref;
588
- if ((_c = p === null || p === void 0 ? void 0 : p.schema) === null || _c === void 0 ? void 0 : _c.$ref) {
589
- ref = p.schema.$ref;
590
- }
591
- p.schema = {
592
- type: 'array',
593
- items: {
594
- format: param.format,
595
- },
596
- };
597
- if (ref) {
598
- p.schema.items.$ref = ref;
599
- }
600
- else {
601
- p.schema.items.type = convertSchemaType(param.type);
602
- }
603
- }
604
- else {
605
- if (!p.schema) {
606
- p.schema = {
607
- type: param.type ? convertSchemaType(param.type) : p.type,
608
- format: param.format || p.format,
609
- };
610
- }
611
- }
612
- }
613
- else if (param.format) {
614
- p.schema.format = param.format;
615
- }
616
- }
617
- }
618
- }
619
- }
620
546
  /**
621
547
  * 解析 ApiExtraModel
622
548
  * @param clzz
@@ -1054,4 +980,35 @@ function parseTypeSchema(ref) {
1054
980
  return ref;
1055
981
  }
1056
982
  }
983
+ function parseTags(tags) {
984
+ let strTags = [];
985
+ if (tags.length > 0) {
986
+ for (const t of tags) {
987
+ // 这里 metadata => string[]
988
+ strTags = strTags.concat(t.metadata);
989
+ }
990
+ }
991
+ return strTags;
992
+ }
993
+ function addTag(newTag, tags = []) {
994
+ /**
995
+ * tag 结构
996
+ * ['name', 'description'] 或者 'name'
997
+ */
998
+ if (
999
+ // 处理重复的标签
1000
+ tags.find(t => {
1001
+ if (Array.isArray(newTag)) {
1002
+ return t === newTag[0];
1003
+ }
1004
+ else {
1005
+ return t === newTag;
1006
+ }
1007
+ })) {
1008
+ // ignore
1009
+ }
1010
+ else {
1011
+ tags.push(newTag);
1012
+ }
1013
+ }
1057
1014
  //# sourceMappingURL=swaggerExplorer.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midwayjs/swagger",
3
- "version": "3.17.2",
3
+ "version": "3.17.3",
4
4
  "main": "dist/index.js",
5
5
  "typings": "index.d.ts",
6
6
  "files": [
@@ -13,7 +13,7 @@
13
13
  "@midwayjs/core": "^3.17.1",
14
14
  "@midwayjs/koa": "^3.17.1",
15
15
  "@midwayjs/mock": "^3.17.1",
16
- "@midwayjs/validate": "^3.17.1",
16
+ "@midwayjs/validate": "^3.17.3",
17
17
  "swagger-ui-dist": "5.17.14"
18
18
  },
19
19
  "author": "Kurten Chan <chinkurten@gmail.com>",
@@ -27,5 +27,5 @@
27
27
  "type": "git",
28
28
  "url": "https://github.com/midwayjs/midway.git"
29
29
  },
30
- "gitHead": "c878373ceee03f7922d6c61488e8881d43d6006b"
30
+ "gitHead": "f4086c0e118673052202ffa92149aacd89e9ddc5"
31
31
  }