safeshield 1.2.2__py3-none-any.whl → 1.4.2__py3-none-any.whl
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.
- {safeshield-1.2.2.dist-info → safeshield-1.4.2.dist-info}/METADATA +9 -1
- safeshield-1.4.2.dist-info/RECORD +31 -0
- validator/core/validator.py +10 -10
- validator/factory.py +4 -4
- validator/rules/__init__.py +6 -8
- validator/rules/array.py +27 -34
- validator/rules/base.py +32 -8
- validator/rules/basic.py +105 -10
- validator/rules/boolean.py +157 -0
- validator/rules/comparison.py +130 -40
- validator/rules/date.py +36 -20
- validator/rules/files.py +179 -67
- validator/rules/format.py +122 -31
- validator/rules/numeric.py +188 -0
- validator/rules/string.py +71 -19
- validator/rules/utilities.py +233 -133
- validator/services/rule_conflict.py +2 -2
- validator/services/rule_error_handler.py +221 -24
- validator/services/rule_preparer.py +12 -25
- safeshield-1.2.2.dist-info/RECORD +0 -31
- validator/rules/conditional.py +0 -332
- validator/rules/type.py +0 -42
- {safeshield-1.2.2.dist-info → safeshield-1.4.2.dist-info}/LICENSE +0 -0
- {safeshield-1.2.2.dist-info → safeshield-1.4.2.dist-info}/WHEEL +0 -0
- {safeshield-1.2.2.dist-info → safeshield-1.4.2.dist-info}/top_level.txt +0 -0
validator/rules/comparison.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from .base import
|
|
2
|
-
from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
|
|
1
|
+
from .base import Rule
|
|
2
|
+
from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type, Callable
|
|
3
3
|
|
|
4
4
|
# =============================================
|
|
5
5
|
# COMPARISON VALIDATION RULES
|
|
6
6
|
# =============================================
|
|
7
7
|
|
|
8
|
-
class MinRule(
|
|
8
|
+
class MinRule(Rule):
|
|
9
9
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
10
10
|
if not params:
|
|
11
11
|
return False
|
|
@@ -27,9 +27,9 @@ class MinRule(ValidationRule):
|
|
|
27
27
|
return False
|
|
28
28
|
|
|
29
29
|
def message(self, field: str, params: List[str]) -> str:
|
|
30
|
-
return f"The :
|
|
30
|
+
return f"The :attribute must be at least {params[0]}."
|
|
31
31
|
|
|
32
|
-
class MaxRule(
|
|
32
|
+
class MaxRule(Rule):
|
|
33
33
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
34
34
|
if not params or len(params) < 1:
|
|
35
35
|
return False
|
|
@@ -85,15 +85,15 @@ class MaxRule(ValidationRule):
|
|
|
85
85
|
def message(self, field: str, params: List[str]) -> str:
|
|
86
86
|
value = self.get_field_value(field)
|
|
87
87
|
if value is None:
|
|
88
|
-
return f"The
|
|
88
|
+
return f"The :attribute must not exceed {params[0]}"
|
|
89
89
|
|
|
90
90
|
# Check all possible file size attributes
|
|
91
91
|
file_attrs = ['content_length', 'size', 'fileno']
|
|
92
92
|
if any(hasattr(value, attr) for attr in file_attrs):
|
|
93
|
-
return f"File
|
|
94
|
-
return f"The
|
|
93
|
+
return f"File :attribute must not exceed {params[0]} bytes"
|
|
94
|
+
return f"The :attribute must not exceed {params[0]}"
|
|
95
95
|
|
|
96
|
-
class BetweenRule(
|
|
96
|
+
class BetweenRule(Rule):
|
|
97
97
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
98
98
|
if len(params) != 2:
|
|
99
99
|
return False
|
|
@@ -136,10 +136,10 @@ class BetweenRule(ValidationRule):
|
|
|
136
136
|
def message(self, field: str, params: List[str]) -> str:
|
|
137
137
|
value = self.get_field_value(field)
|
|
138
138
|
if hasattr(value, 'content_length') or hasattr(value, 'size'):
|
|
139
|
-
return f"File
|
|
140
|
-
return f"The
|
|
139
|
+
return f"File :attribute must be between {params[0]} and {params[1]} bytes"
|
|
140
|
+
return f"The :attribute must be between {params[0]} and {params[1]}"
|
|
141
141
|
|
|
142
|
-
class SizeRule(
|
|
142
|
+
class SizeRule(Rule):
|
|
143
143
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
144
144
|
if not params or len(params) < 1:
|
|
145
145
|
return False
|
|
@@ -182,59 +182,149 @@ class SizeRule(ValidationRule):
|
|
|
182
182
|
def message(self, field: str, params: List[str]) -> str:
|
|
183
183
|
value = self.get_field_value(field)
|
|
184
184
|
if value is None:
|
|
185
|
-
return f"The
|
|
185
|
+
return f"The :attribute must be exactly {params[0]}"
|
|
186
186
|
|
|
187
187
|
# Check for file attributes
|
|
188
188
|
file_attrs = ['content_length', 'size', 'fileno']
|
|
189
189
|
if any(hasattr(value, attr) for attr in file_attrs):
|
|
190
|
-
return f"File
|
|
190
|
+
return f"File :attribute must be exactly {params[0]} bytes"
|
|
191
191
|
|
|
192
|
-
return f"The
|
|
192
|
+
return f"The :attribute must be exactly {params[0]}"
|
|
193
193
|
|
|
194
|
-
|
|
194
|
+
|
|
195
|
+
class UniqueRule(Rule):
|
|
195
196
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
196
|
-
if not params or not
|
|
197
|
+
if not params or not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
|
|
197
198
|
return False
|
|
198
199
|
|
|
200
|
+
table = params[0]
|
|
201
|
+
column = field if len(params) == 1 else params[1]
|
|
202
|
+
|
|
199
203
|
try:
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
ignore_id = None
|
|
205
|
+
if len(params) > 2 and params[2].startswith('ignore:'):
|
|
206
|
+
ignore_field = params[2].split(':')[1]
|
|
207
|
+
ignore_id = self.get_field_value(ignore_field)
|
|
208
|
+
return self.validator.db_manager.is_unique(table, column, value, ignore_id)
|
|
209
|
+
except Exception as e:
|
|
210
|
+
print(f"Database error in UniqueRule: {e}")
|
|
202
211
|
return False
|
|
203
|
-
|
|
204
|
-
return value.isdigit() and len(value) == digits
|
|
205
212
|
|
|
206
213
|
def message(self, field: str, params: List[str]) -> str:
|
|
207
|
-
return f"The :
|
|
214
|
+
return f"The :attribute has already been taken."
|
|
208
215
|
|
|
209
|
-
class
|
|
216
|
+
class ExistsRule(Rule):
|
|
210
217
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
211
|
-
if
|
|
218
|
+
if not params or not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
|
|
212
219
|
return False
|
|
213
220
|
|
|
221
|
+
table = params[0]
|
|
222
|
+
column = field if len(params) == 1 else params[1]
|
|
223
|
+
|
|
214
224
|
try:
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
225
|
+
return self.validator.db_manager.exists(table, column, value)
|
|
226
|
+
except Exception as e:
|
|
227
|
+
print(f"Database error in ExistsRule: {e}")
|
|
218
228
|
return False
|
|
219
|
-
|
|
220
|
-
return value.isdigit() and min_digits <= len(value) <= max_digits
|
|
221
229
|
|
|
222
230
|
def message(self, field: str, params: List[str]) -> str:
|
|
223
|
-
return f"The :
|
|
231
|
+
return f"The selected :attribute is invalid."
|
|
232
|
+
|
|
233
|
+
class SameRule(Rule):
|
|
234
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
235
|
+
if not params:
|
|
236
|
+
return False
|
|
237
|
+
return value == self.get_field_value(params[0])
|
|
238
|
+
|
|
239
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
240
|
+
return f"The :attribute and {params[0]} must match."
|
|
224
241
|
|
|
225
|
-
class
|
|
242
|
+
class DifferentRule(Rule):
|
|
226
243
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
227
244
|
if not params:
|
|
228
245
|
return False
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
246
|
+
|
|
247
|
+
return str(value) != self.get_field_value(params[0])
|
|
248
|
+
|
|
249
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
250
|
+
return f"The :attribute and {params[0]} must be different."
|
|
251
|
+
|
|
252
|
+
class InRule(Rule):
|
|
253
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
254
|
+
allowed_values = self._parse_option_values(field, params)
|
|
255
|
+
return (str(value) in allowed_values or value in allowed_values)
|
|
256
|
+
|
|
257
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
258
|
+
allowed_values = self._parse_option_values(field, params)
|
|
259
|
+
return f"The selected :attribute must be in : {', '.join(map(str, allowed_values))}"
|
|
260
|
+
|
|
261
|
+
class NotInRule(Rule):
|
|
262
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
263
|
+
not_allowed_values = self._parse_option_values(field, params)
|
|
264
|
+
return str(value) not in not_allowed_values
|
|
265
|
+
|
|
266
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
267
|
+
not_allowed_values = self._parse_option_values(field, params)
|
|
268
|
+
return f"The selected :attribute must be not in : {', '.join(map(str, not_allowed_values))}"
|
|
269
|
+
|
|
270
|
+
class EnumRule(Rule):
|
|
271
|
+
def __init__(self, *params):
|
|
272
|
+
super().__init__()
|
|
273
|
+
self._params: List[str] = list(params)
|
|
274
|
+
self.allowed_values = self._parse_option_values(None, self._params, raise_for_error=False)
|
|
275
|
+
|
|
276
|
+
def exclude(self, *options):
|
|
277
|
+
except_values = self._parse_option_values(None, options, raise_for_error=False)
|
|
278
|
+
self.allowed_values = [value for value in self.allowed_values if value not in except_values]
|
|
279
|
+
return self
|
|
280
|
+
|
|
281
|
+
def only(self, *options):
|
|
282
|
+
only_values = self._parse_option_values(None, options, raise_for_error=False)
|
|
283
|
+
self.allowed_values = [value for value in self.allowed_values if value in only_values]
|
|
284
|
+
return self
|
|
285
|
+
|
|
286
|
+
def when(self,
|
|
287
|
+
condition: bool,
|
|
288
|
+
when_true: Callable[['EnumRule'], 'EnumRule'],
|
|
289
|
+
when_false: Optional[Callable[['EnumRule'], 'EnumRule']] = None
|
|
290
|
+
) -> 'EnumRule':
|
|
291
|
+
|
|
292
|
+
if condition:
|
|
293
|
+
return when_true(self)
|
|
294
|
+
elif when_false:
|
|
295
|
+
return when_false(self)
|
|
296
|
+
return self
|
|
297
|
+
|
|
298
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
299
|
+
return (str(value) in self.allowed_values or value in self.allowed_values)
|
|
300
|
+
|
|
301
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
302
|
+
allowed_values = self._parse_option_values(field, self.allowed_values)
|
|
303
|
+
return f"The :attribute must be one of: {', '.join(map(str, allowed_values))}"
|
|
304
|
+
|
|
305
|
+
class ContainsRule(Rule):
|
|
306
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
307
|
+
if not params:
|
|
237
308
|
return False
|
|
309
|
+
|
|
310
|
+
for search_value in params:
|
|
311
|
+
if isinstance(value, str) and search_value in value:
|
|
312
|
+
return True
|
|
313
|
+
|
|
314
|
+
if isinstance(value, (int, float)) and str(search_value) in str(value):
|
|
315
|
+
return True
|
|
316
|
+
|
|
317
|
+
if isinstance(value, (list, tuple, set)) and search_value in value:
|
|
318
|
+
return True
|
|
319
|
+
|
|
320
|
+
if isinstance(value, dict) and search_value in value.keys():
|
|
321
|
+
return True
|
|
322
|
+
|
|
323
|
+
return False
|
|
238
324
|
|
|
239
325
|
def message(self, field: str, params: List[str]) -> str:
|
|
240
|
-
|
|
326
|
+
if len(params) == 1:
|
|
327
|
+
return f"The {field} must contain {params[0]}"
|
|
328
|
+
|
|
329
|
+
joined_params = ", ".join(params[:-1]) + f" or {params[-1]}"
|
|
330
|
+
return f"The {field} must contain at least one of: {joined_params}"
|
validator/rules/date.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from .base import
|
|
1
|
+
from .base import Rule
|
|
2
2
|
from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
|
|
3
3
|
from datetime import datetime
|
|
4
|
+
import zoneinfo
|
|
4
5
|
from dateutil.parser import parse
|
|
5
6
|
|
|
6
|
-
class DateRule(
|
|
7
|
+
class DateRule(Rule):
|
|
7
8
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
8
9
|
if not isinstance(value, str):
|
|
9
10
|
return False
|
|
@@ -15,18 +16,17 @@ class DateRule(ValidationRule):
|
|
|
15
16
|
return False
|
|
16
17
|
|
|
17
18
|
def message(self, field: str, params: List[str]) -> str:
|
|
18
|
-
return f"The :
|
|
19
|
+
return f"The :attribute is not a valid date."
|
|
19
20
|
|
|
20
|
-
class DateEqualsRule(
|
|
21
|
+
class DateEqualsRule(Rule):
|
|
21
22
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
22
23
|
if not params or not isinstance(value, str):
|
|
23
24
|
return False
|
|
24
25
|
|
|
25
|
-
value = self.get_field_value(value, value)
|
|
26
|
-
|
|
27
26
|
try:
|
|
28
27
|
date1 = parse(value)
|
|
29
28
|
|
|
29
|
+
params = list(params)
|
|
30
30
|
params[0] = self.get_field_value(params[0], params[0])
|
|
31
31
|
|
|
32
32
|
date2 = parse(params[0])
|
|
@@ -35,9 +35,9 @@ class DateEqualsRule(ValidationRule):
|
|
|
35
35
|
return False
|
|
36
36
|
|
|
37
37
|
def message(self, field: str, params: List[str]) -> str:
|
|
38
|
-
return f"The :
|
|
38
|
+
return f"The :attribute must be equal to {params[0]}."
|
|
39
39
|
|
|
40
|
-
class AfterRule(
|
|
40
|
+
class AfterRule(Rule):
|
|
41
41
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
42
42
|
if not params or len(params) < 1:
|
|
43
43
|
return False
|
|
@@ -51,7 +51,8 @@ class AfterRule(ValidationRule):
|
|
|
51
51
|
else:
|
|
52
52
|
return False
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
|
|
55
|
+
params = list(params)# Parse comparison date
|
|
55
56
|
params[0] = self.get_field_value(params[0], params[0])
|
|
56
57
|
compare_date = parse(params[0])
|
|
57
58
|
|
|
@@ -61,13 +62,12 @@ class AfterRule(ValidationRule):
|
|
|
61
62
|
return False
|
|
62
63
|
|
|
63
64
|
def message(self, field: str, params: List[str]) -> str:
|
|
64
|
-
return f"The :
|
|
65
|
+
return f"The :attribute must be after {params[0]}"
|
|
65
66
|
|
|
66
|
-
class AfterOrEqualRule(
|
|
67
|
+
class AfterOrEqualRule(Rule):
|
|
67
68
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
68
69
|
if not params or len(params) < 1:
|
|
69
70
|
return False
|
|
70
|
-
|
|
71
71
|
try:
|
|
72
72
|
if isinstance(value, str):
|
|
73
73
|
date_value = parse(value)
|
|
@@ -76,18 +76,19 @@ class AfterOrEqualRule(ValidationRule):
|
|
|
76
76
|
else:
|
|
77
77
|
return False
|
|
78
78
|
|
|
79
|
+
params = list(params)
|
|
79
80
|
params[0] = self.get_field_value(params[0], params[0])
|
|
80
81
|
compare_date = parse(params[0])
|
|
81
82
|
|
|
82
83
|
return date_value >= compare_date
|
|
83
84
|
|
|
84
|
-
except (ValueError, TypeError):
|
|
85
|
+
except (ValueError, TypeError) as e:
|
|
85
86
|
return False
|
|
86
87
|
|
|
87
88
|
def message(self, field: str, params: List[str]) -> str:
|
|
88
|
-
return f"The :
|
|
89
|
+
return f"The :attribute must be after or equal to {params[0]}"
|
|
89
90
|
|
|
90
|
-
class BeforeRule(
|
|
91
|
+
class BeforeRule(Rule):
|
|
91
92
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
92
93
|
if not params or len(params) < 1:
|
|
93
94
|
return False
|
|
@@ -102,6 +103,7 @@ class BeforeRule(ValidationRule):
|
|
|
102
103
|
else:
|
|
103
104
|
return False
|
|
104
105
|
|
|
106
|
+
params = list(params)
|
|
105
107
|
params[0] = self.get_field_value(params[0], params[0])
|
|
106
108
|
compare_date = parse(params[0])
|
|
107
109
|
|
|
@@ -111,9 +113,9 @@ class BeforeRule(ValidationRule):
|
|
|
111
113
|
return False
|
|
112
114
|
|
|
113
115
|
def message(self, field: str, params: List[str]) -> str:
|
|
114
|
-
return f"The :
|
|
116
|
+
return f"The :attribute must be before {params[0]}"
|
|
115
117
|
|
|
116
|
-
class BeforeOrEqualRule(
|
|
118
|
+
class BeforeOrEqualRule(Rule):
|
|
117
119
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
118
120
|
if not params or len(params) < 1:
|
|
119
121
|
return False
|
|
@@ -128,6 +130,7 @@ class BeforeOrEqualRule(ValidationRule):
|
|
|
128
130
|
else:
|
|
129
131
|
return False
|
|
130
132
|
|
|
133
|
+
params = list(params)
|
|
131
134
|
params[0] = self.get_field_value(params[0], params[0])
|
|
132
135
|
compare_date = parse(params[0])
|
|
133
136
|
|
|
@@ -137,9 +140,9 @@ class BeforeOrEqualRule(ValidationRule):
|
|
|
137
140
|
return False
|
|
138
141
|
|
|
139
142
|
def message(self, field: str, params: List[str]) -> str:
|
|
140
|
-
return f"The :
|
|
143
|
+
return f"The :attribute must be before or equal to {params[0]}"
|
|
141
144
|
|
|
142
|
-
class DateFormatRule(
|
|
145
|
+
class DateFormatRule(Rule):
|
|
143
146
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
144
147
|
if not params or len(params) < 1 or not isinstance(value, str):
|
|
145
148
|
return False
|
|
@@ -151,4 +154,17 @@ class DateFormatRule(ValidationRule):
|
|
|
151
154
|
return False
|
|
152
155
|
|
|
153
156
|
def message(self, field: str, params: List[str]) -> str:
|
|
154
|
-
return f"The :
|
|
157
|
+
return f"The :attribute must match the format {params[0]}"
|
|
158
|
+
|
|
159
|
+
class TimezoneRule(Rule):
|
|
160
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
161
|
+
if not isinstance(value, str):
|
|
162
|
+
return False
|
|
163
|
+
try:
|
|
164
|
+
zoneinfo.ZoneInfo(value)
|
|
165
|
+
return True
|
|
166
|
+
except zoneinfo.ZoneInfoNotFoundError:
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
170
|
+
return f"The :attribute must be a valid timezone."
|