cuenca-validations 2.0.5.dev3__tar.gz → 2.0.5.dev5__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.
Files changed (35) hide show
  1. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/PKG-INFO +1 -1
  2. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/types/__init__.py +2 -2
  3. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/types/general.py +3 -3
  4. cuenca_validations-2.0.5.dev5/cuenca_validations/types/helpers.py +22 -0
  5. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/types/requests.py +2 -2
  6. cuenca_validations-2.0.5.dev5/cuenca_validations/version.py +1 -0
  7. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations.egg-info/PKG-INFO +1 -1
  8. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/tests/test_types.py +54 -13
  9. cuenca_validations-2.0.5.dev3/cuenca_validations/types/helpers.py +0 -10
  10. cuenca_validations-2.0.5.dev3/cuenca_validations/version.py +0 -1
  11. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/LICENSE +0 -0
  12. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/README.md +0 -0
  13. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/__init__.py +0 -0
  14. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/card_bins.py +0 -0
  15. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/errors.py +0 -0
  16. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/py.typed +0 -0
  17. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/types/card.py +0 -0
  18. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/types/enums.py +0 -0
  19. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/types/files.py +0 -0
  20. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/types/identities.py +0 -0
  21. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/types/morals.py +0 -0
  22. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/types/queries.py +0 -0
  23. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/typing.py +0 -0
  24. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations/validators.py +0 -0
  25. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations.egg-info/SOURCES.txt +0 -0
  26. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations.egg-info/dependency_links.txt +0 -0
  27. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations.egg-info/requires.txt +0 -0
  28. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/cuenca_validations.egg-info/top_level.txt +0 -0
  29. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/setup.cfg +0 -0
  30. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/setup.py +0 -0
  31. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/tests/__init__.py +0 -0
  32. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/tests/test_card.py +0 -0
  33. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/tests/test_errors.py +0 -0
  34. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/tests/test_helpers.py +0 -0
  35. {cuenca_validations-2.0.5.dev3 → cuenca_validations-2.0.5.dev5}/tests/test_statement.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cuenca_validations
3
- Version: 2.0.5.dev3
3
+ Version: 2.0.5.dev5
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -104,7 +104,7 @@ __all__ = [
104
104
  'digits',
105
105
  'get_state_name',
106
106
  'uuid_field',
107
- 'Metadata',
107
+ 'LogConfig',
108
108
  ]
109
109
 
110
110
  from .card import StrictPaymentCardNumber
