Repository: nestjs/swagger
Pull request #2742 (2023-12-06 21:35:38)

feat: refactoring and extending the support of validation property assignments 
## PR Checklist
Please check if your PR fulfills the following requirements:

- [x] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)


## PR Type
What kind of change does this PR introduce?

<!-- Please check the one that applies to this PR using "x". -->
- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, local variables)
- [x] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Other... Please describe:

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
Only a limited number of decorators are supported, and are listed directly in the model-class visitor.
Issue Number: N/A

## What is the new behavior?

1- Extended it to support the following decorators:  

- Contains 
- ArrayNotEmpty 
- ArrayUnique 
- ArrayMaxSize 
- ArrayMinSize 
- IsDivisibleBy 
- IsEmail 
- IsMobilePhone 
- IsJSON 
- IsUrl 
- IsUuid 
- IsBase64 
- IsCreditCard 
- IsCurrency 
- IsAscii 
- IsHexColor 
- IsHexadecimal, 

2- After adding these decorators the file became too long and hard to read, that's why I thought of refactoring it to separate the concerns and isolate the facts ( decorators to properties mappings ) so it's not flooding the visitor, and easier to read and maintain. 

If you think this refactor is unnecessary or anything could be improved, let me know.

## Does this PR introduce a breaking change?
- [ ] Yes
- [x] No

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->


## Other information




Patch:
diff --git a/lib/plugin/visitors/model-class.visitor.ts b/lib/plugin/visitors/model-class.visitor.ts
index 1ca3f4ea..67b52767 100644
--- a/lib/plugin/visitors/model-class.visitor.ts
+++ b/lib/plugin/visitors/model-class.visitor.ts
@@ -28,6 +28,10 @@ import {
 } from '../utils/plugin-utils';
 import { typeReferenceToIdentifier } from '../utils/type-reference-to-identifier.util';
 import { AbstractFileVisitor } from './abstract.visitor';
+import {
+  decoratorsProperties,
+  decoratorsPropertiesMappingType
+} from '../../services/decorators-properties';
 
 type ClassMetadata = Record<string, ts.ObjectLiteralExpression>;
 
@@ -560,66 +564,73 @@ export class ModelClassVisitor extends AbstractFileVisitor {
         options
       );
     }
