From 9a4d8298efcfebae846d33ed1c4f9d37ac532e6c Mon Sep 17 00:00:00 2001 From: Alex Viquez Date: Fri, 14 Oct 2022 14:27:05 -0500 Subject: [PATCH] Errors (#102) * adding errors from docuemnts * rename errors * non optional * update codecov action * uses * errors tested * lint * verification steps * version * v1 --- mati/resources/verifications.py | 16 ++ mati/types/__init__.py | 2 + mati/types/enums.py | 48 ++++++ mati/version.py | 2 +- tests/conftest.py | 7 + .../test_retrieve_dni_verification.yaml | 161 ++++++++++++++++++ tests/resources/test_verifications.py | 10 ++ 7 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 tests/resources/cassettes/test_retrieve_dni_verification.yaml diff --git a/mati/resources/verifications.py b/mati/resources/verifications.py index 41d11d6..8c41342 100644 --- a/mati/resources/verifications.py +++ b/mati/resources/verifications.py @@ -4,6 +4,7 @@ from ..types.enums import ( DocumentScore, + Errors, Liveness, UserValidationFile, VerificationDocument, @@ -83,6 +84,21 @@ def proof_of_life_document(self) -> Optional[Liveness]: pol = [pol for pol in self.steps if pol.id == 'liveness'] return pol[-1] if pol else None + @property + def proof_of_life_errors(self) -> List[Errors]: + return [ + Errors( + identifier=pol.id, + type=pol.error['type'] if 'type' in pol.error else None, + code=pol.error['code'] if 'code' in pol.error else None, + message=pol.error['message'] + if 'message' in pol.error + else None, + ) + for pol in self.steps # type: ignore + if pol.id == 'liveness' and pol.error + ] + @property def govt_id_document(self) -> Optional[VerificationDocument]: govs = [ diff --git a/mati/types/__init__.py b/mati/types/__init__.py index 4c09475..f408b59 100644 --- a/mati/types/__init__.py +++ b/mati/types/__init__.py @@ -1,4 +1,5 @@ __all__ = [ + 'Errors', 'SerializableEnum', 'PageType', 'ValidationInputType', @@ -13,6 +14,7 @@ from .enums import ( DocumentScore, + Errors, Liveness, LivenessMedia, PageType, diff --git a/mati/types/enums.py b/mati/types/enums.py index 3e95894..d5086fb 100644 --- a/mati/types/enums.py +++ b/mati/types/enums.py @@ -35,6 +35,14 @@ class VerificationDocumentStep: data: Optional[Dict] = field(default_factory=dict) +@dataclass +class Errors: + identifier: str + type: str + code: str + message: str + + @dataclass class VerificationDocument: country: str @@ -44,6 +52,46 @@ class VerificationDocument: type: str fields: Optional[dict] = None + @property + def errors(self) -> List[Errors]: + if not self.steps: + return [] + errors = [ + Errors( + identifier=step.id, + type=step.error['type'] if 'type' in step.error else None, + code=step.error['code'] if 'code' in step.error else None, + message=step.error['message'] + if 'message' in step.error + else None, + ) + for step in self.steps + if step.error + ] + if self.type == 'proof-of-residency' and self.steps: + step = self.steps[0] + keys = step.data.keys() # type: ignore + required_fileds = [] + for key in keys: + data = step.data[key] # type: ignore + if ( + 'required' in data + and data['required'] + and not data['value'] + ): + required_fileds.append(data['label']) + if required_fileds: + errors.append( + Errors( + identifier=step.id, + type='StepError', + code='document.extractRequiredFields', + message=f"We can't extract the following " + f"fields in the document: {required_fileds}", + ) + ) + return errors + @property def document_type(self) -> str: if self.type in ['national-id', 'passport']: diff --git a/mati/version.py b/mati/version.py index 3cb60e3..0d8c2aa 100644 --- a/mati/version.py +++ b/mati/version.py @@ -1 +1 @@ -__version__ = '2.0.0' # pragma: no cover +__version__ = '2.0.1' # pragma: no cover diff --git a/tests/conftest.py b/tests/conftest.py index ae859aa..f18d327 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -221,6 +221,13 @@ def verification(client: Client) -> Generator: ) +@pytest.fixture +def verification_without_pol(client: Client): + verification = client.verifications.retrieve('634870763768f1001cac7591') + verification.steps = [] + yield verification + + @pytest.fixture def verification_document_national_id() -> VerificationDocument: return VerificationDocument( diff --git a/tests/resources/cassettes/test_retrieve_dni_verification.yaml b/tests/resources/cassettes/test_retrieve_dni_verification.yaml new file mode 100644 index 0000000..28e0224 --- /dev/null +++ b/tests/resources/cassettes/test_retrieve_dni_verification.yaml @@ -0,0 +1,161 @@ +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/2.0.1.dev1 + 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: + Cache-Control: + - no-store, no-cache, must-revalidate, proxy-revalidate + Connection: + - keep-alive + Content-Length: + - '746' + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' + https: data:;frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src + ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json; charset=utf-8 + Date: + - Fri, 14 Oct 2022 19:18:06 GMT + Expect-Ct: + - max-age=0 + Expires: + - '0' + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Surrogate-Control: + - no-store + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - 'off' + X-Download-Options: + - noopen + X-Frame-Options: + - DENY + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - 56dafc49-6548-4cb2-8be3-497d15d36645 + X-Xss-Protection: + - '1' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - mati-python/2.0.1.dev1 + method: GET + uri: https://api.getmati.com/v2/verifications/634870763768f1001cac7591 + response: + body: + string: '{"expired": false, "identity": {"status": "verified"}, "flow": {"id": + "some_flow", "name": "Default flow"}, "documents": [{"country": "MX", "region": + "", "type": "national-id", "photos": ["https://media.getmati.com/media/xxx", + "https://media.getmati.com/media/yyy"], "steps": [{"error": null, "status": + 200, "id": "template-matching"}, {"error": null, "status": 200, "id": "mexican-curp-validation", + "data": {"curp": "CURP", "fullName": "LAST FIRST", "birthDate": "01/01/1980", + "gender": "HOMBRE", "nationality": "MEXICO", "surname": "LAST", "secondSurname": + "", "name": "FIRST"}}, {"error": null, "status": 200, "id": "document-reading", + "data": {"fullName": {"value": "FIRST LAST", "label": "Name", "sensitive": + true}, "documentNumber": {"value": "111", "label": "Document Number"}, "dateOfBirth": + {"value": "1980-01-01", "label": "Day of Birth", "format": "date"}, "expirationDate": + {"value": "2030-12-31", "label": "Date of Expiration", "format": "date"}, + "curp": {"value": "CURP", "label": "CURP"}, "address": {"value": "Varsovia + 36, 06600 CDMX", "label": "Address"}, "emissionDate": {"value": "2010-01-01", + "label": "Emission Date", "format": "date"}}}, {"error": null, "status": 200, + "id": "alteration-detection"}, {"error": null, "status": 200, "id": "watchlists"}], + "fields": {"fullName": {"value": "FIRST LAST", "label": "Name", "sensitive": + true}, "documentNumber": {"value": "111", "label": "Document Number"}, "dateOfBirth": + {"value": "1980-01-01", "label": "Day of Birth", "format": "date"}, "expirationDate": + {"value": "2030-12-31", "label": "Date of Expiration", "format": "date"}, + "curp": {"value": "CURP", "label": "CURP"}, "address": {"value": "Varsovia + 36, 06600 CDMX", "label": "Address"}, "emissionDate": {"value": "2010-01-01", + "label": "Emission Date", "format": "date"}}}, {"country": "MX", "region": + null, "type": "proof-of-residency", "steps": [{"status": 200, "id": "document-reading", + "data": {"fullName": {"required": true, "label": "Name", "value": "FIRST NAME"}, + "address": {"label": "Address", "value": "Varsovia 36, 06600 CDMX"}, "emissionDate": + {"format": "date", "label": "Emission Date", "value": "1880-01-01"}}, "error": + null}, {"status": 200, "id": "watchlists", "error": null}], "fields": {"address": + {"value": "Varsovia 36, 06600 CDMX"}, "emissionDate": {"value": "1880-01-01"}, + "fullName": {"value": "FIRST LASTNAME"}}, "photos": ["https://media.getmati.com/file?location=xyc"]}], + "steps": [{"status": 200, "id": "liveness", "data": {"videoUrl": "https://media.getmati.com/file?location=abc", + "spriteUrl": "https://media.getmati.com/file?location=def", "selfieUrl": "https://media.getmati.com/file?location=hij"}, + "error": null}], "hasProblem": false, "computed": {"age": {"data": 100}, "isDocumentExpired": + {"data": {"national-id": false, "proof-of-residency": false}}}, "id": "5d9fb1f5bfbfac001a349bfb", + "metadata": {"name": "First Last", "dob": "1980-01-01"}}' + headers: + Cache-Control: + - no-store, no-cache, must-revalidate, proxy-revalidate + Connection: + - keep-alive + Content-Length: + - '4288' + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' + https: data:;frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src + ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json; charset=utf-8 + Date: + - Fri, 14 Oct 2022 19:18:06 GMT + Expect-Ct: + - max-age=0 + Expires: + - '0' + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Surrogate-Control: + - no-store + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - 'off' + X-Download-Options: + - noopen + X-Frame-Options: + - DENY + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - 2a5d765d-f1f6-42b8-8de9-0343691d4a4f + X-Xss-Protection: + - '1' + status: + code: 200 + message: OK +version: 1 diff --git a/tests/resources/test_verifications.py b/tests/resources/test_verifications.py index 60474ab..f6651d8 100644 --- a/tests/resources/test_verifications.py +++ b/tests/resources/test_verifications.py @@ -21,6 +21,9 @@ def test_retrieve_full_verification(client: Client): assert verification.govt_id_validation.is_valid assert verification.proof_of_life_validation.is_valid assert verification.proof_of_residency_validation.is_valid + assert not verification.govt_id_document.errors + assert not verification.proof_of_residency_document.errors + assert not verification.proof_of_life_errors assert ( verification.proof_of_residency_document.address == 'Varsovia 36, 06600 CDMX' @@ -67,3 +70,10 @@ def test_create_verification(client: Client): assert verification.flow['id'] == FAKE_FLOW_ID assert verification.metadata['user'] == 'some_id' assert verification.identity + + +@pytest.mark.vcr +def test_retrieve_dni_verification(verification_without_pol): + verification = verification_without_pol + assert not verification.proof_of_life_errors + assert not verification.proof_of_life_document