diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 0000000..ea3a867 --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,25 @@ +[mypy] +disable_error_code = type-abstract +plugins = pydantic.mypy + +# --strict +disallow_any_generics = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +check_untyped_defs = true +disallow_untyped_decorators = true +no_implicit_optional = true +warn_redundant_casts = true +warn_unused_ignores = true +warn_return_any = true +implicit_reexport = false +strict_equality = true + +# --strict end + +[pydantic-mypy] +init_forbid_extra = True +init_typed = True +warn_required_dynamic_aliases = True \ No newline at end of file diff --git a/README.md b/README.md index b2f9c7f..a9cf901 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,25 @@ pip install alice-onboarding Configure your credentials with *Config* class -``` +```python from alice import Config -config = Config(api_key=given_valid_api_key) +config = Config(api_key="") ``` +By default, this configure the `production` environment, if you +want to use the `sandbox` environment, you have two options: + +* Add `environment` parameter on `Config` + ```python + from alice import Config, Environment + + config = Config(api_key="", environment=Environment.SANDBOX) + ``` +* Or, just export the `ALICE_ENVIRONMENT` environment variable + ```console + export ALICE_ENVIRONMENT=sandbox + ``` #### Onboarding diff --git a/alice/auth/auth.py b/alice/auth/auth.py index db7f849..9c030dc 100644 --- a/alice/auth/auth.py +++ b/alice/auth/auth.py @@ -23,7 +23,7 @@ def from_config(config: Config) -> "Auth": return Auth( api_key=config.api_key, # type: ignore session=session, - url=config.onboarding_url, + url=config.onboarding_url, # type: ignore timeout=config.timeout, verbose=config.verbose, ) diff --git a/alice/auth/auth_client.py b/alice/auth/auth_client.py index 771715e..7bc6822 100644 --- a/alice/auth/auth_client.py +++ b/alice/auth/auth_client.py @@ -43,7 +43,6 @@ def __init__( def create_user_token( self, user_id: str, verbose: Optional[bool] = False ) -> Response: - print_intro("create_user_token", verbose=verbose) token = self._cached_user_token_stack.get(user_id) diff --git a/alice/auth/cached_token_stack.py b/alice/auth/cached_token_stack.py index 01d143d..696eba1 100644 --- a/alice/auth/cached_token_stack.py +++ b/alice/auth/cached_token_stack.py @@ -46,11 +46,9 @@ def show(self) -> None: @timeit def _clear_expired_tokens(self) -> None: - num_data = len(self._data) if num_data > 0: - latest_expired_token = None for i, token in enumerate(reversed(list(self._data.values()))): if not is_valid_token(token): diff --git a/alice/config.py b/alice/config.py index a3a02b7..6d3fecc 100644 --- a/alice/config.py +++ b/alice/config.py @@ -1,22 +1,60 @@ from typing import Union -from pydantic import BaseModel, Field +from pydantic import Field, model_validator +from pydantic_settings import BaseSettings, SettingsConfigDict from requests import Session +from alice.onboarding.enums.environment import Environment -class Config(BaseModel): - class Config: - arbitrary_types_allowed = True - onboarding_url: str = Field(default="https://apis.alicebiometrics.com/onboarding") - sandbox_url: str = Field( - default="https://apis.alicebiometrics.com/onboarding/sandbox" - ) +class Config(BaseSettings): + model_config = SettingsConfigDict(arbitrary_types_allowed=True) + api_key: Union[str, None] = Field(default=None) - sandbox_token: Union[str, None] = Field(default=None) + environment: Union[Environment, None] = Field( + default=Environment.PRODUCTION, alias="ALICE_ENVIRONMENT" + ) + verbose: bool = Field(default=False) + session: Union[Session, None] = Field(default=None) timeout: Union[float, None] = Field( default=None, description="Timeout for every request in seconds", ge=0, le=100 ) send_agent: bool = Field(default=True) - verbose: bool = Field(default=False) - session: Union[Session, None] = Field(default=None) + onboarding_url: Union[str, None] = Field( + default="https://apis.alicebiometrics.com/onboarding" + ) + sandbox_url: Union[str, None] = Field( + default="https://apis.alicebiometrics.com/onboarding/sandbox", + description="This path is only used for trials", + ) + sandbox_token: Union[str, None] = Field( + default=None, description="This token is only used for trials" + ) + + @model_validator(mode="after") + def validate_urls(self) -> "Config": + if self.environment is None: + if isinstance(self.onboarding_url, str): + if "sandbox" in self.onboarding_url: + self.environment = Environment.SANDBOX + elif "staging" in self.onboarding_url: + self.environment = Environment.STAGING + else: + if self.environment == Environment.PRODUCTION: + self.onboarding_url = "https://apis.alicebiometrics.com/onboarding" + self.sandbox_url = "https://apis.alicebiometrics.com/onboarding/sandbox" + elif self.environment == Environment.SANDBOX: + self.onboarding_url = ( + "https://apis.sandbox.alicebiometrics.com/onboarding" + ) + self.sandbox_url = ( + "https://apis.sandbox.alicebiometrics.com/onboarding/sandbox" + ) + elif self.environment == Environment.STAGING: + self.onboarding_url = ( + "https://apis.staging.alicebiometrics.com/onboarding" + ) + self.sandbox_url = ( + "https://apis.staging.alicebiometrics.com/onboarding/sandbox" + ) + return self diff --git a/alice/onboarding/enums/environment.py b/alice/onboarding/enums/environment.py new file mode 100644 index 0000000..47991c7 --- /dev/null +++ b/alice/onboarding/enums/environment.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class Environment(Enum): + SANDBOX = "sandbox" + PRODUCTION = "production" + STAGING = "staging" diff --git a/alice/onboarding/models/report/document/document_side_report_meta.py b/alice/onboarding/models/report/document/document_side_report_meta.py index 1c0016c..228751a 100644 --- a/alice/onboarding/models/report/document/document_side_report_meta.py +++ b/alice/onboarding/models/report/document/document_side_report_meta.py @@ -1,3 +1,5 @@ +from typing import Union + from pydantic import BaseModel, Field @@ -6,9 +8,9 @@ class DocumentSideReportMeta(BaseModel): It collects document side metadata """ - template: str = Field( + template: Union[str, None] = Field( default=None, description="Document version used to extract the info" ) - manual: bool = Field( + manual: Union[bool, None] = Field( default=None, description="True if the capture was taken in manual mode" ) diff --git a/alice/onboarding/onboarding.py b/alice/onboarding/onboarding.py index 4ba0d15..6ae0666 100644 --- a/alice/onboarding/onboarding.py +++ b/alice/onboarding/onboarding.py @@ -37,7 +37,7 @@ def from_config(config: Config) -> "Onboarding": session = Session() return Onboarding( auth=Auth.from_config(config), - url=config.onboarding_url, + url=config.onboarding_url, # type: ignore timeout=config.timeout, send_agent=config.send_agent, verbose=config.verbose, @@ -2027,7 +2027,6 @@ def request( user_id: Union[str, None] = None, verbose: bool = False, ) -> Result[bool, OnboardingError]: - response = self.onboarding_client.request( func=func, user_id=user_id, diff --git a/alice/onboarding/onboarding_client.py b/alice/onboarding/onboarding_client.py index fbd67ce..f5a206a 100644 --- a/alice/onboarding/onboarding_client.py +++ b/alice/onboarding/onboarding_client.py @@ -2342,7 +2342,6 @@ def request( user_id: Union[str, None] = None, verbose: bool = False, ) -> Result[Response, Error]: - print_intro("request", verbose=verbose) token = self.auth.create_backend_token(user_id).unwrap_or_return() diff --git a/alice/public_api.py b/alice/public_api.py index 00e7166..b6eb772 100644 --- a/alice/public_api.py +++ b/alice/public_api.py @@ -3,6 +3,8 @@ """Public API of Alice Onboarding Python SDK""" from typing import List +from alice.onboarding.enums.environment import Environment + # Modules from alice.webhooks.webhook import Webhook from alice.webhooks.webhooks import Webhooks @@ -61,6 +63,7 @@ "DocumentReport", "SelfieReport", "OtherTrustedDocumentReport", + "Environment", ] diff --git a/alice/sandbox/sandbox.py b/alice/sandbox/sandbox.py index 478d89b..1b23b59 100644 --- a/alice/sandbox/sandbox.py +++ b/alice/sandbox/sandbox.py @@ -18,7 +18,7 @@ class Sandbox: def from_config(config: Config) -> "Sandbox": return Sandbox( sandbox_token=config.sandbox_token, # type: ignore - url=config.sandbox_url, + url=config.sandbox_url, # type: ignore verbose=config.verbose, ) diff --git a/alice/webhooks/webhooks.py b/alice/webhooks/webhooks.py index 76058e5..7afdde2 100644 --- a/alice/webhooks/webhooks.py +++ b/alice/webhooks/webhooks.py @@ -22,7 +22,7 @@ def from_config(config: Config) -> "Webhooks": session = Session() return Webhooks( auth=Auth.from_config(config), - url=config.onboarding_url, + url=config.onboarding_url, # type: ignore send_agent=config.send_agent, verbose=config.verbose, session=session, diff --git a/requirements/dev-requirements.txt b/requirements/dev-requirements.txt index 49f5090..82e3d24 100644 --- a/requirements/dev-requirements.txt +++ b/requirements/dev-requirements.txt @@ -1,8 +1,8 @@ -black==22.10.0 -flake8==5.0.4 -isort[colors]==5.10.1 -pre-commit==2.20.0 -mypy==0.982 +black==23.7.0 +flake8==6.1.0 +isort[colors]==5.12.0 +pre-commit==3.3.3 +mypy==1.4.1 pytest==7.2.0 pytest-cov==4.0.0 pytest-mock==3.10.0 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index fbd3a1d..8030fac 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,4 +1,5 @@ pyjwt>=2.3.0,<3 pydantic>=1.8.2,<3 +pydantic-settings<3 requests>=2.26.0,<3 meiga>=1.9.1,<2 \ No newline at end of file diff --git a/tests/test_integration_auth.py b/tests/test_integration_auth.py index 05075a5..114aa11 100644 --- a/tests/test_integration_auth.py +++ b/tests/test_integration_auth.py @@ -24,7 +24,6 @@ def should_return_an_error_when_the_api_key_is_not_configured(self): result.assert_failure(value_is_instance_of=AuthError) def should_create_a_valid_backend_token(self, given_valid_api_key): - config = Config(api_key=given_valid_api_key) auth = Auth.from_config(config) @@ -32,7 +31,6 @@ def should_create_a_valid_backend_token(self, given_valid_api_key): result.assert_success(value_is_instance_of=str) def should_create_a_valid_backend_token_with_timeout(self, given_valid_api_key): - config = Config(api_key=given_valid_api_key, timeout=5) auth = Auth.from_config(config) @@ -40,7 +38,6 @@ def should_create_a_valid_backend_token_with_timeout(self, given_valid_api_key): result.assert_success(value_is_instance_of=str) def should_create_a_valid_backend_token_with_user(self, given_valid_api_key): - config = Config(api_key=given_valid_api_key) auth = Auth.from_config(config) @@ -48,7 +45,6 @@ def should_create_a_valid_backend_token_with_user(self, given_valid_api_key): result.assert_success(value_is_instance_of=str) def should_create_a_valid_user_token(self, given_valid_api_key): - config = Config(api_key=given_valid_api_key) auth = Auth.from_config(config) diff --git a/tests/test_integration_flows.py b/tests/test_integration_flows.py index b318755..72a7895 100644 --- a/tests/test_integration_flows.py +++ b/tests/test_integration_flows.py @@ -8,7 +8,6 @@ @pytest.mark.unit def test_should_return_an_error_when_the_api_key_is_not_configured(): - config = Config() onboarding = Onboarding.from_config(config) diff --git a/tests/test_integration_onboarding.py b/tests/test_integration_onboarding.py index 6701982..feffa94 100644 --- a/tests/test_integration_onboarding.py +++ b/tests/test_integration_onboarding.py @@ -13,7 +13,6 @@ @pytest.mark.unit def test_should_return_an_error_when_the_api_key_is_not_configured(): - config = Config() onboarding = Onboarding.from_config(config) diff --git a/tests/test_integration_sandbox.py b/tests/test_integration_sandbox.py index 5bfbd6a..0a5cbde 100644 --- a/tests/test_integration_sandbox.py +++ b/tests/test_integration_sandbox.py @@ -6,7 +6,6 @@ @pytest.mark.unit def test_should_return_an_error_when_the_sandbox_token_is_not_configured(): - config = Config() sandbox = Sandbox.from_config(config) @@ -19,7 +18,6 @@ def test_should_return_an_error_when_the_sandbox_token_is_not_configured(): def test_should_create_a_user_and_get_user_token_and_delete_it( given_valid_sandbox_token, given_any_valid_mail ): - config = Config(sandbox_token=given_valid_sandbox_token) sandbox = Sandbox.from_config(config) diff --git a/tests/test_integration_webhooks.py b/tests/test_integration_webhooks.py index 4b2adaf..b838323 100644 --- a/tests/test_integration_webhooks.py +++ b/tests/test_integration_webhooks.py @@ -10,7 +10,6 @@ @pytest.mark.unit def test_should_return_an_error_when_the_api_key_is_not_configured(): - config = Config() webhooks_client = Webhooks.from_config(config)