-    this.addPropertyByValidationDecorator(
-      factory,
-      'Min',
-      'minimum',
-      decorators,
-      assignments,
-      options
-    );
-    this.addPropertyByValidationDecorator(
-      factory,
-      'Max',
-      'maximum',
-      decorators,
-      assignments,
-      options
-    );
-    this.addPropertyByValidationDecorator(
-      factory,
-      'MinLength',
-      'minLength',
-      decorators,
-      assignments,
-      options
-    );
-    this.addPropertyByValidationDecorator(
-      factory,
-      'MaxLength',
-      'maxLength',
-      decorators,
-      assignments,
-      options
-    );
-    this.addPropertiesByValidationDecorator(
-      factory,
-      'IsPositive',
-      decorators,
-      assignments,
-      () => {
-        return [
-          factory.createPropertyAssignment(
-            'minimum',
-            createPrimitiveLiteral(factory, 1)
-          )
-        ];
-      }
-    );
-    this.addPropertiesByValidationDecorator(
-      factory,
-      'IsNegative',
-      decorators,
-      assignments,
-      () => {
-        return [
-          factory.createPropertyAssignment(
-            'maximum',
-            createPrimitiveLiteral(factory, -1)
-          )
-        ];
+
+    decoratorsProperties.forEach((decoratorProperty) => {
+      if (
+        decoratorProperty.mappingType === decoratorsPropertiesMappingType.DIRECT
+      ) {
+        this.addPropertyByValidationDecorator(
+          factory,
+          decoratorProperty.decorator,
+          decoratorProperty.property,
+          decorators,
+          assignments,
+          options
+        );
+      } else if (
+        decoratorProperty.mappingType ===
+        decoratorsPropertiesMappingType.INDIRECT_VALUE
+      ) {
+        this.addPropertiesByValidationDecorator(
+          factory,
+          decoratorProperty.decorator,
+          decorators,
+          assignments,
+          () => {
+            return [
+              factory.createPropertyAssignment(
+                decoratorProperty.property,
+                createPrimitiveLiteral(factory, decoratorProperty.value)
+              )
+            ];
+          }
+        );
+      } else if (
+        decoratorProperty.mappingType ===
+        decoratorsPropertiesMappingType.INDIRECT_ARGUMENT
+      ) {
+        this.addPropertiesByValidationDecorator(
+          factory,
+          decoratorProperty.decorator,
+          decorators,
+          assignments,
+          (decoratorRef: ts.Decorator) => {
+            const decoratorArguments = getDecoratorArguments(decoratorRef);
+            const result = [];
+
+            const argumentValue = head(decoratorArguments);
+            if (!canReferenceNode(argumentValue, options)) {
+              return result;
+            }
+
+            const clonedArgumentValue = this.clonePrimitiveLiteral(
+              factory,
+              argumentValue
+            );
+            if (clonedArgumentValue) {
+              result.push(
+                factory.createPropertyAssignment(
+                  decoratorProperty.property,
+                  clonedArgumentValue
+                )
+              );
+            }
+            return result;
+          }
+        );
       }
-    );
+    });
+
     this.addPropertiesByValidationDecorator(
       factory,
       'Length',
diff --git a/lib/services/decorators-properties.ts b/lib/services/decorators-properties.ts
new file mode 100644
index 00000000..d9c93886
--- /dev/null
+++ b/lib/services/decorators-properties.ts
@@ -0,0 +1,145 @@
+export enum decoratorsPropertiesMappingType {
+  DIRECT,
+  INDIRECT_VALUE,
+  INDIRECT_ARGUMENT
+}
+export const decoratorsProperties = [
+  {
+    mappingType: decoratorsPropertiesMappingType.DIRECT,
+    decorator: 'Min',
+    property: 'minimum',
+    value: undefined
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.DIRECT,
+    decorator: 'Max',
+    property: 'maximum',
+    value: undefined
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.DIRECT,
+    decorator: 'MinLength',
+    property: 'minLength',
+    value: undefined
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.DIRECT,
+    decorator: 'MaxLength',
+    property: 'maxLength',
+    value: undefined
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'ArrayNotEmpty',
+    property: 'minItems',
+    value: 1
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsPositive',
+    property: 'minimum',
+    value: 1
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsNegative',
+    property: 'maximum',
+    value: -1
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'ArrayUnique',
+    property: 'uniqueItems',
+    value: true
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsBase64',
+    property: 'format',
+    value: 'base64'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsCreditCard',
+    property: 'format',
+    value: 'credit-card'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsCurrency',
+    property: 'format',
+    value: 'currency'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsEmail',
+    property: 'format',
+    value: 'email'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsJSON',
+    property: 'format',
+    value: 'json'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsUrl',
+    property: 'format',
+    value: 'uri'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsUuid',
+    property: 'format',
+    value: 'uuid'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsMobilePhone',
+    property: 'format',
+    value: 'mobile-phone'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsAscii',
+    property: 'pattern',
+    value: '^[\\x00-\\x7F]+$'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsHexColor',
+    property: 'pattern',
+    value: '^#?([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_VALUE,
+    decorator: 'IsHexadecimal',
+    property: 'pattern',
+    value: '^(0x|0h)?[0-9A-F]+$'
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_ARGUMENT,
+    decorator: 'ArrayMinSize',
+    property: 'minItems',
+    value: undefined
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_ARGUMENT,
+    decorator: 'ArrayMaxSize',
+    property: 'maxItems',
+    value: undefined
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_ARGUMENT,
+    decorator: 'IsDivisibleBy',
+    property: 'multipleOf',
+    value: undefined
+  },
+  {
+    mappingType: decoratorsPropertiesMappingType.INDIRECT_ARGUMENT,
+    decorator: 'Contains',
+    property: 'pattern',
+    value: undefined
+  }
+];
diff --git a/package-lock.json b/package-lock.json
index 7831f533..7f585960 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,12 +23,12 @@
         "@nestjs/core": "10.2.10",
         "@nestjs/platform-express": "10.2.10",
         "@nestjs/platform-fastify": "10.2.10",
-        "@types/jest": "29.5.11",
+        "@types/jest": "29.5.10",
         "@types/js-yaml": "4.0.9",
         "@types/lodash": "4.14.202",
         "@types/node": "20.10.3",
-        "@typescript-eslint/eslint-plugin": "6.13.2",
-        "@typescript-eslint/parser": "6.13.2",
+        "@typescript-eslint/eslint-plugin": "6.13.1",
+        "@typescript-eslint/parser": "6.13.1",
         "class-transformer": "0.5.1",
         "class-validator": "0.14.0",
         "eslint": "8.55.0",
@@ -2717,9 +2717,9 @@
       }
     },
     "node_modules/@types/jest": {
-      "version": "29.5.11",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz",
-      "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==",
+      "version": "29.5.10",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.10.tgz",
+      "integrity": "sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ==",
       "dev": true,
       "dependencies": {
         "expect": "^29.0.0",
@@ -2805,16 +2805,16 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz",
-      "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.1.tgz",
+      "integrity": "sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "6.13.2",
-        "@typescript-eslint/type-utils": "6.13.2",
-        "@typescript-eslint/utils": "6.13.2",
-        "@typescript-eslint/visitor-keys": "6.13.2",
+        "@typescript-eslint/scope-manager": "6.13.1",
+        "@typescript-eslint/type-utils": "6.13.1",
+        "@typescript-eslint/utils": "6.13.1",
+        "@typescript-eslint/visitor-keys": "6.13.1",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.2.4",
@@ -2840,15 +2840,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz",
-      "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz",
+      "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "6.13.2",
-        "@typescript-eslint/types": "6.13.2",
-        "@typescript-eslint/typescript-estree": "6.13.2",
-        "@typescript-eslint/visitor-keys": "6.13.2",
+        "@typescript-eslint/scope-manager": "6.13.1",
+        "@typescript-eslint/types": "6.13.1",
+        "@typescript-eslint/typescript-estree": "6.13.1",
+        "@typescript-eslint/visitor-keys": "6.13.1",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2868,13 +2868,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz",
-      "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz",
+      "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.13.2",
-        "@typescript-eslint/visitor-keys": "6.13.2"
+        "@typescript-eslint/types": "6.13.1",
+        "@typescript-eslint/visitor-keys": "6.13.1"
       },
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -2885,13 +2885,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz",
-      "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz",
+      "integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "6.13.2",
-        "@typescript-eslint/utils": "6.13.2",
+        "@typescript-eslint/typescript-estree": "6.13.1",
+        "@typescript-eslint/utils": "6.13.1",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.0.1"
       },
