@midwayjs/swagger 4.0.0-beta.15 → 4.0.0-beta.17

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.
@@ -15,6 +15,13 @@ export declare class SwaggerExplorer {
15
15
  * 构造 router 提取方法
16
16
  */
17
17
  private generateRouteMethod;
18
+ private upsertOperationParameter;
19
+ private cloneOpenAPIValue;
20
+ private normalizeContentSchemas;
21
+ private normalizeOperationParameters;
22
+ private normalizeOperationRequestBody;
23
+ private normalizeOperationResponse;
24
+ private normalizeOperationResponses;
18
25
  getOperationId(controllerKey: string, webRouter: RouterOption): string;
19
26
  /**
20
27
  * 解析 ApiExtraModel
@@ -268,13 +268,19 @@ let SwaggerExplorer = class SwaggerExplorer {
268
268
  if (!opts) {
269
269
  opts = {};
270
270
  }
271
- const parameters = [];
271
+ const operationParameters = this.normalizeOperationParameters(operMeta?.metadata?.parameters);
272
+ const operationRequestBody = this.normalizeOperationRequestBody(operMeta?.metadata?.requestBody);
273
+ const operationResponses = this.normalizeOperationResponses(operMeta?.metadata?.responses);
274
+ const parameters = [...operationParameters];
275
+ let requestBodyFromOperation = !!operationRequestBody;
272
276
  opts[webRouter.requestMethod] = {
273
277
  summary: getNotEmptyValue(operMeta?.metadata?.summary, webRouter.summary),
274
278
  description: getNotEmptyValue(operMeta?.metadata?.description, webRouter.description),
275
279
  operationId: operMeta?.metadata?.operationId ||
276
280
  this.getOperationId(target.name, webRouter),
277
281
  tags: routerTags.length ? routerTags : (operMeta?.metadata?.tags ?? []),
282
+ ...(operationRequestBody ? { requestBody: operationRequestBody } : {}),
283
+ ...(operationResponses ? { responses: operationResponses } : {}),
278
284
  };
279
285
  if (operMeta?.metadata?.deprecated != null) {
280
286
  opts[webRouter.requestMethod].deprecated =
@@ -320,7 +326,7 @@ let SwaggerExplorer = class SwaggerExplorer {
320
326
  const p = param.metadata;
321
327
  p.schema = this.formatType(param.metadata.schema);
322
328
  if (p.in === 'query' || p.in === 'path' || p.in === 'header') {
323
- parameters.push(p);
329
+ this.upsertOperationParameter(parameters, p);
324
330
  }
325
331
  else if (p.in === 'body') {
326
332
  p.content = p.content ?? {};
@@ -336,13 +342,28 @@ let SwaggerExplorer = class SwaggerExplorer {
336
342
  // if requestBody is already set, skip
337
343
  opts[webRouter.requestMethod].requestBody =
338
344
  opts[webRouter.requestMethod].requestBody ?? {};
339
- opts[webRouter.requestMethod].requestBody.description =
340
- opts[webRouter.requestMethod].requestBody.description ??
341
- p.description;
342
- opts[webRouter.requestMethod].requestBody.content =
343
- opts[webRouter.requestMethod].requestBody.content ?? p.content;
344
- opts[webRouter.requestMethod].requestBody.required =
345
- opts[webRouter.requestMethod].requestBody.required ?? p.required;
345
+ if (requestBodyFromOperation) {
346
+ if (p.description !== undefined) {
347
+ opts[webRouter.requestMethod].requestBody.description =
348
+ p.description;
349
+ }
350
+ if (p.content !== undefined) {
351
+ opts[webRouter.requestMethod].requestBody.content = p.content;
352
+ }
353
+ if (p.required !== undefined) {
354
+ opts[webRouter.requestMethod].requestBody.required = p.required;
355
+ }
356
+ requestBodyFromOperation = false;
357
+ }
358
+ else {
359
+ opts[webRouter.requestMethod].requestBody.description =
360
+ opts[webRouter.requestMethod].requestBody.description ??
361
+ p.description;
362
+ opts[webRouter.requestMethod].requestBody.content =
363
+ opts[webRouter.requestMethod].requestBody.content ?? p.content;
364
+ opts[webRouter.requestMethod].requestBody.required =
365
+ opts[webRouter.requestMethod].requestBody.required ?? p.required;
366
+ }
346
367
  }
347
368
  }
348
369
  for (const arg of args) {
@@ -381,7 +402,7 @@ let SwaggerExplorer = class SwaggerExplorer {
381
402
  schema: schema.properties[pName],
382
403
  required: schema.required?.includes(pName) || false,
383
404
  };
384
- parameters.push(pp);
405
+ this.upsertOperationParameter(parameters, pp);
385
406
  });
386
407
  continue;
387
408
  }
@@ -465,76 +486,23 @@ let SwaggerExplorer = class SwaggerExplorer {
465
486
  // in body 不需要处理
466
487
  continue;
467
488
  }
468
- parameters.push(p);
489
+ this.upsertOperationParameter(parameters, p);
469
490
  }
470
491
  // class header 需要使用 ApiHeader 装饰器
471
492
  if (headers && headers.length) {
472
- headers.forEach(header => parameters.unshift(header));
493
+ headers.forEach(header => this.upsertOperationParameter(parameters, header, true));
473
494
  }
474
495
  // 获取方法上的 @ApiHeader
475
496
  const methodHeaders = metaForMethods.filter(item => item.key === constants_1.DECORATORS.API_HEADERS);
476
497
  if (methodHeaders.length > 0) {
477
- methodHeaders.forEach(item => parameters.unshift(item.metadata));
498
+ methodHeaders.forEach(item => this.upsertOperationParameter(parameters, item.metadata, true));
478
499
  }
479
500
  opts[webRouter.requestMethod].parameters = parameters;
480
501
  const responses = metaForMethods.filter(item => item.key === constants_1.DECORATORS.API_RESPONSE &&
481
502
  item.propertyName === webRouter.method);
482
- const returnResponses = {};
503
+ const returnResponses = operationResponses ?? {};
483
504
  for (const r of responses) {
484
- const resp = r.metadata;
485
- const keys = Object.keys(resp);
486
- for (const k of keys) {
487
- // 这里是引用,赋值可以直接更改
488
- const tt = resp[k];
489
- if (tt.schema) {
490
- // response 的 schema 需要包含在 content 内
491
- tt.content = {
492
- 'application/json': {
493
- schema: this.formatType(tt.schema),
494
- },
495
- };
496
- delete tt.schema;
497
- }
498
- else if (tt.type) {
499
- if (core_1.Types.isClass(tt.type)) {
500
- this.parseClzz(tt.type);
501
- if (tt.isArray) {
502
- tt.content = {
503
- 'application/json': {
504
- schema: {
505
- type: 'array',
506
- items: {
507
- $ref: '#/components/schemas/' + tt.type.name,
508
- },
509
- },
510
- },
511
- };
512
- }
513
- else {
514
- tt.content = {
515
- 'application/json': {
516
- schema: {
517
- $ref: '#/components/schemas/' + tt.type.name,
518
- },
519
- },
520
- };
521
- }
522
- }
523
- else {
524
- tt.content = {
525
- 'text/plain': {
526
- schema: {
527
- type: convertSchemaType(tt.type),
528
- },
529
- },
530
- };
531
- }
532
- }
533
- delete tt.status;
534
- delete tt.type;
535
- delete tt.isArray;
536
- delete tt.format;
537
- }
505
+ const resp = this.normalizeOperationResponses(r.metadata);
538
506
  Object.assign(returnResponses, resp);
539
507
  }
540
508
  if (Object.keys(returnResponses).length > 0) {
@@ -549,6 +517,133 @@ let SwaggerExplorer = class SwaggerExplorer {
549
517
  }
550
518
  paths[url] = opts;
551
519
  }
520
+ upsertOperationParameter(parameters, parameter, prepend = false) {
521
+ const index = parameters.findIndex(item => {
522
+ return item?.name === parameter?.name && item?.in === parameter?.in;
523
+ });
524
+ if (index >= 0) {
525
+ parameters.splice(index, 1);
526
+ }
527
+ if (prepend) {
528
+ parameters.unshift(parameter);
529
+ }
530
+ else {
531
+ parameters.push(parameter);
532
+ }
533
+ }
534
+ cloneOpenAPIValue(value) {
535
+ if (Array.isArray(value)) {
536
+ return value.map(item => this.cloneOpenAPIValue(item));
537
+ }
538
+ if (value && typeof value === 'object') {
539
+ const cloned = {};
540
+ for (const key in value) {
541
+ cloned[key] = this.cloneOpenAPIValue(value[key]);
542
+ }
543
+ return cloned;
544
+ }
545
+ return value;
546
+ }
547
+ normalizeContentSchemas(content) {
548
+ if (!content) {
549
+ return content;
550
+ }
551
+ for (const key in content) {
552
+ if (content[key]?.schema) {
553
+ content[key].schema = this.formatType(content[key].schema);
554
+ }
555
+ }
556
+ return content;
557
+ }
558
+ normalizeOperationParameters(parameters) {
559
+ if (!Array.isArray(parameters)) {
560
+ return [];
561
+ }
562
+ return parameters.map(parameter => {
563
+ const normalized = this.cloneOpenAPIValue(parameter);
564
+ if (normalized?.schema) {
565
+ normalized.schema = this.formatType(normalized.schema);
566
+ }
567
+ if (normalized.content) {
568
+ normalized.content = this.normalizeContentSchemas(normalized.content);
569
+ }
570
+ return normalized;
571
+ });
572
+ }
573
+ normalizeOperationRequestBody(requestBody) {
574
+ if (!requestBody) {
575
+ return undefined;
576
+ }
577
+ const normalized = this.cloneOpenAPIValue(requestBody);
578
+ if (normalized.content) {
579
+ normalized.content = this.normalizeContentSchemas(normalized.content);
580
+ }
581
+ return normalized;
582
+ }
583
+ normalizeOperationResponse(response) {
584
+ const normalized = this.cloneOpenAPIValue(response);
585
+ if (normalized.schema) {
586
+ normalized.content = {
587
+ 'application/json': {
588
+ schema: this.formatType(normalized.schema),
589
+ },
590
+ };
591
+ delete normalized.schema;
592
+ }
593
+ else if (normalized.type) {
594
+ if (core_1.Types.isClass(normalized.type)) {
595
+ this.parseClzz(normalized.type);
596
+ if (normalized.isArray) {
597
+ normalized.content = {
598
+ 'application/json': {
599
+ schema: {
600
+ type: 'array',
601
+ items: {
602
+ $ref: '#/components/schemas/' + normalized.type.name,
603
+ },
604
+ },
605
+ },
606
+ };
607
+ }
608
+ else {
609
+ normalized.content = {
610
+ 'application/json': {
611
+ schema: {
612
+ $ref: '#/components/schemas/' + normalized.type.name,
613
+ },
614
+ },
615
+ };
616
+ }
617
+ }
618
+ else {
619
+ normalized.content = {
620
+ 'text/plain': {
621
+ schema: {
622
+ type: convertSchemaType(normalized.type),
623
+ },
624
+ },
625
+ };
626
+ }
627
+ }
628
+ else if (normalized.content) {
629
+ normalized.content = this.normalizeContentSchemas(normalized.content);
630
+ }
631
+ delete normalized.status;
632
+ delete normalized.type;
633
+ delete normalized.isArray;
634
+ delete normalized.format;
635
+ return normalized;
636
+ }
637
+ normalizeOperationResponses(responses) {
638
+ if (!responses) {
639
+ return undefined;
640
+ }
641
+ const normalizedResponses = this.cloneOpenAPIValue(responses);
642
+ for (const status in normalizedResponses) {
643
+ normalizedResponses[status] = this.normalizeOperationResponse(normalizedResponses[status]);
644
+ }
645
+ return normalizedResponses;
646
+ }
552
647
  getOperationId(controllerKey, webRouter) {
553
648
  return this.operationIdFactory(controllerKey, webRouter);
554
649
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midwayjs/swagger",
3
- "version": "4.0.0-beta.15",
3
+ "version": "4.0.0-beta.17",
4
4
  "description": "Midway Component for Swagger API Documentation",
5
5
  "main": "dist/index.js",
6
6
  "typings": "index.d.ts",
@@ -12,10 +12,10 @@
12
12
  ],
13
13
  "devDependencies": {
14
14
  "@apidevtools/swagger-parser": "11.0.1",
15
- "@midwayjs/core": "^4.0.0-beta.15",
16
- "@midwayjs/koa": "^4.0.0-beta.15",
17
- "@midwayjs/mock": "^4.0.0-beta.15",
18
- "@midwayjs/validate": "^4.0.0-beta.15",
15
+ "@midwayjs/core": "^4.0.0-beta.17",
16
+ "@midwayjs/koa": "^4.0.0-beta.17",
17
+ "@midwayjs/mock": "^4.0.0-beta.17",
18
+ "@midwayjs/validate": "^4.0.0-beta.17",
19
19
  "swagger-ui-dist": "5.18.3"
20
20
  },
21
21
  "author": "Kurten Chan <chinkurten@gmail.com>",
@@ -30,5 +30,5 @@
30
30
  "type": "git",
31
31
  "url": "https://github.com/midwayjs/midway.git"
32
32
  },
33
- "gitHead": "e9b3c1672326bc22fe8658ccbe8afd58b9d75d53"
33
+ "gitHead": "6a20a4fc8c3ddb71dd89f55cf600c34fd817c628"
34
34
  }