@@ -31,4 +31,151 @@ function validateHexSeed(seed: string, length: number): boolean {
31
31
return true ;
32
32
}
33
33
34
- export { validateObject , validateInput , validateHexSeed } ;
34
+ interface ValidationOptions {
35
+ props ?: boolean | string | string [ ] ;
36
+ methods ?: boolean | string | string [ ] ;
37
+ }
38
+
39
+ /*
40
+ * <script lang="ts">
41
+ * import { isLength, isInt } from "class-validator";
42
+ * // Example 1
43
+ * @ValidateMembers ()
44
+ * class User {
45
+ * @isLength (2) name: string
46
+ * @isInt () age: number
47
+ *
48
+ * greeting() {
49
+ * // Some logic
50
+ * }
51
+ * }
52
+ * </script> */
53
+
54
+ /**
55
+ * @description
56
+ * This `ValidateMembers` is a config method which returns back a *classDecrator*
57
+ * Allows to configure which setter/methods should trigger validation for that specific class
58
+ *
59
+ * Example As follow
60
+ * @example
61
+ * ```typescript
62
+ * import { isLength, isInt } from "class-validator";
63
+ *
64
+ * // @ValidateMembers({ props: false, methods: true }) // - disable validation on set props
65
+ * // @ValidateMembers({ props: true, methods: false }) // - disable validation on call methods
66
+ * // @ValidateMembers({ props: 'name', methods: false }) // - validate only on setting 'name' prop
67
+ * // And so on...
68
+ * @ValidateMembers() // = @ValidateMembers({ props: true, methods: true })
69
+ * class User {
70
+ * @isLength (2) name: string
71
+ * @isInt() age: number
72
+ *
73
+ * greeting() {
74
+ * // Some logic
75
+ * }
76
+ * }
77
+ * ```
78
+ *
79
+ *
80
+ *
81
+ * @param options { ValidationOptions | undefined }
82
+ * @returns { ClassDecorator }
83
+ */
84
+ function ValidateMembers ( options ?: ValidationOptions ) : ClassDecorator {
85
+ const _options = _normalizeValidationOptions ( options ) ;
86
+ return ( target : any ) : any => {
87
+ const methods = _getMethods ( target , _options ) ;
88
+ for ( const method of methods ) {
89
+ const fn = target . prototype [ method ] ;
90
+ target . prototype [ method ] = function ( ...args : any [ ] ) : any {
91
+ const errors = validateSync ( this ) ;
92
+ if ( errors . length ) {
93
+ throw errors ;
94
+ }
95
+ return fn . apply ( this , args ) ;
96
+ } ;
97
+ }
98
+
99
+ return class extends target {
100
+ constructor ( ...args : any [ ] ) {
101
+ super ( ...args ) ;
102
+
103
+ const props = _getProps ( this , _options ) ;
104
+ for ( const prop of props ) {
105
+ let _value = this [ prop ] ;
106
+
107
+ Object . defineProperty ( this , prop , {
108
+ configurable : false ,
109
+ enumerable : true ,
110
+ get : ( ) => _value ,
111
+ set ( value ) {
112
+ _value = value ;
113
+ const errors = validateSync ( this ) ;
114
+ for ( const error of errors ) {
115
+ if ( error . property === prop ) {
116
+ throw error ;
117
+ }
118
+ }
119
+ } ,
120
+ } ) ;
121
+ }
122
+ }
123
+ } ;
124
+ } ;
125
+ }
126
+
127
+ function _normalizeValidationOptions ( options ?: ValidationOptions ) : Required < ValidationOptions > {
128
+ return {
129
+ props : options ?. props ?? true ,
130
+ methods : options ?. methods ?? true ,
131
+ } ;
132
+ }
133
+
134
+ function _getProps ( ctor : any , options : Required < ValidationOptions > ) : string [ ] {
135
+ /* This env variable should be used while testing to prevent throw error while setting values */
136
+ if ( process . env . SKIP_PROPS_VALIDATION ) {
137
+ return [ ] ;
138
+ }
139
+
140
+ if ( options . props === true ) {
141
+ return Object . getOwnPropertyNames ( ctor ) ;
142
+ }
143
+
144
+ if ( typeof options . props === "string" ) {
145
+ return [ options . props ] ;
146
+ }
147
+
148
+ if ( Array . isArray ( options . props ) ) {
149
+ return options . props ;
150
+ }
151
+
152
+ return [ ] ;
153
+ }
154
+
155
+ function _getMethods ( ctor : any , options : Required < ValidationOptions > ) : string [ ] {
156
+ /* This env variable should be used to prevent throw error while calling methods if needed */
157
+ if ( process . env . SKIP_METHODS_VALIDATION ) {
158
+ return [ ] ;
159
+ }
160
+
161
+ if ( options . methods === true ) {
162
+ const methods = Object . getOwnPropertyNames ( ctor . prototype ) ;
163
+ const constructorIndex = methods . indexOf ( "constructor" ) ;
164
+ if ( constructorIndex !== - 1 ) {
165
+ methods . splice ( constructorIndex , 1 ) ;
166
+ }
167
+ return methods ;
168
+ }
169
+
170
+ if ( typeof options . methods === "string" ) {
171
+ return [ options . methods ] ;
172
+ }
173
+
174
+ if ( Array . isArray ( options . methods ) ) {
175
+ return options . methods ;
176
+ }
177
+
178
+ return [ ] ;
179
+ }
180
+
181
+ export { validateObject , validateInput , validateHexSeed , type ValidationOptions , ValidateMembers } ;
0 commit comments