diff --git a/README.md b/README.md index 9b9e214..7f4568e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ pip install mati ``` make venv -source venv/bin/active +source venv/bin/activate make test ``` diff --git a/mati/client.py b/mati/client.py index 2a6e556..01b2d9d 100644 --- a/mati/client.py +++ b/mati/client.py @@ -48,7 +48,9 @@ def get_valid_bearer_token( except KeyError: expired = True if expired: # renew token - self.bearer_tokens[score] = self.access_tokens.create(score) + self.bearer_tokens[score] = self.access_tokens.create( + score, client=self + ) return self.bearer_tokens[score] def get(self, endpoint: str, **kwargs: Any) -> Dict[str, Any]: diff --git a/mati/resources/access_tokens.py b/mati/resources/access_tokens.py index f060a86..29edc96 100644 --- a/mati/resources/access_tokens.py +++ b/mati/resources/access_tokens.py @@ -22,16 +22,15 @@ class AccessToken(Resource): user_id: Optional[str] @classmethod - def create(cls, score: Optional[str] = None) -> 'AccessToken': + def create(cls, score: Optional[str] = None, client=None) -> 'AccessToken': + client = client or cls._client data = dict(grant_type='client_credentials') endpoint = cls._endpoint if score: data['score'] = score endpoint += '/token' - resp = cls._client.post( - endpoint, - data=data, - auth=basic_auth_str(*cls._client.basic_auth_creds), + resp = client.post( + endpoint, data=data, auth=basic_auth_str(*client.basic_auth_creds), ) try: expires_in = resp['expiresIn'] diff --git a/mati/resources/identities.py b/mati/resources/identities.py index cae1231..1f013cc 100644 --- a/mati/resources/identities.py +++ b/mati/resources/identities.py @@ -25,26 +25,34 @@ class Identity(Resource): metadata: Union[dict, List[str]] = field(default_factory=dict) fullName: Optional[str] = None facematchScore: Optional[float] = None + photo: Optional[str] = None + video: Optional[str] = None @classmethod - def create(cls, **metadata) -> 'Identity': - resp = cls._client.post(cls._endpoint, json=dict(metadata=metadata)) + def create(cls, client=None, **metadata) -> 'Identity': + client = client or cls._client + resp = client.post(cls._endpoint, json=dict(metadata=metadata)) resp['id'] = resp.pop('_id') return cls(**resp) @classmethod - def retrieve(cls, identity_id: str) -> 'Identity': + def retrieve(cls, identity_id: str, client=None) -> 'Identity': + client = client or cls._client endpoint = f'{cls._endpoint}/{identity_id}' - resp = cls._client.get(endpoint) + resp = client.get(endpoint) resp['id'] = resp.pop('_id') return cls(**resp) - def refresh(self) -> None: - identity = self.retrieve(self.id) + def refresh(self, client=None) -> None: + client = client or self._client + identity = self.retrieve(self.id, client=client) for k, v in identity.__dict__.items(): setattr(self, k, v) def upload_validation_data( - self, user_validation_files: List[UserValidationFile] + self, user_validation_files: List[UserValidationFile], client=None ) -> List[dict]: - return UserValidationData.upload(self.id, user_validation_files) + client = client or self._client + return UserValidationData.upload( + self.id, user_validation_files, client=client + ) diff --git a/mati/resources/user_verification_data.py b/mati/resources/user_verification_data.py index e7a0f7c..c11b5f8 100644 --- a/mati/resources/user_verification_data.py +++ b/mati/resources/user_verification_data.py @@ -52,7 +52,10 @@ def _append_file( @classmethod def upload( - cls, identity_id: str, user_validation_files: List[UserValidationFile] + cls, + identity_id: str, + user_validation_files: List[UserValidationFile], + client=None, ) -> List[Dict[str, Any]]: endpoint = cls._endpoint.format(identity_id=identity_id) files_metadata: List[Dict[str, Any]] = [] @@ -60,7 +63,7 @@ def upload( for file in user_validation_files: cls._append_file(files_metadata, file) files_with_types.append((get_file_type(file), file.content)) - resp = cls._client.post( + resp = client.post( endpoint, data=dict(inputs=json.dumps(files_metadata)), files=files_with_types, diff --git a/mati/resources/verifications.py b/mati/resources/verifications.py index 066a49f..cf09fab 100644 --- a/mati/resources/verifications.py +++ b/mati/resources/verifications.py @@ -1,3 +1,4 @@ +import datetime as dt from dataclasses import dataclass, field from typing import Any, ClassVar, Dict, List, Optional @@ -18,11 +19,13 @@ class Verification(Resource): identity: Dict[str, str] = field(default_factory=dict) hasProblem: Optional[bool] = None computed: Optional[Dict[str, Any]] = None + obfuscatedAt: Optional[dt.datetime] = None @classmethod - def retrieve(cls, verification_id: str) -> 'Verification': + def retrieve(cls, verification_id: str, client=None) -> 'Verification': + client = client or cls._client endpoint = f'{cls._endpoint}/{verification_id}' - resp = cls._client.get(endpoint, token_score=cls._token_score) + resp = client.get(endpoint, token_score=cls._token_score) docs = [] for doc in resp['documents']: doc['steps'] = [ diff --git a/mati/version.py b/mati/version.py index 6a2bd7b..a595e89 100644 --- a/mati/version.py +++ b/mati/version.py @@ -1 +1 @@ -__version__ = '0.2.9' # pragma: no cover +__version__ = '0.2.10' # pragma: no cover diff --git a/tests/cassettes/test_two_clients.yaml b/tests/cassettes/test_two_clients.yaml new file mode 100644 index 0000000..2a2a75c --- /dev/null +++ b/tests/cassettes/test_two_clients.yaml @@ -0,0 +1,206 @@ +interactions: +- request: + body: grant_type=client_credentials + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '29' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - mati-python/0.2.9 + method: POST + uri: https://api.getmati.com/oauth + response: + body: + string: '{"access_token": "ACCESS_TOKEN", "expiresIn": 3600, "payload": {"user": + {"_id": "ID", "firstName": "FIRST_NAME", "lastName": "LAST_NAME"}}}' + headers: + Connection: + - keep-alive + Content-Length: + - '504' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 06 Jan 2020 19:28:48 GMT + X-Request-Id: + - fba06368-b340-4386-8174-77cf8a1d428c + status: + code: 200 + message: OK +- request: + body: '{"metadata": {"nombres": "Georg Wilhelm", "primer_apellido": "Friedrich", + "segundo_apellido": "Hegel", "dob": "1770-08-27"}}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '124' + Content-Type: + - application/json + User-Agent: + - mati-python/0.2.9 + method: POST + uri: https://api.getmati.com/v2/identities + response: + body: + string: '{"_id":"5e138a7016dc68001b379e13","alive":null,"dateCreated":"2020-01-06T19:28:48.248Z","dateUpdated":"2020-01-06T19:28:48.248Z","metadata":{"nombres":"Georg + Wilhelm","primer_apellido":"Friedrich","segundo_apellido":"Hegel","dob":"1770-08-27"},"status":"pending","user":"5cec5d4e69eb4d001b8544ce"}' + headers: + Connection: + - keep-alive + Content-Length: + - '297' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 06 Jan 2020 19:28:48 GMT + X-Request-Id: + - 5d621f09-0bae-4678-bcf6-e24f2b6f6aa1 + status: + code: 200 + message: OK +- request: + body: grant_type=client_credentials + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '29' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - mati-python/0.2.9 + method: POST + uri: https://api.getmati.com/oauth + response: + body: + string: '{"access_token": "ACCESS_TOKEN", "expiresIn": 3600, "payload": {"user": + {"_id": "ID", "firstName": "FIRST_NAME", "lastName": "LAST_NAME"}}}' + headers: + Connection: + - keep-alive + Content-Length: + - '413' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 06 Jan 2020 19:28:48 GMT + X-Request-Id: + - 78a64d42-ea9a-4216-9abb-64f93aef5f72 + status: + code: 200 + message: OK +- request: + body: '{"metadata": {"nombres": "Georg Wilhelm", "primer_apellido": "Friedrich", + "segundo_apellido": "Hegel", "dob": "1770-08-27"}}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '124' + Content-Type: + - application/json + User-Agent: + - mati-python/0.2.9 + method: POST + uri: https://api.getmati.com/v2/identities + response: + body: + string: '{"_id":"5e138a7016dc68001b379e1a","alive":null,"dateCreated":"2020-01-06T19:28:48.778Z","dateUpdated":"2020-01-06T19:28:48.778Z","metadata":{"nombres":"Georg + Wilhelm","primer_apellido":"Friedrich","segundo_apellido":"Hegel","dob":"1770-08-27"},"status":"pending","user":"5e00fdf522f92f001b69993c"}' + headers: + Connection: + - keep-alive + Content-Length: + - '297' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 06 Jan 2020 19:28:48 GMT + X-Request-Id: + - ef550cc0-6d92-464c-88b4-6bc860292634 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - mati-python/0.2.9 + method: GET + uri: https://api.getmati.com/v2/identities/5e138a7016dc68001b379e13 + response: + body: + string: '{"_id":"5e138a7016dc68001b379e13","alive":null,"dateCreated":"2020-01-06T19:28:48.248Z","dateUpdated":"2020-01-06T19:28:48.248Z","metadata":{"nombres":"Georg + Wilhelm","primer_apellido":"Friedrich","segundo_apellido":"Hegel","dob":"1770-08-27"},"status":"pending","user":"5cec5d4e69eb4d001b8544ce"}' + headers: + Connection: + - keep-alive + Content-Length: + - '297' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 06 Jan 2020 19:28:48 GMT + X-Request-Id: + - f003a554-315f-4a72-9a27-5c475e6b7b9a + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - mati-python/0.2.9 + method: GET + uri: https://api.getmati.com/v2/identities/5e138a7016dc68001b379e1a + response: + body: + string: '{"_id":"5e138a7016dc68001b379e1a","alive":null,"dateCreated":"2020-01-06T19:28:48.778Z","dateUpdated":"2020-01-06T19:28:48.778Z","metadata":{"nombres":"Georg + Wilhelm","primer_apellido":"Friedrich","segundo_apellido":"Hegel","dob":"1770-08-27"},"status":"pending","user":"5e00fdf522f92f001b69993c"}' + headers: + Connection: + - keep-alive + Content-Length: + - '297' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 06 Jan 2020 19:28:49 GMT + X-Request-Id: + - 3369b285-4e7a-42f0-a75b-19c1ab3f9ae7 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/conftest.py b/tests/conftest.py index f6bb24a..2d38bb7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -156,13 +156,13 @@ def vcr_config() -> dict: @pytest.fixture def client() -> Generator: - # using credentials from env - yield Client() + yield Client('api_key', 'secret_key') @pytest.fixture def identity(client: Client) -> Generator: yield client.identities.create( + client=client, nombres='Georg Wilhelm', primer_apellido='Friedrich', segundo_apellido='Hegel', diff --git a/tests/test_client.py b/tests/test_client.py index dfd35e7..e81e5ea 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -7,7 +7,7 @@ @pytest.mark.vcr @pytest.mark.parametrize('score', [None, 'identity']) def test_client_renew_access_token(score): - client = Client() + client = Client('api_key', 'secret_key') assert client.bearer_tokens.get(score) is None client.get_valid_bearer_token(score) assert not client.bearer_tokens[score].expired diff --git a/tests/test_multiple_clients.py b/tests/test_multiple_clients.py new file mode 100644 index 0000000..a6a6abb --- /dev/null +++ b/tests/test_multiple_clients.py @@ -0,0 +1,41 @@ +import pytest + +from mati import Client +from mati.resources import Resource + + +@pytest.mark.vcr() +def test_two_clients(): + secondary_client = Client('test', 'test') + main_client = Client('api_key', 'secret_key') + + # NOTE: Resource._client is the default client for requests and + # always has the reference to the last client initialized + assert Resource._client == main_client + + scope = None + assert main_client.bearer_tokens.get(scope) is None + assert secondary_client.bearer_tokens.get(scope) is None + + metadata = dict( + nombres='Georg Wilhelm', + primer_apellido='Friedrich', + segundo_apellido='Hegel', + dob='1770-08-27', + ) + + main_identity = main_client.identities.create(**metadata) + secondary_identity = secondary_client.identities.create( + client=secondary_client, **metadata + ) + + main_retrieve = main_client.identities.retrieve(main_identity.id) + secondary_retrieve = secondary_client.identities.retrieve( + secondary_identity.id, client=secondary_client + ) + assert main_retrieve.id == main_identity.id + assert secondary_retrieve.id == secondary_identity.id + + assert main_client.bearer_tokens.get( + scope + ) == main_client.get_valid_bearer_token(scope)