cuenca-validations 2.1.28__tar.gz → 2.1.29.dev0__tar.gz
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.
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/PKG-INFO +1 -1
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/general.py +8 -1
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/queries.py +5 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/validators.py +14 -0
- cuenca_validations-2.1.29.dev0/cuenca_validations/version.py +1 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations.egg-info/PKG-INFO +1 -1
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/tests/test_types.py +46 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/tests/test_validators.py +17 -0
- cuenca_validations-2.1.28/cuenca_validations/version.py +0 -1
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/LICENSE +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/README.md +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/__init__.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/card_bins.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/errors.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/py.typed +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/__init__.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/card.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/enums.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/files.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/helpers.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/identities.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/morals.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/requests.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/typing.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations.egg-info/SOURCES.txt +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations.egg-info/dependency_links.txt +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations.egg-info/requires.txt +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations.egg-info/top_level.txt +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/setup.cfg +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/setup.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/tests/__init__.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/tests/test_card.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/tests/test_errors.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/tests/test_helpers.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/tests/test_requests.py +0 -0
- {cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/tests/test_statement.py +0 -0
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/general.py
RENAMED
|
@@ -3,6 +3,7 @@ from dataclasses import dataclass
|
|
|
3
3
|
from typing import Annotated, Any, Optional
|
|
4
4
|
|
|
5
5
|
from pydantic import (
|
|
6
|
+
AfterValidator,
|
|
6
7
|
AnyUrl,
|
|
7
8
|
Field,
|
|
8
9
|
HttpUrl,
|
|
@@ -11,7 +12,7 @@ from pydantic import (
|
|
|
11
12
|
StringConstraints,
|
|
12
13
|
)
|
|
13
14
|
|
|
14
|
-
from ..validators import sanitize_dict, sanitize_item
|
|
15
|
+
from ..validators import normalize_name, sanitize_dict, sanitize_item
|
|
15
16
|
from .enums import (
|
|
16
17
|
AccountUseType,
|
|
17
18
|
IncomeType,
|
|
@@ -33,6 +34,12 @@ NonEmptyStr = Annotated[
|
|
|
33
34
|
str, StringConstraints(strip_whitespace=True, min_length=1)
|
|
34
35
|
]
|
|
35
36
|
|
|
37
|
+
NormalizedName = Annotated[
|
|
38
|
+
str,
|
|
39
|
+
StringConstraints(strip_whitespace=True, min_length=2),
|
|
40
|
+
AfterValidator(normalize_name),
|
|
41
|
+
]
|
|
42
|
+
|
|
36
43
|
|
|
37
44
|
class SantizedDict(dict):
|
|
38
45
|
def __init__(self, *args, **kwargs):
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/queries.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
2
|
from typing import Annotated, Optional
|
|
3
3
|
|
|
4
|
+
from clabe import Clabe
|
|
4
5
|
from pydantic import (
|
|
5
6
|
BaseModel,
|
|
6
7
|
ConfigDict,
|
|
@@ -26,6 +27,7 @@ from .enums import (
|
|
|
26
27
|
TransferNetwork,
|
|
27
28
|
UserStatus,
|
|
28
29
|
)
|
|
30
|
+
from .general import NormalizedName
|
|
29
31
|
from .identities import Curp
|
|
30
32
|
|
|
31
33
|
MAX_PAGE_SIZE = 100
|
|
@@ -157,6 +159,9 @@ class UserQuery(QueryParams):
|
|
|
157
159
|
status: Optional[UserStatus] = None
|
|
158
160
|
identity_uri: Optional[str] = None
|
|
159
161
|
has_curp_document: Optional[bool] = None
|
|
162
|
+
clabe: Optional[Clabe] = None
|
|
163
|
+
curp: Optional[Curp] = None
|
|
164
|
+
name: Optional[NormalizedName] = None
|
|
160
165
|
|
|
161
166
|
|
|
162
167
|
class IdentityQuery(QueryParams):
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/validators.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import datetime as dt
|
|
3
3
|
import re
|
|
4
|
+
import unicodedata
|
|
4
5
|
from enum import Enum
|
|
5
6
|
from typing import Any, Callable, Optional, Union
|
|
6
7
|
|
|
@@ -34,6 +35,19 @@ def normalize_phone_number(phone_number: str) -> str:
|
|
|
34
35
|
return f'+{pn}'
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
def normalize_name(name: str) -> str:
|
|
39
|
+
"""Normalize names for search/index matching.
|
|
40
|
+
|
|
41
|
+
Strips accents, lowercases, and collapses internal whitespace.
|
|
42
|
+
|
|
43
|
+
Raúl Andrés -> raul andres
|
|
44
|
+
MARÍA JOSÉ -> maria jose
|
|
45
|
+
"""
|
|
46
|
+
collapsed = ' '.join(name.split())
|
|
47
|
+
nfkd = unicodedata.normalize('NFKD', collapsed)
|
|
48
|
+
return ''.join(c for c in nfkd if not unicodedata.combining(c)).lower()
|
|
49
|
+
|
|
50
|
+
|
|
37
51
|
def sanitize_dict(d: dict) -> dict:
|
|
38
52
|
for k, v in d.items():
|
|
39
53
|
d[k] = sanitize_item(v)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.1.29.dev0'
|
|
@@ -5,6 +5,7 @@ from enum import Enum
|
|
|
5
5
|
from typing import Annotated
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
|
+
from clabe import Clabe
|
|
8
9
|
from freezegun import freeze_time
|
|
9
10
|
from pydantic import AfterValidator, BaseModel, SecretStr, ValidationError
|
|
10
11
|
from pydantic.fields import FieldInfo
|
|
@@ -16,6 +17,7 @@ from cuenca_validations.types import (
|
|
|
16
17
|
SantizedDict,
|
|
17
18
|
SessionRequest,
|
|
18
19
|
TransactionStatus,
|
|
20
|
+
UserQuery,
|
|
19
21
|
digits,
|
|
20
22
|
get_account_use_type_name,
|
|
21
23
|
get_income_type_name,
|
|
@@ -201,6 +203,50 @@ def test_card_query_exp_cvv_if_number_not_set(input_value):
|
|
|
201
203
|
CardQuery(**input_value)
|
|
202
204
|
|
|
203
205
|
|
|
206
|
+
def test_user_query_accepts_new_fields():
|
|
207
|
+
query = UserQuery(
|
|
208
|
+
clabe='646180157098510917',
|
|
209
|
+
curp='ABCD920604HDFSRN03',
|
|
210
|
+
name='Pedro Páramo',
|
|
211
|
+
)
|
|
212
|
+
assert isinstance(query.clabe, Clabe)
|
|
213
|
+
assert query.clabe == '646180157098510917'
|
|
214
|
+
assert query.curp == 'ABCD920604HDFSRN03'
|
|
215
|
+
assert query.name == 'pedro paramo'
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@pytest.mark.parametrize(
|
|
219
|
+
'raw, normalized',
|
|
220
|
+
[
|
|
221
|
+
('ab', 'ab'),
|
|
222
|
+
(' ab ', 'ab'),
|
|
223
|
+
('Raúl Andrés', 'raul andres'),
|
|
224
|
+
('raul Andres', 'raul andres'),
|
|
225
|
+
(' RAÚL ANDRÉS ', 'raul andres'),
|
|
226
|
+
('María José', 'maria jose'),
|
|
227
|
+
('ÑANDÚ', 'nandu'),
|
|
228
|
+
],
|
|
229
|
+
)
|
|
230
|
+
def test_user_query_name_normalizes(raw, normalized):
|
|
231
|
+
assert UserQuery(name=raw).name == normalized
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@pytest.mark.parametrize(
|
|
235
|
+
'field, value',
|
|
236
|
+
[
|
|
237
|
+
('clabe', 'not-a-clabe'),
|
|
238
|
+
('curp', 'not-a-curp'),
|
|
239
|
+
('name', ''),
|
|
240
|
+
('name', ' '),
|
|
241
|
+
('name', 'a'),
|
|
242
|
+
('name', ' a '),
|
|
243
|
+
],
|
|
244
|
+
)
|
|
245
|
+
def test_user_query_rejects_invalid(field, value):
|
|
246
|
+
with pytest.raises(ValidationError):
|
|
247
|
+
UserQuery(**{field: value})
|
|
248
|
+
|
|
249
|
+
|
|
204
250
|
def test_exclude_none_in_dict():
|
|
205
251
|
request = ApiKeyUpdateRequest(user_id='US123')
|
|
206
252
|
assert request.model_dump() == dict(user_id='US123')
|
|
@@ -2,6 +2,7 @@ import pytest
|
|
|
2
2
|
|
|
3
3
|
from cuenca_validations.validators import (
|
|
4
4
|
normalize_email,
|
|
5
|
+
normalize_name,
|
|
5
6
|
normalize_phone_number,
|
|
6
7
|
)
|
|
7
8
|
|
|
@@ -33,3 +34,19 @@ def test_normalize_email(raw: str, normalized: str) -> None:
|
|
|
33
34
|
)
|
|
34
35
|
def test_normalize_phone_number(raw: str, normalized: str) -> None:
|
|
35
36
|
assert normalize_phone_number(raw) == normalized
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.parametrize(
|
|
40
|
+
'raw, normalized',
|
|
41
|
+
[
|
|
42
|
+
('Raúl Andrés', 'raul andres'), # accents + mixed case
|
|
43
|
+
('raul andres', 'raul andres'), # already normalized
|
|
44
|
+
('RAÚL ANDRÉS', 'raul andres'), # uppercase with accents
|
|
45
|
+
('ÑANDÚ', 'nandu'), # ñ and accent
|
|
46
|
+
('María José', 'maria jose'), # collapse internal whitespace
|
|
47
|
+
(' Raúl ', 'raul'), # trim + lowercase
|
|
48
|
+
('Nuño Garçía', 'nuno garcia'), # tilde and cedilla
|
|
49
|
+
],
|
|
50
|
+
)
|
|
51
|
+
def test_normalize_name(raw: str, normalized: str) -> None:
|
|
52
|
+
assert normalize_name(raw) == normalized
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.1.28'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/card_bins.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/__init__.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/card.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/enums.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/files.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/helpers.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/identities.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/morals.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations/types/requests.py
RENAMED
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.28 → cuenca_validations-2.1.29.dev0}/cuenca_validations.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|