@@ -153,7 +153,7 @@ from .enums import (
153
153
  from .files import BatchFileMetadata
154
154
  from .general import (
155
155
  JSONEncoder,
156
- Metadata,
156
+ LogConfig,
157
157
  SantizedDict,
158
158
  StrictPositiveInt,
159
159
  digits,
@@ -86,6 +86,6 @@ def get_state_name(state: State):
86
86
 
87
87
 
88
88
  @dataclass
89
- class Metadata:
90
- sensitive: bool
91
- log_chars: int = 0
89
+ class LogConfig:
90
+ masked: bool = False
91
+ unmasked_chars_length: int = 0
@@ -0,0 +1,22 @@
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
@@ -55,7 +55,7 @@ from .card import (
55
55
  StrictPaymentCardNumber,
56
56
  )
57
57
  from .general import (
58
- Metadata,
58
+ LogConfig,
59
59
  SerializableAnyUrl,
60
60
  SerializableHttpUrl,
61
61
  StrictPositiveInt,
@@ -496,7 +496,7 @@ class UserUpdateRequest(BaseModel):
496
496
 
497
497
  class UserLoginRequest(BaseRequest):
498
498
  password: Annotated[
499
- str, Metadata(sensitive=True)
499
+ str, LogConfig(masked=True)
500
500
  ] # Set password field to str for backward compatibility.
501
501
  user_id: Optional[str] = Field(None, description='Deprecated field')
502
502
  model_config = ConfigDict(
@@ -0,0 +1 @@
1
+ __version__ = '2.0.5.dev5'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cuenca_validations
3
- Version: 2.0.5.dev3
3
+ Version: 2.0.5.dev5
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -6,7 +6,8 @@ from typing import Annotated
6
6
 
7
7
  import pytest
8
8
  from freezegun import freeze_time
9
- from pydantic import BaseModel, SecretStr, ValidationError
9
+ from pydantic import AfterValidator, BaseModel, SecretStr, ValidationError
10
+ from pydantic.fields import FieldInfo
10
11
 
11
12
  from cuenca_validations.types import (
12
13
  Address,
@@ -25,7 +26,9 @@ from cuenca_validations.types.enums import (
25
26
  SessionType,
26
27
  State,
27
28
  )
28
- from cuenca_validations.types.general import Metadata, StrictPositiveInt
29
+ from cuenca_validations.types.general import LogConfig, StrictPositiveInt
30
+ from cuenca_validations.types.helpers import get_log_config
31
+ from cuenca_validations.types.identities import Password
29
32
  from cuenca_validations.types.requests import (
30
33
  ApiKeyUpdateRequest,
31
34
  BankAccountValidationRequest,
@@ -589,22 +592,60 @@ def test_strict_positive_int_invalid(value, expected_error, expected_message):
589
592
  IntModel(value=value)
590
593
 
591
594
 
592
- class MetadataModel(BaseModel):
593
- secret: Annotated[str, Metadata(sensitive=True)]
594
- partial_secret: Annotated[str, Metadata(sensitive=True, log_chars=4)]
595
+ def validate_repeated_digits(password: str) -> str:
596
+ """
597
+ Example of a custom validator
598
+ Check if the str contains repeated numbers
599
+ """
600
+ import re
601
+
602
+ if re.search(r'(\d).*\1', password):
603
+ raise ValueError("str cannot contain repeated digits")
604
+ return password
605
+
606
+
607
+ class LogConfigModel(BaseModel):
608
+ password: Annotated[Password, LogConfig(masked=True)]
609
+ validated: Annotated[
610
+ str, AfterValidator(validate_repeated_digits), LogConfig(masked=True)
611
+ ]
612
+ secret: Annotated[str, LogConfig(masked=True)]
613
+ partial_secret: Annotated[
614
+ str, LogConfig(masked=True, unmasked_chars_length=4)
615
+ ]
616
+
617
+
618
+ def test_log_config():
619
+ model = LogConfigModel(
620
+ password="Mypass123.",
621
+ validated="str123",
622
+ secret="super-secret",
623
+ partial_secret="1234567890",
624
+ )
595
625
 
626
+ password_field = LogConfigModel.model_fields["password"]
627
+ validated_field = LogConfigModel.model_fields["validated"]
628
+ secret_field = LogConfigModel.model_fields["secret"]
629
+ partial_field = LogConfigModel.model_fields["partial_secret"]
596
630
 
597
- def test_metadata():
598
- model = MetadataModel(secret="super-secret", partial_secret="1234567890")
631
+ assert get_log_config(password_field).masked is True
632
+ assert get_log_config(password_field).unmasked_chars_length == 0
599
633
 
600
- secret_field = MetadataModel.model_fields["secret"]
601
- partial_field = MetadataModel.model_fields["partial_secret"]
634
+ assert get_log_config(validated_field).masked is True
635
+ assert get_log_config(validated_field).unmasked_chars_length == 0
602
636
 
603
- assert secret_field.metadata[0].sensitive is True
604
- assert secret_field.metadata[0].log_chars == 0
637
+ assert get_log_config(secret_field).masked is True
638
+ assert get_log_config(secret_field).unmasked_chars_length == 0
605
639
 
606
- assert partial_field.metadata[0].sensitive is True
607
- assert partial_field.metadata[0].log_chars == 4
640
+ assert get_log_config(partial_field).masked is True
641
+ assert get_log_config(partial_field).unmasked_chars_length == 4
608
642
 
643
+ assert model.password.get_secret_value() == "Mypass123."
644
+ assert model.validated == "str123"
609
645
  assert model.secret == "super-secret"
610
646
  assert model.partial_secret == "1234567890"
647
+
648
+
649
+ def test_get_log_config_no_log_config():
650
+ field = FieldInfo(default=None)
651
+ assert get_log_config(field) is None
@@ -1,10 +0,0 @@
1
- import uuid
2
- from base64 import urlsafe_b64encode
3
- from typing import Callable
4
-
5
-
6
- def uuid_field(prefix: str = '') -> Callable[[], str]:
7
- def base64_uuid_func() -> str:
8
- return prefix + urlsafe_b64encode(uuid.uuid4().bytes).decode()[:-2]
9
-
10
- return base64_uuid_func
@@ -1 +0,0 @@
1
- __version__ = '2.0.5.dev3'