cuenca-validations 2.1.14.dev100__tar.gz → 2.1.14.dev102__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.14.dev100 → cuenca_validations-2.1.14.dev102}/PKG-INFO +1 -1
- cuenca_validations-2.1.14.dev102/cuenca_validations/types/helpers.py +44 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/types/requests.py +17 -19
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/validators.py +0 -9
- cuenca_validations-2.1.14.dev102/cuenca_validations/version.py +1 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations.egg-info/PKG-INFO +1 -1
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/tests/test_requests.py +2 -2
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/tests/test_types.py +6 -0
- cuenca_validations-2.1.14.dev100/cuenca_validations/types/helpers.py +0 -22
- cuenca_validations-2.1.14.dev100/cuenca_validations/version.py +0 -1
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/LICENSE +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/README.md +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/__init__.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/card_bins.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/errors.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/py.typed +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/types/__init__.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/types/card.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/types/enums.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/types/files.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/types/general.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/types/identities.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/types/morals.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/types/queries.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/typing.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations.egg-info/SOURCES.txt +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations.egg-info/dependency_links.txt +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations.egg-info/requires.txt +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations.egg-info/top_level.txt +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/setup.cfg +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/setup.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/tests/__init__.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/tests/test_card.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/tests/test_errors.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/tests/test_helpers.py +0 -0
- {cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/tests/test_statement.py +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import datetime as dt
|
|
2
|
+
import uuid
|
|
3
|
+
from base64 import urlsafe_b64encode
|
|
4
|
+
from typing import Callable, Optional, Union
|
|
5
|
+
|
|
6
|
+
from dateutil.relativedelta import relativedelta
|
|
7
|
+
from pydantic.fields import FieldInfo
|
|
8
|
+
|
|
9
|
+
from .general import LogConfig
|
|
10
|
+
from .identities import Curp
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def uuid_field(prefix: str = '') -> Callable[[], str]:
|
|
14
|
+
def base64_uuid_func() -> str:
|
|
15
|
+
return prefix + urlsafe_b64encode(uuid.uuid4().bytes).decode()[:-2]
|
|
16
|
+
|
|
17
|
+
return base64_uuid_func
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_log_config(field: FieldInfo) -> Optional[LogConfig]:
|
|
21
|
+
"""Helper function to find LogConfig in field metadata"""
|
|
22
|
+
try:
|
|
23
|
+
return next(m for m in field.metadata if isinstance(m, LogConfig))
|
|
24
|
+
except StopIteration:
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_birth_date_from_curp(curp: Curp) -> dt.date:
|
|
29
|
+
curp_date = curp[4:10] # YYMMDD
|
|
30
|
+
yy = int(curp_date[:2])
|
|
31
|
+
current_yy = dt.date.today().year % 100
|
|
32
|
+
century = '19' if yy > current_yy else '20'
|
|
33
|
+
birth_date = dt.datetime.strptime(century + curp_date, '%Y%m%d').date()
|
|
34
|
+
return birth_date
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def validate_age_requirement(birth_date: Union[dt.date, Curp]) -> dt.date:
|
|
38
|
+
if isinstance(birth_date, str):
|
|
39
|
+
birth_date = get_birth_date_from_curp(birth_date)
|
|
40
|
+
|
|
41
|
+
current_date = dt.date.today()
|
|
42
|
+
if relativedelta(current_date, birth_date).years < 18:
|
|
43
|
+
raise ValueError('User does not meet age requirement.')
|
|
44
|
+
return birth_date
|
|
@@ -51,7 +51,6 @@ from ..types.enums import (
|
|
|
51
51
|
WebhookObject,
|
|
52
52
|
)
|
|
53
53
|
from ..typing import DictStrAny
|
|
54
|
-
from ..validators import validate_age_requirement
|
|
55
54
|
from .card import (
|
|
56
55
|
Cvv,
|
|
57
56
|
ExpMonth,
|
|
@@ -67,6 +66,7 @@ from .general import (
|
|
|
67
66
|
SerializableIPvAnyAddress,
|
|
68
67
|
StrictPositiveInt,
|
|
69
68
|
)
|
|
69
|
+
from .helpers import validate_age_requirement
|
|
70
70
|
from .identities import (
|
|
71
71
|
AddressRequest,
|
|
72
72
|
Beneficiary,
|
|
@@ -87,7 +87,7 @@ from .morals import (
|
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
CUENCA_FILE_URL = (
|
|
90
|
-
r'^https:\/\/(?:stage|sandbox|api)\.cuenca\.com\/files\/([a-zA-Z0-9\-]+)$'
|
|
90
|
+
r'^https:\/\/(?:stage|sandbox|api)\.cuenca\.com\/files\/([a-zA-Z0-9\-_]+)$'
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
|
|
@@ -330,14 +330,14 @@ class CurpValidationRequest(BaseModel):
|
|
|
330
330
|
names: Optional[str] = None
|
|
331
331
|
first_surname: Optional[str] = None
|
|
332
332
|
second_surname: Optional[str] = Field(
|
|
333
|
-
None, description='Not necessary for foreigners'
|
|
333
|
+
default=None, description='Not necessary for foreigners'
|
|
334
334
|
)
|
|
335
335
|
date_of_birth: Optional[dt.date] = None
|
|
336
336
|
state_of_birth: Optional[State] = Field(
|
|
337
|
-
None, description='In format ISO 3166 Alpha-2'
|
|
337
|
+
default=None, description='In format ISO 3166 Alpha-2'
|
|
338
338
|
)
|
|
339
339
|
country_of_birth: Optional[Country] = Field(
|
|
340
|
-
None, description='In format ISO 3166 Alpha-2'
|
|
340
|
+
default=None, description='In format ISO 3166 Alpha-2'
|
|
341
341
|
)
|
|
342
342
|
gender: Optional[Gender] = None
|
|
343
343
|
manual_curp: Optional[Curp] = Field(
|
|
@@ -376,6 +376,15 @@ class CurpValidationRequest(BaseModel):
|
|
|
376
376
|
raise
|
|
377
377
|
return date_of_birth
|
|
378
378
|
|
|
379
|
+
@field_validator('manual_curp')
|
|
380
|
+
@classmethod
|
|
381
|
+
def validate_manual_curp_birth_date(
|
|
382
|
+
cls, manual_curp: Optional[Curp]
|
|
383
|
+
) -> Optional[Curp]:
|
|
384
|
+
if manual_curp:
|
|
385
|
+
validate_age_requirement(manual_curp)
|
|
386
|
+
return manual_curp
|
|
387
|
+
|
|
379
388
|
@model_validator(mode="before")
|
|
380
389
|
@classmethod
|
|
381
390
|
def validate_state_of_birth(cls, values: DictStrAny) -> DictStrAny:
|
|
@@ -468,24 +477,12 @@ class UserRequest(BaseModel):
|
|
|
468
477
|
@classmethod
|
|
469
478
|
def validate_birth_date(cls, curp: Optional[Curp]) -> Optional[Curp]:
|
|
470
479
|
if curp:
|
|
471
|
-
|
|
472
|
-
curp_date = curp[4:10]
|
|
473
|
-
century = (
|
|
474
|
-
'19'
|
|
475
|
-
if int(curp_date[:2]) > int(str(current_date.year)[:2])
|
|
476
|
-
else '20'
|
|
477
|
-
)
|
|
478
|
-
birth_date = dt.datetime.strptime(century + curp_date, '%Y%m%d')
|
|
479
|
-
try:
|
|
480
|
-
validate_age_requirement(birth_date)
|
|
481
|
-
except ValueError:
|
|
482
|
-
raise
|
|
480
|
+
validate_age_requirement(curp)
|
|
483
481
|
return curp
|
|
484
482
|
|
|
485
483
|
|
|
486
484
|
class UserUpdateRequest(BaseModel):
|
|
487
485
|
profession: Optional[Profession] = None
|
|
488
|
-
verification_id: Optional[str] = None
|
|
489
486
|
email_verification_id: Optional[str] = None
|
|
490
487
|
phone_verification_id: Optional[str] = None
|
|
491
488
|
address: Optional[AddressRequest] = None
|
|
@@ -493,10 +490,11 @@ class UserUpdateRequest(BaseModel):
|
|
|
493
490
|
govt_id: Optional[KYCFile] = None
|
|
494
491
|
proof_of_address: Optional[KYCFile] = None
|
|
495
492
|
proof_of_life: Optional[KYCFile] = None
|
|
496
|
-
signature: Optional[KYCFile] = None
|
|
497
493
|
curp_document_uri: Optional[SerializableHttpUrl] = None
|
|
498
494
|
fiscal_regime_code: Optional[SATRegimeCode] = None
|
|
495
|
+
pronouns: Optional[str] = None
|
|
499
496
|
status: Optional[UserStatus] = None
|
|
497
|
+
required_level: Optional[int] = None
|
|
500
498
|
|
|
501
499
|
@field_validator('beneficiaries')
|
|
502
500
|
@classmethod
|
|
@@ -3,8 +3,6 @@ import datetime as dt
|
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from typing import Any, Callable, Optional, Union
|
|
5
5
|
|
|
6
|
-
from dateutil.relativedelta import relativedelta
|
|
7
|
-
|
|
8
6
|
|
|
9
7
|
def sanitize_dict(d: dict) -> dict:
|
|
10
8
|
for k, v in d.items():
|
|
@@ -42,10 +40,3 @@ def sanitize_item(
|
|
|
42
40
|
else:
|
|
43
41
|
rv = item
|
|
44
42
|
return rv
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def validate_age_requirement(birth_date: dt.date) -> dt.date:
|
|
48
|
-
current_date = dt.date.today()
|
|
49
|
-
if relativedelta(current_date, birth_date).years < 18:
|
|
50
|
-
raise ValueError('User does not meet age requirement.')
|
|
51
|
-
return birth_date
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.1.14.dev102'
|
{cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/tests/test_requests.py
RENAMED
|
@@ -11,12 +11,12 @@ def test_file_cuenca_url(environment: str) -> None:
|
|
|
11
11
|
tos_id='TS67dcae8e74e81bba5a77bf47',
|
|
12
12
|
location=(19.432607, -99.133209),
|
|
13
13
|
signature_image_url=(
|
|
14
|
-
f'https://{environment}.cuenca.com/files/
|
|
14
|
+
f'https://{environment}.cuenca.com/files/EFQL8_ohvoRp-PkOTYgvQYFA'
|
|
15
15
|
),
|
|
16
16
|
)
|
|
17
17
|
utos = UserTOSAgreementRequest(**request_data)
|
|
18
18
|
assert utos.signature_image_url is not None
|
|
19
|
-
assert utos.signature_image_url.file_id == '
|
|
19
|
+
assert utos.signature_image_url.file_id == 'EFQL8_ohvoRp-PkOTYgvQYFA'
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def test_file_cuenca_url_invalid() -> None:
|
|
@@ -385,6 +385,12 @@ def test_curp_validation_request():
|
|
|
385
385
|
assert 'state_of_birth required' in str(v)
|
|
386
386
|
|
|
387
387
|
|
|
388
|
+
def test_curp_validation_request_underage() -> None:
|
|
389
|
+
with pytest.raises(ValueError) as v:
|
|
390
|
+
CurpValidationRequest(manual_curp='ABCD240614HDFSRN03')
|
|
391
|
+
assert 'User does not meet age requirement.' in str(v)
|
|
392
|
+
|
|
393
|
+
|
|
388
394
|
def test_user_update_request():
|
|
389
395
|
request = dict(
|
|
390
396
|
beneficiaries=[
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import uuid
|
|
2
|
-
from base64 import urlsafe_b64encode
|
|
3
|
-
from typing import Callable, Optional
|
|
4
|
-
|
|
5
|
-
from pydantic.fields import FieldInfo
|
|
6
|
-
|
|
7
|
-
from .general import LogConfig
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def uuid_field(prefix: str = '') -> Callable[[], str]:
|
|
11
|
-
def base64_uuid_func() -> str:
|
|
12
|
-
return prefix + urlsafe_b64encode(uuid.uuid4().bytes).decode()[:-2]
|
|
13
|
-
|
|
14
|
-
return base64_uuid_func
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def get_log_config(field: FieldInfo) -> Optional[LogConfig]:
|
|
18
|
-
"""Helper function to find LogConfig in field metadata"""
|
|
19
|
-
try:
|
|
20
|
-
return next(m for m in field.metadata if isinstance(m, LogConfig))
|
|
21
|
-
except StopIteration:
|
|
22
|
-
return None
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.1.14.dev100'
|
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/errors.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/py.typed
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
|
{cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/cuenca_validations/typing.py
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
|
{cuenca_validations-2.1.14.dev100 → cuenca_validations-2.1.14.dev102}/tests/test_statement.py
RENAMED
|
File without changes
|