@midwayjs/swagger 4.0.0-beta.12 → 4.0.0-beta.14

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.
@@ -14,5 +14,6 @@ exports.swagger = {
14
14
  : render_1.renderSwaggerUIRemote,
15
15
  swaggerUIRenderOptions: {},
16
16
  isGenerateTagForController: true,
17
+ useValidationSchema: true,
17
18
  };
18
19
  //# sourceMappingURL=config.default.js.map
@@ -381,6 +381,12 @@ export interface SwaggerOptions {
381
381
  * Weather to generate the Tag for controller
382
382
  */
383
383
  isGenerateTagForController?: boolean;
384
+ /**
385
+ * Enable inferring schema metadata from @midwayjs/validation DTO rules.
386
+ * When enabled, inferred metadata only fills fields that are not explicitly
387
+ * defined by @ApiProperty.
388
+ */
389
+ useValidationSchema?: boolean;
384
390
  }
385
391
  export interface MixDecoratorMetadata {
386
392
  key: string;
@@ -52,6 +52,11 @@ export declare class SwaggerExplorer {
52
52
  * @param clzz
53
53
  */
54
54
  protected parseClzz(clzz: Type): any;
55
+ private mergePropertyMetadata;
56
+ protected getValidationSchemaHelper(): any;
57
+ private inferValidationProperties;
58
+ private inferValidateProperties;
59
+ protected hasClassValidatorMetadata(clzz: Type): boolean;
55
60
  /**
56
61
  * 授权验证
57
62
  * @param opts
@@ -15,6 +15,8 @@ const constants_1 = require("./constants");
15
15
  const documentBuilder_1 = require("./documentBuilder");
16
16
  const _1 = require(".");
17
17
  const enum_utils_1 = require("./common/enum.utils");
18
+ const VALIDATION_RULES_KEY = 'validation:rules';
19
+ const VALIDATE_RULES_KEY = 'common:rules';
18
20
  let SwaggerExplorer = class SwaggerExplorer {
19
21
  swaggerConfig = {};
20
22
  documentBuilder = new documentBuilder_1.DocumentBuilder();
@@ -774,6 +776,18 @@ let SwaggerExplorer = class SwaggerExplorer {
774
776
  for (const key in props) {
775
777
  props[key] = props[key][props[key].length - 1];
776
778
  }
779
+ if (this.swaggerConfig?.useValidationSchema) {
780
+ const inferredProps = this.inferValidationProperties(clzz);
781
+ for (const key of Object.keys(inferredProps)) {
782
+ if (!props[key]) {
783
+ props[key] = inferredProps[key];
784
+ continue;
785
+ }
786
+ const existingMeta = props[key].metadata || {};
787
+ const inferredMeta = inferredProps[key].metadata || {};
788
+ props[key].metadata = this.mergePropertyMetadata(existingMeta, inferredMeta);
789
+ }
790
+ }
777
791
  const tt = {
778
792
  type: 'object',
779
793
  properties: {},
@@ -845,6 +859,120 @@ let SwaggerExplorer = class SwaggerExplorer {
845
859
  // just for test
846
860
  return tt;
847
861
  }
862
+ mergePropertyMetadata(swaggerMetadata, inferredMetadata) {
863
+ const mergedMetadata = { ...swaggerMetadata };
864
+ const fillableKeys = [
865
+ 'type',
866
+ 'items',
867
+ 'format',
868
+ 'enum',
869
+ '$ref',
870
+ 'pattern',
871
+ 'default',
872
+ ];
873
+ for (const key of fillableKeys) {
874
+ if (mergedMetadata[key] === undefined &&
875
+ inferredMetadata[key] !== undefined) {
876
+ mergedMetadata[key] = inferredMetadata[key];
877
+ }
878
+ }
879
+ if (mergedMetadata.required === undefined &&
880
+ inferredMetadata.required !== undefined) {
881
+ mergedMetadata.required = inferredMetadata.required;
882
+ }
883
+ return mergedMetadata;
884
+ }
885
+ getValidationSchemaHelper() {
886
+ try {
887
+ const validationPkg = (0, core_1.safeRequire)('@midwayjs/validation');
888
+ const registry = validationPkg?.registry;
889
+ if (!registry?.getDefaultValidator) {
890
+ return;
891
+ }
892
+ return registry.getDefaultValidator()?.schemaHelper;
893
+ }
894
+ catch {
895
+ // When @midwayjs/validate and @midwayjs/validation are both installed in
896
+ // the same process, loading validation package may throw duplicated error
897
+ // group exceptions. Swagger should degrade gracefully in this case.
898
+ return;
899
+ }
900
+ }
901
+ inferValidationProperties(clzz) {
902
+ const ruleProps = core_1.MetadataManager.getPropertiesWithMetadata(VALIDATION_RULES_KEY, clzz) ||
903
+ {};
904
+ const hasRuleMetadata = Object.keys(ruleProps).length > 0;
905
+ const validateRuleProps = core_1.MetadataManager.getPropertiesWithMetadata(VALIDATE_RULES_KEY, clzz) || {};
906
+ const hasValidateRuleMetadata = Object.keys(validateRuleProps).length > 0;
907
+ const hasClassValidatorMetadata = this.hasClassValidatorMetadata(clzz);
908
+ if (!hasRuleMetadata &&
909
+ !hasClassValidatorMetadata &&
910
+ !hasValidateRuleMetadata) {
911
+ return {};
912
+ }
913
+ const inferredProps = {};
914
+ if (hasRuleMetadata || hasClassValidatorMetadata) {
915
+ const schemaHelper = this.getValidationSchemaHelper();
916
+ if (schemaHelper &&
917
+ typeof schemaHelper.getSwaggerPropertyKeys === 'function' &&
918
+ typeof schemaHelper.getSwaggerPropertyMetadata === 'function') {
919
+ const propertyKeys = schemaHelper.getSwaggerPropertyKeys(clzz) || [];
920
+ for (const key of propertyKeys) {
921
+ const metadata = schemaHelper.getSwaggerPropertyMetadata(clzz, key);
922
+ if (metadata) {
923
+ inferredProps[key] = {
924
+ metadata,
925
+ };
926
+ }
927
+ }
928
+ }
929
+ }
930
+ if (hasValidateRuleMetadata) {
931
+ const validateInferredProps = this.inferValidateProperties(clzz);
932
+ for (const [key, value] of Object.entries(validateInferredProps)) {
933
+ if (!inferredProps[key]) {
934
+ inferredProps[key] = value;
935
+ continue;
936
+ }
937
+ const mergedMetadata = this.mergePropertyMetadata(inferredProps[key].metadata || {}, value.metadata || {});
938
+ inferredProps[key] = {
939
+ metadata: mergedMetadata,
940
+ };
941
+ }
942
+ }
943
+ return inferredProps;
944
+ }
945
+ inferValidateProperties(clzz) {
946
+ const inferredProps = {};
947
+ const ruleProps = core_1.MetadataManager.getPropertiesWithMetadata(VALIDATE_RULES_KEY, clzz) || {};
948
+ for (const key of Object.keys(ruleProps)) {
949
+ let schema = ruleProps[key];
950
+ if (typeof schema === 'function') {
951
+ schema = schema();
952
+ }
953
+ const metadata = inferJoiPropertyMetadata(schema);
954
+ if (metadata) {
955
+ inferredProps[key] = {
956
+ metadata,
957
+ };
958
+ }
959
+ }
960
+ return inferredProps;
961
+ }
962
+ hasClassValidatorMetadata(clzz) {
963
+ try {
964
+ const classValidator = (0, core_1.safeRequire)('class-validator-multi-lang-lite');
965
+ const storage = classValidator?.getMetadataStorage?.();
966
+ if (!storage?.getTargetValidationMetadatas) {
967
+ return false;
968
+ }
969
+ const metadatas = storage.getTargetValidationMetadatas(clzz, '', false, false) || [];
970
+ return metadatas.length > 0;
971
+ }
972
+ catch {
973
+ return false;
974
+ }
975
+ }
848
976
  /**
849
977
  * 授权验证
850
978
  * @param opts
@@ -962,6 +1090,47 @@ function convertSchemaType(value) {
962
1090
  return 'object';
963
1091
  }
964
1092
  }
1093
+ function inferJoiPropertyMetadata(schema) {
1094
+ if (!schema || typeof schema.describe !== 'function') {
1095
+ return null;
1096
+ }
1097
+ const desc = schema.describe();
1098
+ if (!desc || typeof desc !== 'object') {
1099
+ return null;
1100
+ }
1101
+ const typeMap = {
1102
+ string: 'string',
1103
+ number: 'number',
1104
+ boolean: 'boolean',
1105
+ array: 'array',
1106
+ date: 'string',
1107
+ object: 'object',
1108
+ };
1109
+ const metadata = {
1110
+ type: typeMap[desc.type] || 'object',
1111
+ };
1112
+ if (desc?.flags?.presence === 'required') {
1113
+ metadata.required = true;
1114
+ }
1115
+ else if (desc?.flags?.presence === 'optional' || !desc?.flags?.presence) {
1116
+ metadata.required = false;
1117
+ }
1118
+ if (desc.type === 'array' && Array.isArray(desc.items)) {
1119
+ metadata.items = {
1120
+ type: typeMap[desc.items[0]?.type] || 'object',
1121
+ };
1122
+ }
1123
+ if (desc.type === 'date') {
1124
+ metadata.format = 'date-time';
1125
+ }
1126
+ if (Array.isArray(desc.allow)) {
1127
+ const enumValues = desc.allow.filter(item => item !== '' && item !== null && item !== undefined);
1128
+ if (enumValues.length > 0) {
1129
+ metadata.enum = enumValues;
1130
+ }
1131
+ }
1132
+ return metadata;
1133
+ }
965
1134
  function getNotEmptyValue(...args) {
966
1135
  for (const arg of args) {
967
1136
  if (arg) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midwayjs/swagger",
3
- "version": "4.0.0-beta.12",
3
+ "version": "4.0.0-beta.14",
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.12",
16
- "@midwayjs/koa": "^4.0.0-beta.12",
17
- "@midwayjs/mock": "^4.0.0-beta.12",
18
- "@midwayjs/validate": "^4.0.0-beta.12",
15
+ "@midwayjs/core": "^4.0.0-beta.14",
16
+ "@midwayjs/koa": "^4.0.0-beta.14",
17
+ "@midwayjs/mock": "^4.0.0-beta.14",
18
+ "@midwayjs/validate": "^4.0.0-beta.14",
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": "1c48179b7c827ba8ec9351e9b6c36ec1726d3e11"
33
+ "gitHead": "52335cb1baa00754fffd48487f6153a3b2f1f50a"
34
34
  }