@@ -2912,9 +2912,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz",
-      "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz",
+      "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==",
       "dev": true,
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -2925,13 +2925,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz",
-      "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz",
+      "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.13.2",
-        "@typescript-eslint/visitor-keys": "6.13.2",
+        "@typescript-eslint/types": "6.13.1",
+        "@typescript-eslint/visitor-keys": "6.13.1",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -2952,17 +2952,17 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz",
-      "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz",
+      "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.12",
         "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "6.13.2",
-        "@typescript-eslint/types": "6.13.2",
-        "@typescript-eslint/typescript-estree": "6.13.2",
+        "@typescript-eslint/scope-manager": "6.13.1",
+        "@typescript-eslint/types": "6.13.1",
+        "@typescript-eslint/typescript-estree": "6.13.1",
         "semver": "^7.5.4"
       },
       "engines": {
@@ -2977,12 +2977,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz",
-      "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz",
+      "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.13.2",
+        "@typescript-eslint/types": "6.13.1",
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
@@ -15231,9 +15231,9 @@
       }
     },
     "@types/jest": {
-      "version": "29.5.11",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz",
-      "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==",
+      "version": "29.5.10",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.10.tgz",
+      "integrity": "sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ==",
       "dev": true,
       "requires": {
         "expect": "^29.0.0",
@@ -15319,16 +15319,16 @@
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz",
-      "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.1.tgz",
+      "integrity": "sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==",
       "dev": true,
       "requires": {
         "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "6.13.2",
-        "@typescript-eslint/type-utils": "6.13.2",
-        "@typescript-eslint/utils": "6.13.2",
-        "@typescript-eslint/visitor-keys": "6.13.2",
+        "@typescript-eslint/scope-manager": "6.13.1",
+        "@typescript-eslint/type-utils": "6.13.1",
+        "@typescript-eslint/utils": "6.13.1",
+        "@typescript-eslint/visitor-keys": "6.13.1",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.2.4",
@@ -15338,54 +15338,54 @@
       }
     },
     "@typescript-eslint/parser": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz",
-      "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz",
+      "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/scope-manager": "6.13.2",
-        "@typescript-eslint/types": "6.13.2",
-        "@typescript-eslint/typescript-estree": "6.13.2",
-        "@typescript-eslint/visitor-keys": "6.13.2",
+        "@typescript-eslint/scope-manager": "6.13.1",
+        "@typescript-eslint/types": "6.13.1",
+        "@typescript-eslint/typescript-estree": "6.13.1",
+        "@typescript-eslint/visitor-keys": "6.13.1",
         "debug": "^4.3.4"
       }
     },
     "@typescript-eslint/scope-manager": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz",
