safeshield 1.5.1__py3-none-any.whl → 1.5.3__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.
Potentially problematic release.
This version of safeshield might be problematic. Click here for more details.
- {safeshield-1.5.1.dist-info → safeshield-1.5.3.dist-info}/METADATA +1 -1
- safeshield-1.5.3.dist-info/RECORD +31 -0
- validator/core/validator.py +21 -19
- validator/rules/array.py +46 -12
- validator/rules/base.py +42 -6
- validator/rules/basic.py +46 -4
- validator/rules/boolean.py +56 -57
- validator/rules/comparison.py +126 -34
- validator/rules/date.py +79 -27
- validator/rules/files.py +52 -11
- validator/rules/format.py +9 -0
- validator/rules/numeric.py +105 -36
- validator/rules/string.py +54 -9
- validator/rules/utilities.py +294 -131
- validator/services/rule_error_handler.py +24 -208
- validator/services/rule_preparer.py +21 -7
- safeshield-1.5.1.dist-info/RECORD +0 -31
- {safeshield-1.5.1.dist-info → safeshield-1.5.3.dist-info}/LICENSE +0 -0
- {safeshield-1.5.1.dist-info → safeshield-1.5.3.dist-info}/WHEEL +0 -0
- {safeshield-1.5.1.dist-info → safeshield-1.5.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
validator/__init__.py,sha256=udxDzUicPfxBOAQvzsnl3pHur9VUppKbWMgg35hpiww,244
|
|
2
|
+
validator/exceptions.py,sha256=y2v7CaXmeGFHWcnigtLl4U-sFta_jMiXkGKXWIIVglY,366
|
|
3
|
+
validator/factory.py,sha256=SJ2iJmTnFS-4ccH-gzZl_7CAW7Z6YJkZbMImu-po80w,1163
|
|
4
|
+
validator/core/__init__.py,sha256=ZcqlXJSk03i_CVzmIN-nVe1UOyvwwO5jhbEj7f62Y_o,59
|
|
5
|
+
validator/core/validator.py,sha256=cflLhGo_rS6Zh61X7lZfr0q-xR0bBHfylGudZ1uTn7o,12765
|
|
6
|
+
validator/database/__init__.py,sha256=O-cB6-MhNapJ3iwe5jvifbMfr1dPjXLtEdfNTKIu0hc,171
|
|
7
|
+
validator/database/detector.py,sha256=Vac7oVL26GjU6expGo01-6mgUtXqldr-jirzpYokZBM,9597
|
|
8
|
+
validator/database/manager.py,sha256=XJM_I0WaWfZWV710duAc32p1gtiP2or-MAj75WPw1oM,6478
|
|
9
|
+
validator/rules/__init__.py,sha256=z3Vk3R5CRgjeqyDWZxdofD2tBMTgdyYVuFmo1mKOTj4,830
|
|
10
|
+
validator/rules/array.py,sha256=7xwdXnthWZNDbD4lONgqtOf44OOQq1J6XexYm6BXea4,3512
|
|
11
|
+
validator/rules/base.py,sha256=yhxQdGiiM-wY7VtRH-KRU-_wmw7RVJnmJcmayO1EqBk,5870
|
|
12
|
+
validator/rules/basic.py,sha256=8k-REzsSyknhNuw0vPKMxtovvJBVlyZDihXXVznRQpM,6420
|
|
13
|
+
validator/rules/boolean.py,sha256=UuSO9BdBMR5l6sB3dgZqoqOVjqu47yjFrmJ7xylTVKs,5452
|
|
14
|
+
validator/rules/comparison.py,sha256=BnufmgCkSqzUjSFWvlsKN8auOCKTBs7Pxy5i9rwSQs8,15564
|
|
15
|
+
validator/rules/date.py,sha256=k27U3e08OPZoXyEX0Yj86ZfjCUuhLy4eBmqYbyc4Jn4,7292
|
|
16
|
+
validator/rules/files.py,sha256=3Q0KoadDKqNIiVlBR7b5b-XBs4gOg3ZFu7wU7SrdqWQ,12280
|
|
17
|
+
validator/rules/format.py,sha256=u_BhPQwW30JYHwZIjHD0ZX449csgTPPCJOeoXaQr4M4,7361
|
|
18
|
+
validator/rules/numeric.py,sha256=UgawwV_ZKiJ3Dano32UrSBC6Qe69zbiL69OyMDo91yY,8699
|
|
19
|
+
validator/rules/string.py,sha256=3wgrqAO4V97ZPMc97mwDV6vsNvs6oyJs-ciqQX-FLBs,6268
|
|
20
|
+
validator/rules/utilities.py,sha256=BaErsE0TPt1u342aOuSBkjh0CMD2Sq7ZwiBqM8j0GLo,16910
|
|
21
|
+
validator/services/__init__.py,sha256=zzKTmqL7v4niFGWHJBfWLqgJ0iTaW_69OzYZN8uInzQ,210
|
|
22
|
+
validator/services/rule_conflict.py,sha256=T3IhWLmZsRUccJ4oFO-OKRjrc5Xt7r71kktxjjj2IA8,9505
|
|
23
|
+
validator/services/rule_error_handler.py,sha256=l4jA6WtTRBNGytVA_nSwJpbix_viNWhqlwKcxIKXzcY,2325
|
|
24
|
+
validator/services/rule_preparer.py,sha256=7HSMuSGGgaYWv8dt_m7pL5MQkJcTwFBH9gfmCZwzt8U,5284
|
|
25
|
+
validator/utils/__init__.py,sha256=Yzo-xv285Be-a233M4duDdYtscuHiuBbPSX_C8yViJI,20
|
|
26
|
+
validator/utils/string.py,sha256=0YACzeEaWNEOR9_7O9A8D1ItIbtWfOJ8IfrzcB8VMYA,515
|
|
27
|
+
safeshield-1.5.3.dist-info/LICENSE,sha256=qugtRyKckyaks6hd2xyxOFSOYM6au1N80pMXuMTPvC4,1090
|
|
28
|
+
safeshield-1.5.3.dist-info/METADATA,sha256=afpA0oU6UvthlLNjUBW9q3ojPU9teOQdNVSdhrfHOjw,2313
|
|
29
|
+
safeshield-1.5.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
30
|
+
safeshield-1.5.3.dist-info/top_level.txt,sha256=iUtV3dlHOIiMfLuY4pruY00lFni8JzOkQ3Nh1II19OE,10
|
|
31
|
+
safeshield-1.5.3.dist-info/RECORD,,
|
validator/core/validator.py
CHANGED
|
@@ -12,7 +12,7 @@ from collections.abc import Mapping
|
|
|
12
12
|
class Validator:
|
|
13
13
|
"""Main validation class with proper abstractions"""
|
|
14
14
|
PRIORITY_RULES = {
|
|
15
|
-
'bail', 'sometimes', 'exclude', 'exclude_unless', 'exclude_if', 'exclude_with', 'exclude_without', 'required', 'required_if', 'required_unless',
|
|
15
|
+
'bail', 'sometimes', 'dynamic', 'exclude', 'exclude_unless', 'exclude_if', 'exclude_with', 'exclude_without', 'required', 'required_if', 'required_unless',
|
|
16
16
|
'required_with', 'required_without'
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -60,6 +60,7 @@ class Validator:
|
|
|
60
60
|
):
|
|
61
61
|
self.data = data or {}
|
|
62
62
|
self._raw_rules = rules or {}
|
|
63
|
+
self.prepared_rules = None
|
|
63
64
|
self._stop_on_first_failure = False
|
|
64
65
|
self._field_to_exclude = []
|
|
65
66
|
|
|
@@ -67,6 +68,7 @@ class Validator:
|
|
|
67
68
|
# Initialize dependencies
|
|
68
69
|
self.rule_preparer = RulePreparer(RuleFactory())
|
|
69
70
|
self.error_handler = RuleErrorHandler(messages, custom_attributes)
|
|
71
|
+
self.custom_attributes = custom_attributes or {}
|
|
70
72
|
|
|
71
73
|
# Database configuration
|
|
72
74
|
self.db_config = db_config or DatabaseAutoDetector.detect()
|
|
@@ -79,21 +81,22 @@ class Validator:
|
|
|
79
81
|
|
|
80
82
|
def validate(self) -> bool:
|
|
81
83
|
"""Validate the data against the rules"""
|
|
82
|
-
prepared_rules = self.rule_preparer.prepare(self._raw_rules)
|
|
84
|
+
self.prepared_rules = self.rule_preparer.prepare(self._raw_rules)
|
|
83
85
|
self.error_handler.errors.clear()
|
|
84
|
-
|
|
85
|
-
validated = self._validate_rules(prepared_rules, priority_only=True)
|
|
86
|
+
|
|
87
|
+
validated = self._validate_rules(self.prepared_rules, priority_only=True)
|
|
86
88
|
|
|
87
89
|
# First pass: priority rules
|
|
88
90
|
if self.error_handler.has_errors and self._stop_on_first_failure:
|
|
89
91
|
return False
|
|
90
|
-
|
|
92
|
+
|
|
91
93
|
# Second pass: remaining rules
|
|
92
|
-
self._validate_rules(prepared_rules, priority_only=False)
|
|
94
|
+
self._validate_rules(self.prepared_rules, priority_only=False)
|
|
93
95
|
|
|
94
96
|
if self.error_handler.has_errors:
|
|
95
97
|
return False
|
|
96
|
-
|
|
98
|
+
|
|
99
|
+
return self.data
|
|
97
100
|
|
|
98
101
|
def _validate_rules(self, prepared_rules: Dict[str, List[Rule]], priority_only: bool):
|
|
99
102
|
validated = []
|
|
@@ -219,22 +222,18 @@ class Validator:
|
|
|
219
222
|
return _check(self.data, parts)
|
|
220
223
|
|
|
221
224
|
def _apply_rule(self, field: str, value: Any, rule: Rule) -> bool:
|
|
222
|
-
|
|
223
|
-
rule.set_validator(self)
|
|
225
|
+
rule.set_validator(self)
|
|
224
226
|
|
|
225
|
-
|
|
227
|
+
display_field = getattr(self, '_current_actual_path', field)
|
|
228
|
+
rule._require_parameter_count(getattr(rule, '_count_parameter', 0), getattr(rule, 'params', []), rule)
|
|
226
229
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
+
if not rule.validate(field, value, getattr(rule, 'params', [])):
|
|
231
|
+
msg = rule.message(display_field, getattr(rule, 'params', []))
|
|
232
|
+
self.error_handler.add_error(display_field, rule, getattr(rule, 'params', []), msg, value)
|
|
230
233
|
|
|
231
|
-
return False
|
|
232
|
-
else:
|
|
233
|
-
return True
|
|
234
|
-
except Exception as e:
|
|
235
|
-
display_field = getattr(self, '_current_actual_path', field)
|
|
236
|
-
self.error_handler.add_error(display_field, rule.rule_name, getattr(rule, 'params', []), str(e), value)
|
|
237
234
|
return False
|
|
235
|
+
else:
|
|
236
|
+
return True
|
|
238
237
|
|
|
239
238
|
def _get_nested_value(self, path: str) -> Any:
|
|
240
239
|
parts = path.split('.')
|
|
@@ -293,6 +292,9 @@ class Validator:
|
|
|
293
292
|
def errors(self) -> Dict[str, List[str]]:
|
|
294
293
|
"""Get current validation errors"""
|
|
295
294
|
return self.error_handler.errors
|
|
295
|
+
|
|
296
|
+
def get_errors(self):
|
|
297
|
+
return self.error_handler.errors or {}
|
|
296
298
|
|
|
297
299
|
@property
|
|
298
300
|
def has_errors(self) -> bool:
|
validator/rules/array.py
CHANGED
|
@@ -3,24 +3,30 @@ from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
|
|
|
3
3
|
|
|
4
4
|
class ArrayRule(Rule):
|
|
5
5
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
6
|
-
self._missing_keys = params
|
|
7
|
-
|
|
8
6
|
if params:
|
|
9
7
|
params = self._parse_option_values(self.rule_name, params)
|
|
10
8
|
if not isinstance(value, dict):
|
|
11
9
|
return False
|
|
12
|
-
|
|
10
|
+
|
|
13
11
|
missing = [param for param in params if param not in value]
|
|
14
|
-
self._missing_keys = missing
|
|
15
12
|
return len(missing) == 0
|
|
16
13
|
|
|
17
14
|
return isinstance(value, (list, tuple, set))
|
|
18
15
|
|
|
19
16
|
def message(self, field: str, params: List[str]) -> str:
|
|
20
17
|
if params:
|
|
21
|
-
return f"The :attribute must contain the keys:
|
|
18
|
+
return f"The :attribute must contain the keys: :values."
|
|
22
19
|
return f"The :attribute must be an array."
|
|
23
20
|
|
|
21
|
+
def replacements(self, field, value):
|
|
22
|
+
replacements = {
|
|
23
|
+
':attribute': self._get_display_name(field),
|
|
24
|
+
':input': value,
|
|
25
|
+
':values': ', '.join(self._params)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return replacements
|
|
29
|
+
|
|
24
30
|
class DistinctRule(Rule):
|
|
25
31
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
26
32
|
if not isinstance(value, (list, tuple, set)):
|
|
@@ -48,23 +54,51 @@ class DistinctRule(Rule):
|
|
|
48
54
|
return f"{base_msg} (case insensitive)"
|
|
49
55
|
else:
|
|
50
56
|
return f"{base_msg} (strict comparison)"
|
|
57
|
+
|
|
58
|
+
def replacements(self, field, value):
|
|
59
|
+
replacements = {
|
|
60
|
+
':attribute': self._get_display_name(field),
|
|
61
|
+
':input': value,
|
|
62
|
+
':value': self._params[0] if self._params else None
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return replacements
|
|
51
66
|
|
|
52
67
|
class InArrayRule(Rule):
|
|
68
|
+
_count_parameter = 1
|
|
69
|
+
|
|
53
70
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
54
|
-
|
|
55
|
-
return False
|
|
56
|
-
|
|
57
|
-
return str(value) in params
|
|
71
|
+
return str(value) in self.get_field_value(params[0], [])
|
|
58
72
|
|
|
59
73
|
def message(self, field: str, params: List[str]) -> str:
|
|
60
|
-
return f"The :attribute must be one of:
|
|
74
|
+
return f"The :attribute must be one of: :values"
|
|
75
|
+
|
|
76
|
+
def replacements(self, field, value):
|
|
77
|
+
replacements = {
|
|
78
|
+
':attribute': self._get_display_name(field),
|
|
79
|
+
':input': value,
|
|
80
|
+
':values': ', '.join(self.get_field_value(self._params[0], []))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return replacements
|
|
61
84
|
|
|
62
85
|
class InArrayKeysRule(Rule):
|
|
86
|
+
_count_parameter = 1
|
|
87
|
+
|
|
63
88
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
64
|
-
if not
|
|
89
|
+
if not isinstance(value, dict):
|
|
65
90
|
return False
|
|
66
91
|
|
|
67
92
|
return any(key in value for key in params)
|
|
68
93
|
|
|
69
94
|
def message(self, field: str, params: List[str]) -> str:
|
|
70
|
-
return f"The :attribute must contain at least one of these keys:
|
|
95
|
+
return f"The :attribute must contain at least one of these keys: :values"
|
|
96
|
+
|
|
97
|
+
def replacements(self, field, value):
|
|
98
|
+
replacements = {
|
|
99
|
+
':attribute': self._get_display_name(field),
|
|
100
|
+
':input': value,
|
|
101
|
+
':values': ', '.join(self._params)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return replacements
|
validator/rules/base.py
CHANGED
|
@@ -8,20 +8,15 @@ class Rule(ABC):
|
|
|
8
8
|
"""Abstract base class for all validation rules"""
|
|
9
9
|
|
|
10
10
|
_rule_classes: Dict[str, Type['Rule']] = {}
|
|
11
|
-
_rule_cache: Dict[str, 'Rule'] = {}
|
|
12
11
|
|
|
13
12
|
def __init__(self, *params: str):
|
|
14
13
|
self._validator: Optional['Validator'] = None
|
|
15
14
|
self._params: List[str] = list(params)
|
|
16
15
|
|
|
17
|
-
def __init_subclass__(cls
|
|
16
|
+
def __init_subclass__(cls):
|
|
18
17
|
cls.rule_name = cls.pascal_to_snake(cls.__name__) if not hasattr(cls, '_name') else cls._name
|
|
19
18
|
cls._register_rule_class()
|
|
20
19
|
cls._generate_rule_methods()
|
|
21
|
-
|
|
22
|
-
@classmethod
|
|
23
|
-
def _register_rule_class(cls):
|
|
24
|
-
cls._rule_classes[cls.rule_name] = cls
|
|
25
20
|
|
|
26
21
|
@property
|
|
27
22
|
def params(self):
|
|
@@ -33,6 +28,10 @@ class Rule(ABC):
|
|
|
33
28
|
params.append(rule_set)
|
|
34
29
|
return tuple(params)
|
|
35
30
|
|
|
31
|
+
@classmethod
|
|
32
|
+
def _register_rule_class(cls):
|
|
33
|
+
cls._rule_classes[cls.rule_name] = cls
|
|
34
|
+
|
|
36
35
|
@classmethod
|
|
37
36
|
def _generate_rule_methods(cls):
|
|
38
37
|
for rule_name, rule_class in cls._rule_classes.items():
|
|
@@ -42,6 +41,14 @@ class Rule(ABC):
|
|
|
42
41
|
|
|
43
42
|
setattr(cls.__class__, rule_name, method)
|
|
44
43
|
|
|
44
|
+
@classmethod
|
|
45
|
+
def _require_parameter_count(self, count, params, rule):
|
|
46
|
+
if getattr(rule, '_accept_closure', False) and (isinstance(params[0], bool) or callable(params[0])):
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
if count > len(params):
|
|
50
|
+
raise ValueError(f"Validation rule {rule.rule_name} requires at least {count} parameter")
|
|
51
|
+
|
|
45
52
|
@params.setter
|
|
46
53
|
def params(self, value: List[str]) -> None:
|
|
47
54
|
self._params = value
|
|
@@ -81,6 +88,35 @@ class Rule(ABC):
|
|
|
81
88
|
"""Generate an error message if validation fails."""
|
|
82
89
|
pass
|
|
83
90
|
|
|
91
|
+
def replacements(self, field, value) -> Dict[str, str]:
|
|
92
|
+
"""Default replacements for all rules"""
|
|
93
|
+
return {':attribute': self._get_display_name(field), 'value': value }
|
|
94
|
+
|
|
95
|
+
def _get_display_name(self, fields: str) -> str:
|
|
96
|
+
single_input = isinstance(fields, str)
|
|
97
|
+
if single_input:
|
|
98
|
+
fields = [fields]
|
|
99
|
+
|
|
100
|
+
attributes = []
|
|
101
|
+
|
|
102
|
+
for field in fields:
|
|
103
|
+
if field in self.validator.custom_attributes:
|
|
104
|
+
attributes.append(self.validator.custom_attributes[field])
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
parts = field.split('.')
|
|
108
|
+
base_name = parts[-1].replace('_', ' ').title()
|
|
109
|
+
|
|
110
|
+
for i in range(len(parts), 0, -1):
|
|
111
|
+
wildcard = '.'.join(parts[:i]) + '.*'
|
|
112
|
+
if wildcard in self.validator.custom_attributes:
|
|
113
|
+
attributes.append(self.validator.custom_attributes[wildcard])
|
|
114
|
+
break
|
|
115
|
+
else:
|
|
116
|
+
attributes.append(self.validator.custom_attributes.get(parts[-1], base_name))
|
|
117
|
+
|
|
118
|
+
return attributes[0] if single_input else attributes
|
|
119
|
+
|
|
84
120
|
@property
|
|
85
121
|
@abstractmethod
|
|
86
122
|
def rule_name(self) -> str:
|
validator/rules/basic.py
CHANGED
|
@@ -6,8 +6,10 @@ from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
|
|
|
6
6
|
# =============================================
|
|
7
7
|
|
|
8
8
|
class AnyOfRule(Rule):
|
|
9
|
+
_count_parameter = 1
|
|
10
|
+
|
|
9
11
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
10
|
-
self._last_failed_rules = []
|
|
12
|
+
self._last_failed_rules = []
|
|
11
13
|
|
|
12
14
|
for i, rule_set in enumerate(params):
|
|
13
15
|
if isinstance(rule_set, str):
|
|
@@ -56,6 +58,30 @@ class AnyOfRule(Rule):
|
|
|
56
58
|
f"The :attribute must satisfy at least one of these conditions:\n" +
|
|
57
59
|
"\n".join(error_messages)
|
|
58
60
|
)
|
|
61
|
+
|
|
62
|
+
class WhenRule(Rule):
|
|
63
|
+
_count_parameter = 1
|
|
64
|
+
|
|
65
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
66
|
+
rules = [rule for rule in self.validator.prepared_rules.get(field, []) if rule.rule_name != 'when']
|
|
67
|
+
rules = [new_rule for new_rule in self.validator.rule_preparer._generate_rule(field, self._select_rules(field))]
|
|
68
|
+
self.validator.prepared_rules[field] += rules
|
|
69
|
+
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
def _select_rules(self, field: str) -> List[str]:
|
|
73
|
+
selected = None
|
|
74
|
+
for check_field, rules_map in self._params[0].items():
|
|
75
|
+
current_value = str(self.get_field_value(check_field)) if self.get_field_value(check_field) is not None else ''
|
|
76
|
+
for expected_value, rules in rules_map.items():
|
|
77
|
+
if current_value == expected_value or expected_value == '*':
|
|
78
|
+
selected = rules
|
|
79
|
+
break
|
|
80
|
+
|
|
81
|
+
return selected
|
|
82
|
+
|
|
83
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
84
|
+
return ""
|
|
59
85
|
|
|
60
86
|
class BailRule(Rule):
|
|
61
87
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
@@ -108,13 +134,29 @@ class MissingRule(Rule):
|
|
|
108
134
|
return f"The :attribute field must be missing."
|
|
109
135
|
|
|
110
136
|
class ProhibitsRule(Rule):
|
|
137
|
+
_count_parameter = 1
|
|
138
|
+
|
|
111
139
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
112
|
-
if
|
|
140
|
+
if value is None or value == '':
|
|
113
141
|
return True
|
|
114
|
-
|
|
142
|
+
|
|
143
|
+
for param in params:
|
|
144
|
+
other_value = self.get_field_value(param, None)
|
|
145
|
+
if other_value in self.validator.data:
|
|
146
|
+
return False
|
|
147
|
+
return True
|
|
115
148
|
|
|
116
149
|
def message(self, field: str, params: List[str]) -> str:
|
|
117
|
-
return f"When :attribute is present,
|
|
150
|
+
return f"When :attribute is present, :others must be empty or absent."
|
|
151
|
+
|
|
152
|
+
def replacements(self, field, value):
|
|
153
|
+
replacements = {
|
|
154
|
+
':attribute': self._get_display_name(field),
|
|
155
|
+
':input': value,
|
|
156
|
+
':others': ', '.join(self._get_display_name(self._params))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return replacements
|
|
118
160
|
|
|
119
161
|
class SometimesRule(Rule):
|
|
120
162
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
validator/rules/boolean.py
CHANGED
|
@@ -6,7 +6,7 @@ class BooleanRule(Rule):
|
|
|
6
6
|
if isinstance(value, bool):
|
|
7
7
|
return True
|
|
8
8
|
if isinstance(value, str):
|
|
9
|
-
return value.lower() in ['true', 'false', '1', '0', 'yes', 'no', 'on', 'off']
|
|
9
|
+
return value.lower() in ['true', 'false', '1', '0', 'yes', 'no', 'on', 'off', 'False', 'True']
|
|
10
10
|
if isinstance(value, int):
|
|
11
11
|
return value in [0, 1]
|
|
12
12
|
return False
|
|
@@ -45,6 +45,8 @@ class DeclinedRule(Rule):
|
|
|
45
45
|
return f"The :attribute must be declined."
|
|
46
46
|
|
|
47
47
|
class AcceptedIfRule(AcceptedRule):
|
|
48
|
+
_count_parameter = 2
|
|
49
|
+
|
|
48
50
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
49
51
|
condition_met = False
|
|
50
52
|
|
|
@@ -53,54 +55,52 @@ class AcceptedIfRule(AcceptedRule):
|
|
|
53
55
|
elif len(params) == 1 and isinstance(params[0], bool):
|
|
54
56
|
condition_met = params[0]
|
|
55
57
|
else:
|
|
56
|
-
|
|
58
|
+
expected_value = self.get_field_value(params[0], None)
|
|
59
|
+
condition = params[1]
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
continue
|
|
61
|
-
|
|
62
|
-
actual_value = self.get_field_value(other_field, '')
|
|
63
|
-
|
|
64
|
-
if actual_value == expected_value:
|
|
65
|
-
condition_met = True
|
|
66
|
-
break
|
|
61
|
+
if expected_value == condition:
|
|
62
|
+
condition_met = True
|
|
67
63
|
|
|
68
64
|
if condition_met:
|
|
69
65
|
return super().validate(field, value, params)
|
|
70
66
|
|
|
71
67
|
return True
|
|
72
68
|
|
|
69
|
+
def replacements(self, field, value):
|
|
70
|
+
replacements = {
|
|
71
|
+
':attribute': self._get_display_name(field),
|
|
72
|
+
':input': value,
|
|
73
|
+
':other': self._get_display_name(self._params[0]),
|
|
74
|
+
':value': self._params[1],
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return replacements
|
|
78
|
+
|
|
73
79
|
class AcceptedUnlessRule(AcceptedRule):
|
|
80
|
+
_count_parameter = 2
|
|
81
|
+
|
|
74
82
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
75
|
-
if len(params) < 2:
|
|
76
|
-
return False
|
|
77
|
-
|
|
78
83
|
other_field, other_value = params[0], params[1]
|
|
79
|
-
actual_value = self.get_field_value(other_field,
|
|
84
|
+
actual_value = self.get_field_value(other_field, None)
|
|
80
85
|
|
|
81
86
|
if actual_value == other_value:
|
|
82
87
|
return True
|
|
83
88
|
|
|
84
89
|
return super().validate(field, value, params)
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
def replacements(self, field, value):
|
|
92
|
+
replacements = {
|
|
93
|
+
':attribute': self._get_display_name(field),
|
|
94
|
+
':input': value,
|
|
95
|
+
':other': self._get_display_name(self._params[0]),
|
|
96
|
+
':value': self._params[1],
|
|
97
|
+
}
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
self.get_field_value(f) == v
|
|
95
|
-
for f, v in conditions
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
if not all_conditions_met:
|
|
99
|
-
return True
|
|
100
|
-
|
|
101
|
-
return super().validate(field, value, params)
|
|
99
|
+
return replacements
|
|
102
100
|
|
|
103
101
|
class DeclinedIfRule(DeclinedRule):
|
|
102
|
+
_count_parameter = 2
|
|
103
|
+
|
|
104
104
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
105
105
|
condition_met = False
|
|
106
106
|
|
|
@@ -109,24 +109,30 @@ class DeclinedIfRule(DeclinedRule):
|
|
|
109
109
|
elif len(params) == 1 and isinstance(params[0], bool):
|
|
110
110
|
condition_met = params[0]
|
|
111
111
|
else:
|
|
112
|
-
|
|
112
|
+
expected_value = self.get_field_value(params[0], None)
|
|
113
|
+
condition = params[1]
|
|
114
|
+
|
|
115
|
+
if expected_value == condition:
|
|
116
|
+
condition_met = True
|
|
113
117
|
|
|
114
|
-
for other_field, expected_value in conditions:
|
|
115
|
-
if not other_field or expected_value is None:
|
|
116
|
-
continue
|
|
117
|
-
|
|
118
|
-
actual_value = self.get_field_value(other_field, '')
|
|
119
|
-
|
|
120
|
-
if actual_value == expected_value:
|
|
121
|
-
condition_met = True
|
|
122
|
-
break
|
|
123
|
-
|
|
124
118
|
if condition_met:
|
|
125
119
|
return super().validate(field, value, params)
|
|
126
|
-
|
|
120
|
+
|
|
127
121
|
return True
|
|
128
122
|
|
|
123
|
+
def replacements(self, field, value):
|
|
124
|
+
replacements = {
|
|
125
|
+
':attribute': self._get_display_name(field),
|
|
126
|
+
':input': value,
|
|
127
|
+
':other': self._get_display_name(self._params[0]),
|
|
128
|
+
':value': self._params[1],
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return replacements
|
|
132
|
+
|
|
129
133
|
class DeclinedUnlessRule(DeclinedRule):
|
|
134
|
+
_count_parameter = 2
|
|
135
|
+
|
|
130
136
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
131
137
|
if len(params) < 2:
|
|
132
138
|
return False
|
|
@@ -139,19 +145,12 @@ class DeclinedUnlessRule(DeclinedRule):
|
|
|
139
145
|
|
|
140
146
|
return super().validate(field, value, params)
|
|
141
147
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
all_conditions_met = all(
|
|
150
|
-
self.get_field_value(f) == v
|
|
151
|
-
for f, v in conditions
|
|
152
|
-
)
|
|
148
|
+
def replacements(self, field, value):
|
|
149
|
+
replacements = {
|
|
150
|
+
':attribute': self._get_display_name(field),
|
|
151
|
+
':input': value,
|
|
152
|
+
':other': self._get_display_name(self._params[0]),
|
|
153
|
+
':value': self._params[1],
|
|
154
|
+
}
|
|
153
155
|
|
|
154
|
-
|
|
155
|
-
return True
|
|
156
|
-
|
|
157
|
-
return super().validate(field, value, params)
|
|
156
|
+
return replacements
|