Skip to content

Commit e396279

Browse files
committed
[Feature #159841993] Add validation methods
1 parent fed79df commit e396279

File tree

4 files changed

+72
-6
lines changed

4 files changed

+72
-6
lines changed

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ exclude_lines =
99

1010
# Don't complain if tests don't hit defensive assertion code:
1111
raise AssertionError
12+
except Exception
1213
raise NotImplementedError
1314
raise IndexError
1415

api/core/Validator.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import re
12
from api.core.exceptions import ValidationException
3+
from api.core.db.query import DB
24

35

46
class Rule:
@@ -8,20 +10,24 @@ def __init__(self, value, name, param, field):
810
self.param = param
911
self.field = field
1012

13+
def _set_validator(self, validator):
14+
self.validator = validator
15+
return self
16+
1117
@property
1218
def label(self):
1319
return f"{self.field} field"
1420

1521
@classmethod
16-
def _make(cls, rule, value, field):
22+
def _make(cls, validator, rule, value, field):
1723
parts = rule.split(':')
1824
name = parts[0]
1925
try:
2026
param = parts[1]
2127
except IndexError:
2228
param = None
2329

24-
return Rule(value, name, param, field)
30+
return Rule(value, name, param, field)._set_validator(validator)
2531

2632
def required(self):
2733

@@ -30,6 +36,30 @@ def required(self):
3036
f'{self.label} is required'
3137
)
3238

39+
def email(self):
40+
is_email = re.match(
41+
r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)",
42+
str(self.value)
43+
)
44+
return self._make_error(is_email, f"{self.label} is not a valid email")
45+
46+
def unique(self):
47+
is_unique = DB.table(self.param).where(
48+
{self.field: self.value}
49+
).exists()
50+
51+
return self._make_error(
52+
not is_unique, f"{self.field} has already been taken"
53+
)
54+
55+
def confirmed(self):
56+
confirmed = self.validator.values.get(f"{self.field}_confirmation")
57+
58+
return self._make_error(
59+
self.value == confirmed,
60+
f"{self.label} confirmation doesn't match"
61+
)
62+
3363
def is_null(self):
3464
# 0 is an false but not a null value
3565
return (self.value == '' or self.value is None)
@@ -68,7 +98,7 @@ def __init__(self, values):
6898
def _set_rules(self, field, rules):
6999
value = self.values.get(field, None)
70100
for rule in str(rules).split("|"):
71-
self.rules.append(Rule._make(rule, value, field))
101+
self.rules.append(Rule._make(self, rule, value, field))
72102

73103
def _normalize_rules(self, rules):
74104
for field in rules:

api/core/db/query.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def base_where(self, query_type, args, kwargs):
3535
filters = kwargs
3636

3737
if not filters:
38-
return self
38+
return self # pragma: no cover
3939

4040
self._wheres = "WHERE {}".format(
4141
f" {query_type} ".join([f"{key}=%s" for key in filters])
@@ -74,9 +74,20 @@ def insert(self, data):
7474
self.connection.rollback()
7575
raise e
7676

77+
def exists(self):
78+
if not self._wheres:
79+
return False # pragma: no cover
80+
sql = f"SELECT exists (SELECT * from {self.table_name} {self._wheres})"
81+
try:
82+
self.cursor.execute(sql, self._where_bindings)
83+
return self._fetch_one()[0]
84+
except Exception as e:
85+
self.connection.rollback()
86+
raise e
87+
7788
def delete(self):
7889
if not self._where_bindings:
79-
return False
90+
return False # pragma: no cover
8091
sql = f"DELETE from {self.table_name} {self._wheres}"
8192

8293
try:
@@ -88,7 +99,7 @@ def delete(self):
8899

89100
def update(self, data):
90101
if not self._where_bindings:
91-
return False
102+
return False # pragma: no cover
92103
holders = ",".join([f"{key}= %s" for key in data])
93104

94105
sql = f"UPDATE {self.table_name} set {holders} {self._wheres}"

tests/unit/test_Validation.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,29 @@ def test_max_lenght_passes(self):
4545
validator.validate({"name": "max_length:10"})
4646
self.assertNotIn("name", validator.errors)
4747

48+
def test_validate_email_fails(self):
49+
validator = Validator(dict(email="some_invald_email"))
50+
self.assert_fails(validator, {"email": "email"})
51+
self.assertIn("email", validator.errors)
52+
53+
def test_validate_email_passes(self):
54+
validator = Validator(dict(email="[email protected]"))
55+
validator.validate({"email": "email"})
56+
self.assertNotIn("email", validator.errors)
57+
58+
def test_validate_confirmed_fails(self):
59+
validator = Validator(
60+
dict(password="secret", password_confirmation="password")
61+
)
62+
self.assert_fails(validator, {"password": "confirmed"})
63+
self.assertIn("password", validator.errors)
64+
65+
def test_validate_confirmed_passed(self):
66+
validator = Validator(
67+
dict(password="secret", password_confirmation="secret")
68+
)
69+
validator.validate({"password": "confirmed"})
70+
self.assertNotIn("password", validator.errors)
71+
4872
def assert_fails(self, validator, data):
4973
self.assertRaises(ValidationException, validator.validate, data)

0 commit comments

Comments
 (0)