-      "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz",
+      "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "6.13.2",
-        "@typescript-eslint/visitor-keys": "6.13.2"
+        "@typescript-eslint/types": "6.13.1",
+        "@typescript-eslint/visitor-keys": "6.13.1"
       }
     },
     "@typescript-eslint/type-utils": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz",
-      "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz",
+      "integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/typescript-estree": "6.13.2",
-        "@typescript-eslint/utils": "6.13.2",
+        "@typescript-eslint/typescript-estree": "6.13.1",
+        "@typescript-eslint/utils": "6.13.1",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.0.1"
       }
     },
     "@typescript-eslint/types": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz",
-      "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz",
+      "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==",
       "dev": true
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz",
-      "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz",
+      "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "6.13.2",
-        "@typescript-eslint/visitor-keys": "6.13.2",
+        "@typescript-eslint/types": "6.13.1",
+        "@typescript-eslint/visitor-keys": "6.13.1",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -15394,27 +15394,27 @@
       }
     },
     "@typescript-eslint/utils": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz",
-      "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz",
+      "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==",
       "dev": true,
       "requires": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.12",
         "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "6.13.2",
-        "@typescript-eslint/types": "6.13.2",
-        "@typescript-eslint/typescript-estree": "6.13.2",
+        "@typescript-eslint/scope-manager": "6.13.1",
+        "@typescript-eslint/types": "6.13.1",
+        "@typescript-eslint/typescript-estree": "6.13.1",
         "semver": "^7.5.4"
       }
     },
     "@typescript-eslint/visitor-keys": {
-      "version": "6.13.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz",
-      "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==",
+      "version": "6.13.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz",
+      "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "6.13.2",
+        "@typescript-eslint/types": "6.13.1",
         "eslint-visitor-keys": "^3.4.1"
       }
     },
diff --git a/package.json b/package.json
index 8ec366c8..8d6ee57a 100644
--- a/package.json
+++ b/package.json
@@ -38,12 +38,12 @@
     "@nestjs/core": "10.2.10",
     "@nestjs/platform-express": "10.2.10",
     "@nestjs/platform-fastify": "10.2.10",
