From 806fdd607dd61b3e94473b2e62a9fd8a3bd6d36c Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Wed, 29 Jan 2020 15:42:48 +0000 Subject: [PATCH 1/9] SDK-1299: Add initial support for Doc Scan --- .coveragerc | 6 +- examples/docscan_example_flask/app.py | 71 +++++ .../docscan_example_flask/requirements.txt | 0 examples/docscan_example_flask/settings.py | 5 + .../templates/index.html | 26 ++ requirements.in | 1 + requirements.txt | 31 +- yoti_python_sdk/__init__.py | 22 +- yoti_python_sdk/docs/__init__.py | 19 ++ yoti_python_sdk/docs/client.py | 171 +++++++++++ yoti_python_sdk/docs/constants.py | 21 ++ yoti_python_sdk/docs/endpoint.py | 22 ++ yoti_python_sdk/docs/exception/__init__.py | 3 + .../docs/exception/doc_scan_exception.py | 45 +++ yoti_python_sdk/docs/session/__init__.py | 0 .../docs/session/create/__init__.py | 15 + .../docs/session/create/check/__init__.py | 9 + .../create/check/document_authenticity.py | 47 +++ .../docs/session/create/check/face_match.py | 97 ++++++ .../docs/session/create/check/liveness.py | 112 +++++++ .../session/create/check/requested_check.py | 36 +++ .../session/create/notification_config.py | 162 ++++++++++ .../docs/session/create/sdk_config.py | 276 ++++++++++++++++++ .../docs/session/create/session_spec.py | 248 ++++++++++++++++ .../docs/session/create/task/__init__.py | 3 + .../session/create/task/requested_task.py | 36 +++ .../session/create/task/text_extraction.py | 91 ++++++ .../docs/session/retrieve/__init__.py | 0 .../session/retrieve/breakdown_response.py | 77 +++++ .../docs/session/retrieve/check_response.py | 170 +++++++++++ .../session/retrieve/create_session_result.py | 51 ++++ .../retrieve/document_fields_response.py | 33 +++ .../session/retrieve/face_map_response.py | 30 ++ .../docs/session/retrieve/frame_response.py | 30 ++ .../retrieve/generated_check_response.py | 48 +++ .../docs/session/retrieve/generated_media.py | 39 +++ .../session/retrieve/get_session_result.py | 174 +++++++++++ .../retrieve/id_document_resource_response.py | 93 ++++++ .../retrieve/liveness_resource_response.py | 57 ++++ .../docs/session/retrieve/media_response.py | 74 +++++ .../docs/session/retrieve/media_value.py | 25 ++ .../docs/session/retrieve/page_response.py | 43 +++ .../retrieve/recommendation_response.py | 46 +++ .../docs/session/retrieve/report_response.py | 46 +++ .../session/retrieve/resource_container.py | 77 +++++ .../session/retrieve/resource_response.py | 52 ++++ .../docs/session/retrieve/task_response.py | 159 ++++++++++ yoti_python_sdk/http.py | 53 +++- .../create/check/test_face_match_check.py | 42 +++ .../create/check/test_liveness_check.py | 55 ++++ ...t_requested_document_authenticity_check.py | 34 +++ .../create/task/test_text_extraction_task.py | 46 +++ .../create/test_notification_config.py | 128 ++++++++ .../docs/session/create/test_sdk_config.py | 66 +++++ .../docs/session/create/test_session_spec.py | 77 +++++ .../tests/docs/session/retrieve/__init__.py | 0 .../retrieve/test_breakdown_response.py | 35 +++ .../session/retrieve/test_check_response.py | 85 ++++++ .../retrieve/test_create_session_result.py | 35 +++ .../retrieve/test_document_fields_response.py | 22 ++ .../retrieve/test_face_map_response.py | 20 ++ .../session/retrieve/test_frame_response.py | 21 ++ .../retrieve/test_generated_check_response.py | 29 ++ .../session/retrieve/test_generated_media.py | 27 ++ .../retrieve/test_get_session_result.py | 66 +++++ .../test_id_document_resource_response.py | 50 ++++ .../test_liveness_resource_response.py | 32 ++ .../session/retrieve/test_media_response.py | 59 ++++ .../docs/session/retrieve/test_media_value.py | 28 ++ .../session/retrieve/test_page_response.py | 26 ++ .../retrieve/test_recommendation_response.py | 35 +++ .../session/retrieve/test_report_response.py | 29 ++ .../retrieve/test_resource_container.py | 37 +++ .../retrieve/test_resource_response.py | 37 +++ .../session/retrieve/test_task_response.py | 79 +++++ .../response_create_docs_scan_session.txt | 5 + .../response_get_docs_scan_media_image.txt | 1 + .../response_get_docs_scan_media_json.txt | 3 + .../response_get_docs_scan_session.txt | 196 +++++++++++++ yoti_python_sdk/tests/mocks.py | 53 +++- yoti_python_sdk/utils.py | 24 +- 81 files changed, 4402 insertions(+), 32 deletions(-) create mode 100644 examples/docscan_example_flask/app.py create mode 100644 examples/docscan_example_flask/requirements.txt create mode 100644 examples/docscan_example_flask/settings.py create mode 100644 examples/docscan_example_flask/templates/index.html create mode 100644 yoti_python_sdk/docs/__init__.py create mode 100644 yoti_python_sdk/docs/client.py create mode 100644 yoti_python_sdk/docs/constants.py create mode 100644 yoti_python_sdk/docs/endpoint.py create mode 100644 yoti_python_sdk/docs/exception/__init__.py create mode 100644 yoti_python_sdk/docs/exception/doc_scan_exception.py create mode 100644 yoti_python_sdk/docs/session/__init__.py create mode 100644 yoti_python_sdk/docs/session/create/__init__.py create mode 100644 yoti_python_sdk/docs/session/create/check/__init__.py create mode 100644 yoti_python_sdk/docs/session/create/check/document_authenticity.py create mode 100644 yoti_python_sdk/docs/session/create/check/face_match.py create mode 100644 yoti_python_sdk/docs/session/create/check/liveness.py create mode 100644 yoti_python_sdk/docs/session/create/check/requested_check.py create mode 100644 yoti_python_sdk/docs/session/create/notification_config.py create mode 100644 yoti_python_sdk/docs/session/create/sdk_config.py create mode 100644 yoti_python_sdk/docs/session/create/session_spec.py create mode 100644 yoti_python_sdk/docs/session/create/task/__init__.py create mode 100644 yoti_python_sdk/docs/session/create/task/requested_task.py create mode 100644 yoti_python_sdk/docs/session/create/task/text_extraction.py create mode 100644 yoti_python_sdk/docs/session/retrieve/__init__.py create mode 100644 yoti_python_sdk/docs/session/retrieve/breakdown_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/check_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/create_session_result.py create mode 100644 yoti_python_sdk/docs/session/retrieve/document_fields_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/face_map_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/frame_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/generated_check_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/generated_media.py create mode 100644 yoti_python_sdk/docs/session/retrieve/get_session_result.py create mode 100644 yoti_python_sdk/docs/session/retrieve/id_document_resource_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/liveness_resource_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/media_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/media_value.py create mode 100644 yoti_python_sdk/docs/session/retrieve/page_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/recommendation_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/report_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/resource_container.py create mode 100644 yoti_python_sdk/docs/session/retrieve/resource_response.py create mode 100644 yoti_python_sdk/docs/session/retrieve/task_response.py create mode 100644 yoti_python_sdk/tests/docs/session/create/check/test_face_match_check.py create mode 100644 yoti_python_sdk/tests/docs/session/create/check/test_liveness_check.py create mode 100644 yoti_python_sdk/tests/docs/session/create/check/test_requested_document_authenticity_check.py create mode 100644 yoti_python_sdk/tests/docs/session/create/task/test_text_extraction_task.py create mode 100644 yoti_python_sdk/tests/docs/session/create/test_notification_config.py create mode 100644 yoti_python_sdk/tests/docs/session/create/test_sdk_config.py create mode 100644 yoti_python_sdk/tests/docs/session/create/test_session_spec.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/__init__.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_breakdown_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_check_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_create_session_result.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_document_fields_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_face_map_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_frame_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_generated_check_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_generated_media.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_get_session_result.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_id_document_resource_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_liveness_resource_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_media_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_media_value.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_page_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_recommendation_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_report_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_resource_container.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_resource_response.py create mode 100644 yoti_python_sdk/tests/docs/session/retrieve/test_task_response.py create mode 100644 yoti_python_sdk/tests/fixtures/response_create_docs_scan_session.txt create mode 100644 yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_image.txt create mode 100644 yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_json.txt create mode 100644 yoti_python_sdk/tests/fixtures/response_get_docs_scan_session.txt diff --git a/.coveragerc b/.coveragerc index 51873f2a..f5c96c80 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,4 +2,8 @@ omit = yoti_python_sdk/tests/** yoti_python_sdk/protobuf/**/* - examples/** \ No newline at end of file + examples/** + +[report] +exclude_lines = + raise NotImplementedError \ No newline at end of file diff --git a/examples/docscan_example_flask/app.py b/examples/docscan_example_flask/app.py new file mode 100644 index 00000000..99a030b7 --- /dev/null +++ b/examples/docscan_example_flask/app.py @@ -0,0 +1,71 @@ +from yoti_python_sdk.docs import DocScanClient +from yoti_python_sdk.docs import SessionSpecBuilder +from yoti_python_sdk.docs.session.create.check import DocumentAuthenticityCheckBuilder +from yoti_python_sdk.docs import NotificationConfigBuilder +from yoti_python_sdk.docs import SDKConfigBuilder +from os.path import join, dirname +from flask import Flask, render_template +from dotenv import load_dotenv + +dotenv_path = join(dirname(__file__), ".env") +load_dotenv(dotenv_path) +from settings import YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH # noqa + +app = Flask(__name__) + + +@app.route("/") +def index(): + client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) + session = client.create_session( + SessionSpecBuilder() + .with_requested_checks(DocumentAuthenticityCheckBuilder().build()) + .with_notifications( + NotificationConfigBuilder() + .with_endpoint("https://example.com") + .with_topics("session_completion") + .build() + ) + .with_sdk_config( + SDKConfigBuilder() + .with_success_url("https://localhost:5000/success") + .with_error_url("https://localhost:5000") + .build() + ) + .build() + ) + print("Doc Scan Session ID: %s" % session.client_session_id) + + return render_template( + "index.html", + session_id=session.client_session_id, + client_token=session.client_session_token, + ) + + +@app.route("/success") +def success(): + return "Doc Scan request completed" + + +@app.route("/session/") +def get_session(session_id): + client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) + session = client.get_session(session_id) + resources = [ + [page.media.id for page in document.pages] + for document in session.resources.id_documents + ] + return "Session status: %s, Page Resources: %s" % (session.state, resources) + + +@app.route("/session//resource/") +def get_resource(session_id, resource_id): + client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) + resource = client.get_media_content(session_id, resource_id) + + return '' % resource.base64_content + + +if __name__ == "__main__": + app.run(host="0.0.0.0", ssl_context="adhoc") diff --git a/examples/docscan_example_flask/requirements.txt b/examples/docscan_example_flask/requirements.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/docscan_example_flask/settings.py b/examples/docscan_example_flask/settings.py new file mode 100644 index 00000000..00724dbc --- /dev/null +++ b/examples/docscan_example_flask/settings.py @@ -0,0 +1,5 @@ +from os import environ + +YOTI_CLIENT_SDK_ID = environ.get("YOTI_CLIENT_SDK_ID") +YOTI_KEY_FILE_PATH = environ.get("YOTI_KEY_FILE_PATH") +YOTI_BASE_URL = environ.get("YOTI_BASE_URL") diff --git a/examples/docscan_example_flask/templates/index.html b/examples/docscan_example_flask/templates/index.html new file mode 100644 index 00000000..bfe31b4d --- /dev/null +++ b/examples/docscan_example_flask/templates/index.html @@ -0,0 +1,26 @@ + + + + + + + Yoti Doc Scan example + + + + + + +
+

Doc Scan Session {{session_id}}

+
+ +
+
+ + + + + diff --git a/requirements.in b/requirements.in index 5b435b12..6c920272 100644 --- a/requirements.in +++ b/requirements.in @@ -12,3 +12,4 @@ requests>=2.20.0 urllib3>=1.24.2 deprecated==1.2.6 wheel==0.24.0 +iso8601==0.1.12 diff --git a/requirements.txt b/requirements.txt index 6125c9a6..4557e644 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,26 +4,27 @@ # # pip-compile --output-file=requirements.txt requirements.in # -asn1==2.2.0 # via -r requirements.in +asn1==2.2.0 certifi==2018.11.29 # via requests -cffi==1.13.0 # via -r requirements.in, cryptography +cffi==1.13.0 chardet==3.0.4 # via requests -cryptography==2.8 # via -r requirements.in, pyopenssl -deprecated==1.2.6 # via -r requirements.in -future==0.18.2 # via -r requirements.in +cryptography==2.8 +deprecated==1.2.6 +future==0.18.2 idna==2.7 # via requests -itsdangerous==0.24 # via -r requirements.in -pbr==1.10.0 # via -r requirements.in -protobuf==3.11.3 # via -r requirements.in +iso8601==0.1.12 +itsdangerous==0.24 +pbr==1.10.0 +protobuf==3.11.3 pycparser==2.18 # via cffi -pyopenssl==18.0.0 # via -r requirements.in -pytz==2018.9 # via -r requirements.in -pyyaml==5.2 # via -r requirements.in -requests==2.21.0 # via -r requirements.in +pyopenssl==18.0.0 +pytz==2018.9 +pyyaml==5.2 +requests==2.21.0 six==1.10.0 # via cryptography, protobuf, pyopenssl -urllib3==1.24.2 # via -r requirements.in, requests -wheel==0.24.0 # via -r requirements.in +urllib3==1.24.2 +wheel==0.24.0 wrapt==1.11.2 # via deprecated # The following packages are considered to be unsafe in a requirements file: -# setuptools +# setuptools==46.1.3 # via protobuf diff --git a/yoti_python_sdk/__init__.py b/yoti_python_sdk/__init__.py index 8486f9a9..74c4b2f9 100644 --- a/yoti_python_sdk/__init__.py +++ b/yoti_python_sdk/__init__.py @@ -7,9 +7,10 @@ DEFAULTS = { "YOTI_API_URL": "https://api.yoti.com", + "DOCS_API_URL": "https://stg1.api.internal.yoti.com/idverify/v1", "YOTI_API_PORT": 443, "YOTI_API_VERSION": "v1", - "YOTI_API_VERIFY_SSL": "true" + "YOTI_API_VERIFY_SSL": "true", } main_ns = {} @@ -23,19 +24,20 @@ __version__ = main_ns["__version__"] YOTI_API_URL = environ.get("YOTI_API_URL", DEFAULTS["YOTI_API_URL"]) +DOCS_API_URL = environ.get("DOCS_API_URL", DEFAULTS["DOCS_API_URL"]) YOTI_API_PORT = environ.get("YOTI_API_PORT", DEFAULTS["YOTI_API_PORT"]) YOTI_API_VERSION = environ.get("YOTI_API_VERSION", DEFAULTS["YOTI_API_VERSION"]) -YOTI_API_ENDPOINT = environ.get("YOTI_API_ENDPOINT", "{0}:{1}/api/{2}".format( - YOTI_API_URL, YOTI_API_PORT, YOTI_API_VERSION -)) - -YOTI_API_VERIFY_SSL = environ.get("YOTI_API_VERIFY_SSL", DEFAULTS["YOTI_API_VERIFY_SSL"]) +YOTI_API_ENDPOINT = environ.get( + "YOTI_API_ENDPOINT", + "{0}:{1}/api/{2}".format(YOTI_API_URL, YOTI_API_PORT, YOTI_API_VERSION), +) + +YOTI_API_VERIFY_SSL = environ.get( + "YOTI_API_VERIFY_SSL", DEFAULTS["YOTI_API_VERIFY_SSL"] +) if YOTI_API_VERIFY_SSL.lower() == "false": YOTI_API_VERIFY_SSL = False else: YOTI_API_VERIFY_SSL = True -__all__ = [ - "Client", - __version__ -] +__all__ = ["Client", __version__] diff --git a/yoti_python_sdk/docs/__init__.py b/yoti_python_sdk/docs/__init__.py new file mode 100644 index 00000000..3c3ee8c4 --- /dev/null +++ b/yoti_python_sdk/docs/__init__.py @@ -0,0 +1,19 @@ +from .session.create.check.document_authenticity import ( + RequestedDocumentAuthenticityCheckBuilder, +) +from .session.create.check.face_match import RequestedFaceMatchCheckBuilder +from .session.create.check.liveness import RequestedLivenessCheckBuilder +from .session.create.task.text_extraction import RequestedTextExtractionTaskBuilder +from .session.create.notification_config import NotificationConfigBuilder +from .session.create.sdk_config import SdkConfigBuilder +from .session.create.session_spec import SessionSpecBuilder + +__all__ = [ + RequestedDocumentAuthenticityCheckBuilder, + RequestedLivenessCheckBuilder, + RequestedFaceMatchCheckBuilder, + RequestedTextExtractionTaskBuilder, + SessionSpecBuilder, + NotificationConfigBuilder, + SdkConfigBuilder, +] diff --git a/yoti_python_sdk/docs/client.py b/yoti_python_sdk/docs/client.py new file mode 100644 index 00000000..70925ef7 --- /dev/null +++ b/yoti_python_sdk/docs/client.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import json + +import yoti_python_sdk +from yoti_python_sdk.docs.endpoint import Endpoint +from yoti_python_sdk.docs.session.retrieve.create_session_result import ( + CreateSessionResult, +) +from yoti_python_sdk.docs.session.retrieve.get_session_result import GetSessionResult +from yoti_python_sdk.docs.session.retrieve.media_value import MediaValue +from yoti_python_sdk.http import MediaRequestHandler +from yoti_python_sdk.http import SignedRequest +from yoti_python_sdk.utils import YotiEncoder +from .exception import DocScanException + + +class DocScanClient(object): + """ + Client used for communication with the Yoti Doc Scan service where any + signed request is required + """ + + def __init__(self, sdk_id, key, api_url=None): + self.__sdk_id = sdk_id + self.__key = key + if api_url is not None: + self.__api_url = api_url + else: + self.__api_url = yoti_python_sdk.DOCS_API_URL + + def create_session(self, session_spec): + """ + Creates a Doc Scan session using the supplied session specification + + :param session_spec: the session specification + :type session_spec: SessionSpec + :return: the create session result + :rtype: CreateSessionResult + :raises DocScanException: if there was an error creating the session + """ + payload = json.dumps(session_spec, cls=YotiEncoder).encode("utf-8") + + request = ( + SignedRequest.builder() + .with_post() + .with_pem_file(self.__key) + .with_base_url(self.__api_url) + .with_endpoint(Endpoint.create_docs_session_path()) + .with_param("sdkId", self.__sdk_id) + .with_payload(payload) + .with_header("Content-Type", "application/json") + .build() + ) + response = request.execute() + + if response.status_code != 201: + raise DocScanException("Failed to create session", response) + + data = json.loads(response.text) + return CreateSessionResult(data) + + def get_session(self, session_id): + """ + Retrieves the state of a previously created Yoti Doc Scan session + + :param session_id: the session ID + :type session_id: str + :return: the session state + :rtype: GetSessionResult + :raises DocScanException: if there was an error retrieving the session + """ + request = ( + SignedRequest.builder() + .with_get() + .with_pem_file(self.__key) + .with_base_url(self.__api_url) + .with_endpoint(Endpoint.retrieve_docs_session_path(session_id)) + .with_param("sdkId", self.__sdk_id) + .build() + ) + response = request.execute() + + if response.status_code != 200: + raise DocScanException("Failed to retrieve session", response) + + data = json.loads(response.text) + return GetSessionResult(data) + + def delete_session(self, session_id): + """ + Deletes a previously created Yoti Doc Scan session and + all of its related resources + + :param session_id: the session id to delete + :type session_id: str + :rtype: None + :raises DocScanException: if there was an error deleting the session + """ + request = ( + SignedRequest.builder() + .with_http_method("DELETE") + .with_pem_file(self.__key) + .with_base_url(self.__api_url) + .with_endpoint(Endpoint.delete_docs_session_path(session_id)) + .with_param("sdkId", self.__sdk_id) + .build() + ) + response = request.execute() + + if response.status_code < 200 or response.status_code >= 300: + raise DocScanException("Failed to delete session", response) + + def get_media_content(self, session_id, media_id): + """ + Retrieves media related to a Yoti Doc Scan session + based on the supplied media ID + + :param session_id: the session ID + :type session_id: str + :param media_id: the media ID + :type media_id: str + :return: the media + :rtype: MediaValue + :raises DocScanException: if there was an error retrieving the media content + """ + request = ( + SignedRequest.builder() + .with_request_handler(MediaRequestHandler) + .with_get() + .with_pem_file(self.__key) + .with_base_url(self.__api_url) + .with_endpoint(Endpoint.get_media_content_path(session_id, media_id)) + .with_param("sdkId", self.__sdk_id) + .build() + ) + response = request.execute() + + if response.status_code != 200: + raise DocScanException("Failed to retrieve media content", response) + + media_mime_type = response.headers["Content-Type"] + media_content = response.content + return MediaValue(media_mime_type, media_content) + + def delete_media_content(self, session_id, media_id): + """ + Deletes media related to a Yoti Doc Scan session + based on the supplied media ID + + :param session_id: the session ID + :type session_id: str + :param media_id: the media ID + :type media_id: str + :rtype: None + :raises DocScanException: if there was an error deleting the media content + """ + request = ( + SignedRequest.builder() + .with_http_method("DELETE") + .with_pem_file(self.__key) + .with_base_url(self.__api_url) + .with_endpoint(Endpoint.delete_media_path(session_id, media_id)) + .with_param("sdkId", self.__sdk_id) + .build() + ) + + response = request.execute() + if response.status_code < 200 or response.status_code >= 300: + raise DocScanException("Failed to delete media content", response) diff --git a/yoti_python_sdk/docs/constants.py b/yoti_python_sdk/docs/constants.py new file mode 100644 index 00000000..3af3a8c3 --- /dev/null +++ b/yoti_python_sdk/docs/constants.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +ID_DOCUMENT_AUTHENTICITY = "ID_DOCUMENT_AUTHENTICITY" +ID_DOCUMENT_TEXT_DATA_CHECK = "ID_DOCUMENT_TEXT_DATA_CHECK" +ID_DOCUMENT_TEXT_DATA_EXTRACTION = "ID_DOCUMENT_TEXT_DATA_EXTRACTION" +ID_DOCUMENT_FACE_MATCH = "ID_DOCUMENT_FACE_MATCH" +LIVENESS = "LIVENESS" +ZOOM = "ZOOM" + +CAMERA = "CAMERA" +CAMERA_AND_UPLOAD = "CAMERA_AND_UPLOAD" + +RESOURCE_UPDATE = "RESOURCE_UPDATE" +TASK_COMPLETION = "TASK_COMPLETION" +CHECK_COMPLETION = "CHECK_COMPLETION" +SESSION_COMPLETION = "SESSION_COMPLETION" + +ALWAYS = "ALWAYS" +FALLBACK = "FALLBACK" +NEVER = "NEVER" diff --git a/yoti_python_sdk/docs/endpoint.py b/yoti_python_sdk/docs/endpoint.py new file mode 100644 index 00000000..74425fd6 --- /dev/null +++ b/yoti_python_sdk/docs/endpoint.py @@ -0,0 +1,22 @@ +class Endpoint(object): + @staticmethod + def create_docs_session_path(): + return "/sessions" + + @staticmethod + def retrieve_docs_session_path(session_id): + return "/sessions/{sessionId}".format(sessionId=session_id) + + @staticmethod + def delete_docs_session_path(session_id): + return Endpoint.retrieve_docs_session_path(session_id) + + @staticmethod + def get_media_content_path(session_id, media_id): + return "/sessions/{sessionId}/media/{mediaId}/content".format( + sessionId=session_id, mediaId=media_id + ) + + @staticmethod + def delete_media_path(session_id, media_id): + return Endpoint.get_media_content_path(session_id, media_id) diff --git a/yoti_python_sdk/docs/exception/__init__.py b/yoti_python_sdk/docs/exception/__init__.py new file mode 100644 index 00000000..aab9483e --- /dev/null +++ b/yoti_python_sdk/docs/exception/__init__.py @@ -0,0 +1,3 @@ +from .doc_scan_exception import DocScanException + +__all__ = [DocScanException] diff --git a/yoti_python_sdk/docs/exception/doc_scan_exception.py b/yoti_python_sdk/docs/exception/doc_scan_exception.py new file mode 100644 index 00000000..fa1ce5df --- /dev/null +++ b/yoti_python_sdk/docs/exception/doc_scan_exception.py @@ -0,0 +1,45 @@ +class DocScanException(Exception): + """ + Exception thrown by the Yoti Doc Scan client + when an error has occurred when communicating with the API + """ + + def __init__(self, message, response): + """ + :param message: the exception message + :type message: str + :param response: the http response + :type response: requests.Response + """ + Exception.__init__(self, message) + self.__response = response + + @property + def status_code(self): + """ + Get the status code of the HTTP response + + :return: the status code + :rtype: int or None + """ + return self.__response.status_code + + @property + def text(self): + """ + Return the HTTP response body as text + + :return: the body as text + :rtype: str + """ + return self.__response.text + + @property + def content(self): + """ + Return the HTTP response body as bytes + + :return: the body as bytes + :rtype: bytearray or None + """ + return self.__response.content diff --git a/yoti_python_sdk/docs/session/__init__.py b/yoti_python_sdk/docs/session/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/yoti_python_sdk/docs/session/create/__init__.py b/yoti_python_sdk/docs/session/create/__init__.py new file mode 100644 index 00000000..7b7896e6 --- /dev/null +++ b/yoti_python_sdk/docs/session/create/__init__.py @@ -0,0 +1,15 @@ +from .check.document_authenticity import RequestedDocumentAuthenticityCheckBuilder +from .check.face_match import RequestedFaceMatchCheckBuilder +from .check.liveness import RequestedLivenessCheckBuilder +from .notification_config import NotificationConfigBuilder +from .session_spec import SessionSpecBuilder +from .sdk_config import SdkConfigBuilder + +__all__ = [ + RequestedDocumentAuthenticityCheckBuilder, + RequestedFaceMatchCheckBuilder, + RequestedLivenessCheckBuilder, + NotificationConfigBuilder, + SessionSpecBuilder, + SdkConfigBuilder, +] diff --git a/yoti_python_sdk/docs/session/create/check/__init__.py b/yoti_python_sdk/docs/session/create/check/__init__.py new file mode 100644 index 00000000..3f14e174 --- /dev/null +++ b/yoti_python_sdk/docs/session/create/check/__init__.py @@ -0,0 +1,9 @@ +from .document_authenticity import RequestedDocumentAuthenticityCheckBuilder +from .face_match import RequestedFaceMatchCheckBuilder +from .liveness import RequestedLivenessCheckBuilder + +__all__ = [ + RequestedDocumentAuthenticityCheckBuilder, + RequestedFaceMatchCheckBuilder, + RequestedLivenessCheckBuilder, +] diff --git a/yoti_python_sdk/docs/session/create/check/document_authenticity.py b/yoti_python_sdk/docs/session/create/check/document_authenticity.py new file mode 100644 index 00000000..3d5fa87f --- /dev/null +++ b/yoti_python_sdk/docs/session/create/check/document_authenticity.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs.constants import ID_DOCUMENT_AUTHENTICITY +from yoti_python_sdk.utils import YotiSerializable +from .requested_check import RequestedCheck + + +class RequestedDocumentAuthenticityCheckConfig(YotiSerializable): + """ + The configuration applied when creating a Document Authenticity Check + """ + + def to_json(self): + return {} + + +class RequestedDocumentAuthenticityCheck(RequestedCheck): + """ + Requests creation of a Document Authenticity Check + """ + + def __init__(self, config): + """ + :param config: the requested document authenticity check configuration + :type config: RequestedDocumentAuthenticityCheckConfig + """ + self.__config = config + + @property + def type(self): + return ID_DOCUMENT_AUTHENTICITY + + @property + def config(self): + return self.__config + + +class RequestedDocumentAuthenticityCheckBuilder(object): + """ + Builder to assist creation of :class:`RequestedDocumentAuthenticityCheck` + """ + + @staticmethod + def build(): + config = RequestedDocumentAuthenticityCheckConfig() + return RequestedDocumentAuthenticityCheck(config) diff --git a/yoti_python_sdk/docs/session/create/check/face_match.py b/yoti_python_sdk/docs/session/create/check/face_match.py new file mode 100644 index 00000000..90cd3028 --- /dev/null +++ b/yoti_python_sdk/docs/session/create/check/face_match.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs import constants +from yoti_python_sdk.utils import YotiSerializable +from .requested_check import RequestedCheck + + +class RequestedFaceMatchCheckConfig(YotiSerializable): + """ + The configuration applied when creating a FaceMatch Check + """ + + def __init__(self, manual_check): + """ + :param manual_check: the manual check value + :type manual_check: str + """ + self.__manual_check = manual_check + + @property + def manual_check(self): + """ + Returns a value for a manual check for a given + FaceMatch Check + + :return: the manual check value + :rtype: str + """ + return self.__manual_check + + def to_json(self): + return {"manual_check": self.manual_check} + + +class RequestedFaceMatchCheck(RequestedCheck): + """ + Requests creation of a FaceMatch Check + """ + + def __init__(self, config): + """ + :param config: the requested FaceMatch check configuration + :type config: RequestedFaceMatchCheckConfig + """ + self.__config = config + + @property + def type(self): + return constants.ID_DOCUMENT_FACE_MATCH + + @property + def config(self): + return self.__config + + +class RequestedFaceMatchCheckBuilder(object): + """ + Builder to assist with creation of :class:`RequestedFaceMatchCheck` + """ + + def __init__(self): + self.__manual_check = None + + def with_manual_check_always(self): + """ + Sets the value of manual check to "ALWAYS" + + :return: the builder + :rtype: RequestedFaceMatchCheckBuilder + """ + self.__manual_check = constants.ALWAYS + return self + + def with_manual_check_fallback(self): + """ + Sets the value of manual check to "FALLBACK" + + :return: the builder + :rtype: RequestedFaceMatchCheckBuilder + """ + self.__manual_check = constants.FALLBACK + return self + + def with_manual_check_never(self): + """ + Sets the value of manual check to "NEVER" + + :return: the builder + :rtype: RequestedFaceMatchCheckBuilder + """ + self.__manual_check = constants.NEVER + return self + + def build(self): + config = RequestedFaceMatchCheckConfig(self.__manual_check) + return RequestedFaceMatchCheck(config) diff --git a/yoti_python_sdk/docs/session/create/check/liveness.py b/yoti_python_sdk/docs/session/create/check/liveness.py new file mode 100644 index 00000000..38df1df9 --- /dev/null +++ b/yoti_python_sdk/docs/session/create/check/liveness.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs import constants +from yoti_python_sdk.utils import YotiSerializable +from .requested_check import RequestedCheck + + +class RequestedLivenessCheckConfig(YotiSerializable): + """ + The configuration applied when creating a Liveness Check + """ + + def __init__(self, liveness_type, max_retries): + """ + :param liveness_type: the liveness type + :type liveness_type: str + :param max_retries: the maximum number of retries + :type max_retries: int + """ + self.__liveness_type = liveness_type + self.__max_retries = max_retries + + @property + def liveness_type(self): + """ + The type of the liveness check, e.g. "ZOOM" + + :return: the liveness type + """ + return self.__liveness_type + + @property + def max_retries(self): + """ + The maximum number of retries a user is allowed for a liveness check + + :return: the maximum number of retries + """ + return self.__max_retries + + def to_json(self): + return {"liveness_type": self.liveness_type, "max_retries": self.max_retries} + + +class RequestedLivenessCheck(RequestedCheck): + """ + Requests creation of a Liveness Check + """ + + def __init__(self, liveness_check_config): + """ + :param liveness_check_config: the requested liveness check configuration + :type liveness_check_config: RequestedLivenessCheckConfig + """ + self.__config = liveness_check_config + + @property + def type(self): + return constants.LIVENESS + + @property + def config(self): + return self.__config + + +class RequestedLivenessCheckBuilder(object): + """ + Builder to assist creation of :class:`RequestedLivenessCheck` + """ + + def __init__(self): + self.__liveness_type = None + self.__max_retries = None + + def for_zoom_liveness(self): + """ + Sets the liveness type to "ZOOM" + + :return: the builder + :rtype: RequestedLivenessCheckBuilder + """ + return self.with_liveness_type(constants.ZOOM) + + def with_liveness_type(self, liveness_type): + """ + Sets the liveness type on the builder + + :param liveness_type: the liveness type + :type liveness_type: str + :return: the builder + :rtype: RequestedLivenessCheckBuilder + """ + self.__liveness_type = liveness_type + return self + + def with_max_retries(self, max_retries): + """ + Sets the maximum number of retries allowed for liveness check + on the builder + + :param max_retries: the maximum number of retries + :type max_retries: int + :return: the builder + :rtype: RequestedLivenessCheckBuilder + """ + self.__max_retries = max_retries + return self + + def build(self): + config = RequestedLivenessCheckConfig(self.__liveness_type, self.__max_retries) + return RequestedLivenessCheck(config) diff --git a/yoti_python_sdk/docs/session/create/check/requested_check.py b/yoti_python_sdk/docs/session/create/check/requested_check.py new file mode 100644 index 00000000..71a293e0 --- /dev/null +++ b/yoti_python_sdk/docs/session/create/check/requested_check.py @@ -0,0 +1,36 @@ +from abc import ABCMeta +from abc import abstractmethod + +from yoti_python_sdk.utils import YotiSerializable + + +class RequestedCheck(YotiSerializable): + """ + Requests creation of a Check to be performed on a document + """ + + __metaclass__ = ABCMeta + + @property + @abstractmethod + def type(self): + """ + Return the type of the Check to create + + :return: the type + :rtype: str + """ + raise NotImplementedError + + @property + @abstractmethod + def config(self): + """ + Return configuration to apply to the Check + + :return: the configuration + """ + raise NotImplementedError + + def to_json(self): + return {"type": self.type, "config": self.config} diff --git a/yoti_python_sdk/docs/session/create/notification_config.py b/yoti_python_sdk/docs/session/create/notification_config.py new file mode 100644 index 00000000..7d629a37 --- /dev/null +++ b/yoti_python_sdk/docs/session/create/notification_config.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs.constants import CHECK_COMPLETION +from yoti_python_sdk.docs.constants import RESOURCE_UPDATE +from yoti_python_sdk.docs.constants import SESSION_COMPLETION +from yoti_python_sdk.docs.constants import TASK_COMPLETION +from yoti_python_sdk.utils import YotiSerializable + + +class NotificationConfig(YotiSerializable): + """ + Configures call-back Notifications to some backend endpoint provided by the Relying Business. + + Notifications can be configured to notify a clients backend of certain events, avoiding the need + to poll for the state of the Session. + """ + + def __init__(self, auth_token, endpoint, topics=None): + """ + :param auth_token: the authorization token + :type auth_token: str + :param endpoint: the endpoint + :type endpoint: str + :param topics: the list of topics + :type topics: list[str] + """ + if topics is None: + topics = [] + + self.__auth_token = auth_token + self.__endpoint = endpoint + self.__topics = list(set(topics)) # Get unique values + + @property + def auth_token(self): + """ + The authorization token to be included in call-back messages + + :return: the authorization token + :rtype: str + """ + return self.__auth_token + + @property + def endpoint(self): + """ + The endpoint that notifications should be sent to + + :return: the endpoint + :rtype: str + """ + return self.__endpoint + + @property + def topics(self): + """ + The list of topics that should trigger notifications + + :return: the list of topics + :rtype: list[str] + """ + return self.__topics + + def to_json(self): + return { + "auth_token": self.auth_token, + "endpoint": self.endpoint, + "topics": self.topics, + } + + +class NotificationConfigBuilder(object): + """ + Builder to assist in the creation of :class:`NotificationConfig` + """ + + def __init__(self): + self.__auth_token = None + self.__endpoint = None + self.__topics = [] + + def with_auth_token(self, token): + """ + Sets the authorization token to be included in call-back messages + + :param token: the authorization token + :type token: str + :return: the builder + :rtype: NotificationConfigBuilder + """ + self.__auth_token = token + return self + + def with_endpoint(self, endpoint): + """ + Sets the endpoint that notifications should be sent to + + :param endpoint: the endpoint + :type endpoint: str + :return: the builder + :rtype: NotificationConfigBuilder + """ + self.__endpoint = endpoint + return self + + def with_topic(self, topic): + """ + Adds a topic to the list of topics that trigger notification messages + + :param topic: the topic + :type topic: str + :return: the builder + :rtype: NotificationConfigBuilder + """ + self.__topics.append(topic) + return self + + def for_resource_update(self): + """ + Adds RESOURCE_UPDATE to the list of topics that trigger notification messages + + :return: the builder + :rtype: NotificationConfigBuilder + """ + return self.with_topic(RESOURCE_UPDATE) + + def for_task_completion(self): + """ + Adds TASK_COMPLETION to the list of topics that trigger notification messages + + :return: the builder + :rtype: NotificationConfigBuilder + """ + return self.with_topic(TASK_COMPLETION) + + def for_session_completion(self): + """ + Adds SESSION_COMPLETION to the list of topics that trigger notification messages + + :return: the builder + :rtype: NotificationConfigBuilder + """ + return self.with_topic(SESSION_COMPLETION) + + def for_check_completion(self): + """ + Adds CHECK_COMPLETION to the list of topics that trigger notification messages + + :return: the builder + :rtype: NotificationConfigBuilder + """ + return self.with_topic(CHECK_COMPLETION) + + def build(self): + """ + Builds the :class:`NotificationConfig` using the supplied values + + :return: the build notification config + :rtype: NotificationConfig + """ + return NotificationConfig(self.__auth_token, self.__endpoint, self.__topics) diff --git a/yoti_python_sdk/docs/session/create/sdk_config.py b/yoti_python_sdk/docs/session/create/sdk_config.py new file mode 100644 index 00000000..84dddca9 --- /dev/null +++ b/yoti_python_sdk/docs/session/create/sdk_config.py @@ -0,0 +1,276 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs.constants import CAMERA +from yoti_python_sdk.docs.constants import CAMERA_AND_UPLOAD +from yoti_python_sdk.utils import YotiSerializable + + +class SdkConfig(YotiSerializable): + """ + Provides configuration properties using by the web/native clients + """ + + def __init__( + self, + allowed_capture_methods, + primary_colour, + secondary_colour, + font_colour, + locale, + preset_issuing_country, + success_url, + error_url, + ): + """ + :param allowed_capture_methods: the allowed capture methods + :type allowed_capture_methods: str + :param primary_colour: the primary colour + :type primary_colour: str + :param secondary_colour: the secondary colour + :type secondary_colour: str + :param font_colour: the font colour + :type font_colour: str + :param locale: the locale + :type locale: str + :param preset_issuing_country: the preset issuing country + :type preset_issuing_country: str + :param success_url: the success url + :type success_url: str + :param error_url: the error url + :type error_url: str + """ + self.__allowed_capture_methods = allowed_capture_methods + self.__primary_colour = primary_colour + self.__secondary_colour = secondary_colour + self.__font_colour = font_colour + self.__locale = locale + self.__preset_issuing_country = preset_issuing_country + self.__success_url = success_url + self.__error_url = error_url + + @property + def allowed_capture_methods(self): + """ + The methods allowed for capturing document images + + :return: the allowed capture methods + """ + return self.__allowed_capture_methods + + @property + def primary_colour(self): + """ + The primary colour + + :return: the primary colour + """ + return self.__primary_colour + + @property + def secondary_colour(self): + """ + The secondary colour + + :return: the secondary colour + """ + return self.__secondary_colour + + @property + def font_colour(self): + """ + The font colour + + :return: the font colour + """ + return self.__font_colour + + @property + def locale(self): + """ + The locale + + :return: the locale + """ + return self.__locale + + @property + def preset_issuing_country(self): + """ + The preset issuing country + + :return: the preset issuing country + """ + return self.__preset_issuing_country + + @property + def success_url(self): + """ + The success URL + + :return: the success url + """ + return self.__success_url + + @property + def error_url(self): + """ + The error URL + + :return: the error url + """ + return self.__error_url + + def to_json(self): + return { + "allowed_capture_methods": self.allowed_capture_methods, + "primary_colour": self.primary_colour, + "secondary_colour": self.secondary_colour, + "font_colour": self.font_colour, + "locale": self.locale, + "preset_issuing_country": self.preset_issuing_country, + "success_url": self.success_url, + "error_url": self.error_url, + } + + +class SdkConfigBuilder(object): + """ + Builder to assist in the creation of :class:`SdkConfig` + """ + + def __init__(self): + self.__allowed_capture_methods = None + self.__primary_colour = None + self.__secondary_colour = None + self.__font_colour = None + self.__locale = None + self.__preset_issuing_country = None + self.__success_url = None + self.__error_url = None + + def with_allowed_capture_methods(self, allowed_capture_methods): + """ + Sets the allowed capture methods on the builder + + :param allowed_capture_methods: the allowed capture methods + :type allowed_capture_methods: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__allowed_capture_methods = allowed_capture_methods + return self + + def with_allows_camera(self): + """ + Sets the allowed capture method to "CAMERA" + + :return: the builder + :rtype: SdkConfigBuilder + """ + return self.with_allowed_capture_methods(CAMERA) + + def with_allows_camera_and_upload(self): + """ + Sets the allowed capture method to "CAMERA_AND_UPLOAD" + + :return: the builder + :rtype: SdkConfigBuilder + """ + return self.with_allowed_capture_methods(CAMERA_AND_UPLOAD) + + def with_primary_colour(self, colour): + """ + Sets the primary colour to be used by the web/native client + + :param colour: the primary colour, hexadecimal value e.g. #ff0000 + :type colour: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__primary_colour = colour + return self + + def with_secondary_colour(self, colour): + """ + Sets the secondary colour to be used by the web/native client (used on the button) + + :param colour: the secondary colour, hexadecimal value e.g. #ff0000 + :type colour: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__secondary_colour = colour + return self + + def with_font_colour(self, colour): + """ + Sets the font colour to be used by the web/native client (used on the button) + + :param colour: the font colour, hexadecimal value e.g. #ff0000 + :type colour: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__font_colour = colour + return self + + def with_locale(self, locale): + """ + Sets the language locale use by the web/native client + + :param locale: the locale, e.g. "en" + :type locale: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__locale = locale + return self + + def with_preset_issuing_country(self, country): + """ + Sets the preset issuing country used by the web/native client + + :param country: the preset issuing country + :type country: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__preset_issuing_country = country + return self + + def with_success_url(self, url): + """ + Sets the success URL for the redirect that follows the web/native client uploading documents successfully + + :param url: the success URL + :type url: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__success_url = url + return self + + def with_error_url(self, url): + """ + Sets the error URL for the redirect that follows the web/native client uploading documents unsuccessfully + + :param url: the error URL + :type url: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__error_url = url + return self + + def build(self): + return SdkConfig( + self.__allowed_capture_methods, + self.__primary_colour, + self.__secondary_colour, + self.__font_colour, + self.__locale, + self.__preset_issuing_country, + self.__success_url, + self.__error_url, + ) diff --git a/yoti_python_sdk/docs/session/create/session_spec.py b/yoti_python_sdk/docs/session/create/session_spec.py new file mode 100644 index 00000000..eb7aa90d --- /dev/null +++ b/yoti_python_sdk/docs/session/create/session_spec.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.utils import YotiSerializable + + +class SessionSpec(YotiSerializable): + """ + Definition for the Doc Scan Session to be created + """ + + def __init__( + self, + client_session_token_ttl, + resources_ttl, + user_tracking_id, + notifications, + sdk_config, + requested_checks=None, + requested_tasks=None, + ): + """ + :param client_session_token_ttl: the client session token TTL + :type client_session_token_ttl: int + :param resources_ttl: the resources TTL + :type resources_ttl: int + :param user_tracking_id: the user tracking ID + :type user_tracking_id: str + :param notifications: the notification configuration + :type notifications: NotificationConfig + :param sdk_config: the SDK configuration + :type sdk_config: SdkConfig + :param requested_checks: the list of requested checks + :type requested_checks: list[RequestedCheck] + :param requested_tasks: the list of requested tasks + :type requested_tasks: list[RequestedTask] + """ + if requested_tasks is None: + requested_tasks = [] + if requested_checks is None: + requested_checks = [] + + self.__client_session_token_ttl = client_session_token_ttl + self.__resources_ttl = resources_ttl + self.__user_tracking_id = user_tracking_id + self.__notifications = notifications + self.__sdk_config = sdk_config + self.__requested_checks = requested_checks + self.__requested_tasks = requested_tasks + + @property + def client_session_token_ttl(self): + """ + Client-session-token time-to-live to apply to the created Session + + :return: the client-session-token time-to-live + :rtype: int + """ + return self.__client_session_token_ttl + + @property + def resources_ttl(self): + """ + Time-to-live used for all Resources created in the course of the session + + :return: the time-to-live for Resources + :rtype: int + """ + return self.__resources_ttl + + @property + def user_tracking_id(self): + """ + User tracking ID, for the Relying Business to track returning users + + :return: the user tracking ID + :rtype: str + """ + return self.__user_tracking_id + + @property + def notifications(self): + """ + :class:`NotificationConfig` for configuring call-back messages + + :return: the notification config + :rtype: NotificationConfig + """ + return self.__notifications + + @property + def sdk_config(self): + """ + Retrieves the SDK configuration set of the session specification + + :return: the SDK config + :rtype: SdkConfig + """ + return self.__sdk_config + + @property + def requested_checks(self): + """ + List of :class:`RequestedCheck` objects defining the Checks to be performed + on each Document + + :return: the requested checks + :rtype: list[RequestedCheck] + """ + return self.__requested_checks + + @property + def requested_tasks(self): + """ + List of :class:`RequestedTask` objects defining the Tasks to be performed + on each Document + + :return: the requested tasks + :rtype: list[RequestedTask] + """ + return self.__requested_tasks + + def to_json(self): + return { + "client_session_token_ttl": self.client_session_token_ttl, + "resources_ttl": self.resources_ttl, + "user_tracking_id": self.user_tracking_id, + "notifications": self.notifications, + "requested_checks": self.requested_checks, + "requested_tasks": self.requested_tasks, + "sdk_config": self.sdk_config, + } + + +class SessionSpecBuilder(object): + """ + Builder to assist the creation of :class:`SessionSpec` + """ + + def __init__(self): + self.__client_session_token_ttl = None + self.__resources_ttl = None + self.__user_tracking_id = None + self.__notifications = None + self.__sdk_config = None + self.__requested_checks = [] + self.__requested_tasks = [] + + def with_client_session_token_ttl(self, value): + """ + Sets the client session token TTL (time-to-live) + + :param value: the client session token TTL + :type value: int + :return: the builder + :rtype: SessionSpecBuilder + """ + self.__client_session_token_ttl = value + return self + + def with_resources_ttl(self, value): + """ + Sets the resources TTL (time-to-live) + + :param value: the resources TTL + :type value: int + :return: the builder + :rtype: SessionSpecBuilder + """ + self.__resources_ttl = value + return self + + def with_user_tracking_id(self, value): + """ + Sets the user tracking ID + + :param value: the user tracking ID + :type value: str + :return: the builder + :rtype: SessionSpecBuilder + """ + self.__user_tracking_id = value + return self + + def with_notifications(self, notifications): + """ + Sets the notification configuration + + :param notifications: the notification config + :type notifications: NotificationConfig + :return: the builder + :rtype: SessionSpecBuilder + """ + self.__notifications = notifications + return self + + def with_requested_check(self, check): + """ + Adds a :class:`RequestedCheck` to the list + + :param check: the check to add + :type check: RequestedCheck + :return: the builder + :rtype: SessionSpecBuilder + """ + self.__requested_checks.append(check) + return self + + def with_requested_task(self, task): + """ + Adds a :class:`RequestedTask` to the list + + :param task: the task to add + :type task: RequestedTask + :return: the builder + :rtype: SessionSpecBuilder + """ + self.__requested_tasks.append(task) + return self + + def with_sdk_config(self, value): + """ + Sets the SDK configuration + + :param value: the SDK config + :type value: SdkConfig + :return: the builder + :rtype: SessionSpecBuilder + """ + self.__sdk_config = value + return self + + def build(self): + """ + Builds a :class:`SessionSpec` using the supplied values + + :return: the built Session Specification + :rtype: SessionSpec + """ + return SessionSpec( + self.__client_session_token_ttl, + self.__resources_ttl, + self.__user_tracking_id, + self.__notifications, + self.__sdk_config, + self.__requested_checks, + self.__requested_tasks, + ) diff --git a/yoti_python_sdk/docs/session/create/task/__init__.py b/yoti_python_sdk/docs/session/create/task/__init__.py new file mode 100644 index 00000000..44755304 --- /dev/null +++ b/yoti_python_sdk/docs/session/create/task/__init__.py @@ -0,0 +1,3 @@ +from .text_extraction import RequestedTextExtractionTaskBuilder + +__all__ = [RequestedTextExtractionTaskBuilder] diff --git a/yoti_python_sdk/docs/session/create/task/requested_task.py b/yoti_python_sdk/docs/session/create/task/requested_task.py new file mode 100644 index 00000000..9dccc8eb --- /dev/null +++ b/yoti_python_sdk/docs/session/create/task/requested_task.py @@ -0,0 +1,36 @@ +from abc import ABCMeta +from abc import abstractmethod + +from yoti_python_sdk.utils import YotiSerializable + + +class RequestedTask(YotiSerializable): + """ + Requests creation of a Task to be performed on each document + """ + + __metaclass__ = ABCMeta + + @property + @abstractmethod + def type(self): + """ + Returns the tyoe of the Task to create + + :return: the type + :rtype: str + """ + raise NotImplementedError + + @property + @abstractmethod + def config(self): + """ + Configuration to apply to the Task + + :return: the configuration + """ + raise NotImplementedError + + def to_json(self): + return {"type": self.type, "config": self.config} diff --git a/yoti_python_sdk/docs/session/create/task/text_extraction.py b/yoti_python_sdk/docs/session/create/task/text_extraction.py new file mode 100644 index 00000000..4af11fa4 --- /dev/null +++ b/yoti_python_sdk/docs/session/create/task/text_extraction.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs import constants +from yoti_python_sdk.utils import YotiSerializable +from .requested_task import RequestedTask + + +class RequestedTextExtractionTaskConfig(YotiSerializable): + def __init__(self, manual_check): + """ + :param manual_check: the manual check value + :type manual_check: str + """ + self.__manual_check = manual_check + + @property + def manual_check(self): + """ + Describes the manual fallback behaviour applied to each Task + + :return: the manual check value + """ + return self.__manual_check + + def to_json(self): + return {"manual_check": self.manual_check} + + +class RequestedTextExtractionTask(RequestedTask): + """ + Builder to assist creation of :class:`RequestedTextExtractionTask` + """ + + def __init__(self, config): + """ + :param config: the text extraction task configuration + :type config: RequestedTextExtractionTaskConfig + """ + self.__config = config + + @property + def type(self): + return constants.ID_DOCUMENT_TEXT_DATA_EXTRACTION + + @property + def config(self): + return self.__config + + +class RequestedTextExtractionTaskBuilder(object): + """ + Builder to assist creation of :class:`RequestedTextExtractionTask` + """ + + def __init__(self): + self.__manual_check = None + + def with_manual_check_always(self): + """ + Sets the manual check value to be "ALWAYS" + + :return: the builder + :rtype: RequestedTextExtractionTaskBuilder + """ + self.__manual_check = constants.ALWAYS + return self + + def with_manual_check_fallback(self): + """ + Sets the manual check value to be "FALLBACK" + + :return: the builder + :rtype: RequestedTextExtractionTaskBuilder + """ + self.__manual_check = constants.FALLBACK + return self + + def with_manual_check_never(self): + """ + Sets the manual check value to be "NEVER" + + :return: the builder + :rtype: RequestedTextExtractionTaskBuilder + """ + self.__manual_check = constants.NEVER + return self + + def build(self): + config = RequestedTextExtractionTaskConfig(self.__manual_check) + return RequestedTextExtractionTask(config) diff --git a/yoti_python_sdk/docs/session/retrieve/__init__.py b/yoti_python_sdk/docs/session/retrieve/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/yoti_python_sdk/docs/session/retrieve/breakdown_response.py b/yoti_python_sdk/docs/session/retrieve/breakdown_response.py new file mode 100644 index 00000000..20622087 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/breakdown_response.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + + +class BreakdownResponse(object): + """ + Represents one breakdown item for a given check + """ + + def __init__(self, data): + """ + :param data: the data to parse + :type data: dict + """ + self.__sub_check = data.get("sub_check", None) + self.__result = data.get("result", None) + self.__details = [DetailsResponse(detail) for detail in data.get("details", [])] + + @property + def sub_check(self): + """ + The sub check value for the breakdown + + :return: the sub check value + :rtype: str or None + """ + return self.__sub_check + + @property + def result(self): + """ + The result of the sub check + + :return: the result + :rtype: str or None + """ + return self.__result + + @property + def details(self): + """ + The details of the sub check + + :return: the details + :rtype: list[DetailsResponse] + """ + return self.__details + + +class DetailsResponse(object): + """ + Represents a specific detail for a breakdown + """ + + def __init__(self, data): + self.__name = data.get("name", None) + self.__value = data.get("value", None) + + @property + def name(self): + """ + The name of the details item + + :return: the name + :rtype: str or None + """ + return self.__name + + @property + def value(self): + """ + The value of the details item + + :return: the value + :rtype: str or None + """ + return self.__value diff --git a/yoti_python_sdk/docs/session/retrieve/check_response.py b/yoti_python_sdk/docs/session/retrieve/check_response.py new file mode 100644 index 00000000..c5660ab0 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/check_response.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from iso8601 import ParseError +from iso8601 import iso8601 + +from .generated_media import GeneratedMedia +from .report_response import ReportResponse + + +class CheckResponse(object): + """ + Represents the base attributes for a check for any given session + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__state = data.get("state", None) + self.__type = data.get("type", None) + self.__resources_used = data.get("resources_used", []) + + self.__report = ( + ReportResponse(data["report"]) + if data.get("report", None) is not None + else None + ) + self.__generated_media = [ + GeneratedMedia(media) for media in data.get("generated_media", []) + ] + + self.__created = self.__parse_date(data.get("created", None)) + self.__last_updated = self.__parse_date(data.get("last_updated", None)) + + @staticmethod + def __parse_date(date): + """ + Attempts to parse a date from string using the + iso8601 library. Returns None if there was an error + + :param date: the datestring to parse + :type date: str + :return: the parsed date + :rtype: datetime.datetime or None + """ + if date is None: + return date + + try: + return iso8601.parse_date(date) + except ParseError: + return None + + @property + def id(self): + """ + The ID of the check + + :return: the ID + :rtype: str or None + """ + return self.__id + + @property + def type(self): + """ + The type of the check + + :return: the type + :rtype: str or None + """ + return self.__type + + @property + def state(self): + """ + The state of the check, e.g. "COMPLETED" + + :return: the state + :rtype: str or None + """ + return self.__state + + @property + def resources_used(self): + """ + The resources used by the check + + :return: the list of resources used + :rtype: list[str] + """ + return self.__resources_used + + @property + def created(self): + """ + The date the check was created + + :return: the created date + :rtype: datetime.datetime or None + """ + return self.__created + + @property + def last_updated(self): + """ + The date the check was last updated + + :return: the last updated date + :rtype: datetime.datetime or None + """ + return self.__last_updated + + @property + def generated_media(self): + """ + The list of media generated by the check + + :return: the list of generated media + :rtype: list[GeneratedMedia] + """ + return self.__generated_media + + @property + def report(self): + """ + Report for the check + + :return: the report + :rtype: ReportResponse or None + """ + return self.__report + + +class AuthenticityCheckResponse(CheckResponse): + """ + Represents a Document Authenticity check for a given session + """ + + pass + + +class FaceMatchCheckResponse(CheckResponse): + """ + Represents a FaceMatch Check for a given session + """ + + pass + + +class LivenessCheckResponse(CheckResponse): + """ + Represents a Liveness Check for a given session + """ + + pass + + +class TextDataCheckResponse(CheckResponse): + """ + Represents a Text Data check for a given session + """ + + pass diff --git a/yoti_python_sdk/docs/session/retrieve/create_session_result.py b/yoti_python_sdk/docs/session/retrieve/create_session_result.py new file mode 100644 index 00000000..c64dd9da --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/create_session_result.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + + +class CreateSessionResult(object): + """ + The response to a successful CreateSession call + """ + + def __init__(self, data=None): + """ + :param data: the data + :type data: dict or None + """ + if data is None: + data = dict() + + self.__client_session_token_ttl = data.get("client_session_token_ttl", None) + self.__session_id = data.get("session_id", None) + self.__client_session_token = data.get("client_session_token", None) + + @property + def client_session_token_ttl(self): + """ + Returns the time-to-live (TTL) for the client session + token for the created session + + :return: the client session token TTL + :rtype: int or None + """ + return self.__client_session_token_ttl + + @property + def client_session_token(self): + """ + Returns the client session token for the created session + + :return: the client session token + :rtype: str or None + """ + return self.__client_session_token + + @property + def session_id(self): + """ + Session ID of the created session + + :return: the session ID + :rtype: str or None + """ + return self.__session_id diff --git a/yoti_python_sdk/docs/session/retrieve/document_fields_response.py b/yoti_python_sdk/docs/session/retrieve/document_fields_response.py new file mode 100644 index 00000000..ace4e16a --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/document_fields_response.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse + + +class DocumentFieldsResponse(object): + """ + Represents the document fields response + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + if "media" in data.keys(): + self.__media = MediaResponse(data["media"]) + else: + self.__media = None + + @property + def media(self): + """ + The media object for the document fields + + :return: the media + :rtype: MediaResponse or None + """ + return self.__media diff --git a/yoti_python_sdk/docs/session/retrieve/face_map_response.py b/yoti_python_sdk/docs/session/retrieve/face_map_response.py new file mode 100644 index 00000000..448d0ff3 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/face_map_response.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse + + +class FaceMapResponse(object): + """ + Represents a FaceMap response object + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__media = MediaResponse(data["media"]) if "media" in data.keys() else None + + @property + def media(self): + """ + Returns the associated media of the FaceMap + + :return: the media + :rtype: MediaResponse or None + """ + return self.__media diff --git a/yoti_python_sdk/docs/session/retrieve/frame_response.py b/yoti_python_sdk/docs/session/retrieve/frame_response.py new file mode 100644 index 00000000..169ed15a --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/frame_response.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse + + +class FrameResponse(object): + """ + Represents a frame of a resource + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__media = MediaResponse(data["media"]) if "media" in data.keys() else None + + @property + def media(self): + """ + Returns the media associated with the frame + + :return: the media + :rtype: MediaResponse or None + """ + return self.__media diff --git a/yoti_python_sdk/docs/session/retrieve/generated_check_response.py b/yoti_python_sdk/docs/session/retrieve/generated_check_response.py new file mode 100644 index 00000000..eddcb508 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/generated_check_response.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + + +class GeneratedCheckResponse(object): + """ + Represents a check response that has been generated by + the session + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__type = data.get("type", None) + + @property + def id(self): + """ + The id of the generated check + + :return: the id + :rtype: str or None + """ + return self.__id + + @property + def type(self): + """ + Returns the type of the generated check + + :return: the type + :rtype: str or None + """ + return self.__type + + +class GeneratedTextDataCheckResponse(GeneratedCheckResponse): + """ + Represents a generated Text Data check response + """ + + pass diff --git a/yoti_python_sdk/docs/session/retrieve/generated_media.py b/yoti_python_sdk/docs/session/retrieve/generated_media.py new file mode 100644 index 00000000..61dfae63 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/generated_media.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + + +class GeneratedMedia(object): + """ + Represents media that has been generated by the session + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__type = data.get("type", None) + + @property + def id(self): + """ + The ID of the generated media + + :return: the ID + :rtype: str or None + """ + return self.__id + + @property + def type(self): + """ + The type of the generated media, e.g. "JSON" + + :return: the type + :rtype: str or None + """ + return self.__type diff --git a/yoti_python_sdk/docs/session/retrieve/get_session_result.py b/yoti_python_sdk/docs/session/retrieve/get_session_result.py new file mode 100644 index 00000000..fe1ac1de --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/get_session_result.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs import constants +from .check_response import AuthenticityCheckResponse +from .check_response import CheckResponse +from .check_response import FaceMatchCheckResponse +from .check_response import LivenessCheckResponse +from .check_response import TextDataCheckResponse +from .resource_container import ResourceContainer + + +class GetSessionResult(object): + """ + Represents all information about the state of a session + at the time of the call, including check information, + resources etc. + """ + + def __init__(self, data): + """ + :param data: the data to parse + :type data: dict + """ + self.__client_session_token_ttl = data.get("client_session_token_ttl", None) + self.__session_id = data.get("session_id", None) + self.__user_tracking_id = data.get("user_tracking_id", None) + self.__state = data.get("state", None) + self.__client_session_token = data.get("client_session_token", None) + self.__checks = [self.__parse_check(check) for check in data.get("checks", [])] + + resources = data.get("resources", None) + self.__resources = ResourceContainer(resources) or None + + @staticmethod + def __parse_check(check): + """ + Parses a check into a sub-type of :class:`CheckResponse`, + or falls back to CheckResponse if an unknown type + + :param check: the check object + :type check: dict + :return: the parsed check + :rtype: CheckResponse + """ + types = { + constants.ID_DOCUMENT_AUTHENTICITY: AuthenticityCheckResponse, + constants.ID_DOCUMENT_FACE_MATCH: FaceMatchCheckResponse, + constants.ID_DOCUMENT_TEXT_DATA_CHECK: TextDataCheckResponse, + constants.LIVENESS: LivenessCheckResponse, + } + clazz = types.get(check.get("type", None), CheckResponse) + return clazz(check) + + @property + def client_session_token_ttl(self): + """ + The client session token time-to-live (TTL) + + :return: the client session token ttl + :rtype: int or None + """ + return self.__client_session_token_ttl + + @property + def session_id(self): + """ + The session ID + + :return: the session id + :rtype: str or None + """ + return self.__session_id + + @property + def user_tracking_id(self): + """ + The user tracking ID for the session + + :return: the user tracking id + :rtype: str or None + """ + return self.__user_tracking_id + + @property + def state(self): + """ + The state of the session, represented as a string e.g. "COMPLETED" + + :return: the state + :rtype: str or None + """ + return self.__state + + @property + def client_session_token(self): + """ + The client session token + + :return: the client session token + :rtype: str or None + """ + return self.__client_session_token + + @property + def checks(self): + """ + The list of Checks associated with the session + + :return: the list of Checks + :rtype: list[CheckResponse] + """ + return self.__checks + + def __checks_of_type(self, clazz): + """ + Filter the list of checks by the class type + + :param clazz: the class + :type clazz: tuple[type] + :return: + :rtype: + """ + return [check for check in self.checks if isinstance(check, clazz)] + + @property + def authenticity_checks(self): + """ + A filtered list of checks, returning only document authenticity checks + + :return: the document authenticity checks + :rtype: list[AuthenticityCheckResponse] + """ + return self.__checks_of_type((AuthenticityCheckResponse,)) + + @property + def face_match_checks(self): + """ + A filtered list of checks, returning only FaceMatch checks + + :return: the FaceMatch checks + :rtype: list[FaceMatchCheckResponse] + """ + return self.__checks_of_type((FaceMatchCheckResponse,)) + + @property + def text_data_checks(self): + """ + A filtered list of checks, returning only Text Data checks + + :return: the Text Data checks + :rtype: list[TextDataCheckResponse] + """ + return self.__checks_of_type((TextDataCheckResponse,)) + + @property + def liveness_checks(self): + """ + A filtered list of checks, returning only Liveness checks + + :return: the Liveness checks + :rtype: list[LivenessCheckResponse] + """ + return self.__checks_of_type((LivenessCheckResponse,)) + + @property + def resources(self): + """ + The resources associated with the session + + :return: the resources + :rtype: ResourceContainer or None + """ + return self.__resources diff --git a/yoti_python_sdk/docs/session/retrieve/id_document_resource_response.py b/yoti_python_sdk/docs/session/retrieve/id_document_resource_response.py new file mode 100644 index 00000000..520f55b6 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/id_document_resource_response.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs.session.retrieve.document_fields_response import ( + DocumentFieldsResponse, +) +from yoti_python_sdk.docs.session.retrieve.page_response import PageResponse +from yoti_python_sdk.docs.session.retrieve.task_response import TaskResponse + + +class IdDocumentResourceResponse(object): + """ + Represents an Identity Document resource for a given session + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__document_type = data.get("document_type", None) + self.__issuing_country = data.get("issuing_country", None) + self.__tasks = [TaskResponse(task) for task in data.get("tasks", [])] + self.__pages = [PageResponse(page) for page in data.get("pages", [])] + self.__document_fields = ( + DocumentFieldsResponse(data["document_fields"]) + if "document_fields" in data.keys() + else None + ) + + @property + def id(self): + """ + Returns the ID of the identity document + + :return: the ID + :rtype: str or None + """ + return self.__id + + @property + def tasks(self): + """ + Returns all associated tasks of the identity document + + :return: the associated tasks + :rtype: list[TaskResponse] + """ + return self.__tasks + + @property + def document_type(self): + """ + Returns the identity document type, e.g. "PASSPORT" + + :return: the document type + :rtype: str or None + """ + return self.__document_type + + @property + def issuing_country(self): + """ + Returns the issuing country of the identity document + + :return: the issuing country + :rtype: str or None + """ + return self.__issuing_country + + @property + def pages(self): + """ + Returns the individual pages of the identity document + + :return: the pages + :rtype: list[PageResponse] + """ + return self.__pages + + @property + def document_fields(self): + """ + Returns the associated document fields + + :return: the document fields + :rtype: DocumentFieldsResponse + """ + return self.__document_fields diff --git a/yoti_python_sdk/docs/session/retrieve/liveness_resource_response.py b/yoti_python_sdk/docs/session/retrieve/liveness_resource_response.py new file mode 100644 index 00000000..7f629475 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/liveness_resource_response.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from .resource_response import ResourceResponse +from .face_map_response import FaceMapResponse +from .frame_response import FrameResponse + + +class LivenessResourceResponse(ResourceResponse): + """ + Represents a Liveness resource for a given session + """ + + pass + + +class ZoomLivenessResourceResponse(LivenessResourceResponse): + """ + Represents a Zoom Liveness resource for a given session + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + LivenessResourceResponse.__init__(self, data) + + self.__facemap = ( + FaceMapResponse(data["facemap"]) if "facemap" in data.keys() else None + ) + self.__frames = [FrameResponse(frame) for frame in data.get("frames", [])] + + @property + def facemap(self): + """ + Returns the associated facemap information for + the zoom liveness resource + + :return: the facemap + :rtype: FaceMapResponse or None + """ + return self.__facemap + + @property + def frames(self): + """ + Returns the list of associated frames for + the zoom liveness resource + + :return: the frames + :rtype: list[FrameResponse] + """ + return self.__frames diff --git a/yoti_python_sdk/docs/session/retrieve/media_response.py b/yoti_python_sdk/docs/session/retrieve/media_response.py new file mode 100644 index 00000000..995250fd --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/media_response.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import iso8601 +from iso8601 import ParseError + + +class MediaResponse(object): + """ + Represents a media resource + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__type = data.get("type", None) + self.__created = self.__parse_date(data.get("created", None)) + self.__last_updated = self.__parse_date(data.get("last_updated", None)) + + @staticmethod + def __parse_date(date): + if date is None: + return date + + try: + return iso8601.parse_date(date) + except ParseError: + return None + + @property + def id(self): + """ + The ID of the media resource + + :return: the ID + :rtype: str or None + """ + return self.__id + + @property + def type(self): + """ + The type of the media resource, e.g. "JSON" + + :return: the type + :rtype: str or None + """ + return self.__type + + @property + def created(self): + """ + The date the media resource was created + + :return: the created date + :rtype: datetime.datetime or None + """ + return self.__created + + @property + def last_updated(self): + """ + The date the media resource was last updated + + :return: the last updated date + :rtype: datetime.datetime or None + """ + return self.__last_updated diff --git a/yoti_python_sdk/docs/session/retrieve/media_value.py b/yoti_python_sdk/docs/session/retrieve/media_value.py new file mode 100644 index 00000000..7bf13458 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/media_value.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import base64 + + +class MediaValue(object): + def __init__(self, content_type, content): + self.__mime_type = content_type + self.__content = content + + @property + def mime_type(self): + return self.__mime_type + + @property + def content(self): + return self.__content + + @property + def base64_content(self): + return "data:%s;base64,%s" % ( + self.mime_type, + base64.b64encode(self.__content).decode("utf-8"), + ) diff --git a/yoti_python_sdk/docs/session/retrieve/page_response.py b/yoti_python_sdk/docs/session/retrieve/page_response.py new file mode 100644 index 00000000..949ae345 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/page_response.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from .media_response import MediaResponse + + +class PageResponse(object): + """ + Represents information about an uploaded document Page + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__capture_method = ( + data["capture_method"] if "capture_method" in data.keys() else None + ) + self.__media = MediaResponse(data["media"]) if "media" in data.keys() else None + + @property + def capture_method(self): + """ + The capture method that was used for the Page + + :return: the capture method + :rtype: str or None + """ + return self.__capture_method + + @property + def media(self): + """ + The media associated with the Page + + :return: the media + :rtype: MediaResponse or None + """ + return self.__media diff --git a/yoti_python_sdk/docs/session/retrieve/recommendation_response.py b/yoti_python_sdk/docs/session/retrieve/recommendation_response.py new file mode 100644 index 00000000..243b1a64 --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/recommendation_response.py @@ -0,0 +1,46 @@ +class RecommendationResponse(object): + """ + Represents the recommendation given for a check + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__value = data.get("value", None) + self.__reason = data.get("reason", None) + self.__recovery_suggestion = data.get("recovery_suggestion", None) + + @property + def value(self): + """ + Returns the value of the recommendation + + :return: the value + :rtype: str or None + """ + return self.__value + + @property + def reason(self): + """ + Returns the reason of the recommendation + + :return: the reason + :rtype: str or None + """ + return self.__reason + + @property + def recovery_suggestion(self): + """ + Returns the recovery suggestion of the recommendtion + + :return: the recovery suggestion + :rtype: str or None + """ + return self.__recovery_suggestion diff --git a/yoti_python_sdk/docs/session/retrieve/report_response.py b/yoti_python_sdk/docs/session/retrieve/report_response.py new file mode 100644 index 00000000..4ab1f45b --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/report_response.py @@ -0,0 +1,46 @@ +from .breakdown_response import BreakdownResponse +from .recommendation_response import RecommendationResponse + + +class ReportResponse(object): + """ + Represents a report for a given check + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__recommendation = ( + RecommendationResponse(data["recommendation"]) + if "recommendation" in data.keys() + else None + ) + + self.__breakdown = [ + BreakdownResponse(breakdown) for breakdown in data.get("breakdown", []) + ] + + @property + def recommendation(self): + """ + The recommendation given for a given check/task + + :return: the recommendation + :rtype: RecommendationResponse + """ + return self.__recommendation + + @property + def breakdown(self): + """ + A list of breakdowns for different sub-checks performed + + :return: the list of breakdowns + :rtype: list[BreakdownResponse] + """ + return self.__breakdown diff --git a/yoti_python_sdk/docs/session/retrieve/resource_container.py b/yoti_python_sdk/docs/session/retrieve/resource_container.py new file mode 100644 index 00000000..b4bfc97a --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/resource_container.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.docs.session.retrieve.id_document_resource_response import ( + IdDocumentResourceResponse, +) +from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( + LivenessResourceResponse, +) +from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( + ZoomLivenessResourceResponse, +) + + +class ResourceContainer(object): + """ + Contains different resources that are part of the Yoti + Doc Scan session + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id_documents = [ + IdDocumentResourceResponse(document) + for document in data.get("id_documents", []) + ] + + self.__liveness_capture = [ + self.__parse_liveness_capture(liveness) + for liveness in data.get("liveness_capture", []) + ] + + @staticmethod + def __parse_liveness_capture(liveness_capture): + """ + Parses a liveness capture into a specific sub-class based on the + liveness type. If no liveness type is available, it falls back + to the parent class :class:`LivensssResourceResponse` + + :param liveness_capture: the liveness capture + :type liveness_capture: dict + :return: the parsed liveness capture + :rtype: LivenessResourceResponse + """ + types = {"ZOOM": ZoomLivenessResourceResponse} + + clazz = types.get( + liveness_capture.get("liveness_type", None), + LivenessResourceResponse, # Fallback value for unknown type + ) + return clazz(liveness_capture) + + @property + def id_documents(self): + """ + Return a list of ID document resources + + :return: list of ID documents + :rtype: list[IdDocumentResourceResponse] + """ + return self.__id_documents + + @property + def liveness_capture(self): + """ + Return a list of liveness capture resources + + :return: list of liveness captures + :rtype: list[LivenessResourceResponse] + """ + return self.__liveness_capture diff --git a/yoti_python_sdk/docs/session/retrieve/resource_response.py b/yoti_python_sdk/docs/session/retrieve/resource_response.py new file mode 100644 index 00000000..c840231c --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/resource_response.py @@ -0,0 +1,52 @@ +from yoti_python_sdk.docs import constants +from .task_response import TaskResponse +from .task_response import TextExtractionTaskResponse + + +class ResourceResponse(object): + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__tasks = [self.__parse_task(task) for task in data.get("tasks", [])] + + @staticmethod + def __parse_task(task): + """ + Return a parsed task from a dictionary + + :param task: the raw task + :type task: dict + :return: the parsed task + :rtype: TaskResponse + """ + types = {constants.ID_DOCUMENT_TEXT_DATA_EXTRACTION: TextExtractionTaskResponse} + clazz = types.get( + task.get("type", None), TaskResponse # Default fallback for task type + ) + return clazz(task) + + @property + def id(self): + """ + The ID of the resource + + :return: the id + :rtype: str + """ + return self.__id + + @property + def tasks(self): + """ + Tasks associated with a resource + + :return: the list of tasks + :rtype: list[TaskResponse] + """ + return self.__tasks diff --git a/yoti_python_sdk/docs/session/retrieve/task_response.py b/yoti_python_sdk/docs/session/retrieve/task_response.py new file mode 100644 index 00000000..fb6f9fba --- /dev/null +++ b/yoti_python_sdk/docs/session/retrieve/task_response.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import iso8601 +from iso8601 import ParseError + +from yoti_python_sdk.docs import constants +from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( + GeneratedCheckResponse, +) +from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( + GeneratedTextDataCheckResponse, +) +from yoti_python_sdk.docs.session.retrieve.generated_media import GeneratedMedia + + +class TaskResponse(object): + """ + Represents a task + """ + + def __init__(self, data=None): + """ + :param data: the data to parse + :type data: dict or None + """ + if data is None: + data = dict() + + self.__id = data.get("id", None) + self.__type = data.get("type", None) + self.__state = data.get("state", None) + + self.__created = self.__parse_date(data.get("created", None)) + self.__last_updated = self.__parse_date(data.get("last_updated", None)) + + self.__generated_checks = [ + self.__parse_generated_check(check) + for check in data.get("generated_checks", []) + ] + self.__generated_media = [ + GeneratedMedia(media) for media in data.get("generated_media", []) + ] + + @staticmethod + def __parse_generated_check(generated_check): + """ + Parse a generated check into an object from a dict + :param generated_check: the raw generated check + :type generated_check: dict + :return: the parse generated check + :rtype: GeneratedCheckResponse + """ + types = {constants.ID_DOCUMENT_TEXT_DATA_CHECK: GeneratedTextDataCheckResponse} + + clazz = types.get( + generated_check.get("type", None), + GeneratedCheckResponse, # Default fallback for type + ) + return clazz(generated_check) + + @staticmethod + def __parse_date(date): + """ + Parse a date using the iso8601 library, + returning None if there was an error + + :param date: the date string to parse + :type date: str + :return: the parse date + :rtype: datetime.datetime or None + """ + if date is None: + return None + + try: + return iso8601.parse_date(date) + except ParseError: + return None + + @property + def type(self): + """ + Return the type of the task + + :return: the type + :rtype: str or None + """ + return self.__type + + @property + def id(self): + """ + Return the ID of the task + + :return: the ID + :rtype: str or None + """ + return self.__id + + @property + def state(self): + """ + Return the state of the task + + :return: the state + :rtype: str or None + """ + return self.__state + + @property + def created(self): + """ + Return the date the task was created + + :return: the created date + :rtype: datetime.datetime or None + """ + return self.__created + + @property + def last_updated(self): + """ + Return the date the task was last updated + + :return: the last updated date + :rtype: datetime.datetime or None + """ + return self.__last_updated + + @property + def generated_checks(self): + """ + Return the list of checks that were generated + by the task + + :return: the generated checks + :rtype: list[GeneratedCheckResponse] + """ + return self.__generated_checks + + @property + def generated_media(self): + """ + Return the list of media that has been generated + by the task + + :return: the list of generated media + :rtype: list[GeneratedMedia] + """ + return self.__generated_media + + +class TextExtractionTaskResponse(TaskResponse): + """ + Represents a Text Extraction task response + """ + + pass diff --git a/yoti_python_sdk/http.py b/yoti_python_sdk/http.py index 09225983..46ee5a40 100644 --- a/yoti_python_sdk/http.py +++ b/yoti_python_sdk/http.py @@ -24,12 +24,37 @@ class YotiResponse(object): - def __init__(self, status_code, text): + def __init__(self, status_code, text, headers=None): + if headers is None: + headers = {} + self.status_code = status_code self.text = text + self.headers = headers + + +class MediaResponse(object): + def __init__(self, response): + self.response = response + + @property + def status_code(self): + return self.response.status_code + + @property + def content(self): + return self.response.content + @property + def text(self): + return self.response.text -class RequestHandler: + @property + def headers(self): + return self.response.headers + + +class RequestHandler(object): """ Default request handler for signing requests using the requests library. This type can be inherited and the execute method overridden to use any @@ -60,7 +85,29 @@ def execute(request): headers=request.headers, ) - return YotiResponse(status_code=response.status_code, text=response.text) + return YotiResponse( + status_code=response.status_code, + text=response.text, + headers=response.headers, + ) + + +class MediaRequestHandler(RequestHandler): + @staticmethod + def execute(request): + """ + Execute the HTTP request supplied + """ + if not isinstance(request, SignedRequest): + raise TypeError("RequestHandler expects instance of SignedRequest") + + response = requests.request( + url=request.url, + method=request.method, + data=request.data, + headers=request.headers, + ) + return MediaResponse(response) class SignedRequest(object): diff --git a/yoti_python_sdk/tests/docs/session/create/check/test_face_match_check.py b/yoti_python_sdk/tests/docs/session/create/check/test_face_match_check.py new file mode 100644 index 00000000..728caceb --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/create/check/test_face_match_check.py @@ -0,0 +1,42 @@ +import json +import unittest + +from yoti_python_sdk.docs.session.create import RequestedFaceMatchCheckBuilder +from yoti_python_sdk.docs.session.create.check.face_match import RequestedFaceMatchCheck +from yoti_python_sdk.docs.session.create.check.face_match import ( + RequestedFaceMatchCheckConfig, +) +from yoti_python_sdk.docs.session.create.check.requested_check import RequestedCheck +from yoti_python_sdk.utils import YotiEncoder + + +class RequestedFaceMatchCheckTest(unittest.TestCase): + def test_should_build_with_manual_check_always(self): + result = RequestedFaceMatchCheckBuilder().with_manual_check_always().build() + + assert isinstance(result, RequestedCheck) + assert isinstance(result, RequestedFaceMatchCheck) + assert isinstance(result.config, RequestedFaceMatchCheckConfig) + + assert result.type == "ID_DOCUMENT_FACE_MATCH" + assert result.config.manual_check == "ALWAYS" + + def test_should_build_with_manual_check_fallback(self): + result = RequestedFaceMatchCheckBuilder().with_manual_check_fallback().build() + + assert result.config.manual_check == "FALLBACK" + + def test_should_build_with_manual_check_never(self): + result = RequestedFaceMatchCheckBuilder().with_manual_check_never().build() + + assert result.config.manual_check == "NEVER" + + def test_should_serialize_to_json_without_error(self): + result = RequestedFaceMatchCheckBuilder().with_manual_check_never().build() + + s = json.dumps(result, cls=YotiEncoder) + assert s is not None and s != "" + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/create/check/test_liveness_check.py b/yoti_python_sdk/tests/docs/session/create/check/test_liveness_check.py new file mode 100644 index 00000000..7b6bd730 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/create/check/test_liveness_check.py @@ -0,0 +1,55 @@ +import unittest +import json + +from yoti_python_sdk.docs.session.create.check import RequestedLivenessCheckBuilder +from yoti_python_sdk.docs.session.create.check.liveness import RequestedLivenessCheck +from yoti_python_sdk.docs.session.create.check.liveness import ( + RequestedLivenessCheckConfig, +) +from yoti_python_sdk.docs.session.create.check.requested_check import RequestedCheck +from yoti_python_sdk.utils import YotiEncoder + + +class RequestedLivenessCheckTest(unittest.TestCase): + def test_should_build_correctly(self): + result = ( + RequestedLivenessCheckBuilder() + .with_liveness_type("SOME_LIVENESS_TYPE") + .with_max_retries(3) + .build() + ) + + assert isinstance(result, RequestedCheck) + assert isinstance(result, RequestedLivenessCheck) + assert isinstance(result.config, RequestedLivenessCheckConfig) + + assert result.type == "LIVENESS" + assert result.config.liveness_type == "SOME_LIVENESS_TYPE" + assert result.config.max_retries == 3 + + def test_should_build_with_zoom_liveness_type(self): + result = ( + RequestedLivenessCheckBuilder() + .for_zoom_liveness() + .with_max_retries(5) + .build() + ) + + assert result.type == "LIVENESS" + assert result.config.liveness_type == "ZOOM" + assert result.config.max_retries == 5 + + def test_should_serialize_to_json_without_error(self): + result = ( + RequestedLivenessCheckBuilder() + .for_zoom_liveness() + .with_max_retries(5) + .build() + ) + + s = json.dumps(result, cls=YotiEncoder) + assert s is not None and s != "" + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/create/check/test_requested_document_authenticity_check.py b/yoti_python_sdk/tests/docs/session/create/check/test_requested_document_authenticity_check.py new file mode 100644 index 00000000..9e1914cc --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/create/check/test_requested_document_authenticity_check.py @@ -0,0 +1,34 @@ +import json +import unittest + +from yoti_python_sdk.docs.session.create.check import ( + RequestedDocumentAuthenticityCheckBuilder, +) +from yoti_python_sdk.docs.session.create.check.document_authenticity import ( + RequestedDocumentAuthenticityCheck, +) +from yoti_python_sdk.docs.session.create.check.document_authenticity import ( + RequestedDocumentAuthenticityCheckConfig, +) +from yoti_python_sdk.docs.session.create.check.requested_check import RequestedCheck +from yoti_python_sdk.utils import YotiEncoder + + +class RequestedDocumentAuthenticityCheckTest(unittest.TestCase): + def test_should_build_correctly(self): + result = RequestedDocumentAuthenticityCheckBuilder().build() + + assert isinstance(result, RequestedCheck) + assert isinstance(result, RequestedDocumentAuthenticityCheck) + assert isinstance(result.config, RequestedDocumentAuthenticityCheckConfig) + assert result.type == "ID_DOCUMENT_AUTHENTICITY" + + def test_should_serialize_to_json_without_error(self): + result = RequestedDocumentAuthenticityCheckBuilder().build() + + s = json.dumps(result, cls=YotiEncoder) + assert s is not None and s != "" + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/create/task/test_text_extraction_task.py b/yoti_python_sdk/tests/docs/session/create/task/test_text_extraction_task.py new file mode 100644 index 00000000..050f9a5f --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/create/task/test_text_extraction_task.py @@ -0,0 +1,46 @@ +import json +import unittest + +from yoti_python_sdk.docs.session.create.task import RequestedTextExtractionTaskBuilder +from yoti_python_sdk.docs.session.create.task.requested_task import RequestedTask +from yoti_python_sdk.docs.session.create.task.text_extraction import ( + RequestedTextExtractionTask, +) +from yoti_python_sdk.docs.session.create.task.text_extraction import ( + RequestedTextExtractionTaskConfig, +) +from yoti_python_sdk.utils import YotiEncoder + + +class RequestedTextExtractionTaskTest(unittest.TestCase): + def test_should_build_with_manual_check_always(self): + result = RequestedTextExtractionTaskBuilder().with_manual_check_always().build() + + assert isinstance(result, RequestedTask) + assert isinstance(result, RequestedTextExtractionTask) + assert isinstance(result.config, RequestedTextExtractionTaskConfig) + + assert result.type == "ID_DOCUMENT_TEXT_DATA_EXTRACTION" + assert result.config.manual_check == "ALWAYS" + + def test_should_build_with_manual_check_fallback(self): + result = ( + RequestedTextExtractionTaskBuilder().with_manual_check_fallback().build() + ) + + assert result.config.manual_check == "FALLBACK" + + def test_should_build_with_manual_check_never(self): + result = RequestedTextExtractionTaskBuilder().with_manual_check_never().build() + + assert result.config.manual_check == "NEVER" + + def test_should_serialize_to_json_without_error(self): + result = RequestedTextExtractionTaskBuilder().with_manual_check_never().build() + + s = json.dumps(result, cls=YotiEncoder) + assert s is not None and s != "" + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/create/test_notification_config.py b/yoti_python_sdk/tests/docs/session/create/test_notification_config.py new file mode 100644 index 00000000..709920a6 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/create/test_notification_config.py @@ -0,0 +1,128 @@ +import json +import unittest + +from yoti_python_sdk.docs.session.create import NotificationConfigBuilder +from yoti_python_sdk.docs.session.create.notification_config import NotificationConfig +from yoti_python_sdk.utils import YotiEncoder + + +class NotificationConfigTest(unittest.TestCase): + SOME_AUTH_TOKEN = "someAuthToken" + SOME_ENDPOINT = "someEndpoint" + SOME_TOPIC = "someTopic" + + def test_should_build_correctly(self): + result = ( + NotificationConfigBuilder() + .with_auth_token(self.SOME_AUTH_TOKEN) + .with_endpoint(self.SOME_ENDPOINT) + .with_topic(self.SOME_TOPIC) + .build() + ) + + assert isinstance(result, NotificationConfig) + assert result.auth_token is self.SOME_AUTH_TOKEN + assert result.endpoint is self.SOME_ENDPOINT + assert self.SOME_TOPIC in result.topics + + def test_should_add_resource_update_topic(self): + result = ( + NotificationConfigBuilder() + .with_auth_token(self.SOME_AUTH_TOKEN) + .with_endpoint(self.SOME_ENDPOINT) + .for_resource_update() + .build() + ) + + assert "RESOURCE_UPDATE" in result.topics + + def test_should_add_task_completion_topic(self): + result = ( + NotificationConfigBuilder() + .with_auth_token(self.SOME_AUTH_TOKEN) + .with_endpoint(self.SOME_ENDPOINT) + .for_task_completion() + .build() + ) + + assert "TASK_COMPLETION" in result.topics + + def test_should_add_session_completion_topic(self): + result = ( + NotificationConfigBuilder() + .with_auth_token(self.SOME_AUTH_TOKEN) + .with_endpoint(self.SOME_ENDPOINT) + .for_session_completion() + .build() + ) + + assert "SESSION_COMPLETION" in result.topics + + def test_should_add_check_completion_topic(self): + result = ( + NotificationConfigBuilder() + .with_auth_token(self.SOME_AUTH_TOKEN) + .with_endpoint(self.SOME_ENDPOINT) + .for_check_completion() + .build() + ) + + assert "CHECK_COMPLETION" in result.topics + + def test_should_allow_multiple_topics(self): + result = ( + NotificationConfigBuilder() + .with_auth_token(self.SOME_AUTH_TOKEN) + .with_endpoint(self.SOME_ENDPOINT) + .for_resource_update() + .for_task_completion() + .for_session_completion() + .for_check_completion() + .build() + ) + + expected = [ + "RESOURCE_UPDATE", + "TASK_COMPLETION", + "SESSION_COMPLETION", + "CHECK_COMPLETION", + ] + assert all(x in result.topics for x in expected) + + def test_should_store_unique_topics(self): + result = ( + NotificationConfigBuilder() + .with_auth_token(self.SOME_AUTH_TOKEN) + .with_endpoint(self.SOME_ENDPOINT) + .for_resource_update() + .for_resource_update() + .for_resource_update() + .build() + ) + + assert len(result.topics) == 1 + + def test_should_serialize_to_json_without_error(self): + result = ( + NotificationConfigBuilder() + .with_auth_token(self.SOME_AUTH_TOKEN) + .with_endpoint(self.SOME_ENDPOINT) + .for_resource_update() + .for_task_completion() + .for_session_completion() + .for_check_completion() + .build() + ) + + s = json.dumps(result, cls=YotiEncoder) + assert s is not None and s != "" + + def test_topics_should_default_to_empty_list_if_none(self): + result = NotificationConfig("someAuthToken", "someEndpoint", None) + + assert isinstance(result.topics, list) + assert len(result.topics) == 0 + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/create/test_sdk_config.py b/yoti_python_sdk/tests/docs/session/create/test_sdk_config.py new file mode 100644 index 00000000..f30e3c96 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/create/test_sdk_config.py @@ -0,0 +1,66 @@ +import json +import unittest + +from yoti_python_sdk.docs.session.create import SdkConfigBuilder +from yoti_python_sdk.docs.session.create.sdk_config import SdkConfig +from yoti_python_sdk.utils import YotiEncoder + + +class SdkConfigTest(unittest.TestCase): + SOME_PRIMARY_COLOUR = "#77355f" + SOME_SECONDARY_COLOUR = "#5bfc31" + SOME_FONT_COLOUR = "#60f021" + SOME_LOCALE = "en" + SOME_PRESET_ISSUING_COUNTRY = "USA" + SOME_SUCCESS_URL = "https://mysite.com/yoti/success" + SOME_ERROR_URL = "https://mysite.com/yoti/error" + + def test_should_build_correctly(self): + result = ( + SdkConfigBuilder() + .with_allows_camera_and_upload() + .with_primary_colour(self.SOME_PRIMARY_COLOUR) + .with_secondary_colour(self.SOME_SECONDARY_COLOUR) + .with_font_colour(self.SOME_FONT_COLOUR) + .with_locale(self.SOME_LOCALE) + .with_preset_issuing_country(self.SOME_PRESET_ISSUING_COUNTRY) + .with_success_url(self.SOME_SUCCESS_URL) + .with_error_url(self.SOME_ERROR_URL) + .build() + ) + + assert isinstance(result, SdkConfig) + assert result.allowed_capture_methods == "CAMERA_AND_UPLOAD" + assert result.primary_colour is self.SOME_PRIMARY_COLOUR + assert result.secondary_colour is self.SOME_SECONDARY_COLOUR + assert result.font_colour is self.SOME_FONT_COLOUR + assert result.locale is self.SOME_LOCALE + assert result.preset_issuing_country is self.SOME_PRESET_ISSUING_COUNTRY + assert result.success_url is self.SOME_SUCCESS_URL + assert result.error_url is self.SOME_ERROR_URL + + def test_should_allows_camera(self): + result = SdkConfigBuilder().with_allows_camera().build() + + assert result.allowed_capture_methods == "CAMERA" + + def test_should_serialize_to_json_without_error(self): + result = ( + SdkConfigBuilder() + .with_allows_camera_and_upload() + .with_primary_colour(self.SOME_PRIMARY_COLOUR) + .with_secondary_colour(self.SOME_SECONDARY_COLOUR) + .with_font_colour(self.SOME_FONT_COLOUR) + .with_locale(self.SOME_LOCALE) + .with_preset_issuing_country(self.SOME_PRESET_ISSUING_COUNTRY) + .with_success_url(self.SOME_SUCCESS_URL) + .with_error_url(self.SOME_ERROR_URL) + .build() + ) + + s = json.dumps(result, cls=YotiEncoder) + assert s is not None and s != "" + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/create/test_session_spec.py b/yoti_python_sdk/tests/docs/session/create/test_session_spec.py new file mode 100644 index 00000000..1001ecfa --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/create/test_session_spec.py @@ -0,0 +1,77 @@ +import json +import unittest + +from mock import Mock + +from yoti_python_sdk.docs.session.create import SessionSpecBuilder +from yoti_python_sdk.docs.session.create.check.requested_check import RequestedCheck +from yoti_python_sdk.docs.session.create.notification_config import NotificationConfig +from yoti_python_sdk.docs.session.create.sdk_config import SdkConfig +from yoti_python_sdk.docs.session.create.task.requested_task import RequestedTask +from yoti_python_sdk.utils import YotiEncoder + + +class SessionSpecTest(unittest.TestCase): + SOME_CLIENT_SESSION_TOKEN_TTL = 300 + SOME_RESOURCES_TTL = 100000 + SOME_USER_TRACKING_ID = "someUserTrackingId" + + def test_should_build_correctly(self): + sdk_config_mock = Mock(spec=SdkConfig) + notification_mock = Mock(spec=NotificationConfig) + requested_check_mock = Mock(spec=RequestedCheck) + requested_task_mock = Mock(spec=RequestedTask) + + result = ( + SessionSpecBuilder() + .with_client_session_token_ttl(self.SOME_CLIENT_SESSION_TOKEN_TTL) + .with_resources_ttl(self.SOME_RESOURCES_TTL) + .with_user_tracking_id(self.SOME_USER_TRACKING_ID) + .with_notifications(notification_mock) + .with_sdk_config(sdk_config_mock) + .with_requested_check(requested_check_mock) + .with_requested_task(requested_task_mock) + .build() + ) + + assert result.client_session_token_ttl is self.SOME_CLIENT_SESSION_TOKEN_TTL + assert result.resources_ttl is self.SOME_RESOURCES_TTL + assert result.user_tracking_id is self.SOME_USER_TRACKING_ID + assert result.sdk_config is sdk_config_mock + assert result.notifications is notification_mock + assert len(result.requested_checks) == 1 + assert requested_check_mock in result.requested_checks + assert len(result.requested_tasks) == 1 + assert requested_task_mock in result.requested_tasks + + def test_should_serialize_to_json_without_error(self): + sdk_config_mock = Mock(spec=SdkConfig) + sdk_config_mock.to_json.return_value = {} + + notification_mock = Mock(spec=NotificationConfig) + notification_mock.to_json.return_value = {} + + requested_check_mock = Mock(spec=RequestedCheck) + requested_check_mock.to_json.return_value = {} + + requested_task_mock = Mock(spec=RequestedTask) + requested_task_mock.to_json.return_value = {} + + result = ( + SessionSpecBuilder() + .with_client_session_token_ttl(self.SOME_CLIENT_SESSION_TOKEN_TTL) + .with_resources_ttl(self.SOME_RESOURCES_TTL) + .with_user_tracking_id(self.SOME_USER_TRACKING_ID) + .with_notifications(notification_mock) + .with_sdk_config(sdk_config_mock) + .with_requested_check(requested_check_mock) + .with_requested_task(requested_task_mock) + .build() + ) + + s = json.dumps(result, cls=YotiEncoder) + assert s is not None and s != "" + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/__init__.py b/yoti_python_sdk/tests/docs/session/retrieve/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_breakdown_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_breakdown_response.py new file mode 100644 index 00000000..8048fb04 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_breakdown_response.py @@ -0,0 +1,35 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.breakdown_response import BreakdownResponse + + +class BreakdownResponseTest(unittest.TestCase): + SOME_SUB_CHECK = "someSubCheck" + SOME_RESULT = "someResult" + SOME_DETAILS = [ + {"name": "firstDetailName", "value": "firstDetailValue"}, + {"name": "secondDetailName", "value": "secondDetailValue"}, + ] + + def test_should_build_correctly(self): + data = { + "sub_check": self.SOME_SUB_CHECK, + "result": self.SOME_RESULT, + "details": self.SOME_DETAILS, + } + + result = BreakdownResponse(data) + + assert result.sub_check is self.SOME_SUB_CHECK + assert result.result is self.SOME_RESULT + assert len(result.details) == 2 + assert result.details[0].name == "firstDetailName" + assert result.details[0].value == "firstDetailValue" + + def test_should_default_details_to_empty_list(self): + result = BreakdownResponse({}) + assert len(result.details) == 0 + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_check_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_check_response.py new file mode 100644 index 00000000..9acb2d20 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_check_response.py @@ -0,0 +1,85 @@ +import unittest +from datetime import datetime + +import pytz + +from yoti_python_sdk.docs.session.retrieve.check_response import CheckResponse +from yoti_python_sdk.docs.session.retrieve.generated_media import GeneratedMedia +from yoti_python_sdk.docs.session.retrieve.report_response import ReportResponse + + +class CheckResponseTest(unittest.TestCase): + SOME_ID = "someId" + SOME_STATE = "someState" + SOME_TYPE = "someType" + SOME_RESOURCES_USED = ["someFirstId", "someSecondId"] + SOME_REPORT = {} + SOME_GENERATED_MEDIA = [{"someKey": "someValue"}] + SOME_CREATED = "2019-05-01T05:01:48.000Z" + SOME_LAST_UPDATED = "2019-05-01T05:01:48.000Z" + + EXPECTED_DATETIME = datetime( + year=2019, + month=5, + day=1, + hour=5, + minute=1, + second=48, + microsecond=0, + tzinfo=pytz.utc, + ) + + def test_should_build_correctly(self): + data = { + "id": self.SOME_ID, + "state": self.SOME_STATE, + "type": self.SOME_TYPE, + "resources_used": self.SOME_RESOURCES_USED, + "report": self.SOME_REPORT, + "generated_media": self.SOME_GENERATED_MEDIA, + "created": self.SOME_CREATED, + "last_updated": self.SOME_LAST_UPDATED, + } + + result = CheckResponse(data) + + assert result.id is self.SOME_ID + assert result.state is self.SOME_STATE + assert result.type is self.SOME_TYPE + assert len(result.resources_used) == 2 + assert isinstance(result.report, ReportResponse) + assert len(result.generated_media) == 1 + assert isinstance(result.generated_media[0], GeneratedMedia) + assert isinstance(result.created, datetime) + + assert result.created == self.EXPECTED_DATETIME + assert result.last_updated == self.EXPECTED_DATETIME + + def test_should_default_relevant_properties_to_empty_list(self): + result = CheckResponse({}) + + assert len(result.resources_used) == 0 + assert len(result.generated_media) == 0 + + def test_should_default_data_if_none(self): + result = CheckResponse(None) + + assert result.id is None + assert result.state is None + assert result.type is None + assert result.created is None + assert result.last_updated is None + assert len(result.resources_used) == 0 + assert len(result.generated_media) == 0 + + def test_should_set_dates_to_none_if_invalid_format(self): + data = {"created": "someInvalidDate", "last_updated": "someInvalidDate"} + + result = CheckResponse(data) + + assert result.created is None + assert result.last_updated is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_create_session_result.py b/yoti_python_sdk/tests/docs/session/retrieve/test_create_session_result.py new file mode 100644 index 00000000..710cacab --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_create_session_result.py @@ -0,0 +1,35 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.create_session_result import ( + CreateSessionResult, +) + + +class CreateSessionResultTest(unittest.TestCase): + SOME_CLIENT_SESSION_TOKEN_TTL = 300 + SOME_CLIENT_SESSION_TOKEN = "someClientSessionToken" + SOME_SESSION_ID = "someSessionId" + + def test_should_build_correctly(self): + data = { + "client_session_token_ttl": self.SOME_CLIENT_SESSION_TOKEN_TTL, + "client_session_token": self.SOME_CLIENT_SESSION_TOKEN, + "session_id": self.SOME_SESSION_ID, + } + + result = CreateSessionResult(data) + + assert result.client_session_token_ttl is self.SOME_CLIENT_SESSION_TOKEN_TTL + assert result.client_session_token is self.SOME_CLIENT_SESSION_TOKEN + assert result.session_id is self.SOME_SESSION_ID + + def test_should_parse_when_given_none(self): + result = CreateSessionResult(None) + + assert result.client_session_token_ttl is None + assert result.client_session_token is None + assert result.session_id is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_document_fields_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_document_fields_response.py new file mode 100644 index 00000000..7d108fdc --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_document_fields_response.py @@ -0,0 +1,22 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.document_fields_response import ( + DocumentFieldsResponse, +) +from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse + + +class DocumentFieldsResponseTest(unittest.TestCase): + def test_should_parse_correctly(self): + data = {"media": {}} + + result = DocumentFieldsResponse(data) + assert isinstance(result.media, MediaResponse) + + def test_should_not_throw_exception_for_none(self): + result = DocumentFieldsResponse(None) + assert result.media is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_face_map_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_face_map_response.py new file mode 100644 index 00000000..8cb0e14f --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_face_map_response.py @@ -0,0 +1,20 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.face_map_response import FaceMapResponse +from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse + + +class FaceMapResponseTest(unittest.TestCase): + def test_should_build_correctly(self): + data = {"media": {}} + + result = FaceMapResponse(data) + assert isinstance(result.media, MediaResponse) + + def test_should_parse_with_none(self): + result = FaceMapResponse(None) + assert result.media is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_frame_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_frame_response.py new file mode 100644 index 00000000..31843c1b --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_frame_response.py @@ -0,0 +1,21 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.frame_response import FrameResponse +from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse + + +class FrameResponseTest(unittest.TestCase): + def test_should_parse_correctly(self): + data = {"media": {}} + + result = FrameResponse(data) + assert isinstance(result.media, MediaResponse) + + def test_should_parse_when_none(self): + result = FrameResponse(None) + assert isinstance(result, FrameResponse) + assert result.media is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_generated_check_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_generated_check_response.py new file mode 100644 index 00000000..8b946c70 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_generated_check_response.py @@ -0,0 +1,29 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( + GeneratedCheckResponse, +) + + +class GeneratedCheckResponseTest(unittest.TestCase): + SOME_ID = "someId" + SOME_TYPE = "someType" + + def test_should_parse_correctly(self): + data = {"id": self.SOME_ID, "type": self.SOME_TYPE} + + result = GeneratedCheckResponse(data) + + assert result.id is self.SOME_ID + assert result.type is self.SOME_TYPE + + def test_should_parse_when_none(self): + result = GeneratedCheckResponse(None) + + assert isinstance(result, GeneratedCheckResponse) + assert result.id is None + assert result.type is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_generated_media.py b/yoti_python_sdk/tests/docs/session/retrieve/test_generated_media.py new file mode 100644 index 00000000..8a1d4642 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_generated_media.py @@ -0,0 +1,27 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.generated_media import GeneratedMedia + + +class GeneratedMediaTest(unittest.TestCase): + SOME_ID = "someId" + SOME_TYPE = "someType" + + def test_should_parse_correctly(self): + data = {"id": self.SOME_ID, "type": self.SOME_TYPE} + + result = GeneratedMedia(data) + + assert result.id is self.SOME_ID + assert result.type is self.SOME_TYPE + + def test_should_parse_with_none(self): + result = GeneratedMedia(None) + + assert isinstance(result, GeneratedMedia) + assert result.id is None + assert result.type is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_get_session_result.py b/yoti_python_sdk/tests/docs/session/retrieve/test_get_session_result.py new file mode 100644 index 00000000..02baf039 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_get_session_result.py @@ -0,0 +1,66 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.check_response import ( + AuthenticityCheckResponse, +) +from yoti_python_sdk.docs.session.retrieve.check_response import FaceMatchCheckResponse +from yoti_python_sdk.docs.session.retrieve.check_response import LivenessCheckResponse +from yoti_python_sdk.docs.session.retrieve.check_response import TextDataCheckResponse +from yoti_python_sdk.docs.session.retrieve.get_session_result import GetSessionResult +from yoti_python_sdk.docs.session.retrieve.resource_container import ResourceContainer + + +class GetSessionResultTest(unittest.TestCase): + SOME_CLIENT_SESSION_TOKEN_TTL = 300 + SOME_SESSION_ID = "someSessionId" + SOME_USER_TRACKING_ID = "someUserTrackingId" + SOME_STATE = "someState" + SOME_CLIENT_SESSION_TOKEN = "someClientSessionToken" + SOME_CHECKS = [ + {"type": "ID_DOCUMENT_AUTHENTICITY"}, + {"type": "ID_DOCUMENT_TEXT_DATA_CHECK"}, + {"type": "ID_DOCUMENT_FACE_MATCH"}, + {"type": "LIVENESS"}, + ] + + def test_should_parse_different_checks(self): + data = { + "client_session_token_ttl": self.SOME_CLIENT_SESSION_TOKEN_TTL, + "client_session_token": self.SOME_CLIENT_SESSION_TOKEN, + "session_id": self.SOME_SESSION_ID, + "state": self.SOME_STATE, + "user_tracking_id": self.SOME_USER_TRACKING_ID, + "checks": self.SOME_CHECKS, + "resources": {}, + } + + result = GetSessionResult(data) + + assert result.client_session_token_ttl is self.SOME_CLIENT_SESSION_TOKEN_TTL + assert result.client_session_token is self.SOME_CLIENT_SESSION_TOKEN + assert result.session_id is self.SOME_SESSION_ID + assert result.state is self.SOME_STATE + assert result.user_tracking_id is self.SOME_USER_TRACKING_ID + + assert len(result.checks) == 4 + assert isinstance(result.checks[0], AuthenticityCheckResponse) + assert isinstance(result.checks[1], TextDataCheckResponse) + assert isinstance(result.checks[2], FaceMatchCheckResponse) + assert isinstance(result.checks[3], LivenessCheckResponse) + + assert isinstance(result.resources, ResourceContainer) + + def test_should_filter_checks(self): + data = {"checks": self.SOME_CHECKS} + + result = GetSessionResult(data) + + assert len(result.checks) == 4 + assert len(result.authenticity_checks) == 1 + assert len(result.face_match_checks) == 1 + assert len(result.liveness_checks) == 1 + assert len(result.text_data_checks) == 1 + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_id_document_resource_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_id_document_resource_response.py new file mode 100644 index 00000000..8ff2b6a1 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_id_document_resource_response.py @@ -0,0 +1,50 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.document_fields_response import ( + DocumentFieldsResponse, +) +from yoti_python_sdk.docs.session.retrieve.id_document_resource_response import ( + IdDocumentResourceResponse, +) + + +class IdDocumentResourceResponseTest(unittest.TestCase): + SOME_ID = "someId" + SOME_DOCUMENT_TYPE = "someDocumentType" + SOME_ISSUING_COUNTRY = "someIssuingCountry" + SOME_TASKS = [{"first": "task"}, {"second": "task"}] + SOME_PAGES = [{"first": "page"}, {"second": "page"}] + SOME_DOCUMENT_FIELDS = {"media": {}} + + def test_should_parse_correctly(self): + data = { + "id": self.SOME_ID, + "document_type": self.SOME_DOCUMENT_TYPE, + "issuing_country": self.SOME_ISSUING_COUNTRY, + "tasks": self.SOME_TASKS, + "pages": self.SOME_PAGES, + "document_fields": self.SOME_DOCUMENT_FIELDS, + } + + result = IdDocumentResourceResponse(data) + + assert result.id is self.SOME_ID + assert result.document_type is self.SOME_DOCUMENT_TYPE + assert result.issuing_country is self.SOME_ISSUING_COUNTRY + assert len(result.tasks) == 2 + assert len(result.pages) == 2 + assert isinstance(result.document_fields, DocumentFieldsResponse) + + def test_should_parse_when_none(self): + result = IdDocumentResourceResponse(None) + + assert result.id is None + assert result.document_type is None + assert result.issuing_country is None + assert len(result.tasks) == 0 + assert len(result.pages) == 0 + assert result.document_fields is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_liveness_resource_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_liveness_resource_response.py new file mode 100644 index 00000000..e50a8d14 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_liveness_resource_response.py @@ -0,0 +1,32 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.face_map_response import FaceMapResponse +from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( + ZoomLivenessResourceResponse, +) + + +class LivenessResourceResponseTest(unittest.TestCase): + SOME_ID = "someId" + SOME_FRAMES = [{"first": "frame"}, {"second": "frame"}] + + def test_zoom_liveness_should_parse_correctly(self): + data = {"id": self.SOME_ID, "facemap": {}, "frames": self.SOME_FRAMES} + + result = ZoomLivenessResourceResponse(data) + + assert result.id is self.SOME_ID + assert isinstance(result.facemap, FaceMapResponse) + assert len(result.frames) == 2 + + def test_should_parse_with_none(self): + result = ZoomLivenessResourceResponse(None) + + assert result.id is None + assert len(result.tasks) == 0 + assert result.facemap is None + assert len(result.frames) == 0 + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_media_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_media_response.py new file mode 100644 index 00000000..c8c260fc --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_media_response.py @@ -0,0 +1,59 @@ +import unittest +from datetime import datetime + +import pytz + +from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse + + +class MediaResponseTest(unittest.TestCase): + SOME_ID = "someId" + SOME_TYPE = "someType" + SOME_CREATED = "2019-05-01T05:01:48.000Z" + SOME_LAST_UPDATED = "2019-05-01T05:01:48.000Z" + + EXPECTED_DATETIME = datetime( + year=2019, + month=5, + day=1, + hour=5, + minute=1, + second=48, + microsecond=0, + tzinfo=pytz.utc, + ) + + def test_should_parse_correctly(self): + data = { + "id": self.SOME_ID, + "type": self.SOME_TYPE, + "created": self.SOME_CREATED, + "last_updated": self.SOME_LAST_UPDATED, + } + + result = MediaResponse(data) + + assert result.id is self.SOME_ID + assert result.type is self.SOME_TYPE + assert result.created == self.EXPECTED_DATETIME + assert result.last_updated == self.EXPECTED_DATETIME + + def test_should_parse_with_none(self): + result = MediaResponse(None) + + assert result.id is None + assert result.type is None + assert result.created is None + assert result.last_updated is None + + def test_should_set_dates_as_none_for_invalid_format(self): + data = {"created": "someInvalidFormat", "last_updated": "someInvalidFormat"} + + result = MediaResponse(data) + + assert result.created is None + assert result.last_updated is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_media_value.py b/yoti_python_sdk/tests/docs/session/retrieve/test_media_value.py new file mode 100644 index 00000000..982faf7c --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_media_value.py @@ -0,0 +1,28 @@ +import base64 +import unittest + +from yoti_python_sdk.docs.session.retrieve.media_value import MediaValue + + +class MediaValueTest(unittest.TestCase): + SOME_MIME_TYPE = "someMimeType" + SOME_CONTENT = b"someByteArray" + + def test_should_parse_correctly(self): + result = MediaValue(self.SOME_MIME_TYPE, self.SOME_CONTENT) + + assert result.mime_type is self.SOME_MIME_TYPE + assert result.content is self.SOME_CONTENT + + expected = ( + "data:" + + self.SOME_MIME_TYPE + + ";base64," + + base64.b64encode(self.SOME_CONTENT).decode("utf-8") + ) + + assert result.base64_content == expected + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_page_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_page_response.py new file mode 100644 index 00000000..a3662cca --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_page_response.py @@ -0,0 +1,26 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse +from yoti_python_sdk.docs.session.retrieve.page_response import PageResponse + + +class PageResponseTest(unittest.TestCase): + SOME_CAPTURE_METHOD = "someCaptureMethod" + + def test_should_parse_correctly(self): + data = {"capture_method": self.SOME_CAPTURE_METHOD, "media": {}} + + result = PageResponse(data) + + assert result.capture_method is self.SOME_CAPTURE_METHOD + assert isinstance(result.media, MediaResponse) + + def test_should_parse_with_none(self): + result = PageResponse(None) + + assert result.capture_method is None + assert result.media is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_recommendation_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_recommendation_response.py new file mode 100644 index 00000000..f2785ef1 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_recommendation_response.py @@ -0,0 +1,35 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.recommendation_response import ( + RecommendationResponse, +) + + +class RecommendationResponseTest(unittest.TestCase): + SOME_VALUE = "someValue" + SOME_REASON = "someReason" + SOME_RECOVERY_SUGGESTION = "someRecoverySuggestion" + + def test_should_parse_correctly(self): + data = { + "value": self.SOME_VALUE, + "reason": self.SOME_REASON, + "recovery_suggestion": self.SOME_RECOVERY_SUGGESTION, + } + + result = RecommendationResponse(data) + + assert result.value is self.SOME_VALUE + assert result.reason is self.SOME_REASON + assert result.recovery_suggestion is self.SOME_RECOVERY_SUGGESTION + + def test_should_parse_with_none(self): + result = RecommendationResponse(None) + + assert result.value is None + assert result.reason is None + assert result.recovery_suggestion is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_report_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_report_response.py new file mode 100644 index 00000000..1f0a03ab --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_report_response.py @@ -0,0 +1,29 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.recommendation_response import ( + RecommendationResponse, +) +from yoti_python_sdk.docs.session.retrieve.report_response import ReportResponse + + +class ReportResponseTest(unittest.TestCase): + def test_should_parse_correctly(self): + data = { + "recommendation": {"some": "recommendation"}, + "breakdown": [{"first": "breakdown"}, {"second": "breakdown"}], + } + + result = ReportResponse(data) + + assert isinstance(result.recommendation, RecommendationResponse) + assert len(result.breakdown) == 2 + + def test_should_parse_with_none(self): + result = ReportResponse(None) + + assert result.recommendation is None + assert len(result.breakdown) == 0 + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_resource_container.py b/yoti_python_sdk/tests/docs/session/retrieve/test_resource_container.py new file mode 100644 index 00000000..489a7e90 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_resource_container.py @@ -0,0 +1,37 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( + LivenessResourceResponse, +) +from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( + ZoomLivenessResourceResponse, +) +from yoti_python_sdk.docs.session.retrieve.resource_container import ResourceContainer + + +class ResourceContainerTest(unittest.TestCase): + def test_should_parse_correctly(self): + data = { + "id_documents": [{"first": "id_document"}, {"second": "id_document"}], + "liveness_capture": [ + {"liveness_type": "ZOOM"}, + {"liveness_type": "someUnknown"}, + ], + } + + result = ResourceContainer(data) + + assert len(result.id_documents) == 2 + assert len(result.liveness_capture) == 2 + assert isinstance(result.liveness_capture[0], ZoomLivenessResourceResponse) + assert isinstance(result.liveness_capture[1], LivenessResourceResponse) + + def test_should_parse_with_none(self): + result = ResourceContainer(None) + + assert len(result.id_documents) == 0 + assert len(result.liveness_capture) == 0 + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_resource_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_resource_response.py new file mode 100644 index 00000000..9e4b37c6 --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_resource_response.py @@ -0,0 +1,37 @@ +import unittest + +from yoti_python_sdk.docs.session.retrieve.resource_response import ResourceResponse +from yoti_python_sdk.docs.session.retrieve.task_response import TaskResponse +from yoti_python_sdk.docs.session.retrieve.task_response import ( + TextExtractionTaskResponse, +) + + +class ResourceResponseTest(unittest.TestCase): + SOME_ID = "someId" + + def test_should_parse_correctly(self): + data = { + "id": self.SOME_ID, + "tasks": [ + {"type": "ID_DOCUMENT_TEXT_DATA_EXTRACTION"}, + {"type": "someUnknownType"}, + ], + } + + result = ResourceResponse(data) + + assert result.id is self.SOME_ID + assert len(result.tasks) == 2 + assert isinstance(result.tasks[0], TextExtractionTaskResponse) + assert isinstance(result.tasks[1], TaskResponse) + + def test_should_parse_with_none(self): + result = ResourceResponse(None) + + assert result.id is None + assert len(result.tasks) == 0 + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_task_response.py b/yoti_python_sdk/tests/docs/session/retrieve/test_task_response.py new file mode 100644 index 00000000..b9b6ab8d --- /dev/null +++ b/yoti_python_sdk/tests/docs/session/retrieve/test_task_response.py @@ -0,0 +1,79 @@ +import unittest +from datetime import datetime + +import pytz + +from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( + GeneratedCheckResponse, +) +from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( + GeneratedTextDataCheckResponse, +) +from yoti_python_sdk.docs.session.retrieve.task_response import TaskResponse + + +class TaskResponseTest(unittest.TestCase): + SOME_ID = "someId" + SOME_TYPE = "someType" + SOME_STATE = "someState" + + SOME_GENERATED_CHECKS = [ + {"type": "ID_DOCUMENT_TEXT_DATA_CHECK"}, + {"type": "someUnknownType"}, + ] + + SOME_GENERATED_MEDIA = [{"first": "generated_media"}, {"second": "generated_media"}] + + SOME_CREATED = "2019-05-01T05:01:48.000Z" + SOME_LAST_UPDATED = "2019-05-01T05:01:48.000Z" + + EXPECTED_DATETIME = datetime( + year=2019, + month=5, + day=1, + hour=5, + minute=1, + second=48, + microsecond=0, + tzinfo=pytz.utc, + ) + + def test_should_parse_correctly(self): + data = { + "id": self.SOME_ID, + "type": self.SOME_TYPE, + "state": self.SOME_STATE, + "created": self.SOME_CREATED, + "last_updated": self.SOME_LAST_UPDATED, + "generated_checks": self.SOME_GENERATED_CHECKS, + "generated_media": self.SOME_GENERATED_MEDIA, + } + + result = TaskResponse(data) + + assert result.id is self.SOME_ID + assert result.type is self.SOME_TYPE + assert result.state is self.SOME_STATE + assert result.created == self.EXPECTED_DATETIME + assert result.last_updated == self.EXPECTED_DATETIME + + assert len(result.generated_checks) == 2 + assert isinstance(result.generated_checks[0], GeneratedTextDataCheckResponse) + assert isinstance(result.generated_checks[1], GeneratedCheckResponse) + + assert len(result.generated_media) == 2 + + def test_should_parse_with_none(self): + result = TaskResponse(None) + + assert result.id is None + assert result.type is None + assert result.state is None + assert result.created is None + assert result.last_updated is None + assert len(result.generated_checks) == 0 + assert len(result.generated_media) == 0 + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/fixtures/response_create_docs_scan_session.txt b/yoti_python_sdk/tests/fixtures/response_create_docs_scan_session.txt new file mode 100644 index 00000000..1059450a --- /dev/null +++ b/yoti_python_sdk/tests/fixtures/response_create_docs_scan_session.txt @@ -0,0 +1,5 @@ +{ + "client_session_token_ttl": 599, + "client_session_token": "", + "session_id": "" +} diff --git a/yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_image.txt b/yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_image.txt new file mode 100644 index 00000000..51319f33 --- /dev/null +++ b/yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_image.txt @@ -0,0 +1 @@ +QUFBQQ== diff --git a/yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_json.txt b/yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_json.txt new file mode 100644 index 00000000..12c75c89 --- /dev/null +++ b/yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_json.txt @@ -0,0 +1,3 @@ +{ + "value": "example" +} diff --git a/yoti_python_sdk/tests/fixtures/response_get_docs_scan_session.txt b/yoti_python_sdk/tests/fixtures/response_get_docs_scan_session.txt new file mode 100644 index 00000000..2bb3194e --- /dev/null +++ b/yoti_python_sdk/tests/fixtures/response_get_docs_scan_session.txt @@ -0,0 +1,196 @@ +{ + "client_session_token_ttl": 599, + "session_id": "", + "user_tracking_id": "", + "state": "COMPLETED", + "client_session_token": "", + "resources": { + "id_documents": [{ + "id": "", + "tasks": [{ + "type": "ID_DOCUMENT_TEXT_DATA_EXTRACTION", + "id": "", + "state": "DONE", + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z", + "generated_checks": [], + "generated_media": [{ + "id": "", + "type": "JSON" + }] + }], + "document_type": "DRIVING_LICENCE", + "issuing_country": "GBR", + "pages": [{ + "capture_method": "CAMERA", + "media": { + "id": "", + "type": "IMAGE", + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z" + } + }], + "document_fields": { + "media": { + "id": "", + "type": "JSON", + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z" + } + } + }], + "liveness_capture": [{ + "id": "", + "tasks": [], + "frames": [{ + "media": { + "id": "", + "type": "IMAGE", + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z" + } + }, + { + "media": { + "id": "", + "type": "IMAGE", + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z" + } + }, + { + "media": { + "id": "", + "type": "IMAGE", + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z" + } + }, + {}, + {}, + {}, + {} + ], + "liveness_type": "ZOOM", + "facemap": { + "media": { + "id": "", + "type": "BINARY", + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z" + } + } + }] + }, + "checks": [{ + "id": "", + "type": "ID_DOCUMENT_AUTHENTICITY", + "state": "DONE", + "resources_used": [ + "" + ], + "report": { + "recommendation": { + "value": "APPROVE" + }, + "breakdown": [{ + "sub_check": "data_in_correct_position", + "result": "PASS", + "details": [] + }, + { + "sub_check": "document_in_date", + "result": "PASS", + "details": [] + }, + { + "sub_check": "expected_data_present", + "result": "PASS", + "details": [] + }, + { + "sub_check": "hologram", + "result": "PASS", + "details": [] + }, + { + "sub_check": "hologram_movement", + "result": "PASS", + "details": [] + }, + { + "sub_check": "no_sign_of_tampering", + "result": "PASS", + "details": [] + }, + { + "sub_check": "other_security_features", + "result": "PASS", + "details": [] + }, + { + "sub_check": "real_document", + "result": "PASS", + "details": [] + } + ] + }, + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z" + + }, + { + "type": "LIVENESS", + "id": "", + "state": "DONE", + "resources_used": [ + "" + ], + "generated_media": [], + "report": { + "recommendation": { + "value": "APPROVE" + }, + "breakdown": [{ + "sub_check": "liveness_auth", + "result": "PASS", + "details": [] + }] + }, + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z" + }, + { + "type": "ID_DOCUMENT_FACE_MATCH", + "id": "", + "state": "DONE", + "resources_used": [ + "", + "" + ], + "generated_media": [], + "report": { + "recommendation": { + "value": "APPROVE" + }, + "breakdown": [{ + "sub_check": "manual_face_match", + "result": "PASS", + "details": [] + }, + { + "sub_check": "ai_face_match", + "result": "PASS", + "details": [{ + "name": "confidence_score", + "value": "1.00" + }] + } + + ] + }, + "created": "2020-01-30T15:00:00Z", + "last_updated": "2020-01-30T15:00:00Z" + } + ] +} diff --git a/yoti_python_sdk/tests/mocks.py b/yoti_python_sdk/tests/mocks.py index f2373d07..587f6789 100644 --- a/yoti_python_sdk/tests/mocks.py +++ b/yoti_python_sdk/tests/mocks.py @@ -1,11 +1,16 @@ from uuid import UUID from yoti_python_sdk.http import RequestHandler, SignedRequest from yoti_python_sdk.http import YotiResponse +import base64 class MockResponse(YotiResponse): - def __init__(self, status_code, text): - super(MockResponse, self).__init__(status_code, text) + def __init__(self, status_code, text, headers={}): + super(MockResponse, self).__init__(status_code, text, headers) + + @property + def content(self): + return self.text class MockRequestHandler(RequestHandler): @@ -74,3 +79,47 @@ def mocked_requests_post_share_url_invalid_json(*args, **kwargs): def mocked_requests_post_share_url_app_not_found(*args, **kwargs): return MockResponse(status_code=404, text="Application not found") + + +def mocked_request_create_docs_scan_session(*args, **kwargs): + with open( + "yoti_python_sdk/tests/fixtures/response_create_docs_scan_session.txt", "r" + ) as f: + response = f.read() + return MockResponse(status_code=201, text=response) + + +def mocked_request_get_docs_scan_session(*args, **kwargs): + with open( + "yoti_python_sdk/tests/fixtures/response_get_docs_scan_session.txt", "r" + ) as f: + response = f.read() + return MockResponse(status_code=200, text=response) + + +def mocked_request_delete_docs_scan_session(*args, **kwargs): + return MockResponse(status_code=200, text=None) + + +def mocked_request_media_json_retrieval(*args, **kwargs): + with open( + "yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_json.txt", "r" + ) as f: + response = f.read() + return MockResponse( + status_code=200, text=response, headers={"Content-Type": "application/json"} + ) + + +def mocked_request_media_image_retrieval(*args, **kwargs): + with open( + "yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_image.txt", "r" + ) as f: + response = base64.b64decode(f.read()) + return MockResponse( + status_code=200, text=response, headers={"Content-Type": "image/png"} + ) + + +def mocked_request_delete_media(*args, **kwargs): + return MockResponse(status_code=200, text=None) diff --git a/yoti_python_sdk/utils.py b/yoti_python_sdk/utils.py index 773b4112..cd05773d 100644 --- a/yoti_python_sdk/utils.py +++ b/yoti_python_sdk/utils.py @@ -1,5 +1,27 @@ -import uuid import time +import uuid +from abc import ABCMeta +from abc import abstractmethod +from json import JSONEncoder + + +class YotiSerializable(object): + """ + Used to describe a class that is serializable by :class:`YotiEncoder`. + """ + + __metaclass__ = ABCMeta + + @abstractmethod + def to_json(self): + raise NotImplementedError + + +class YotiEncoder(JSONEncoder): + def default(self, o): + if isinstance(o, YotiSerializable): + return o.to_json() + return JSONEncoder.default(self, o) def create_nonce(): From 1f0fe7386c3425002fae82ca63a15c85bae78ad6 Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Thu, 2 Apr 2020 16:13:36 +0100 Subject: [PATCH 2/9] SDK-1299: Add test coverage for DocScanClient --- .../docs/exception/doc_scan_exception.py | 17 +- yoti_python_sdk/tests/conftest.py | 4 +- yoti_python_sdk/tests/docs/__init__.py | 0 yoti_python_sdk/tests/docs/conftest.py | 20 +++ .../tests/docs/fixtures/failed_request.txt | 1 + .../fixtures/retrieve_session_success.txt | 1 + .../docs/fixtures/session_create_success.txt | 1 + yoti_python_sdk/tests/docs/mocks.py | 45 ++++++ .../tests/docs/test_doc_scan_client.py | 150 ++++++++++++++++++ yoti_python_sdk/tests/mocks.py | 11 +- 10 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 yoti_python_sdk/tests/docs/__init__.py create mode 100644 yoti_python_sdk/tests/docs/conftest.py create mode 100644 yoti_python_sdk/tests/docs/fixtures/failed_request.txt create mode 100644 yoti_python_sdk/tests/docs/fixtures/retrieve_session_success.txt create mode 100644 yoti_python_sdk/tests/docs/fixtures/session_create_success.txt create mode 100644 yoti_python_sdk/tests/docs/mocks.py create mode 100644 yoti_python_sdk/tests/docs/test_doc_scan_client.py diff --git a/yoti_python_sdk/docs/exception/doc_scan_exception.py b/yoti_python_sdk/docs/exception/doc_scan_exception.py index fa1ce5df..efe0f323 100644 --- a/yoti_python_sdk/docs/exception/doc_scan_exception.py +++ b/yoti_python_sdk/docs/exception/doc_scan_exception.py @@ -11,9 +11,21 @@ def __init__(self, message, response): :param response: the http response :type response: requests.Response """ - Exception.__init__(self, message) + Exception.__init__(self) + + self.__message = message self.__response = response + @property + def message(self): + """ + Get the specific exception message + + :return: the exception message + :rtype: str + """ + return self.__message + @property def status_code(self): """ @@ -43,3 +55,6 @@ def content(self): :rtype: bytearray or None """ return self.__response.content + + def __str__(self): + return self.__message diff --git a/yoti_python_sdk/tests/conftest.py b/yoti_python_sdk/tests/conftest.py index 335e201e..86f080cf 100644 --- a/yoti_python_sdk/tests/conftest.py +++ b/yoti_python_sdk/tests/conftest.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import io -from os.path import dirname, join, abspath +from os.path import abspath +from os.path import dirname +from os.path import join import pytest diff --git a/yoti_python_sdk/tests/docs/__init__.py b/yoti_python_sdk/tests/docs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/yoti_python_sdk/tests/docs/conftest.py b/yoti_python_sdk/tests/docs/conftest.py new file mode 100644 index 00000000..e1bfa945 --- /dev/null +++ b/yoti_python_sdk/tests/docs/conftest.py @@ -0,0 +1,20 @@ +from os.path import abspath +from os.path import dirname +from os.path import join + +import pytest + +from yoti_python_sdk.docs.client import DocScanClient + +FIXTURES_DIR = join(dirname(abspath(__file__)), "..", "fixtures") +PEM_FILE_PATH = join(FIXTURES_DIR, "sdk-test.pem") + +YOTI_CLIENT_SDK_ID = "737204aa-d54e-49a4-8bde-26ddbe6d880c" + + +@pytest.fixture(scope="module") +def doc_scan_client(): + """ + :rtype: DocScanClient + """ + return DocScanClient(YOTI_CLIENT_SDK_ID, PEM_FILE_PATH) diff --git a/yoti_python_sdk/tests/docs/fixtures/failed_request.txt b/yoti_python_sdk/tests/docs/fixtures/failed_request.txt new file mode 100644 index 00000000..9407b369 --- /dev/null +++ b/yoti_python_sdk/tests/docs/fixtures/failed_request.txt @@ -0,0 +1 @@ +{"error": "MALFORMED_REQUEST"} \ No newline at end of file diff --git a/yoti_python_sdk/tests/docs/fixtures/retrieve_session_success.txt b/yoti_python_sdk/tests/docs/fixtures/retrieve_session_success.txt new file mode 100644 index 00000000..add6510b --- /dev/null +++ b/yoti_python_sdk/tests/docs/fixtures/retrieve_session_success.txt @@ -0,0 +1 @@ +{"session_id":"someSessionId"} \ No newline at end of file diff --git a/yoti_python_sdk/tests/docs/fixtures/session_create_success.txt b/yoti_python_sdk/tests/docs/fixtures/session_create_success.txt new file mode 100644 index 00000000..a813cfef --- /dev/null +++ b/yoti_python_sdk/tests/docs/fixtures/session_create_success.txt @@ -0,0 +1 @@ +{"session_id": "someSessionId", "client_session_token": "someClientSessionToken", "client_session_token_ttl": 299} \ No newline at end of file diff --git a/yoti_python_sdk/tests/docs/mocks.py b/yoti_python_sdk/tests/docs/mocks.py new file mode 100644 index 00000000..b1fd4e2f --- /dev/null +++ b/yoti_python_sdk/tests/docs/mocks.py @@ -0,0 +1,45 @@ +from os.path import abspath +from os.path import dirname +from os.path import join + +from yoti_python_sdk.tests.mocks import MockResponse + +FIXTURES_DIR = join(dirname(abspath(__file__)), "fixtures") + + +def mocked_request_successful_session_creation(): + with open(FIXTURES_DIR + "/session_create_success.txt", "r") as f: + response = f.read() + return MockResponse(status_code=201, text=response) + + +def mocked_request_failed_session_creation(): + with open(FIXTURES_DIR + "/failed_request.txt", "r") as f: + response = f.read() + return MockResponse(status_code=400, text=response) + + +def mocked_request_successful_session_retrieval(): + with open(FIXTURES_DIR + "/retrieve_session_success.txt", "r") as f: + response = f.read() + return MockResponse(status_code=200, text=response) + + +def mocked_request_failed_session_retrieval(): + return MockResponse(status_code=400, text="") + + +def mocked_request_media_content(): + return MockResponse( + status_code=200, + text=b"someContent", + headers={"Content-Type": "application/json"}, + ) + + +def mocked_request_missing_content(): + return MockResponse(status_code=404, text="") + + +def mocked_request_server_error(): + return MockResponse(status_code=500, text="") diff --git a/yoti_python_sdk/tests/docs/test_doc_scan_client.py b/yoti_python_sdk/tests/docs/test_doc_scan_client.py new file mode 100644 index 00000000..89c90520 --- /dev/null +++ b/yoti_python_sdk/tests/docs/test_doc_scan_client.py @@ -0,0 +1,150 @@ +import pytest + +from yoti_python_sdk.docs.client import DocScanClient # noqa: F401 +from yoti_python_sdk.docs.exception import DocScanException +from yoti_python_sdk.docs.session.create.session_spec import SessionSpec +from yoti_python_sdk.docs.session.retrieve.create_session_result import ( + CreateSessionResult, +) +from yoti_python_sdk.docs.session.retrieve.get_session_result import GetSessionResult +from yoti_python_sdk.tests.docs.mocks import mocked_request_failed_session_creation +from yoti_python_sdk.tests.docs.mocks import mocked_request_failed_session_retrieval +from yoti_python_sdk.tests.docs.mocks import mocked_request_media_content +from yoti_python_sdk.tests.docs.mocks import mocked_request_missing_content +from yoti_python_sdk.tests.docs.mocks import mocked_request_server_error +from yoti_python_sdk.tests.docs.mocks import mocked_request_successful_session_creation +from yoti_python_sdk.tests.docs.mocks import mocked_request_successful_session_retrieval + +try: + from unittest import mock +except ImportError: + import mock + +SOME_SESSION_ID = "someSessionId" +SOME_MEDIA_ID = "someMediaId" + + +@mock.patch( + "yoti_python_sdk.http.SignedRequest.execute", + side_effect=mocked_request_successful_session_creation, +) +def test_should_return_create_session_result(_, doc_scan_client): + """ + :type doc_scan_client: DocScanClient + :return: + :rtype: + """ + session_spec_mock = mock.Mock(spec=SessionSpec) + session_spec_mock.to_json.return_value = {} + + create_session_result = doc_scan_client.create_session(session_spec_mock) + + assert isinstance(create_session_result, CreateSessionResult) + + +@mock.patch( + "yoti_python_sdk.http.SignedRequest.execute", + side_effect=mocked_request_failed_session_creation, +) +def test_should_raise_doc_scan_exception_for_session_creation(_, doc_scan_client): + """ + :type doc_scan_client: DocScanClient + """ + session_spec_mock = mock.Mock(spec=SessionSpec) + session_spec_mock.to_json.return_value = {} + + with pytest.raises(DocScanException) as ex: + doc_scan_client.create_session(session_spec_mock) + + assert "Failed to create session" in str(ex.value) + + +@mock.patch( + "yoti_python_sdk.http.SignedRequest.execute", + side_effect=mocked_request_successful_session_retrieval, +) +def test_should_return_get_session_result(_, doc_scan_client): + """ + :type doc_scan_client: DocScanClient + """ + session_result = doc_scan_client.get_session(SOME_SESSION_ID) + + assert isinstance(session_result, GetSessionResult) + + +@mock.patch( + "yoti_python_sdk.http.SignedRequest.execute", + side_effect=mocked_request_failed_session_retrieval, +) +def test_should_raise_doc_scan_exception_for_session_retrieval(_, doc_scan_client): + """ + :type doc_scan_client: DocScanClient + """ + with pytest.raises(DocScanException) as ex: + doc_scan_client.get_session(SOME_SESSION_ID) + + doc_scan_exception = ex.value # type: DocScanException + assert "Failed to retrieve session" in str(doc_scan_exception) + assert doc_scan_exception.status_code == 400 + + +@mock.patch( + "yoti_python_sdk.http.SignedRequest.execute", + side_effect=mocked_request_server_error, +) +def test_should_raise_exception_for_delete_session(_, doc_scan_client): + """ + :type doc_scan_client: DocScanClient + """ + with pytest.raises(DocScanException) as ex: + doc_scan_client.delete_session(SOME_SESSION_ID) + + doc_scan_exception = ex.value # type: DocScanException + assert "Failed to delete session" in str(doc_scan_exception) + assert doc_scan_exception.status_code == 500 + + +@mock.patch( + "yoti_python_sdk.http.SignedRequest.execute", + side_effect=mocked_request_missing_content, +) +def test_should_raise_exception_for_invalid_content(_, doc_scan_client): + """ + :type doc_scan_client: DocScanClient + """ + with pytest.raises(DocScanException) as ex: + doc_scan_client.get_media_content(SOME_SESSION_ID, SOME_MEDIA_ID) + + doc_scan_exception = ex.value # type: DocScanException + assert "Failed to retrieve media content" in str(doc_scan_exception) + assert doc_scan_exception.status_code == 404 + + +@mock.patch( + "yoti_python_sdk.http.SignedRequest.execute", + side_effect=mocked_request_media_content, +) +def test_should_return_media_value(_, doc_scan_client): + """ + :type doc_scan_client: DocScanClient + """ + media = doc_scan_client.get_media_content(SOME_SESSION_ID, SOME_MEDIA_ID) + + assert media.mime_type == "application/json" + assert media.content == b"someContent" + + +@mock.patch( + "yoti_python_sdk.http.SignedRequest.execute", + side_effect=mocked_request_missing_content, +) +def test_should_throw_exception_for_delete_media(_, doc_scan_client): + """ + :type doc_scan_client: DocScanClient + """ + with pytest.raises(DocScanException) as ex: + doc_scan_client.delete_media_content(SOME_SESSION_ID, SOME_MEDIA_ID) + + doc_scan_exception = ex.value # type: DocScanException + assert "Failed to delete media content" in str(doc_scan_exception) + assert 404 == doc_scan_exception.status_code diff --git a/yoti_python_sdk/tests/mocks.py b/yoti_python_sdk/tests/mocks.py index 587f6789..2cd69099 100644 --- a/yoti_python_sdk/tests/mocks.py +++ b/yoti_python_sdk/tests/mocks.py @@ -1,11 +1,16 @@ +import base64 from uuid import UUID -from yoti_python_sdk.http import RequestHandler, SignedRequest + +from yoti_python_sdk.http import RequestHandler +from yoti_python_sdk.http import SignedRequest from yoti_python_sdk.http import YotiResponse -import base64 class MockResponse(YotiResponse): - def __init__(self, status_code, text, headers={}): + def __init__(self, status_code, text, headers=None): + if headers is None: + headers = dict() + super(MockResponse, self).__init__(status_code, text, headers) @property From 22485111b6db47c3f7e094cbd2d73459b9abe39a Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Thu, 2 Apr 2020 17:37:34 +0100 Subject: [PATCH 3/9] SDK-1299: Add iso8601 as dependency in setup.py --- examples/docscan_example_flask/app.py | 71 ------------------- .../docscan_example_flask/requirements.txt | 0 examples/docscan_example_flask/settings.py | 5 -- .../templates/index.html | 26 ------- setup.py | 1 + 5 files changed, 1 insertion(+), 102 deletions(-) delete mode 100644 examples/docscan_example_flask/app.py delete mode 100644 examples/docscan_example_flask/requirements.txt delete mode 100644 examples/docscan_example_flask/settings.py delete mode 100644 examples/docscan_example_flask/templates/index.html diff --git a/examples/docscan_example_flask/app.py b/examples/docscan_example_flask/app.py deleted file mode 100644 index 99a030b7..00000000 --- a/examples/docscan_example_flask/app.py +++ /dev/null @@ -1,71 +0,0 @@ -from yoti_python_sdk.docs import DocScanClient -from yoti_python_sdk.docs import SessionSpecBuilder -from yoti_python_sdk.docs.session.create.check import DocumentAuthenticityCheckBuilder -from yoti_python_sdk.docs import NotificationConfigBuilder -from yoti_python_sdk.docs import SDKConfigBuilder -from os.path import join, dirname -from flask import Flask, render_template -from dotenv import load_dotenv - -dotenv_path = join(dirname(__file__), ".env") -load_dotenv(dotenv_path) -from settings import YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH # noqa - -app = Flask(__name__) - - -@app.route("/") -def index(): - client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) - session = client.create_session( - SessionSpecBuilder() - .with_requested_checks(DocumentAuthenticityCheckBuilder().build()) - .with_notifications( - NotificationConfigBuilder() - .with_endpoint("https://example.com") - .with_topics("session_completion") - .build() - ) - .with_sdk_config( - SDKConfigBuilder() - .with_success_url("https://localhost:5000/success") - .with_error_url("https://localhost:5000") - .build() - ) - .build() - ) - print("Doc Scan Session ID: %s" % session.client_session_id) - - return render_template( - "index.html", - session_id=session.client_session_id, - client_token=session.client_session_token, - ) - - -@app.route("/success") -def success(): - return "Doc Scan request completed" - - -@app.route("/session/") -def get_session(session_id): - client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) - session = client.get_session(session_id) - resources = [ - [page.media.id for page in document.pages] - for document in session.resources.id_documents - ] - return "Session status: %s, Page Resources: %s" % (session.state, resources) - - -@app.route("/session//resource/") -def get_resource(session_id, resource_id): - client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) - resource = client.get_media_content(session_id, resource_id) - - return '' % resource.base64_content - - -if __name__ == "__main__": - app.run(host="0.0.0.0", ssl_context="adhoc") diff --git a/examples/docscan_example_flask/requirements.txt b/examples/docscan_example_flask/requirements.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/docscan_example_flask/settings.py b/examples/docscan_example_flask/settings.py deleted file mode 100644 index 00724dbc..00000000 --- a/examples/docscan_example_flask/settings.py +++ /dev/null @@ -1,5 +0,0 @@ -from os import environ - -YOTI_CLIENT_SDK_ID = environ.get("YOTI_CLIENT_SDK_ID") -YOTI_KEY_FILE_PATH = environ.get("YOTI_KEY_FILE_PATH") -YOTI_BASE_URL = environ.get("YOTI_BASE_URL") diff --git a/examples/docscan_example_flask/templates/index.html b/examples/docscan_example_flask/templates/index.html deleted file mode 100644 index bfe31b4d..00000000 --- a/examples/docscan_example_flask/templates/index.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Yoti Doc Scan example - - - - - - -
-

Doc Scan Session {{session_id}}

-
- -
-
- - - - - diff --git a/setup.py b/setup.py index f1bde78f..e6148705 100644 --- a/setup.py +++ b/setup.py @@ -31,6 +31,7 @@ "future>=0.11.0", "asn1==2.2.0", "pyopenssl>=18.0.0", + "iso8601==0.1.12", ], extras_require={ "examples": [ From 9077b7ab276d0f49d422d77e8eb432a1fdc3f460 Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Tue, 14 Apr 2020 11:44:04 +0100 Subject: [PATCH 4/9] SDK-1299: Update package name, and regenerate requirements.txt --- requirements.txt | 32 +++++++++---------- yoti_python_sdk/__init__.py | 14 ++++++-- .../{docs => doc_scan}/__init__.py | 0 yoti_python_sdk/{docs => doc_scan}/client.py | 12 ++++--- .../{docs => doc_scan}/constants.py | 0 .../{docs => doc_scan}/endpoint.py | 0 .../{docs => doc_scan}/exception/__init__.py | 0 .../exception/doc_scan_exception.py | 0 .../{docs => doc_scan}/session/__init__.py | 0 .../session/create/__init__.py | 0 .../session/create/check/__init__.py | 0 .../create/check/document_authenticity.py | 2 +- .../session/create/check/face_match.py | 2 +- .../session/create/check/liveness.py | 2 +- .../session/create/check/requested_check.py | 0 .../session/create/notification_config.py | 8 ++--- .../session/create/sdk_config.py | 4 +-- .../session/create/session_spec.py | 0 .../session/create/task/__init__.py | 0 .../session/create/task/requested_task.py | 0 .../session/create/task/text_extraction.py | 2 +- .../session/retrieve/__init__.py | 0 .../session/retrieve/breakdown_response.py | 0 .../session/retrieve/check_response.py | 0 .../session/retrieve/create_session_result.py | 0 .../retrieve/document_fields_response.py | 2 +- .../session/retrieve/face_map_response.py | 2 +- .../session/retrieve/frame_response.py | 2 +- .../retrieve/generated_check_response.py | 0 .../session/retrieve/generated_media.py | 0 .../session/retrieve/get_session_result.py | 2 +- .../retrieve/id_document_resource_response.py | 6 ++-- .../retrieve/liveness_resource_response.py | 0 .../session/retrieve/media_response.py | 0 .../session/retrieve/media_value.py | 0 .../session/retrieve/page_response.py | 0 .../retrieve/recommendation_response.py | 0 .../session/retrieve/report_response.py | 0 .../session/retrieve/resource_container.py | 6 ++-- .../session/retrieve/resource_response.py | 2 +- .../session/retrieve/task_response.py | 8 ++--- .../tests/{docs => doc_scan}/__init__.py | 0 .../tests/{docs => doc_scan}/conftest.py | 2 +- .../fixtures/failed_request.txt | 0 .../fixtures/retrieve_session_success.txt | 0 .../fixtures/session_create_success.txt | 0 .../tests/{docs => doc_scan}/mocks.py | 0 .../create/check/test_face_match_check.py | 10 +++--- .../create/check/test_liveness_check.py | 10 +++--- ...t_requested_document_authenticity_check.py | 8 ++--- .../create/task/test_text_extraction_task.py | 10 +++--- .../create/test_notification_config.py | 6 ++-- .../session/create/test_sdk_config.py | 4 +-- .../session/create/test_session_spec.py | 12 ++++--- .../session/retrieve/__init__.py | 0 .../retrieve/test_breakdown_response.py | 4 ++- .../session/retrieve/test_check_response.py | 6 ++-- .../retrieve/test_create_session_result.py | 2 +- .../retrieve/test_document_fields_response.py | 4 +-- .../retrieve/test_face_map_response.py | 4 +-- .../session/retrieve/test_frame_response.py | 4 +-- .../retrieve/test_generated_check_response.py | 2 +- .../session/retrieve/test_generated_media.py | 2 +- .../retrieve/test_get_session_result.py | 22 +++++++++---- .../test_id_document_resource_response.py | 4 +-- .../test_liveness_resource_response.py | 4 +-- .../session/retrieve/test_media_response.py | 2 +- .../session/retrieve/test_media_value.py | 2 +- .../session/retrieve/test_page_response.py | 4 +-- .../retrieve/test_recommendation_response.py | 2 +- .../session/retrieve/test_report_response.py | 4 +-- .../retrieve/test_resource_container.py | 8 +++-- .../retrieve/test_resource_response.py | 6 ++-- .../session/retrieve/test_task_response.py | 6 ++-- .../test_doc_scan_client.py | 30 ++++++++++------- 75 files changed, 166 insertions(+), 126 deletions(-) rename yoti_python_sdk/{docs => doc_scan}/__init__.py (100%) rename yoti_python_sdk/{docs => doc_scan}/client.py (93%) rename yoti_python_sdk/{docs => doc_scan}/constants.py (100%) rename yoti_python_sdk/{docs => doc_scan}/endpoint.py (100%) rename yoti_python_sdk/{docs => doc_scan}/exception/__init__.py (100%) rename yoti_python_sdk/{docs => doc_scan}/exception/doc_scan_exception.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/__init__.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/create/__init__.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/create/check/__init__.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/create/check/document_authenticity.py (94%) rename yoti_python_sdk/{docs => doc_scan}/session/create/check/face_match.py (98%) rename yoti_python_sdk/{docs => doc_scan}/session/create/check/liveness.py (98%) rename yoti_python_sdk/{docs => doc_scan}/session/create/check/requested_check.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/create/notification_config.py (94%) rename yoti_python_sdk/{docs => doc_scan}/session/create/sdk_config.py (98%) rename yoti_python_sdk/{docs => doc_scan}/session/create/session_spec.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/create/task/__init__.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/create/task/requested_task.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/create/task/text_extraction.py (98%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/__init__.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/breakdown_response.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/check_response.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/create_session_result.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/document_fields_response.py (89%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/face_map_response.py (88%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/frame_response.py (88%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/generated_check_response.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/generated_media.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/get_session_result.py (99%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/id_document_resource_response.py (90%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/liveness_resource_response.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/media_response.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/media_value.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/page_response.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/recommendation_response.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/report_response.py (100%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/resource_container.py (88%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/resource_response.py (96%) rename yoti_python_sdk/{docs => doc_scan}/session/retrieve/task_response.py (92%) rename yoti_python_sdk/tests/{docs => doc_scan}/__init__.py (100%) rename yoti_python_sdk/tests/{docs => doc_scan}/conftest.py (88%) rename yoti_python_sdk/tests/{docs => doc_scan}/fixtures/failed_request.txt (100%) rename yoti_python_sdk/tests/{docs => doc_scan}/fixtures/retrieve_session_success.txt (100%) rename yoti_python_sdk/tests/{docs => doc_scan}/fixtures/session_create_success.txt (100%) rename yoti_python_sdk/tests/{docs => doc_scan}/mocks.py (100%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/create/check/test_face_match_check.py (78%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/create/check/test_liveness_check.py (80%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/create/check/test_requested_document_authenticity_check.py (74%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/create/task/test_text_extraction_task.py (80%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/create/test_notification_config.py (95%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/create/test_sdk_config.py (94%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/create/test_session_spec.py (86%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/__init__.py (100%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_breakdown_response.py (90%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_check_response.py (90%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_create_session_result.py (93%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_document_fields_response.py (73%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_face_map_response.py (70%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_frame_response.py (72%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_generated_check_response.py (89%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_generated_media.py (87%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_get_session_result.py (78%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_id_document_resource_response.py (90%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_liveness_resource_response.py (82%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_media_response.py (94%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_media_value.py (89%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_page_response.py (78%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_recommendation_response.py (91%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_report_response.py (80%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_resource_container.py (78%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_resource_response.py (77%) rename yoti_python_sdk/tests/{docs => doc_scan}/session/retrieve/test_task_response.py (89%) rename yoti_python_sdk/tests/{docs => doc_scan}/test_doc_scan_client.py (80%) diff --git a/requirements.txt b/requirements.txt index 4557e644..be3f3714 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,27 +4,27 @@ # # pip-compile --output-file=requirements.txt requirements.in # -asn1==2.2.0 +asn1==2.2.0 # via -r requirements.in certifi==2018.11.29 # via requests -cffi==1.13.0 +cffi==1.13.0 # via -r requirements.in, cryptography chardet==3.0.4 # via requests -cryptography==2.8 -deprecated==1.2.6 -future==0.18.2 +cryptography==2.8 # via -r requirements.in, pyopenssl +deprecated==1.2.6 # via -r requirements.in +future==0.18.2 # via -r requirements.in idna==2.7 # via requests -iso8601==0.1.12 -itsdangerous==0.24 -pbr==1.10.0 -protobuf==3.11.3 +iso8601==0.1.12 # via -r requirements.in +itsdangerous==0.24 # via -r requirements.in +pbr==1.10.0 # via -r requirements.in +protobuf==3.11.3 # via -r requirements.in pycparser==2.18 # via cffi -pyopenssl==18.0.0 -pytz==2018.9 -pyyaml==5.2 -requests==2.21.0 +pyopenssl==18.0.0 # via -r requirements.in +pytz==2018.9 # via -r requirements.in +pyyaml==5.2 # via -r requirements.in +requests==2.21.0 # via -r requirements.in six==1.10.0 # via cryptography, protobuf, pyopenssl -urllib3==1.24.2 -wheel==0.24.0 +urllib3==1.24.2 # via -r requirements.in, requests +wheel==0.24.0 # via -r requirements.in wrapt==1.11.2 # via deprecated # The following packages are considered to be unsafe in a requirements file: -# setuptools==46.1.3 # via protobuf +# setuptools diff --git a/yoti_python_sdk/__init__.py b/yoti_python_sdk/__init__.py index 74c4b2f9..a33747d5 100644 --- a/yoti_python_sdk/__init__.py +++ b/yoti_python_sdk/__init__.py @@ -7,7 +7,6 @@ DEFAULTS = { "YOTI_API_URL": "https://api.yoti.com", - "DOCS_API_URL": "https://stg1.api.internal.yoti.com/idverify/v1", "YOTI_API_PORT": 443, "YOTI_API_VERSION": "v1", "YOTI_API_VERIFY_SSL": "true", @@ -24,12 +23,21 @@ __version__ = main_ns["__version__"] YOTI_API_URL = environ.get("YOTI_API_URL", DEFAULTS["YOTI_API_URL"]) -DOCS_API_URL = environ.get("DOCS_API_URL", DEFAULTS["DOCS_API_URL"]) + +YOTI_PROFILE_ENDPOINT = "/api/v1" +YOTI_DOC_SCAN_ENDPOINT = "/idverify/v1" + YOTI_API_PORT = environ.get("YOTI_API_PORT", DEFAULTS["YOTI_API_PORT"]) YOTI_API_VERSION = environ.get("YOTI_API_VERSION", DEFAULTS["YOTI_API_VERSION"]) + +# Fully formatted API URLs YOTI_API_ENDPOINT = environ.get( "YOTI_API_ENDPOINT", - "{0}:{1}/api/{2}".format(YOTI_API_URL, YOTI_API_PORT, YOTI_API_VERSION), + "{0}:{1}{2}".format(YOTI_API_URL, YOTI_API_PORT, YOTI_PROFILE_ENDPOINT), +) +YOTI_DOC_SCAN_API_URL = environ.get( + "YOTI_DOC_SCAN_API_URL", + "{0}:{1}{2}".format(YOTI_API_URL, YOTI_API_PORT, YOTI_DOC_SCAN_ENDPOINT), ) YOTI_API_VERIFY_SSL = environ.get( diff --git a/yoti_python_sdk/docs/__init__.py b/yoti_python_sdk/doc_scan/__init__.py similarity index 100% rename from yoti_python_sdk/docs/__init__.py rename to yoti_python_sdk/doc_scan/__init__.py diff --git a/yoti_python_sdk/docs/client.py b/yoti_python_sdk/doc_scan/client.py similarity index 93% rename from yoti_python_sdk/docs/client.py rename to yoti_python_sdk/doc_scan/client.py index 70925ef7..1df82ddb 100644 --- a/yoti_python_sdk/docs/client.py +++ b/yoti_python_sdk/doc_scan/client.py @@ -4,12 +4,14 @@ import json import yoti_python_sdk -from yoti_python_sdk.docs.endpoint import Endpoint -from yoti_python_sdk.docs.session.retrieve.create_session_result import ( +from yoti_python_sdk.doc_scan.endpoint import Endpoint +from yoti_python_sdk.doc_scan.session.retrieve.create_session_result import ( CreateSessionResult, ) -from yoti_python_sdk.docs.session.retrieve.get_session_result import GetSessionResult -from yoti_python_sdk.docs.session.retrieve.media_value import MediaValue +from yoti_python_sdk.doc_scan.session.retrieve.get_session_result import ( + GetSessionResult, +) +from yoti_python_sdk.doc_scan.session.retrieve.media_value import MediaValue from yoti_python_sdk.http import MediaRequestHandler from yoti_python_sdk.http import SignedRequest from yoti_python_sdk.utils import YotiEncoder @@ -28,7 +30,7 @@ def __init__(self, sdk_id, key, api_url=None): if api_url is not None: self.__api_url = api_url else: - self.__api_url = yoti_python_sdk.DOCS_API_URL + self.__api_url = yoti_python_sdk.YOTI_DOC_SCAN_ENDPOINT def create_session(self, session_spec): """ diff --git a/yoti_python_sdk/docs/constants.py b/yoti_python_sdk/doc_scan/constants.py similarity index 100% rename from yoti_python_sdk/docs/constants.py rename to yoti_python_sdk/doc_scan/constants.py diff --git a/yoti_python_sdk/docs/endpoint.py b/yoti_python_sdk/doc_scan/endpoint.py similarity index 100% rename from yoti_python_sdk/docs/endpoint.py rename to yoti_python_sdk/doc_scan/endpoint.py diff --git a/yoti_python_sdk/docs/exception/__init__.py b/yoti_python_sdk/doc_scan/exception/__init__.py similarity index 100% rename from yoti_python_sdk/docs/exception/__init__.py rename to yoti_python_sdk/doc_scan/exception/__init__.py diff --git a/yoti_python_sdk/docs/exception/doc_scan_exception.py b/yoti_python_sdk/doc_scan/exception/doc_scan_exception.py similarity index 100% rename from yoti_python_sdk/docs/exception/doc_scan_exception.py rename to yoti_python_sdk/doc_scan/exception/doc_scan_exception.py diff --git a/yoti_python_sdk/docs/session/__init__.py b/yoti_python_sdk/doc_scan/session/__init__.py similarity index 100% rename from yoti_python_sdk/docs/session/__init__.py rename to yoti_python_sdk/doc_scan/session/__init__.py diff --git a/yoti_python_sdk/docs/session/create/__init__.py b/yoti_python_sdk/doc_scan/session/create/__init__.py similarity index 100% rename from yoti_python_sdk/docs/session/create/__init__.py rename to yoti_python_sdk/doc_scan/session/create/__init__.py diff --git a/yoti_python_sdk/docs/session/create/check/__init__.py b/yoti_python_sdk/doc_scan/session/create/check/__init__.py similarity index 100% rename from yoti_python_sdk/docs/session/create/check/__init__.py rename to yoti_python_sdk/doc_scan/session/create/check/__init__.py diff --git a/yoti_python_sdk/docs/session/create/check/document_authenticity.py b/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py similarity index 94% rename from yoti_python_sdk/docs/session/create/check/document_authenticity.py rename to yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py index 3d5fa87f..032e0a8f 100644 --- a/yoti_python_sdk/docs/session/create/check/document_authenticity.py +++ b/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs.constants import ID_DOCUMENT_AUTHENTICITY +from yoti_python_sdk.doc_scan.constants import ID_DOCUMENT_AUTHENTICITY from yoti_python_sdk.utils import YotiSerializable from .requested_check import RequestedCheck diff --git a/yoti_python_sdk/docs/session/create/check/face_match.py b/yoti_python_sdk/doc_scan/session/create/check/face_match.py similarity index 98% rename from yoti_python_sdk/docs/session/create/check/face_match.py rename to yoti_python_sdk/doc_scan/session/create/check/face_match.py index 90cd3028..8ae2807f 100644 --- a/yoti_python_sdk/docs/session/create/check/face_match.py +++ b/yoti_python_sdk/doc_scan/session/create/check/face_match.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs import constants +from yoti_python_sdk.doc_scan import constants from yoti_python_sdk.utils import YotiSerializable from .requested_check import RequestedCheck diff --git a/yoti_python_sdk/docs/session/create/check/liveness.py b/yoti_python_sdk/doc_scan/session/create/check/liveness.py similarity index 98% rename from yoti_python_sdk/docs/session/create/check/liveness.py rename to yoti_python_sdk/doc_scan/session/create/check/liveness.py index 38df1df9..82cc2fe3 100644 --- a/yoti_python_sdk/docs/session/create/check/liveness.py +++ b/yoti_python_sdk/doc_scan/session/create/check/liveness.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs import constants +from yoti_python_sdk.doc_scan import constants from yoti_python_sdk.utils import YotiSerializable from .requested_check import RequestedCheck diff --git a/yoti_python_sdk/docs/session/create/check/requested_check.py b/yoti_python_sdk/doc_scan/session/create/check/requested_check.py similarity index 100% rename from yoti_python_sdk/docs/session/create/check/requested_check.py rename to yoti_python_sdk/doc_scan/session/create/check/requested_check.py diff --git a/yoti_python_sdk/docs/session/create/notification_config.py b/yoti_python_sdk/doc_scan/session/create/notification_config.py similarity index 94% rename from yoti_python_sdk/docs/session/create/notification_config.py rename to yoti_python_sdk/doc_scan/session/create/notification_config.py index 7d629a37..8df275a4 100644 --- a/yoti_python_sdk/docs/session/create/notification_config.py +++ b/yoti_python_sdk/doc_scan/session/create/notification_config.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs.constants import CHECK_COMPLETION -from yoti_python_sdk.docs.constants import RESOURCE_UPDATE -from yoti_python_sdk.docs.constants import SESSION_COMPLETION -from yoti_python_sdk.docs.constants import TASK_COMPLETION +from yoti_python_sdk.doc_scan.constants import CHECK_COMPLETION +from yoti_python_sdk.doc_scan.constants import RESOURCE_UPDATE +from yoti_python_sdk.doc_scan.constants import SESSION_COMPLETION +from yoti_python_sdk.doc_scan.constants import TASK_COMPLETION from yoti_python_sdk.utils import YotiSerializable diff --git a/yoti_python_sdk/docs/session/create/sdk_config.py b/yoti_python_sdk/doc_scan/session/create/sdk_config.py similarity index 98% rename from yoti_python_sdk/docs/session/create/sdk_config.py rename to yoti_python_sdk/doc_scan/session/create/sdk_config.py index 84dddca9..a9246068 100644 --- a/yoti_python_sdk/docs/session/create/sdk_config.py +++ b/yoti_python_sdk/doc_scan/session/create/sdk_config.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs.constants import CAMERA -from yoti_python_sdk.docs.constants import CAMERA_AND_UPLOAD +from yoti_python_sdk.doc_scan.constants import CAMERA +from yoti_python_sdk.doc_scan.constants import CAMERA_AND_UPLOAD from yoti_python_sdk.utils import YotiSerializable diff --git a/yoti_python_sdk/docs/session/create/session_spec.py b/yoti_python_sdk/doc_scan/session/create/session_spec.py similarity index 100% rename from yoti_python_sdk/docs/session/create/session_spec.py rename to yoti_python_sdk/doc_scan/session/create/session_spec.py diff --git a/yoti_python_sdk/docs/session/create/task/__init__.py b/yoti_python_sdk/doc_scan/session/create/task/__init__.py similarity index 100% rename from yoti_python_sdk/docs/session/create/task/__init__.py rename to yoti_python_sdk/doc_scan/session/create/task/__init__.py diff --git a/yoti_python_sdk/docs/session/create/task/requested_task.py b/yoti_python_sdk/doc_scan/session/create/task/requested_task.py similarity index 100% rename from yoti_python_sdk/docs/session/create/task/requested_task.py rename to yoti_python_sdk/doc_scan/session/create/task/requested_task.py diff --git a/yoti_python_sdk/docs/session/create/task/text_extraction.py b/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py similarity index 98% rename from yoti_python_sdk/docs/session/create/task/text_extraction.py rename to yoti_python_sdk/doc_scan/session/create/task/text_extraction.py index 4af11fa4..e15939cb 100644 --- a/yoti_python_sdk/docs/session/create/task/text_extraction.py +++ b/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs import constants +from yoti_python_sdk.doc_scan import constants from yoti_python_sdk.utils import YotiSerializable from .requested_task import RequestedTask diff --git a/yoti_python_sdk/docs/session/retrieve/__init__.py b/yoti_python_sdk/doc_scan/session/retrieve/__init__.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/__init__.py rename to yoti_python_sdk/doc_scan/session/retrieve/__init__.py diff --git a/yoti_python_sdk/docs/session/retrieve/breakdown_response.py b/yoti_python_sdk/doc_scan/session/retrieve/breakdown_response.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/breakdown_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/breakdown_response.py diff --git a/yoti_python_sdk/docs/session/retrieve/check_response.py b/yoti_python_sdk/doc_scan/session/retrieve/check_response.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/check_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/check_response.py diff --git a/yoti_python_sdk/docs/session/retrieve/create_session_result.py b/yoti_python_sdk/doc_scan/session/retrieve/create_session_result.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/create_session_result.py rename to yoti_python_sdk/doc_scan/session/retrieve/create_session_result.py diff --git a/yoti_python_sdk/docs/session/retrieve/document_fields_response.py b/yoti_python_sdk/doc_scan/session/retrieve/document_fields_response.py similarity index 89% rename from yoti_python_sdk/docs/session/retrieve/document_fields_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/document_fields_response.py index ace4e16a..1d8b3621 100644 --- a/yoti_python_sdk/docs/session/retrieve/document_fields_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/document_fields_response.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse class DocumentFieldsResponse(object): diff --git a/yoti_python_sdk/docs/session/retrieve/face_map_response.py b/yoti_python_sdk/doc_scan/session/retrieve/face_map_response.py similarity index 88% rename from yoti_python_sdk/docs/session/retrieve/face_map_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/face_map_response.py index 448d0ff3..881cd538 100644 --- a/yoti_python_sdk/docs/session/retrieve/face_map_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/face_map_response.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse class FaceMapResponse(object): diff --git a/yoti_python_sdk/docs/session/retrieve/frame_response.py b/yoti_python_sdk/doc_scan/session/retrieve/frame_response.py similarity index 88% rename from yoti_python_sdk/docs/session/retrieve/frame_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/frame_response.py index 169ed15a..14e98726 100644 --- a/yoti_python_sdk/docs/session/retrieve/frame_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/frame_response.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse class FrameResponse(object): diff --git a/yoti_python_sdk/docs/session/retrieve/generated_check_response.py b/yoti_python_sdk/doc_scan/session/retrieve/generated_check_response.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/generated_check_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/generated_check_response.py diff --git a/yoti_python_sdk/docs/session/retrieve/generated_media.py b/yoti_python_sdk/doc_scan/session/retrieve/generated_media.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/generated_media.py rename to yoti_python_sdk/doc_scan/session/retrieve/generated_media.py diff --git a/yoti_python_sdk/docs/session/retrieve/get_session_result.py b/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py similarity index 99% rename from yoti_python_sdk/docs/session/retrieve/get_session_result.py rename to yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py index fe1ac1de..7405b3e5 100644 --- a/yoti_python_sdk/docs/session/retrieve/get_session_result.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs import constants +from yoti_python_sdk.doc_scan import constants from .check_response import AuthenticityCheckResponse from .check_response import CheckResponse from .check_response import FaceMatchCheckResponse diff --git a/yoti_python_sdk/docs/session/retrieve/id_document_resource_response.py b/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py similarity index 90% rename from yoti_python_sdk/docs/session/retrieve/id_document_resource_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py index 520f55b6..79c31257 100644 --- a/yoti_python_sdk/docs/session/retrieve/id_document_resource_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs.session.retrieve.document_fields_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.document_fields_response import ( DocumentFieldsResponse, ) -from yoti_python_sdk.docs.session.retrieve.page_response import PageResponse -from yoti_python_sdk.docs.session.retrieve.task_response import TaskResponse +from yoti_python_sdk.doc_scan.session.retrieve.page_response import PageResponse +from yoti_python_sdk.doc_scan.session.retrieve.task_response import TaskResponse class IdDocumentResourceResponse(object): diff --git a/yoti_python_sdk/docs/session/retrieve/liveness_resource_response.py b/yoti_python_sdk/doc_scan/session/retrieve/liveness_resource_response.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/liveness_resource_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/liveness_resource_response.py diff --git a/yoti_python_sdk/docs/session/retrieve/media_response.py b/yoti_python_sdk/doc_scan/session/retrieve/media_response.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/media_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/media_response.py diff --git a/yoti_python_sdk/docs/session/retrieve/media_value.py b/yoti_python_sdk/doc_scan/session/retrieve/media_value.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/media_value.py rename to yoti_python_sdk/doc_scan/session/retrieve/media_value.py diff --git a/yoti_python_sdk/docs/session/retrieve/page_response.py b/yoti_python_sdk/doc_scan/session/retrieve/page_response.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/page_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/page_response.py diff --git a/yoti_python_sdk/docs/session/retrieve/recommendation_response.py b/yoti_python_sdk/doc_scan/session/retrieve/recommendation_response.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/recommendation_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/recommendation_response.py diff --git a/yoti_python_sdk/docs/session/retrieve/report_response.py b/yoti_python_sdk/doc_scan/session/retrieve/report_response.py similarity index 100% rename from yoti_python_sdk/docs/session/retrieve/report_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/report_response.py diff --git a/yoti_python_sdk/docs/session/retrieve/resource_container.py b/yoti_python_sdk/doc_scan/session/retrieve/resource_container.py similarity index 88% rename from yoti_python_sdk/docs/session/retrieve/resource_container.py rename to yoti_python_sdk/doc_scan/session/retrieve/resource_container.py index b4bfc97a..00d2ca71 100644 --- a/yoti_python_sdk/docs/session/retrieve/resource_container.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/resource_container.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from yoti_python_sdk.docs.session.retrieve.id_document_resource_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.id_document_resource_response import ( IdDocumentResourceResponse, ) -from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.liveness_resource_response import ( LivenessResourceResponse, ) -from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.liveness_resource_response import ( ZoomLivenessResourceResponse, ) diff --git a/yoti_python_sdk/docs/session/retrieve/resource_response.py b/yoti_python_sdk/doc_scan/session/retrieve/resource_response.py similarity index 96% rename from yoti_python_sdk/docs/session/retrieve/resource_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/resource_response.py index c840231c..f64efcf9 100644 --- a/yoti_python_sdk/docs/session/retrieve/resource_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/resource_response.py @@ -1,4 +1,4 @@ -from yoti_python_sdk.docs import constants +from yoti_python_sdk.doc_scan import constants from .task_response import TaskResponse from .task_response import TextExtractionTaskResponse diff --git a/yoti_python_sdk/docs/session/retrieve/task_response.py b/yoti_python_sdk/doc_scan/session/retrieve/task_response.py similarity index 92% rename from yoti_python_sdk/docs/session/retrieve/task_response.py rename to yoti_python_sdk/doc_scan/session/retrieve/task_response.py index fb6f9fba..9511dc4b 100644 --- a/yoti_python_sdk/docs/session/retrieve/task_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/task_response.py @@ -4,14 +4,14 @@ import iso8601 from iso8601 import ParseError -from yoti_python_sdk.docs import constants -from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( +from yoti_python_sdk.doc_scan import constants +from yoti_python_sdk.doc_scan.session.retrieve.generated_check_response import ( GeneratedCheckResponse, ) -from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.generated_check_response import ( GeneratedTextDataCheckResponse, ) -from yoti_python_sdk.docs.session.retrieve.generated_media import GeneratedMedia +from yoti_python_sdk.doc_scan.session.retrieve.generated_media import GeneratedMedia class TaskResponse(object): diff --git a/yoti_python_sdk/tests/docs/__init__.py b/yoti_python_sdk/tests/doc_scan/__init__.py similarity index 100% rename from yoti_python_sdk/tests/docs/__init__.py rename to yoti_python_sdk/tests/doc_scan/__init__.py diff --git a/yoti_python_sdk/tests/docs/conftest.py b/yoti_python_sdk/tests/doc_scan/conftest.py similarity index 88% rename from yoti_python_sdk/tests/docs/conftest.py rename to yoti_python_sdk/tests/doc_scan/conftest.py index e1bfa945..5b85abc6 100644 --- a/yoti_python_sdk/tests/docs/conftest.py +++ b/yoti_python_sdk/tests/doc_scan/conftest.py @@ -4,7 +4,7 @@ import pytest -from yoti_python_sdk.docs.client import DocScanClient +from yoti_python_sdk.doc_scan.client import DocScanClient FIXTURES_DIR = join(dirname(abspath(__file__)), "..", "fixtures") PEM_FILE_PATH = join(FIXTURES_DIR, "sdk-test.pem") diff --git a/yoti_python_sdk/tests/docs/fixtures/failed_request.txt b/yoti_python_sdk/tests/doc_scan/fixtures/failed_request.txt similarity index 100% rename from yoti_python_sdk/tests/docs/fixtures/failed_request.txt rename to yoti_python_sdk/tests/doc_scan/fixtures/failed_request.txt diff --git a/yoti_python_sdk/tests/docs/fixtures/retrieve_session_success.txt b/yoti_python_sdk/tests/doc_scan/fixtures/retrieve_session_success.txt similarity index 100% rename from yoti_python_sdk/tests/docs/fixtures/retrieve_session_success.txt rename to yoti_python_sdk/tests/doc_scan/fixtures/retrieve_session_success.txt diff --git a/yoti_python_sdk/tests/docs/fixtures/session_create_success.txt b/yoti_python_sdk/tests/doc_scan/fixtures/session_create_success.txt similarity index 100% rename from yoti_python_sdk/tests/docs/fixtures/session_create_success.txt rename to yoti_python_sdk/tests/doc_scan/fixtures/session_create_success.txt diff --git a/yoti_python_sdk/tests/docs/mocks.py b/yoti_python_sdk/tests/doc_scan/mocks.py similarity index 100% rename from yoti_python_sdk/tests/docs/mocks.py rename to yoti_python_sdk/tests/doc_scan/mocks.py diff --git a/yoti_python_sdk/tests/docs/session/create/check/test_face_match_check.py b/yoti_python_sdk/tests/doc_scan/session/create/check/test_face_match_check.py similarity index 78% rename from yoti_python_sdk/tests/docs/session/create/check/test_face_match_check.py rename to yoti_python_sdk/tests/doc_scan/session/create/check/test_face_match_check.py index 728caceb..1da0d8f9 100644 --- a/yoti_python_sdk/tests/docs/session/create/check/test_face_match_check.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/check/test_face_match_check.py @@ -1,12 +1,14 @@ import json import unittest -from yoti_python_sdk.docs.session.create import RequestedFaceMatchCheckBuilder -from yoti_python_sdk.docs.session.create.check.face_match import RequestedFaceMatchCheck -from yoti_python_sdk.docs.session.create.check.face_match import ( +from yoti_python_sdk.doc_scan.session.create import RequestedFaceMatchCheckBuilder +from yoti_python_sdk.doc_scan.session.create.check.face_match import ( + RequestedFaceMatchCheck, +) +from yoti_python_sdk.doc_scan.session.create.check.face_match import ( RequestedFaceMatchCheckConfig, ) -from yoti_python_sdk.docs.session.create.check.requested_check import RequestedCheck +from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck from yoti_python_sdk.utils import YotiEncoder diff --git a/yoti_python_sdk/tests/docs/session/create/check/test_liveness_check.py b/yoti_python_sdk/tests/doc_scan/session/create/check/test_liveness_check.py similarity index 80% rename from yoti_python_sdk/tests/docs/session/create/check/test_liveness_check.py rename to yoti_python_sdk/tests/doc_scan/session/create/check/test_liveness_check.py index 7b6bd730..130deb69 100644 --- a/yoti_python_sdk/tests/docs/session/create/check/test_liveness_check.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/check/test_liveness_check.py @@ -1,12 +1,14 @@ import unittest import json -from yoti_python_sdk.docs.session.create.check import RequestedLivenessCheckBuilder -from yoti_python_sdk.docs.session.create.check.liveness import RequestedLivenessCheck -from yoti_python_sdk.docs.session.create.check.liveness import ( +from yoti_python_sdk.doc_scan.session.create.check import RequestedLivenessCheckBuilder +from yoti_python_sdk.doc_scan.session.create.check.liveness import ( + RequestedLivenessCheck, +) +from yoti_python_sdk.doc_scan.session.create.check.liveness import ( RequestedLivenessCheckConfig, ) -from yoti_python_sdk.docs.session.create.check.requested_check import RequestedCheck +from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck from yoti_python_sdk.utils import YotiEncoder diff --git a/yoti_python_sdk/tests/docs/session/create/check/test_requested_document_authenticity_check.py b/yoti_python_sdk/tests/doc_scan/session/create/check/test_requested_document_authenticity_check.py similarity index 74% rename from yoti_python_sdk/tests/docs/session/create/check/test_requested_document_authenticity_check.py rename to yoti_python_sdk/tests/doc_scan/session/create/check/test_requested_document_authenticity_check.py index 9e1914cc..ae0d0596 100644 --- a/yoti_python_sdk/tests/docs/session/create/check/test_requested_document_authenticity_check.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/check/test_requested_document_authenticity_check.py @@ -1,16 +1,16 @@ import json import unittest -from yoti_python_sdk.docs.session.create.check import ( +from yoti_python_sdk.doc_scan.session.create.check import ( RequestedDocumentAuthenticityCheckBuilder, ) -from yoti_python_sdk.docs.session.create.check.document_authenticity import ( +from yoti_python_sdk.doc_scan.session.create.check.document_authenticity import ( RequestedDocumentAuthenticityCheck, ) -from yoti_python_sdk.docs.session.create.check.document_authenticity import ( +from yoti_python_sdk.doc_scan.session.create.check.document_authenticity import ( RequestedDocumentAuthenticityCheckConfig, ) -from yoti_python_sdk.docs.session.create.check.requested_check import RequestedCheck +from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck from yoti_python_sdk.utils import YotiEncoder diff --git a/yoti_python_sdk/tests/docs/session/create/task/test_text_extraction_task.py b/yoti_python_sdk/tests/doc_scan/session/create/task/test_text_extraction_task.py similarity index 80% rename from yoti_python_sdk/tests/docs/session/create/task/test_text_extraction_task.py rename to yoti_python_sdk/tests/doc_scan/session/create/task/test_text_extraction_task.py index 050f9a5f..2d20ae47 100644 --- a/yoti_python_sdk/tests/docs/session/create/task/test_text_extraction_task.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/task/test_text_extraction_task.py @@ -1,12 +1,14 @@ import json import unittest -from yoti_python_sdk.docs.session.create.task import RequestedTextExtractionTaskBuilder -from yoti_python_sdk.docs.session.create.task.requested_task import RequestedTask -from yoti_python_sdk.docs.session.create.task.text_extraction import ( +from yoti_python_sdk.doc_scan.session.create.task import ( + RequestedTextExtractionTaskBuilder, +) +from yoti_python_sdk.doc_scan.session.create.task.requested_task import RequestedTask +from yoti_python_sdk.doc_scan.session.create.task.text_extraction import ( RequestedTextExtractionTask, ) -from yoti_python_sdk.docs.session.create.task.text_extraction import ( +from yoti_python_sdk.doc_scan.session.create.task.text_extraction import ( RequestedTextExtractionTaskConfig, ) from yoti_python_sdk.utils import YotiEncoder diff --git a/yoti_python_sdk/tests/docs/session/create/test_notification_config.py b/yoti_python_sdk/tests/doc_scan/session/create/test_notification_config.py similarity index 95% rename from yoti_python_sdk/tests/docs/session/create/test_notification_config.py rename to yoti_python_sdk/tests/doc_scan/session/create/test_notification_config.py index 709920a6..f207ff14 100644 --- a/yoti_python_sdk/tests/docs/session/create/test_notification_config.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/test_notification_config.py @@ -1,8 +1,10 @@ import json import unittest -from yoti_python_sdk.docs.session.create import NotificationConfigBuilder -from yoti_python_sdk.docs.session.create.notification_config import NotificationConfig +from yoti_python_sdk.doc_scan.session.create import NotificationConfigBuilder +from yoti_python_sdk.doc_scan.session.create.notification_config import ( + NotificationConfig, +) from yoti_python_sdk.utils import YotiEncoder diff --git a/yoti_python_sdk/tests/docs/session/create/test_sdk_config.py b/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py similarity index 94% rename from yoti_python_sdk/tests/docs/session/create/test_sdk_config.py rename to yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py index f30e3c96..6de7b4ca 100644 --- a/yoti_python_sdk/tests/docs/session/create/test_sdk_config.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py @@ -1,8 +1,8 @@ import json import unittest -from yoti_python_sdk.docs.session.create import SdkConfigBuilder -from yoti_python_sdk.docs.session.create.sdk_config import SdkConfig +from yoti_python_sdk.doc_scan.session.create import SdkConfigBuilder +from yoti_python_sdk.doc_scan.session.create.sdk_config import SdkConfig from yoti_python_sdk.utils import YotiEncoder diff --git a/yoti_python_sdk/tests/docs/session/create/test_session_spec.py b/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py similarity index 86% rename from yoti_python_sdk/tests/docs/session/create/test_session_spec.py rename to yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py index 1001ecfa..b63ae1a3 100644 --- a/yoti_python_sdk/tests/docs/session/create/test_session_spec.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py @@ -3,11 +3,13 @@ from mock import Mock -from yoti_python_sdk.docs.session.create import SessionSpecBuilder -from yoti_python_sdk.docs.session.create.check.requested_check import RequestedCheck -from yoti_python_sdk.docs.session.create.notification_config import NotificationConfig -from yoti_python_sdk.docs.session.create.sdk_config import SdkConfig -from yoti_python_sdk.docs.session.create.task.requested_task import RequestedTask +from yoti_python_sdk.doc_scan.session.create import SessionSpecBuilder +from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck +from yoti_python_sdk.doc_scan.session.create.notification_config import ( + NotificationConfig, +) +from yoti_python_sdk.doc_scan.session.create.sdk_config import SdkConfig +from yoti_python_sdk.doc_scan.session.create.task.requested_task import RequestedTask from yoti_python_sdk.utils import YotiEncoder diff --git a/yoti_python_sdk/tests/docs/session/retrieve/__init__.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/__init__.py similarity index 100% rename from yoti_python_sdk/tests/docs/session/retrieve/__init__.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/__init__.py diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_breakdown_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_breakdown_response.py similarity index 90% rename from yoti_python_sdk/tests/docs/session/retrieve/test_breakdown_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_breakdown_response.py index 8048fb04..2b534512 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_breakdown_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_breakdown_response.py @@ -1,6 +1,8 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.breakdown_response import BreakdownResponse +from yoti_python_sdk.doc_scan.session.retrieve.breakdown_response import ( + BreakdownResponse, +) class BreakdownResponseTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_check_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_check_response.py similarity index 90% rename from yoti_python_sdk/tests/docs/session/retrieve/test_check_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_check_response.py index 9acb2d20..a3ce2318 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_check_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_check_response.py @@ -3,9 +3,9 @@ import pytz -from yoti_python_sdk.docs.session.retrieve.check_response import CheckResponse -from yoti_python_sdk.docs.session.retrieve.generated_media import GeneratedMedia -from yoti_python_sdk.docs.session.retrieve.report_response import ReportResponse +from yoti_python_sdk.doc_scan.session.retrieve.check_response import CheckResponse +from yoti_python_sdk.doc_scan.session.retrieve.generated_media import GeneratedMedia +from yoti_python_sdk.doc_scan.session.retrieve.report_response import ReportResponse class CheckResponseTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_create_session_result.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_create_session_result.py similarity index 93% rename from yoti_python_sdk/tests/docs/session/retrieve/test_create_session_result.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_create_session_result.py index 710cacab..13c6b615 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_create_session_result.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_create_session_result.py @@ -1,6 +1,6 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.create_session_result import ( +from yoti_python_sdk.doc_scan.session.retrieve.create_session_result import ( CreateSessionResult, ) diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_document_fields_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_document_fields_response.py similarity index 73% rename from yoti_python_sdk/tests/docs/session/retrieve/test_document_fields_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_document_fields_response.py index 7d108fdc..c34e6faf 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_document_fields_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_document_fields_response.py @@ -1,9 +1,9 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.document_fields_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.document_fields_response import ( DocumentFieldsResponse, ) -from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse class DocumentFieldsResponseTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_face_map_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_face_map_response.py similarity index 70% rename from yoti_python_sdk/tests/docs/session/retrieve/test_face_map_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_face_map_response.py index 8cb0e14f..5d7e2099 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_face_map_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_face_map_response.py @@ -1,7 +1,7 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.face_map_response import FaceMapResponse -from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse +from yoti_python_sdk.doc_scan.session.retrieve.face_map_response import FaceMapResponse +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse class FaceMapResponseTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_frame_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_frame_response.py similarity index 72% rename from yoti_python_sdk/tests/docs/session/retrieve/test_frame_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_frame_response.py index 31843c1b..1a4868c0 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_frame_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_frame_response.py @@ -1,7 +1,7 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.frame_response import FrameResponse -from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse +from yoti_python_sdk.doc_scan.session.retrieve.frame_response import FrameResponse +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse class FrameResponseTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_generated_check_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_generated_check_response.py similarity index 89% rename from yoti_python_sdk/tests/docs/session/retrieve/test_generated_check_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_generated_check_response.py index 8b946c70..365673e2 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_generated_check_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_generated_check_response.py @@ -1,6 +1,6 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.generated_check_response import ( GeneratedCheckResponse, ) diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_generated_media.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_generated_media.py similarity index 87% rename from yoti_python_sdk/tests/docs/session/retrieve/test_generated_media.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_generated_media.py index 8a1d4642..9b6140e7 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_generated_media.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_generated_media.py @@ -1,6 +1,6 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.generated_media import GeneratedMedia +from yoti_python_sdk.doc_scan.session.retrieve.generated_media import GeneratedMedia class GeneratedMediaTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_get_session_result.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py similarity index 78% rename from yoti_python_sdk/tests/docs/session/retrieve/test_get_session_result.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py index 02baf039..0b259771 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_get_session_result.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py @@ -1,13 +1,23 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.check_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.check_response import ( AuthenticityCheckResponse, ) -from yoti_python_sdk.docs.session.retrieve.check_response import FaceMatchCheckResponse -from yoti_python_sdk.docs.session.retrieve.check_response import LivenessCheckResponse -from yoti_python_sdk.docs.session.retrieve.check_response import TextDataCheckResponse -from yoti_python_sdk.docs.session.retrieve.get_session_result import GetSessionResult -from yoti_python_sdk.docs.session.retrieve.resource_container import ResourceContainer +from yoti_python_sdk.doc_scan.session.retrieve.check_response import ( + FaceMatchCheckResponse, +) +from yoti_python_sdk.doc_scan.session.retrieve.check_response import ( + LivenessCheckResponse, +) +from yoti_python_sdk.doc_scan.session.retrieve.check_response import ( + TextDataCheckResponse, +) +from yoti_python_sdk.doc_scan.session.retrieve.get_session_result import ( + GetSessionResult, +) +from yoti_python_sdk.doc_scan.session.retrieve.resource_container import ( + ResourceContainer, +) class GetSessionResultTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_id_document_resource_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_id_document_resource_response.py similarity index 90% rename from yoti_python_sdk/tests/docs/session/retrieve/test_id_document_resource_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_id_document_resource_response.py index 8ff2b6a1..630ab264 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_id_document_resource_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_id_document_resource_response.py @@ -1,9 +1,9 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.document_fields_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.document_fields_response import ( DocumentFieldsResponse, ) -from yoti_python_sdk.docs.session.retrieve.id_document_resource_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.id_document_resource_response import ( IdDocumentResourceResponse, ) diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_liveness_resource_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_liveness_resource_response.py similarity index 82% rename from yoti_python_sdk/tests/docs/session/retrieve/test_liveness_resource_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_liveness_resource_response.py index e50a8d14..78802b07 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_liveness_resource_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_liveness_resource_response.py @@ -1,7 +1,7 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.face_map_response import FaceMapResponse -from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.face_map_response import FaceMapResponse +from yoti_python_sdk.doc_scan.session.retrieve.liveness_resource_response import ( ZoomLivenessResourceResponse, ) diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_media_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_media_response.py similarity index 94% rename from yoti_python_sdk/tests/docs/session/retrieve/test_media_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_media_response.py index c8c260fc..2e9af3db 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_media_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_media_response.py @@ -3,7 +3,7 @@ import pytz -from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse class MediaResponseTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_media_value.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_media_value.py similarity index 89% rename from yoti_python_sdk/tests/docs/session/retrieve/test_media_value.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_media_value.py index 982faf7c..664f553e 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_media_value.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_media_value.py @@ -1,7 +1,7 @@ import base64 import unittest -from yoti_python_sdk.docs.session.retrieve.media_value import MediaValue +from yoti_python_sdk.doc_scan.session.retrieve.media_value import MediaValue class MediaValueTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_page_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_page_response.py similarity index 78% rename from yoti_python_sdk/tests/docs/session/retrieve/test_page_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_page_response.py index a3662cca..07214d3e 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_page_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_page_response.py @@ -1,7 +1,7 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.media_response import MediaResponse -from yoti_python_sdk.docs.session.retrieve.page_response import PageResponse +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse +from yoti_python_sdk.doc_scan.session.retrieve.page_response import PageResponse class PageResponseTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_recommendation_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_recommendation_response.py similarity index 91% rename from yoti_python_sdk/tests/docs/session/retrieve/test_recommendation_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_recommendation_response.py index f2785ef1..43b923f3 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_recommendation_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_recommendation_response.py @@ -1,6 +1,6 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.recommendation_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.recommendation_response import ( RecommendationResponse, ) diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_report_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_report_response.py similarity index 80% rename from yoti_python_sdk/tests/docs/session/retrieve/test_report_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_report_response.py index 1f0a03ab..e9468c80 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_report_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_report_response.py @@ -1,9 +1,9 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.recommendation_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.recommendation_response import ( RecommendationResponse, ) -from yoti_python_sdk.docs.session.retrieve.report_response import ReportResponse +from yoti_python_sdk.doc_scan.session.retrieve.report_response import ReportResponse class ReportResponseTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_resource_container.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_container.py similarity index 78% rename from yoti_python_sdk/tests/docs/session/retrieve/test_resource_container.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_container.py index 489a7e90..b1507953 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_resource_container.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_container.py @@ -1,12 +1,14 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.liveness_resource_response import ( LivenessResourceResponse, ) -from yoti_python_sdk.docs.session.retrieve.liveness_resource_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.liveness_resource_response import ( ZoomLivenessResourceResponse, ) -from yoti_python_sdk.docs.session.retrieve.resource_container import ResourceContainer +from yoti_python_sdk.doc_scan.session.retrieve.resource_container import ( + ResourceContainer, +) class ResourceContainerTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_resource_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_response.py similarity index 77% rename from yoti_python_sdk/tests/docs/session/retrieve/test_resource_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_response.py index 9e4b37c6..c052a260 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_resource_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_response.py @@ -1,8 +1,8 @@ import unittest -from yoti_python_sdk.docs.session.retrieve.resource_response import ResourceResponse -from yoti_python_sdk.docs.session.retrieve.task_response import TaskResponse -from yoti_python_sdk.docs.session.retrieve.task_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.resource_response import ResourceResponse +from yoti_python_sdk.doc_scan.session.retrieve.task_response import TaskResponse +from yoti_python_sdk.doc_scan.session.retrieve.task_response import ( TextExtractionTaskResponse, ) diff --git a/yoti_python_sdk/tests/docs/session/retrieve/test_task_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_task_response.py similarity index 89% rename from yoti_python_sdk/tests/docs/session/retrieve/test_task_response.py rename to yoti_python_sdk/tests/doc_scan/session/retrieve/test_task_response.py index b9b6ab8d..5ef4029e 100644 --- a/yoti_python_sdk/tests/docs/session/retrieve/test_task_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_task_response.py @@ -3,13 +3,13 @@ import pytz -from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.generated_check_response import ( GeneratedCheckResponse, ) -from yoti_python_sdk.docs.session.retrieve.generated_check_response import ( +from yoti_python_sdk.doc_scan.session.retrieve.generated_check_response import ( GeneratedTextDataCheckResponse, ) -from yoti_python_sdk.docs.session.retrieve.task_response import TaskResponse +from yoti_python_sdk.doc_scan.session.retrieve.task_response import TaskResponse class TaskResponseTest(unittest.TestCase): diff --git a/yoti_python_sdk/tests/docs/test_doc_scan_client.py b/yoti_python_sdk/tests/doc_scan/test_doc_scan_client.py similarity index 80% rename from yoti_python_sdk/tests/docs/test_doc_scan_client.py rename to yoti_python_sdk/tests/doc_scan/test_doc_scan_client.py index 89c90520..b47c89df 100644 --- a/yoti_python_sdk/tests/docs/test_doc_scan_client.py +++ b/yoti_python_sdk/tests/doc_scan/test_doc_scan_client.py @@ -1,19 +1,25 @@ import pytest -from yoti_python_sdk.docs.client import DocScanClient # noqa: F401 -from yoti_python_sdk.docs.exception import DocScanException -from yoti_python_sdk.docs.session.create.session_spec import SessionSpec -from yoti_python_sdk.docs.session.retrieve.create_session_result import ( +from yoti_python_sdk.doc_scan.client import DocScanClient # noqa: F401 +from yoti_python_sdk.doc_scan.exception import DocScanException +from yoti_python_sdk.doc_scan.session.create.session_spec import SessionSpec +from yoti_python_sdk.doc_scan.session.retrieve.create_session_result import ( CreateSessionResult, ) -from yoti_python_sdk.docs.session.retrieve.get_session_result import GetSessionResult -from yoti_python_sdk.tests.docs.mocks import mocked_request_failed_session_creation -from yoti_python_sdk.tests.docs.mocks import mocked_request_failed_session_retrieval -from yoti_python_sdk.tests.docs.mocks import mocked_request_media_content -from yoti_python_sdk.tests.docs.mocks import mocked_request_missing_content -from yoti_python_sdk.tests.docs.mocks import mocked_request_server_error -from yoti_python_sdk.tests.docs.mocks import mocked_request_successful_session_creation -from yoti_python_sdk.tests.docs.mocks import mocked_request_successful_session_retrieval +from yoti_python_sdk.doc_scan.session.retrieve.get_session_result import ( + GetSessionResult, +) +from yoti_python_sdk.tests.doc_scan.mocks import mocked_request_failed_session_creation +from yoti_python_sdk.tests.doc_scan.mocks import mocked_request_failed_session_retrieval +from yoti_python_sdk.tests.doc_scan.mocks import mocked_request_media_content +from yoti_python_sdk.tests.doc_scan.mocks import mocked_request_missing_content +from yoti_python_sdk.tests.doc_scan.mocks import mocked_request_server_error +from yoti_python_sdk.tests.doc_scan.mocks import ( + mocked_request_successful_session_creation, +) +from yoti_python_sdk.tests.doc_scan.mocks import ( + mocked_request_successful_session_retrieval, +) try: from unittest import mock From 261ffb78a7936d3cb1ad41069005dfd5758ee97f Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Tue, 14 Apr 2020 12:24:27 +0100 Subject: [PATCH 5/9] SDK-1299: Address spelling mistakes --- yoti_python_sdk/doc_scan/session/create/task/requested_task.py | 2 +- .../doc_scan/session/retrieve/recommendation_response.py | 2 +- yoti_python_sdk/doc_scan/session/retrieve/resource_container.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/yoti_python_sdk/doc_scan/session/create/task/requested_task.py b/yoti_python_sdk/doc_scan/session/create/task/requested_task.py index 9dccc8eb..aa67c8e7 100644 --- a/yoti_python_sdk/doc_scan/session/create/task/requested_task.py +++ b/yoti_python_sdk/doc_scan/session/create/task/requested_task.py @@ -15,7 +15,7 @@ class RequestedTask(YotiSerializable): @abstractmethod def type(self): """ - Returns the tyoe of the Task to create + Returns the type of the Task to create :return: the type :rtype: str diff --git a/yoti_python_sdk/doc_scan/session/retrieve/recommendation_response.py b/yoti_python_sdk/doc_scan/session/retrieve/recommendation_response.py index 243b1a64..87f8f3f9 100644 --- a/yoti_python_sdk/doc_scan/session/retrieve/recommendation_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/recommendation_response.py @@ -38,7 +38,7 @@ def reason(self): @property def recovery_suggestion(self): """ - Returns the recovery suggestion of the recommendtion + Returns the recovery suggestion of the recommendation :return: the recovery suggestion :rtype: str or None diff --git a/yoti_python_sdk/doc_scan/session/retrieve/resource_container.py b/yoti_python_sdk/doc_scan/session/retrieve/resource_container.py index 00d2ca71..ce60cc28 100644 --- a/yoti_python_sdk/doc_scan/session/retrieve/resource_container.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/resource_container.py @@ -41,7 +41,7 @@ def __parse_liveness_capture(liveness_capture): """ Parses a liveness capture into a specific sub-class based on the liveness type. If no liveness type is available, it falls back - to the parent class :class:`LivensssResourceResponse` + to the parent class :class:`LivenessResourceResponse` :param liveness_capture: the liveness capture :type liveness_capture: dict From 6c39f5bca775d17c287ed480bce26066d64f83e5 Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Tue, 14 Apr 2020 13:54:45 +0100 Subject: [PATCH 6/9] SDK-1299: Remove MediaReponse, add content property to YotiResponse containing raw bytes from response --- yoti_python_sdk/doc_scan/client.py | 2 -- yoti_python_sdk/http.py | 22 +++------------------- yoti_python_sdk/tests/doc_scan/mocks.py | 3 ++- yoti_python_sdk/tests/mocks.py | 8 ++------ 4 files changed, 7 insertions(+), 28 deletions(-) diff --git a/yoti_python_sdk/doc_scan/client.py b/yoti_python_sdk/doc_scan/client.py index 1df82ddb..cee7300a 100644 --- a/yoti_python_sdk/doc_scan/client.py +++ b/yoti_python_sdk/doc_scan/client.py @@ -12,7 +12,6 @@ GetSessionResult, ) from yoti_python_sdk.doc_scan.session.retrieve.media_value import MediaValue -from yoti_python_sdk.http import MediaRequestHandler from yoti_python_sdk.http import SignedRequest from yoti_python_sdk.utils import YotiEncoder from .exception import DocScanException @@ -129,7 +128,6 @@ def get_media_content(self, session_id, media_id): """ request = ( SignedRequest.builder() - .with_request_handler(MediaRequestHandler) .with_get() .with_pem_file(self.__key) .with_base_url(self.__api_url) diff --git a/yoti_python_sdk/http.py b/yoti_python_sdk/http.py index 46ee5a40..e2f768d6 100644 --- a/yoti_python_sdk/http.py +++ b/yoti_python_sdk/http.py @@ -24,12 +24,13 @@ class YotiResponse(object): - def __init__(self, status_code, text, headers=None): + def __init__(self, status_code, text, headers=None, content=None): if headers is None: headers = {} self.status_code = status_code self.text = text + self.content = content self.headers = headers @@ -89,27 +90,10 @@ def execute(request): status_code=response.status_code, text=response.text, headers=response.headers, + content=response.content, ) -class MediaRequestHandler(RequestHandler): - @staticmethod - def execute(request): - """ - Execute the HTTP request supplied - """ - if not isinstance(request, SignedRequest): - raise TypeError("RequestHandler expects instance of SignedRequest") - - response = requests.request( - url=request.url, - method=request.method, - data=request.data, - headers=request.headers, - ) - return MediaResponse(response) - - class SignedRequest(object): def __init__(self, url, http_method, payload, headers, request_handler=None): self.__url = url diff --git a/yoti_python_sdk/tests/doc_scan/mocks.py b/yoti_python_sdk/tests/doc_scan/mocks.py index b1fd4e2f..e3be56d1 100644 --- a/yoti_python_sdk/tests/doc_scan/mocks.py +++ b/yoti_python_sdk/tests/doc_scan/mocks.py @@ -32,7 +32,8 @@ def mocked_request_failed_session_retrieval(): def mocked_request_media_content(): return MockResponse( status_code=200, - text=b"someContent", + text="someContent", + content=b"someContent", headers={"Content-Type": "application/json"}, ) diff --git a/yoti_python_sdk/tests/mocks.py b/yoti_python_sdk/tests/mocks.py index 2cd69099..40cdab6c 100644 --- a/yoti_python_sdk/tests/mocks.py +++ b/yoti_python_sdk/tests/mocks.py @@ -7,15 +7,11 @@ class MockResponse(YotiResponse): - def __init__(self, status_code, text, headers=None): + def __init__(self, status_code, text, headers=None, content=None): if headers is None: headers = dict() - super(MockResponse, self).__init__(status_code, text, headers) - - @property - def content(self): - return self.text + super(MockResponse, self).__init__(status_code, text, headers, content) class MockRequestHandler(RequestHandler): From a7259b8a30a823629874ce47159febcd6badec41 Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Wed, 15 Apr 2020 15:09:51 +0100 Subject: [PATCH 7/9] SDK-1299: Remove MediaResponse as it's no longer needed --- yoti_python_sdk/http.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/yoti_python_sdk/http.py b/yoti_python_sdk/http.py index e2f768d6..0c8913a3 100644 --- a/yoti_python_sdk/http.py +++ b/yoti_python_sdk/http.py @@ -34,27 +34,6 @@ def __init__(self, status_code, text, headers=None, content=None): self.headers = headers -class MediaResponse(object): - def __init__(self, response): - self.response = response - - @property - def status_code(self): - return self.response.status_code - - @property - def content(self): - return self.response.content - - @property - def text(self): - return self.response.text - - @property - def headers(self): - return self.response.headers - - class RequestHandler(object): """ Default request handler for signing requests using the requests library. From e5d148eb6f54f94bba32cfa237a2d8daec53aa2d Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Mon, 20 Apr 2020 10:39:50 +0100 Subject: [PATCH 8/9] Release 2.11.0: Bump version numbers --- sonar-project.properties | 2 +- yoti_python_sdk/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index c505ef56..f5b692d9 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -2,7 +2,7 @@ sonar.host.url = https://sonarcloud.io sonar.organization = getyoti sonar.projectKey = getyoti:python sonar.projectName = Python SDK -sonar.projectVersion = 2.10.2 +sonar.projectVersion = 2.11.0 sonar.exclusions = yoti_python_sdk/tests/**,examples/**,yoti_python_sdk/protobuf/**/* sonar.python.pylint.reportPath = coverage.out diff --git a/yoti_python_sdk/version.py b/yoti_python_sdk/version.py index 0bcaa459..c9b477ce 100644 --- a/yoti_python_sdk/version.py +++ b/yoti_python_sdk/version.py @@ -1,2 +1,2 @@ # -*- coding: utf-8 -*- -__version__ = "2.10.2" +__version__ = "2.11.0" From db5c95977baeb525add0ac8ec3d7d55383ef1792 Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Mon, 20 Apr 2020 10:41:39 +0100 Subject: [PATCH 9/9] Release 2.11.0: Export client from yoti_python_sdk.doc_scan module --- yoti_python_sdk/doc_scan/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yoti_python_sdk/doc_scan/__init__.py b/yoti_python_sdk/doc_scan/__init__.py index 3c3ee8c4..c4abde59 100644 --- a/yoti_python_sdk/doc_scan/__init__.py +++ b/yoti_python_sdk/doc_scan/__init__.py @@ -7,6 +7,7 @@ from .session.create.notification_config import NotificationConfigBuilder from .session.create.sdk_config import SdkConfigBuilder from .session.create.session_spec import SessionSpecBuilder +from .client import DocScanClient __all__ = [ RequestedDocumentAuthenticityCheckBuilder, @@ -16,4 +17,5 @@ SessionSpecBuilder, NotificationConfigBuilder, SdkConfigBuilder, + DocScanClient, ]