-    "@types/jest": "29.5.11",
+    "@types/jest": "29.5.10",
     "@types/js-yaml": "4.0.9",
     "@types/lodash": "4.14.202",
     "@types/node": "20.10.3",
-    "@typescript-eslint/eslint-plugin": "6.13.2",
-    "@typescript-eslint/parser": "6.13.2",
+    "@typescript-eslint/eslint-plugin": "6.13.1",
+    "@typescript-eslint/parser": "6.13.1",
     "class-transformer": "0.5.1",
     "class-validator": "0.14.0",
     "eslint": "8.55.0",


Test patch:
diff --git a/test/plugin/fixtures/create-cat.dto.ts b/test/plugin/fixtures/create-cat.dto.ts
index 60524665..64831572 100644
--- a/test/plugin/fixtures/create-cat.dto.ts
+++ b/test/plugin/fixtures/create-cat.dto.ts
@@ -35,6 +35,38 @@ export class CreateCatDto {
   lengthMin: string;
   @Length(3, 5)
   lengthMinMax: string;
+  @ArrayNotEmpty()
+  @ArrayUnique()
+  @ArrayMaxSize(10)
+  names: string[];
+  @ArrayMinSize(1)
+  employees: string[];
+  @IsDivisibleBy(2)
+  nominator: string;
+  @IsBase64()
+  encodedInfo: string;
+  @IsCreditCard()
+  creditCard: string;
+  @IsCurrency()
+  currency: string;
+  @IsEmail()
+  email: string;
+  @IsJSON()
+  response: Record<string, any>;
+  @IsUrl()
+  githubAccount: string;
+  @IsUuid()
+  transactionId: string;
+  @IsMobilePhone()
+  phoneNumber: string;
+  @IsAscii()
+  char: string;
+  @IsHexColor()
+  color: string;
+  @IsHexadecimal()
+  hex: string;
+  @Contains('log_')
+  searchBy: string;
   tags: string[];
   status: Status = Status.ENABLED;
   status2?: Status;
@@ -85,7 +117,7 @@ export class CreateCatDto {
         this.status = Status.ENABLED;
     }
     static _OPENAPI_METADATA_FACTORY() {
-        return { isIn: { required: true, type: () => String, enum: ['a', 'b'] }, pattern: { required: true, type: () => String, pattern: "/^[+]?abc$/" }, name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3, minimum: 0, maximum: 10 }, positive: { required: true, type: () => Number, default: 5, minimum: 1 }, negative: { required: true, type: () => Number, default: -1, maximum: -1 }, lengthMin: { required: true, type: () => String, minLength: 2 }, lengthMinMax: { required: true, type: () => String, minLength: 3, maxLength: 5 }, tags: { required: true, type: () => [String] }, status: { required: true, default: Status.ENABLED, enum: Status }, status2: { required: false, enum: Status }, statusArr: { required: false, enum: Status, isArray: true }, oneValueEnum: { required: false, enum: OneValueEnum }, oneValueEnumArr: { required: false, enum: OneValueEnum }, breed: { required: false, type: () => String, title: "this is breed im comment" }, nodes: { required: true, type: () => [Object] }, optionalBoolean: { required: false, type: () => Boolean }, date: { required: true, type: () => Date }, twoDimensionPrimitives: { required: true, type: () => [[String]] }, twoDimensionNodes: { required: true, type: () => [[OtherNode]] } };
+        return { isIn: { required: true, type: () => String, enum: ['a', 'b'] }, pattern: { required: true, type: () => String, pattern: "/^[+]?abc$/" }, name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3, minimum: 0, maximum: 10 }, positive: { required: true, type: () => Number, default: 5, minimum: 1 }, negative: { required: true, type: () => Number, default: -1, maximum: -1 }, lengthMin: { required: true, type: () => String, minLength: 2 }, lengthMinMax: { required: true, type: () => String, minLength: 3, maxLength: 5 }, names: { required: true, type: () => [String], minItems: 1, uniqueItems: true, maxItems: 10 }, employees: { required: true, type: () => [String], minItems: 1 }, nominator: { required: true, type: () => String, multipleOf: 2 }, encodedInfo: { required: true, type: () => String, format: "base64" }, creditCard: { required: true, type: () => String, format: "credit-card" }, currency: { required: true, type: () => String, format: "currency" }, email: { required: true, type: () => String, format: "email" }, response: { required: true, type: () => Object, format: "json" }, githubAccount: { required: true, type: () => String, format: "uri" }, transactionId: { required: true, type: () => String, format: "uuid" }, phoneNumber: { required: true, type: () => String, format: "mobile-phone" }, char: { required: true, type: () => String, pattern: "^[\\\\x00-\\\\x7F]+$" }, color: { required: true, type: () => String, pattern: "^#?([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$" }, hex: { required: true, type: () => String, pattern: "^(0x|0h)?[0-9A-F]+$" }, searchBy: { required: true, type: () => String, pattern: "log_" }, tags: { required: true, type: () => [String] }, status: { required: true, default: Status.ENABLED, enum: Status }, status2: { required: false, enum: Status }, statusArr: { required: false, enum: Status, isArray: true }, oneValueEnum: { required: false, enum: OneValueEnum }, oneValueEnumArr: { required: false, enum: OneValueEnum }, breed: { required: false, type: () => String, title: "this is breed im comment" }, nodes: { required: true, type: () => [Object] }, optionalBoolean: { required: false, type: () => Boolean }, date: { required: true, type: () => Date }, twoDimensionPrimitives: { required: true, type: () => [[String]] }, twoDimensionNodes: { required: true, type: () => [[OtherNode]] } };
     }
 }
 __decorate([
@@ -110,6 +142,53 @@ __decorate([
 __decorate([
     Length(3, 5)
 ], CreateCatDto.prototype, \"lengthMinMax\", void 0);
+__decorate([
+    ArrayNotEmpty(),
+    ArrayUnique(),
+    ArrayMaxSize(10)
+], CreateCatDto.prototype, \"names\", void 0);
+__decorate([
+    ArrayMinSize(1)
+], CreateCatDto.prototype, \"employees\", void 0);
+__decorate([
+    IsDivisibleBy(2)
+], CreateCatDto.prototype, \"nominator\", void 0);
+__decorate([
+    IsBase64()
+], CreateCatDto.prototype, "encodedInfo", void 0);
+__decorate([
+    IsCreditCard()
+], CreateCatDto.prototype, "creditCard", void 0);
+__decorate([
+    IsCurrency()
+], CreateCatDto.prototype, "currency", void 0);
+__decorate([
+    IsEmail()
+], CreateCatDto.prototype, "email", void 0);
+__decorate([
+    IsJSON()
+], CreateCatDto.prototype, "response", void 0);
+__decorate([
+    IsUrl()
+], CreateCatDto.prototype, "githubAccount", void 0);
+__decorate([
+    IsUuid()
+], CreateCatDto.prototype, "transactionId", void 0);
+__decorate([
+    IsMobilePhone()
+], CreateCatDto.prototype, "phoneNumber", void 0);
+__decorate([
+    IsAscii()
+], CreateCatDto.prototype, \"char\", void 0);
+__decorate([
+    IsHexColor()
+], CreateCatDto.prototype, \"color\", void 0);
+__decorate([
+    IsHexadecimal()
+], CreateCatDto.prototype, \"hex\", void 0);
+__decorate([
+    Contains('log_')
+], CreateCatDto.prototype, \"searchBy\", void 0);
 __decorate([
     ApiProperty({ description: "this is breed", type: String }),
     IsString()