From af63ec9f231fa3f518a745a0ebd0d857c0e2a7c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jun 2020 05:52:55 +0000 Subject: [PATCH 01/35] Bump virtualenv from 20.0.13 to 20.0.25 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.0.13 to 20.0.25. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/master/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.0.13...20.0.25) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 46d38602..97282efb 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "python-coveralls==2.9.3", "coverage==4.5.4", "mock==2.0.0", - "virtualenv==20.0.13", + "virtualenv==20.0.25", ], }, classifiers=[ From e65ac6fabbbf34637d3f6b845edc0a9bcd399501 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2020 05:54:30 +0000 Subject: [PATCH 02/35] Bump itsdangerous from 0.24 to 1.1.0 Bumps [itsdangerous](https://github.com/pallets/itsdangerous) from 0.24 to 1.1.0. - [Release notes](https://github.com/pallets/itsdangerous/releases) - [Changelog](https://github.com/pallets/itsdangerous/blob/master/CHANGES.rst) - [Commits](https://github.com/pallets/itsdangerous/compare/0.24...1.1.0) Signed-off-by: dependabot[bot] --- requirements.in | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.in b/requirements.in index 2385c225..37477d0a 100644 --- a/requirements.in +++ b/requirements.in @@ -2,7 +2,7 @@ asn1==2.2.0 # asn1 2.3.0 introduces enum34 as a dependency, which causes problem cryptography==2.8.0 cffi>=1.14.0 future==0.18.2 -itsdangerous==0.24 +itsdangerous==1.1.0 pbr==1.10.0 protobuf==3.12.2 pyopenssl==19.1.0 diff --git a/requirements.txt b/requirements.txt index 99048eea..bccc0ef3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ deprecated==1.2.10 # via -r requirements.in future==0.18.2 # via -r requirements.in idna==2.7 # via requests iso8601==0.1.12 # via -r requirements.in -itsdangerous==0.24 # via -r requirements.in +itsdangerous==1.1.0 # via -r requirements.in pbr==1.10.0 # via -r requirements.in protobuf==3.12.2 # via -r requirements.in pycparser==2.18 # via cffi From fc9a6a54a7498527e1a76a565fc98c8b06a75e83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2020 05:07:30 +0000 Subject: [PATCH 03/35] Bump virtualenv from 20.0.25 to 20.0.26 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.0.25 to 20.0.26. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/master/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.0.25...20.0.26) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 97282efb..d00b46fa 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "python-coveralls==2.9.3", "coverage==4.5.4", "mock==2.0.0", - "virtualenv==20.0.25", + "virtualenv==20.0.26", ], }, classifiers=[ From 1d25cc5e0969050a416b1fc393c165fc891fdbaf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2020 05:27:52 +0000 Subject: [PATCH 04/35] Bump virtualenv from 20.0.26 to 20.0.27 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.0.26 to 20.0.27. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/master/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.0.26...20.0.27) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d00b46fa..817b19cf 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "python-coveralls==2.9.3", "coverage==4.5.4", "mock==2.0.0", - "virtualenv==20.0.26", + "virtualenv==20.0.27", ], }, classifiers=[ From 4814a941bf1ef3764ffcdead0f7f0f643cfd5773 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2020 07:42:27 +0000 Subject: [PATCH 05/35] Bump virtualenv from 20.0.27 to 20.0.28 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.0.27 to 20.0.28. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/master/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.0.27...20.0.28) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 817b19cf..cf025bf1 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "python-coveralls==2.9.3", "coverage==4.5.4", "mock==2.0.0", - "virtualenv==20.0.27", + "virtualenv==20.0.28", ], }, classifiers=[ From f4f44ae59ce6fade7071ba468b9eb97cd057bd77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2020 05:35:34 +0000 Subject: [PATCH 06/35] Bump cffi from 1.14.0 to 1.14.1 Bumps [cffi](https://bitbucket.org/cffi/release-doc) from 1.14.0 to 1.14.1. - [Commits](https://bitbucket.org/cffi/release-doc/commits) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bccc0ef3..0e299e90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ # asn1==2.2.0 # via -r requirements.in certifi==2018.11.29 # via requests -cffi==1.14.0 # via -r requirements.in, cryptography +cffi==1.14.1 # via -r requirements.in, cryptography chardet==3.0.4 # via requests cryptography==2.8 # via -r requirements.in, pyopenssl deprecated==1.2.10 # via -r requirements.in From a842e93af2eb7b0c7fb8fcba18d3a040513162fc Mon Sep 17 00:00:00 2001 From: Ed Harrod Date: Mon, 27 Jul 2020 08:27:32 +0100 Subject: [PATCH 07/35] Use `cffi==1.14.1` in requirements.in also --- requirements.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.in b/requirements.in index 37477d0a..74e5bd23 100644 --- a/requirements.in +++ b/requirements.in @@ -1,6 +1,6 @@ asn1==2.2.0 # asn1 2.3.0 introduces enum34 as a dependency, which causes problems on some envs cryptography==2.8.0 -cffi>=1.14.0 +cffi==1.14.1 future==0.18.2 itsdangerous==1.1.0 pbr==1.10.0 From 987ce7e4ef9b8252c240f199cd8ea544984e0ac2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2020 05:18:08 +0000 Subject: [PATCH 08/35] Bump protobuf from 3.12.2 to 3.12.4 Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 3.12.2 to 3.12.4. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/master/generate_changelog.py) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.12.2...v3.12.4) Signed-off-by: dependabot[bot] --- requirements.in | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.in b/requirements.in index 74e5bd23..cef86b65 100644 --- a/requirements.in +++ b/requirements.in @@ -4,7 +4,7 @@ cffi==1.14.1 future==0.18.2 itsdangerous==1.1.0 pbr==1.10.0 -protobuf==3.12.2 +protobuf==3.12.4 pyopenssl==19.1.0 PyYAML==5.2 # PyYAML 5.3 does not support Python 3.4 pytz==2020.1 diff --git a/requirements.txt b/requirements.txt index 0e299e90..d2dc7cb2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ idna==2.7 # via requests iso8601==0.1.12 # via -r requirements.in itsdangerous==1.1.0 # via -r requirements.in pbr==1.10.0 # via -r requirements.in -protobuf==3.12.2 # via -r requirements.in +protobuf==3.12.4 # via -r requirements.in pycparser==2.18 # via cffi pyopenssl==19.1.0 # via -r requirements.in pytz==2020.1 # via -r requirements.in From 7e4f8c5177e81bbfd4af5daec2fbc5952a0bd53e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2020 05:54:29 +0000 Subject: [PATCH 09/35] Bump virtualenv from 20.0.28 to 20.0.29 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.0.28 to 20.0.29. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/master/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.0.28...20.0.29) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index cf025bf1..b2ed7a0d 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "python-coveralls==2.9.3", "coverage==4.5.4", "mock==2.0.0", - "virtualenv==20.0.28", + "virtualenv==20.0.29", ], }, classifiers=[ From 4b67908a69886972a594c11d364bf04d30058e9b Mon Sep 17 00:00:00 2001 From: echarrod Date: Wed, 29 Jul 2020 09:26:44 +0100 Subject: [PATCH 10/35] NA: use pytest instead of py.test --- .travis.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab63218b..180a9b1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ jobs: - pip install -r requirements.txt - pip install -e .[dev] script: - - py.test -v + - pytest -v - <<: *test python: "3.4" - <<: *test diff --git a/README.md b/README.md index 317f6b1c..77bf723b 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ Running the tests is done by the following process, ensuring you are using Pytho 1. Install dependencies: `pip install -r requirements.txt` 1. Install the SDK and development dependencies: `pip install .[dev]` -1. Execute in the main project dir: `py.test` +1. Execute in the main project dir: `pytest` ## API Coverage From db66bfa232b83320bc09e38c10cc7a72ca650c84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2020 06:13:03 +0000 Subject: [PATCH 11/35] Bump virtualenv from 20.0.29 to 20.0.30 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.0.29 to 20.0.30. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/master/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.0.29...20.0.30) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b2ed7a0d..1cf2fcd3 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "python-coveralls==2.9.3", "coverage==4.5.4", "mock==2.0.0", - "virtualenv==20.0.29", + "virtualenv==20.0.30", ], }, classifiers=[ From 3e84016b696657a7d5b2664041e78d7147f032f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2020 05:26:05 +0000 Subject: [PATCH 12/35] Bump protobuf from 3.12.4 to 3.13.0 Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 3.12.4 to 3.13.0. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/master/generate_changelog.py) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.12.4...v3.13.0) Signed-off-by: dependabot[bot] --- requirements.in | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.in b/requirements.in index cef86b65..0e80ea11 100644 --- a/requirements.in +++ b/requirements.in @@ -4,7 +4,7 @@ cffi==1.14.1 future==0.18.2 itsdangerous==1.1.0 pbr==1.10.0 -protobuf==3.12.4 +protobuf==3.13.0 pyopenssl==19.1.0 PyYAML==5.2 # PyYAML 5.3 does not support Python 3.4 pytz==2020.1 diff --git a/requirements.txt b/requirements.txt index d2dc7cb2..5b19131a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ idna==2.7 # via requests iso8601==0.1.12 # via -r requirements.in itsdangerous==1.1.0 # via -r requirements.in pbr==1.10.0 # via -r requirements.in -protobuf==3.12.4 # via -r requirements.in +protobuf==3.13.0 # via -r requirements.in pycparser==2.18 # via cffi pyopenssl==19.1.0 # via -r requirements.in pytz==2020.1 # via -r requirements.in From 5ed10d37bc992a84c3d7986e47bf5ff71646e890 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2020 05:26:51 +0000 Subject: [PATCH 13/35] Bump cffi from 1.14.1 to 1.14.2 Bumps [cffi](https://github.com/python-cffi/release-doc) from 1.14.1 to 1.14.2. - [Release notes](https://github.com/python-cffi/release-doc/releases) - [Commits](https://github.com/python-cffi/release-doc/commits) Signed-off-by: dependabot[bot] --- requirements.in | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.in b/requirements.in index cef86b65..a3c8dc1b 100644 --- a/requirements.in +++ b/requirements.in @@ -1,6 +1,6 @@ asn1==2.2.0 # asn1 2.3.0 introduces enum34 as a dependency, which causes problems on some envs cryptography==2.8.0 -cffi==1.14.1 +cffi==1.14.2 future==0.18.2 itsdangerous==1.1.0 pbr==1.10.0 diff --git a/requirements.txt b/requirements.txt index d2dc7cb2..fa49420e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ # asn1==2.2.0 # via -r requirements.in certifi==2018.11.29 # via requests -cffi==1.14.1 # via -r requirements.in, cryptography +cffi==1.14.2 # via -r requirements.in, cryptography chardet==3.0.4 # via requests cryptography==2.8 # via -r requirements.in, pyopenssl deprecated==1.2.10 # via -r requirements.in From e7aa3608c8d164ac1e1a7728a1600aa4f28d15a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2020 05:08:41 +0000 Subject: [PATCH 14/35] Bump virtualenv from 20.0.30 to 20.0.31 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.0.30 to 20.0.31. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/master/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.0.30...20.0.31) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1cf2fcd3..2dfd85ad 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "python-coveralls==2.9.3", "coverage==4.5.4", "mock==2.0.0", - "virtualenv==20.0.30", + "virtualenv==20.0.31", ], }, classifiers=[ From 46f069cd2bcbcd740a81443e21e3d24c2232ddf3 Mon Sep 17 00:00:00 2001 From: echarrod Date: Thu, 10 Sep 2020 08:57:00 +0100 Subject: [PATCH 15/35] SDK-1749: Remove FaceMap from demo --- examples/doc_scan/app.py | 23 +--------------- examples/doc_scan/templates/success.html | 35 +----------------------- 2 files changed, 2 insertions(+), 56 deletions(-) diff --git a/examples/doc_scan/app.py b/examples/doc_scan/app.py index 963d4140..612bffa2 100644 --- a/examples/doc_scan/app.py +++ b/examples/doc_scan/app.py @@ -1,9 +1,5 @@ -import base64 -from io import BytesIO - import yoti_python_sdk -from filetype import filetype -from flask import Flask, Response, render_template, request, send_file, session +from flask import Flask, Response, render_template, request, session from yoti_python_sdk.doc_scan import ( DocScanClient, RequestedDocumentAuthenticityCheckBuilder, @@ -118,8 +114,6 @@ def media(): doc_scan_client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) - base64_req = request.args.get("base64", "0") - session_id = session.get("doc_scan_session_id", None) if session_id is None: return Response("No session ID available", status=404) @@ -129,21 +123,6 @@ def media(): except DocScanException as e: return render_template("error.html", error=e.text) - if base64_req == "1" and retrieved_media.mime_type == "application/octet-stream": - decoded = base64.b64decode(retrieved_media.content) - info = filetype.guess(decoded) - - buffer = BytesIO() - buffer.write(decoded) - buffer.seek(0) - - return send_file( - buffer, - attachment_filename="media." + info.extension, - mimetype=info.mime, - as_attachment=True, - ) - return Response( retrieved_media.content, content_type=retrieved_media.mime_type, status=200 ) diff --git a/examples/doc_scan/templates/success.html b/examples/doc_scan/templates/success.html index ae3842de..1428cb9f 100644 --- a/examples/doc_scan/templates/success.html +++ b/examples/doc_scan/templates/success.html @@ -317,39 +317,6 @@

Zoom Liveness Resources

- {% if liveness.facemap is not none %} -
-
-

- -

-
-
-
- {% if liveness.facemap.media is not none %} -

Media

- - - - - - - -
ID - - {{ liveness.facemap.media.id }} - -
- {% endif %} -
-
-
- {% endif %} - {% if liveness.frames|length > 0 %}
@@ -383,4 +350,4 @@
Frame
{% endfor %} {% endwith %}
-{% include "layout/footer.html" %} \ No newline at end of file +{% include "layout/footer.html" %} From c9d5850f5493263dceb8d1e7a5eb4b27e8d13bd0 Mon Sep 17 00:00:00 2001 From: echarrod Date: Tue, 4 Aug 2020 10:22:34 +0100 Subject: [PATCH 16/35] SDK-1299: Correct Doc Scan docs --- yoti_python_sdk/doc_scan/session/create/sdk_config.py | 2 +- yoti_python_sdk/doc_scan/session/create/session_spec.py | 2 +- .../session/retrieve/id_document_resource_response.py | 2 +- .../doc_scan/session/retrieve/resource_response.py | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/yoti_python_sdk/doc_scan/session/create/sdk_config.py b/yoti_python_sdk/doc_scan/session/create/sdk_config.py index a9246068..9fd40889 100644 --- a/yoti_python_sdk/doc_scan/session/create/sdk_config.py +++ b/yoti_python_sdk/doc_scan/session/create/sdk_config.py @@ -8,7 +8,7 @@ class SdkConfig(YotiSerializable): """ - Provides configuration properties using by the web/native clients + Provides configuration properties for the web/native clients """ def __init__( diff --git a/yoti_python_sdk/doc_scan/session/create/session_spec.py b/yoti_python_sdk/doc_scan/session/create/session_spec.py index 0a7f47c7..fb048140 100644 --- a/yoti_python_sdk/doc_scan/session/create/session_spec.py +++ b/yoti_python_sdk/doc_scan/session/create/session_spec.py @@ -78,7 +78,7 @@ def resources_ttl(self): @property def user_tracking_id(self): """ - User tracking ID, for the Relying Business to track returning users + User tracking ID, to track returning users :return: the user tracking ID :rtype: str diff --git a/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py b/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py index 156e14ad..44c72869 100644 --- a/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py @@ -79,7 +79,7 @@ def document_fields(self): def text_extraction_tasks(self): """ Returns a list of text extraction tasks associated - with the id document + with the identity document :return: list of text extraction tasks :rtype: list[TextExtractionTaskResponse] diff --git a/yoti_python_sdk/doc_scan/session/retrieve/resource_response.py b/yoti_python_sdk/doc_scan/session/retrieve/resource_response.py index f64efcf9..7a54b0bc 100644 --- a/yoti_python_sdk/doc_scan/session/retrieve/resource_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/resource_response.py @@ -4,6 +4,10 @@ class ResourceResponse(object): + """ + Represents a resource + """ + def __init__(self, data=None): """ :param data: the data to parse From 63db463871894baeee2f562f0ff909b259e33cb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 05:28:28 +0000 Subject: [PATCH 17/35] Bump iso8601 from 0.1.12 to 0.1.13 Bumps [iso8601](https://github.com/micktwomey/pyiso8601) from 0.1.12 to 0.1.13. - [Release notes](https://github.com/micktwomey/pyiso8601/releases) - [Commits](https://github.com/micktwomey/pyiso8601/compare/0.1.12...0.1.13) Signed-off-by: dependabot[bot] --- requirements.in | 2 +- requirements.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.in b/requirements.in index 0e7d7f15..d8a2ef6e 100644 --- a/requirements.in +++ b/requirements.in @@ -12,4 +12,4 @@ requests>=2.20.0 urllib3>=1.24.3 deprecated==1.2.10 wheel==0.33.6 -iso8601==0.1.12 +iso8601==0.1.13 diff --git a/requirements.txt b/requirements.txt index f6402f73..3cc6b53f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ cryptography==2.8 # via -r requirements.in, pyopenssl deprecated==1.2.10 # via -r requirements.in future==0.18.2 # via -r requirements.in idna==2.7 # via requests -iso8601==0.1.12 # via -r requirements.in +iso8601==0.1.13 # via -r requirements.in itsdangerous==1.1.0 # via -r requirements.in pbr==1.10.0 # via -r requirements.in protobuf==3.13.0 # via -r requirements.in diff --git a/setup.py b/setup.py index 2dfd85ad..ac6be3b9 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ "future>=0.11.0", "asn1==2.2.0", "pyopenssl>=18.0.0", - "iso8601==0.1.12", + "iso8601==0.1.13", ], extras_require={ "examples": [ From 66e688306faae3bf2972f2a910948b6cbcf95787 Mon Sep 17 00:00:00 2001 From: echarrod Date: Tue, 15 Sep 2020 14:49:09 +0100 Subject: [PATCH 18/35] SDK-1718: Remove null values from JSON payload --- .../create/check/document_authenticity.py | 3 +++ .../session/create/check/face_match.py | 3 +++ .../doc_scan/session/create/check/liveness.py | 3 +++ .../session/create/check/requested_check.py | 3 +++ .../session/create/filter/document_filter.py | 3 +++ .../filter/document_restrictions_filter.py | 6 ++++++ .../filter/orthogonal_restrictions_filter.py | 9 +++++++++ .../create/filter/required_id_document.py | 3 +++ .../session/create/notification_config.py | 3 +++ .../doc_scan/session/create/sdk_config.py | 3 +++ .../doc_scan/session/create/session_spec.py | 3 +++ .../session/create/task/requested_task.py | 3 +++ .../session/create/task/text_extraction.py | 3 +++ .../test_document_restriction_builder.py | 12 ++++++++++++ yoti_python_sdk/utils.py | 18 ++++++++++++++++++ 15 files changed, 78 insertions(+) diff --git a/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py b/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py index 032e0a8f..ccbc5e27 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py +++ b/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py @@ -14,6 +14,9 @@ class RequestedDocumentAuthenticityCheckConfig(YotiSerializable): def to_json(self): return {} + def include_null_values(self): + return False + class RequestedDocumentAuthenticityCheck(RequestedCheck): """ diff --git a/yoti_python_sdk/doc_scan/session/create/check/face_match.py b/yoti_python_sdk/doc_scan/session/create/check/face_match.py index 8ae2807f..b60fe407 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/face_match.py +++ b/yoti_python_sdk/doc_scan/session/create/check/face_match.py @@ -32,6 +32,9 @@ def manual_check(self): def to_json(self): return {"manual_check": self.manual_check} + def include_null_values(self): + return False + class RequestedFaceMatchCheck(RequestedCheck): """ diff --git a/yoti_python_sdk/doc_scan/session/create/check/liveness.py b/yoti_python_sdk/doc_scan/session/create/check/liveness.py index 82cc2fe3..d0b95bac 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/liveness.py +++ b/yoti_python_sdk/doc_scan/session/create/check/liveness.py @@ -42,6 +42,9 @@ def max_retries(self): def to_json(self): return {"liveness_type": self.liveness_type, "max_retries": self.max_retries} + def include_null_values(self): + return False + class RequestedLivenessCheck(RequestedCheck): """ diff --git a/yoti_python_sdk/doc_scan/session/create/check/requested_check.py b/yoti_python_sdk/doc_scan/session/create/check/requested_check.py index 71a293e0..763a6764 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/requested_check.py +++ b/yoti_python_sdk/doc_scan/session/create/check/requested_check.py @@ -34,3 +34,6 @@ def config(self): def to_json(self): return {"type": self.type, "config": self.config} + + def include_null_values(self): + return False diff --git a/yoti_python_sdk/doc_scan/session/create/filter/document_filter.py b/yoti_python_sdk/doc_scan/session/create/filter/document_filter.py index d42066a8..954c0263 100644 --- a/yoti_python_sdk/doc_scan/session/create/filter/document_filter.py +++ b/yoti_python_sdk/doc_scan/session/create/filter/document_filter.py @@ -15,3 +15,6 @@ def type(self): def to_json(self): return {"type": self.type} + + def include_null_values(self): + return False diff --git a/yoti_python_sdk/doc_scan/session/create/filter/document_restrictions_filter.py b/yoti_python_sdk/doc_scan/session/create/filter/document_restrictions_filter.py index 1d597808..6ca1d80d 100644 --- a/yoti_python_sdk/doc_scan/session/create/filter/document_restrictions_filter.py +++ b/yoti_python_sdk/doc_scan/session/create/filter/document_restrictions_filter.py @@ -26,6 +26,9 @@ def to_json(self): "document_types": self.document_types, } + def include_null_values(self): + return False + class DocumentRestrictionBuilder(object): def __init__(self): @@ -87,6 +90,9 @@ def to_json(self): parent["documents"] = self.documents return parent + def include_null_values(self): + return False + class DocumentRestrictionsFilterBuilder(object): """ diff --git a/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py b/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py index 4782cd4a..b47b54b7 100644 --- a/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py +++ b/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py @@ -33,6 +33,9 @@ def country_codes(self): def to_json(self): return {"inclusion": self.inclusion, "country_codes": self.country_codes} + def include_null_values(self): + return False + class TypeRestriction(YotiSerializable): def __init__(self, inclusion, document_types): @@ -62,6 +65,9 @@ def document_types(self): def to_json(self): return {"inclusion": self.inclusion, "document_types": self.document_types} + def include_null_values(self): + return False + class OrthogonalRestrictionsFilter(DocumentFilter): def __init__(self, country_restriction, type_restriction): @@ -96,6 +102,9 @@ def to_json(self): parent["type_restriction"] = self.type_restriction return parent + def include_null_values(self): + return False + class OrthogonalRestrictionsFilterBuilder(object): """ diff --git a/yoti_python_sdk/doc_scan/session/create/filter/required_id_document.py b/yoti_python_sdk/doc_scan/session/create/filter/required_id_document.py index 3bd3c64d..9271f98f 100644 --- a/yoti_python_sdk/doc_scan/session/create/filter/required_id_document.py +++ b/yoti_python_sdk/doc_scan/session/create/filter/required_id_document.py @@ -22,6 +22,9 @@ def filter(self): def to_json(self): return {"type": self.type, "filter": self.__doc_filter} + def include_null_values(self): + return False + class RequiredIdDocumentBuilder(object): """ diff --git a/yoti_python_sdk/doc_scan/session/create/notification_config.py b/yoti_python_sdk/doc_scan/session/create/notification_config.py index 8df275a4..c6ea3071 100644 --- a/yoti_python_sdk/doc_scan/session/create/notification_config.py +++ b/yoti_python_sdk/doc_scan/session/create/notification_config.py @@ -69,6 +69,9 @@ def to_json(self): "topics": self.topics, } + def include_null_values(self): + return False + class NotificationConfigBuilder(object): """ diff --git a/yoti_python_sdk/doc_scan/session/create/sdk_config.py b/yoti_python_sdk/doc_scan/session/create/sdk_config.py index 9fd40889..4e9c1db1 100644 --- a/yoti_python_sdk/doc_scan/session/create/sdk_config.py +++ b/yoti_python_sdk/doc_scan/session/create/sdk_config.py @@ -133,6 +133,9 @@ def to_json(self): "error_url": self.error_url, } + def include_null_values(self): + return False + class SdkConfigBuilder(object): """ diff --git a/yoti_python_sdk/doc_scan/session/create/session_spec.py b/yoti_python_sdk/doc_scan/session/create/session_spec.py index fb048140..95681f6a 100644 --- a/yoti_python_sdk/doc_scan/session/create/session_spec.py +++ b/yoti_python_sdk/doc_scan/session/create/session_spec.py @@ -150,6 +150,9 @@ def to_json(self): "required_documents": self.required_documents, } + def include_null_values(self): + return False + class SessionSpecBuilder(object): """ 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 aa67c8e7..535e4887 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 @@ -34,3 +34,6 @@ def config(self): def to_json(self): return {"type": self.type, "config": self.config} + + def include_null_values(self): + return False diff --git a/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py b/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py index e15939cb..a54d89a1 100644 --- a/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py +++ b/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py @@ -26,6 +26,9 @@ def manual_check(self): def to_json(self): return {"manual_check": self.manual_check} + def include_null_values(self): + return False + class RequestedTextExtractionTask(RequestedTask): """ diff --git a/yoti_python_sdk/tests/doc_scan/session/create/filter/test_document_restriction_builder.py b/yoti_python_sdk/tests/doc_scan/session/create/filter/test_document_restriction_builder.py index d761827d..6ca116ce 100644 --- a/yoti_python_sdk/tests/doc_scan/session/create/filter/test_document_restriction_builder.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/filter/test_document_restriction_builder.py @@ -41,3 +41,15 @@ def test_to_json_should_not_throw_exception(): s = json.dumps(result, cls=YotiEncoder) assert s is not None and s != "" + + +def test_to_json_should_not_include_null_values(): + result = ( + DocumentRestrictionBuilder() + .with_document_types(["PASSPORT", "DRIVING_LICENCE"]) + .build() + ) + + s = json.dumps(result, cls=YotiEncoder) + assert s is not None + assert "null" not in s diff --git a/yoti_python_sdk/utils.py b/yoti_python_sdk/utils.py index cd05773d..5a804c5e 100644 --- a/yoti_python_sdk/utils.py +++ b/yoti_python_sdk/utils.py @@ -16,10 +16,16 @@ class YotiSerializable(object): def to_json(self): raise NotImplementedError + def include_null_values(self): + return True + class YotiEncoder(JSONEncoder): def default(self, o): if isinstance(o, YotiSerializable): + if not o.include_null_values(): + return remove_null_values(o.to_json()) + return o.to_json() return JSONEncoder.default(self, o) @@ -40,3 +46,15 @@ def create_timestamp(): :return: the timestamp as a int """ return int(time.time() * 1000) + + +def remove_null_values(d): + """ + Delete keys with the value ``None`` in a dictionary, recursively. (None serializes to null) + """ + for key, value in list(d.items()): + if value is None: + del d[key] + elif isinstance(value, dict): + remove_null_values(value) + return d From 5be853f1d62e77fbf2f38005a4dc3bc9ac86f240 Mon Sep 17 00:00:00 2001 From: echarrod Date: Wed, 16 Sep 2020 07:15:05 +0100 Subject: [PATCH 19/35] SDK-1718: Move remove_null_values to be called in each to_json method, rather than adding on an exposed method --- .../create/check/document_authenticity.py | 7 ++--- .../session/create/check/face_match.py | 7 ++--- .../doc_scan/session/create/check/liveness.py | 9 +++---- .../session/create/check/requested_check.py | 7 ++--- .../session/create/filter/document_filter.py | 7 ++--- .../filter/document_restrictions_filter.py | 20 ++++++-------- .../filter/orthogonal_restrictions_filter.py | 21 ++++++--------- .../create/filter/required_id_document.py | 6 ++--- .../session/create/notification_config.py | 17 ++++++------ .../doc_scan/session/create/sdk_config.py | 27 +++++++++---------- .../doc_scan/session/create/session_spec.py | 27 +++++++++---------- .../session/create/task/requested_task.py | 7 ++--- .../session/create/task/text_extraction.py | 7 ++--- yoti_python_sdk/utils.py | 6 ----- 14 files changed, 68 insertions(+), 107 deletions(-) diff --git a/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py b/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py index ccbc5e27..69fdc40c 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py +++ b/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from yoti_python_sdk.doc_scan.constants import ID_DOCUMENT_AUTHENTICITY -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values from .requested_check import RequestedCheck @@ -12,10 +12,7 @@ class RequestedDocumentAuthenticityCheckConfig(YotiSerializable): """ def to_json(self): - return {} - - def include_null_values(self): - return False + return remove_null_values({}) class RequestedDocumentAuthenticityCheck(RequestedCheck): diff --git a/yoti_python_sdk/doc_scan/session/create/check/face_match.py b/yoti_python_sdk/doc_scan/session/create/check/face_match.py index b60fe407..1abc240e 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/face_match.py +++ b/yoti_python_sdk/doc_scan/session/create/check/face_match.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from yoti_python_sdk.doc_scan import constants -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values from .requested_check import RequestedCheck @@ -30,10 +30,7 @@ def manual_check(self): return self.__manual_check def to_json(self): - return {"manual_check": self.manual_check} - - def include_null_values(self): - return False + return remove_null_values({"manual_check": self.manual_check}) class RequestedFaceMatchCheck(RequestedCheck): diff --git a/yoti_python_sdk/doc_scan/session/create/check/liveness.py b/yoti_python_sdk/doc_scan/session/create/check/liveness.py index d0b95bac..f9dc1075 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/liveness.py +++ b/yoti_python_sdk/doc_scan/session/create/check/liveness.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from yoti_python_sdk.doc_scan import constants -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values from .requested_check import RequestedCheck @@ -40,10 +40,9 @@ def max_retries(self): return self.__max_retries def to_json(self): - return {"liveness_type": self.liveness_type, "max_retries": self.max_retries} - - def include_null_values(self): - return False + return remove_null_values( + {"liveness_type": self.liveness_type, "max_retries": self.max_retries} + ) class RequestedLivenessCheck(RequestedCheck): diff --git a/yoti_python_sdk/doc_scan/session/create/check/requested_check.py b/yoti_python_sdk/doc_scan/session/create/check/requested_check.py index 763a6764..0b08e399 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/requested_check.py +++ b/yoti_python_sdk/doc_scan/session/create/check/requested_check.py @@ -1,7 +1,7 @@ from abc import ABCMeta from abc import abstractmethod -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values class RequestedCheck(YotiSerializable): @@ -33,7 +33,4 @@ def config(self): raise NotImplementedError def to_json(self): - return {"type": self.type, "config": self.config} - - def include_null_values(self): - return False + return remove_null_values({"type": self.type, "config": self.config}) diff --git a/yoti_python_sdk/doc_scan/session/create/filter/document_filter.py b/yoti_python_sdk/doc_scan/session/create/filter/document_filter.py index 954c0263..2fd471f9 100644 --- a/yoti_python_sdk/doc_scan/session/create/filter/document_filter.py +++ b/yoti_python_sdk/doc_scan/session/create/filter/document_filter.py @@ -1,6 +1,6 @@ from abc import ABCMeta -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values class DocumentFilter(YotiSerializable): @@ -14,7 +14,4 @@ def type(self): return self.__filter_type def to_json(self): - return {"type": self.type} - - def include_null_values(self): - return False + return remove_null_values({"type": self.type}) diff --git a/yoti_python_sdk/doc_scan/session/create/filter/document_restrictions_filter.py b/yoti_python_sdk/doc_scan/session/create/filter/document_restrictions_filter.py index 6ca1d80d..427ff8b1 100644 --- a/yoti_python_sdk/doc_scan/session/create/filter/document_restrictions_filter.py +++ b/yoti_python_sdk/doc_scan/session/create/filter/document_restrictions_filter.py @@ -3,7 +3,7 @@ INCLUSION_BLACKLIST, INCLUSION_WHITELIST, ) -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values from .document_filter import DocumentFilter @@ -21,13 +21,12 @@ def document_types(self): return self.__document_types def to_json(self): - return { - "country_codes": self.country_codes, - "document_types": self.document_types, - } - - def include_null_values(self): - return False + return remove_null_values( + { + "country_codes": self.country_codes, + "document_types": self.document_types, + } + ) class DocumentRestrictionBuilder(object): @@ -88,10 +87,7 @@ def to_json(self): parent = DocumentFilter.to_json(self) parent["inclusion"] = self.inclusion parent["documents"] = self.documents - return parent - - def include_null_values(self): - return False + return remove_null_values(parent) class DocumentRestrictionsFilterBuilder(object): diff --git a/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py b/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py index b47b54b7..6e0bcf4e 100644 --- a/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py +++ b/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py @@ -1,7 +1,7 @@ from yoti_python_sdk.doc_scan.constants import INCLUSION_BLACKLIST from yoti_python_sdk.doc_scan.constants import INCLUSION_WHITELIST from yoti_python_sdk.doc_scan.constants import ORTHOGONAL_RESTRICTIONS -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values from .document_filter import DocumentFilter @@ -31,10 +31,9 @@ def country_codes(self): return self.__country_codes def to_json(self): - return {"inclusion": self.inclusion, "country_codes": self.country_codes} - - def include_null_values(self): - return False + return remove_null_values( + {"inclusion": self.inclusion, "country_codes": self.country_codes} + ) class TypeRestriction(YotiSerializable): @@ -63,10 +62,9 @@ def document_types(self): return self.__document_types def to_json(self): - return {"inclusion": self.inclusion, "document_types": self.document_types} - - def include_null_values(self): - return False + return remove_null_values( + {"inclusion": self.inclusion, "document_types": self.document_types} + ) class OrthogonalRestrictionsFilter(DocumentFilter): @@ -100,10 +98,7 @@ def to_json(self): parent = DocumentFilter.to_json(self) parent["country_restriction"] = self.country_restriction parent["type_restriction"] = self.type_restriction - return parent - - def include_null_values(self): - return False + return remove_null_values(parent) class OrthogonalRestrictionsFilterBuilder(object): diff --git a/yoti_python_sdk/doc_scan/session/create/filter/required_id_document.py b/yoti_python_sdk/doc_scan/session/create/filter/required_id_document.py index 9271f98f..fe594082 100644 --- a/yoti_python_sdk/doc_scan/session/create/filter/required_id_document.py +++ b/yoti_python_sdk/doc_scan/session/create/filter/required_id_document.py @@ -1,4 +1,5 @@ from yoti_python_sdk.doc_scan.constants import ID_DOCUMENT +from yoti_python_sdk.utils import remove_null_values from .document_filter import DocumentFilter # noqa: F401 from .required_document import RequiredDocument @@ -20,10 +21,7 @@ def filter(self): return self.__doc_filter def to_json(self): - return {"type": self.type, "filter": self.__doc_filter} - - def include_null_values(self): - return False + return remove_null_values({"type": self.type, "filter": self.__doc_filter}) class RequiredIdDocumentBuilder(object): diff --git a/yoti_python_sdk/doc_scan/session/create/notification_config.py b/yoti_python_sdk/doc_scan/session/create/notification_config.py index c6ea3071..476a38b0 100644 --- a/yoti_python_sdk/doc_scan/session/create/notification_config.py +++ b/yoti_python_sdk/doc_scan/session/create/notification_config.py @@ -5,7 +5,7 @@ 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 +from yoti_python_sdk.utils import YotiSerializable, remove_null_values class NotificationConfig(YotiSerializable): @@ -63,14 +63,13 @@ def topics(self): return self.__topics def to_json(self): - return { - "auth_token": self.auth_token, - "endpoint": self.endpoint, - "topics": self.topics, - } - - def include_null_values(self): - return False + return remove_null_values( + { + "auth_token": self.auth_token, + "endpoint": self.endpoint, + "topics": self.topics, + } + ) class NotificationConfigBuilder(object): diff --git a/yoti_python_sdk/doc_scan/session/create/sdk_config.py b/yoti_python_sdk/doc_scan/session/create/sdk_config.py index 4e9c1db1..c76fe897 100644 --- a/yoti_python_sdk/doc_scan/session/create/sdk_config.py +++ b/yoti_python_sdk/doc_scan/session/create/sdk_config.py @@ -3,7 +3,7 @@ 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 +from yoti_python_sdk.utils import YotiSerializable, remove_null_values class SdkConfig(YotiSerializable): @@ -122,19 +122,18 @@ def error_url(self): 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, - } - - def include_null_values(self): - return False + return remove_null_values( + { + "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): diff --git a/yoti_python_sdk/doc_scan/session/create/session_spec.py b/yoti_python_sdk/doc_scan/session/create/session_spec.py index 95681f6a..3b648644 100644 --- a/yoti_python_sdk/doc_scan/session/create/session_spec.py +++ b/yoti_python_sdk/doc_scan/session/create/session_spec.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from .filter.required_document import RequiredDocument # noqa: F401 -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values class SessionSpec(YotiSerializable): @@ -139,19 +139,18 @@ def required_documents(self): return self.__required_documents 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, - "required_documents": self.required_documents, - } - - def include_null_values(self): - return False + return remove_null_values( + { + "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, + "required_documents": self.required_documents, + } + ) class SessionSpecBuilder(object): 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 535e4887..110cf07c 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 @@ -1,7 +1,7 @@ from abc import ABCMeta from abc import abstractmethod -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values class RequestedTask(YotiSerializable): @@ -33,7 +33,4 @@ def config(self): raise NotImplementedError def to_json(self): - return {"type": self.type, "config": self.config} - - def include_null_values(self): - return False + return remove_null_values({"type": self.type, "config": self.config}) diff --git a/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py b/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py index a54d89a1..ebcfdc7a 100644 --- a/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py +++ b/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from yoti_python_sdk.doc_scan import constants -from yoti_python_sdk.utils import YotiSerializable +from yoti_python_sdk.utils import YotiSerializable, remove_null_values from .requested_task import RequestedTask @@ -24,10 +24,7 @@ def manual_check(self): return self.__manual_check def to_json(self): - return {"manual_check": self.manual_check} - - def include_null_values(self): - return False + return remove_null_values({"manual_check": self.manual_check}) class RequestedTextExtractionTask(RequestedTask): diff --git a/yoti_python_sdk/utils.py b/yoti_python_sdk/utils.py index 5a804c5e..110fb8c6 100644 --- a/yoti_python_sdk/utils.py +++ b/yoti_python_sdk/utils.py @@ -16,16 +16,10 @@ class YotiSerializable(object): def to_json(self): raise NotImplementedError - def include_null_values(self): - return True - class YotiEncoder(JSONEncoder): def default(self, o): if isinstance(o, YotiSerializable): - if not o.include_null_values(): - return remove_null_values(o.to_json()) - return o.to_json() return JSONEncoder.default(self, o) From 5e8ca74a30e1d7daf40dc7db7ae7fe665d4e04bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2020 05:50:16 +0000 Subject: [PATCH 20/35] Bump cffi from 1.14.2 to 1.14.3 Bumps [cffi](https://github.com/python-cffi/release-doc) from 1.14.2 to 1.14.3. - [Release notes](https://github.com/python-cffi/release-doc/releases) - [Commits](https://github.com/python-cffi/release-doc/commits) Signed-off-by: dependabot[bot] --- requirements.in | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.in b/requirements.in index d8a2ef6e..14edca50 100644 --- a/requirements.in +++ b/requirements.in @@ -1,6 +1,6 @@ asn1==2.2.0 # asn1 2.3.0 introduces enum34 as a dependency, which causes problems on some envs cryptography==2.8.0 -cffi==1.14.2 +cffi==1.14.3 future==0.18.2 itsdangerous==1.1.0 pbr==1.10.0 diff --git a/requirements.txt b/requirements.txt index 3cc6b53f..4a90f75a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ # asn1==2.2.0 # via -r requirements.in certifi==2018.11.29 # via requests -cffi==1.14.2 # via -r requirements.in, cryptography +cffi==1.14.3 # via -r requirements.in, cryptography chardet==3.0.4 # via requests cryptography==2.8 # via -r requirements.in, pyopenssl deprecated==1.2.10 # via -r requirements.in From 4e342ab51eae5125bceea1a93adc208ac597aef8 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Thu, 17 Sep 2020 23:02:02 +0100 Subject: [PATCH 21/35] SDK-1582: Include response error in exception message --- .../doc_scan/exception/doc_scan_exception.py | 36 +++++++- .../tests/doc_scan/exception/__init__.py | 0 .../exception/test_doc_scan_exception.py | 90 +++++++++++++++++++ yoti_python_sdk/tests/mocks.py | 5 +- 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 yoti_python_sdk/tests/doc_scan/exception/__init__.py create mode 100644 yoti_python_sdk/tests/doc_scan/exception/test_doc_scan_exception.py diff --git a/yoti_python_sdk/doc_scan/exception/doc_scan_exception.py b/yoti_python_sdk/doc_scan/exception/doc_scan_exception.py index efe0f323..64578451 100644 --- a/yoti_python_sdk/doc_scan/exception/doc_scan_exception.py +++ b/yoti_python_sdk/doc_scan/exception/doc_scan_exception.py @@ -1,3 +1,6 @@ +import json + + class DocScanException(Exception): """ Exception thrown by the Yoti Doc Scan client @@ -13,7 +16,11 @@ def __init__(self, message, response): """ Exception.__init__(self) - self.__message = message + response_message = self.__format_response_message(response) + + self.__message = message + ( + " - " + response_message if response_message else "" + ) self.__response = response @property @@ -56,5 +63,32 @@ def content(self): """ return self.__response.content + def __format_response_message(self, response): + """ + Return the formatted response message + + :return: the formatted message + :rtype: string or None + """ + if response.headers.get("Content-Type") != "application/json": + return None + + parsed = json.loads(response.text) + + if not parsed.get("code") or not parsed.get("message"): + return None + + code_message = parsed.get("code") + " - " + parsed.get("message") + + errors = [] + for error in parsed.get("errors", []): + if error.get("property") and error.get("message"): + errors.append(error.get("property") + ' "' + error.get("message") + '"') + + if len(errors) > 0: + return code_message + ": " + ", ".join(errors) + + return code_message + def __str__(self): return self.__message diff --git a/yoti_python_sdk/tests/doc_scan/exception/__init__.py b/yoti_python_sdk/tests/doc_scan/exception/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/yoti_python_sdk/tests/doc_scan/exception/test_doc_scan_exception.py b/yoti_python_sdk/tests/doc_scan/exception/test_doc_scan_exception.py new file mode 100644 index 00000000..aad28b36 --- /dev/null +++ b/yoti_python_sdk/tests/doc_scan/exception/test_doc_scan_exception.py @@ -0,0 +1,90 @@ +import json + +from yoti_python_sdk.doc_scan.exception import DocScanException +from yoti_python_sdk.tests.mocks import MockResponse + + +def test_return_message(): + response = MockResponse(status_code=400, text="some response") + exception = DocScanException("some error", response) + + assert exception.message == "some error" + + +def test_return_only_message_when_html_response(): + response = MockResponse( + status_code=400, + text="some html", + headers={"Content-Type": "text/html"}, + ) + exception = DocScanException("some error", response) + + assert exception.message == "some error" + + +def test_return_only_message_when_json_response_has_no_message_property(): + response = MockResponse( + status_code=400, + text=json.dumps({}), + headers={"Content-Type": "application/json"}, + ) + exception = DocScanException("some error", response) + + assert exception.message == "some error" + + +def test_return_formatted_response_code_and_message(): + response = MockResponse( + status_code=400, + text=json.dumps({"code": "SOME_CODE", "message": "some message"}), + headers={"Content-Type": "application/json"}, + ) + exception = DocScanException("some error", response) + + assert exception.message == "some error - SOME_CODE - some message" + + +def test_return_formatted_response_code_message_and_errors(): + response = MockResponse( + status_code=400, + text=json.dumps( + { + "code": "SOME_CODE", + "message": "some message", + "errors": [ + {"property": "some.property", "message": "some message"}, + { + "property": "some.other.property", + "message": "some other message", + }, + ], + } + ), + headers={"Content-Type": "application/json"}, + ) + exception = DocScanException("some error", response) + + assert ( + exception.message + == 'some error - SOME_CODE - some message: some.property "some message", some.other.property "some other message"' + ) + + +def test_excludes_errors_without_property_or_message(): + response = MockResponse( + status_code=400, + text=json.dumps( + { + "code": "SOME_CODE", + "message": "some message", + "errors": [ + {"message": "some message"}, + {"property": "some.other.property"}, + ], + } + ), + headers={"Content-Type": "application/json"}, + ) + exception = DocScanException("some error", response) + + assert exception.message == "some error - SOME_CODE - some message" diff --git a/yoti_python_sdk/tests/mocks.py b/yoti_python_sdk/tests/mocks.py index 40cdab6c..a1981157 100644 --- a/yoti_python_sdk/tests/mocks.py +++ b/yoti_python_sdk/tests/mocks.py @@ -1,5 +1,6 @@ import base64 from uuid import UUID +from requests.structures import CaseInsensitiveDict from yoti_python_sdk.http import RequestHandler from yoti_python_sdk.http import SignedRequest @@ -11,7 +12,9 @@ def __init__(self, status_code, text, headers=None, content=None): if headers is None: headers = dict() - super(MockResponse, self).__init__(status_code, text, headers, content) + super(MockResponse, self).__init__( + status_code, text, CaseInsensitiveDict(headers), content + ) class MockRequestHandler(RequestHandler): From 737839a8eb2cf3cebeedf59f56868a6cad777beb Mon Sep 17 00:00:00 2001 From: David Grayston Date: Fri, 18 Sep 2020 14:15:26 +0100 Subject: [PATCH 22/35] SDK-1582: Split format message method --- .../doc_scan/exception/doc_scan_exception.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/yoti_python_sdk/doc_scan/exception/doc_scan_exception.py b/yoti_python_sdk/doc_scan/exception/doc_scan_exception.py index 64578451..f3e4e26e 100644 --- a/yoti_python_sdk/doc_scan/exception/doc_scan_exception.py +++ b/yoti_python_sdk/doc_scan/exception/doc_scan_exception.py @@ -16,7 +16,7 @@ def __init__(self, message, response): """ Exception.__init__(self) - response_message = self.__format_response_message(response) + response_message = self.__get_response_message(response) self.__message = message + ( " - " + response_message if response_message else "" @@ -63,18 +63,25 @@ def content(self): """ return self.__response.content - def __format_response_message(self, response): + def __get_response_message(self, response): """ Return the formatted response message :return: the formatted message :rtype: string or None """ - if response.headers.get("Content-Type") != "application/json": - return None + if response.headers.get("Content-Type") == "application/json": + return self.__format_json_response_message(json.loads(response.text)) + + return None - parsed = json.loads(response.text) + def __format_json_response_message(self, parsed): + """ + Return the formatted JSON response message + :return: the formatted message + :rtype: string or None + """ if not parsed.get("code") or not parsed.get("message"): return None From 16a066502c463ed748acaae12cd960c85e19b9b8 Mon Sep 17 00:00:00 2001 From: echarrod Date: Thu, 10 Sep 2020 08:57:00 +0100 Subject: [PATCH 23/35] SDK-1718: Add Document Comparison check, remove instructions for running tests: this is in CONTRIBUTING.md --- README.md | 11 ----- examples/doc_scan/README.md | 2 +- examples/doc_scan/app.py | 26 +++++++++- examples/doc_scan/templates/success.html | 23 +++++++++ setup.py | 2 +- yoti_python_sdk/doc_scan/__init__.py | 4 ++ yoti_python_sdk/doc_scan/constants.py | 1 + .../doc_scan/session/create/check/__init__.py | 2 + .../create/check/document_comparison.py | 47 +++++++++++++++++++ .../session/retrieve/check_response.py | 12 ++++- .../session/retrieve/get_session_result.py | 12 +++++ ...est_requested_document_comparison_check.py | 33 +++++++++++++ .../retrieve/test_get_session_result.py | 10 +++- 13 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 yoti_python_sdk/doc_scan/session/create/check/document_comparison.py create mode 100644 yoti_python_sdk/tests/doc_scan/session/create/check/test_requested_document_comparison_check.py diff --git a/README.md b/README.md index 4579db70..9c054599 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,6 @@ How to manage users 1) [Running the examples](#running-the-examples) - How to retrieve a Yoti profile using the token -1) [Running the tests](#running-the-tests) - -Running tests for SDK example - 1) [API Coverage](#api-coverage) - Attributes defined @@ -196,14 +193,6 @@ To run the Flask or Django container: * [Profile - Flask](examples/yoti_example_flask) * [Doc Scan](examples/doc_scan) -## Running the Tests - -Running the tests is done by the following process, ensuring you are using Python 3.0+: - -1. Install dependencies: `pip install -r requirements.txt` -1. Install the SDK and development dependencies: `pip install .[dev]` -1. Execute in the main project dir: `pytest` - ## API Coverage * Activity Details diff --git a/examples/doc_scan/README.md b/examples/doc_scan/README.md index 9bb59371..0ac37ee4 100644 --- a/examples/doc_scan/README.md +++ b/examples/doc_scan/README.md @@ -5,4 +5,4 @@ 1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values 1. Install the dependencies with `pip install -r requirements.txt` 1. Start the server `flask run --cert=adhoc` -1. Visit `https://localhost:5000` \ No newline at end of file +1. Visit `https://localhost:5000` diff --git a/examples/doc_scan/app.py b/examples/doc_scan/app.py index 612bffa2..54d36f53 100644 --- a/examples/doc_scan/app.py +++ b/examples/doc_scan/app.py @@ -4,12 +4,18 @@ DocScanClient, RequestedDocumentAuthenticityCheckBuilder, RequestedFaceMatchCheckBuilder, + RequestedIDDocumentComparisonCheckBuilder, RequestedLivenessCheckBuilder, RequestedTextExtractionTaskBuilder, SdkConfigBuilder, SessionSpecBuilder, ) from yoti_python_sdk.doc_scan.exception import DocScanException +from yoti_python_sdk.doc_scan.session.create.filter import ( + RequiredIdDocumentBuilder, + DocumentRestrictionBuilder, + DocumentRestrictionsFilterBuilder, +) from .settings import YOTI_APP_BASE_URL, YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH @@ -52,18 +58,34 @@ def create_session(): .build() ) .with_requested_check( - RequestedFaceMatchCheckBuilder().with_manual_check_fallback().build() + RequestedFaceMatchCheckBuilder().with_manual_check_never().build() ) + .with_requested_check(RequestedIDDocumentComparisonCheckBuilder().build()) .with_requested_task( - RequestedTextExtractionTaskBuilder().with_manual_check_always().build() + RequestedTextExtractionTaskBuilder().with_manual_check_never().build() ) .with_sdk_config(sdk_config) + .with_required_document(build_required_id_document_restriction("PASSPORT")) + .with_required_document( + build_required_id_document_restriction("DRIVING_LICENCE") + ) .build() ) return doc_scan_client.create_session(session_spec) +def build_required_id_document_restriction(document_type): + document_restriction = ( + DocumentRestrictionBuilder().with_document_types([document_type]).build() + ) + + filter_builder = DocumentRestrictionsFilterBuilder().for_whitelist() + filter_builder.with_document_restriction(document_restriction) + + return RequiredIdDocumentBuilder().with_filter(filter_builder.build()).build() + + @app.route("/") def index(): try: diff --git a/examples/doc_scan/templates/success.html b/examples/doc_scan/templates/success.html index 1428cb9f..d7afae86 100644 --- a/examples/doc_scan/templates/success.html +++ b/examples/doc_scan/templates/success.html @@ -134,6 +134,29 @@

{% endif %} + + {% if session_result.id_document_comparison_checks|length > 0 %} +
+
+

+ +

+
+
+
+ {% for check in session_result.id_document_comparison_checks %} + {% with check=check %} + {% include "partials/check.html" %} + {% endwith %} + {% endfor %} +
+
+
+ {% endif %} diff --git a/setup.py b/setup.py index ac6be3b9..516047fe 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ ], "dev": [ "pre-commit==1.17.0", - "pytest>=4.6.0", + "pytest>=4.6.11", "pytest-cov>=2.7.1", "pylint==1.9.4", "pylint-exit>=1.1.0", diff --git a/yoti_python_sdk/doc_scan/__init__.py b/yoti_python_sdk/doc_scan/__init__.py index c4abde59..0d6318a1 100644 --- a/yoti_python_sdk/doc_scan/__init__.py +++ b/yoti_python_sdk/doc_scan/__init__.py @@ -1,6 +1,9 @@ from .session.create.check.document_authenticity import ( RequestedDocumentAuthenticityCheckBuilder, ) +from .session.create.check.document_comparison import ( + RequestedIDDocumentComparisonCheckBuilder, +) from .session.create.check.face_match import RequestedFaceMatchCheckBuilder from .session.create.check.liveness import RequestedLivenessCheckBuilder from .session.create.task.text_extraction import RequestedTextExtractionTaskBuilder @@ -13,6 +16,7 @@ RequestedDocumentAuthenticityCheckBuilder, RequestedLivenessCheckBuilder, RequestedFaceMatchCheckBuilder, + RequestedIDDocumentComparisonCheckBuilder, RequestedTextExtractionTaskBuilder, SessionSpecBuilder, NotificationConfigBuilder, diff --git a/yoti_python_sdk/doc_scan/constants.py b/yoti_python_sdk/doc_scan/constants.py index 9cdd96c8..0f4a4bab 100644 --- a/yoti_python_sdk/doc_scan/constants.py +++ b/yoti_python_sdk/doc_scan/constants.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals ID_DOCUMENT_AUTHENTICITY = "ID_DOCUMENT_AUTHENTICITY" +ID_DOCUMENT_COMPARISON = "ID_DOCUMENT_COMPARISON" 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" diff --git a/yoti_python_sdk/doc_scan/session/create/check/__init__.py b/yoti_python_sdk/doc_scan/session/create/check/__init__.py index 3f14e174..cca92358 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/__init__.py +++ b/yoti_python_sdk/doc_scan/session/create/check/__init__.py @@ -1,9 +1,11 @@ from .document_authenticity import RequestedDocumentAuthenticityCheckBuilder +from .document_comparison import RequestedIDDocumentComparisonCheckBuilder from .face_match import RequestedFaceMatchCheckBuilder from .liveness import RequestedLivenessCheckBuilder __all__ = [ RequestedDocumentAuthenticityCheckBuilder, + RequestedIDDocumentComparisonCheckBuilder, RequestedFaceMatchCheckBuilder, RequestedLivenessCheckBuilder, ] diff --git a/yoti_python_sdk/doc_scan/session/create/check/document_comparison.py b/yoti_python_sdk/doc_scan/session/create/check/document_comparison.py new file mode 100644 index 00000000..f7dd43fe --- /dev/null +++ b/yoti_python_sdk/doc_scan/session/create/check/document_comparison.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.doc_scan.constants import ID_DOCUMENT_COMPARISON +from yoti_python_sdk.utils import YotiSerializable +from .requested_check import RequestedCheck + + +class RequestedIDDocumentComparisonCheckConfig(YotiSerializable): + """ + The configuration applied when creating a Document Comparison Check + """ + + def to_json(self): + return {} + + +class RequestedIDDocumentComparisonCheck(RequestedCheck): + """ + Requests creation of a Document Comparison Check + """ + + def __init__(self, config): + """ + :param config: the requested document Comparison check configuration + :type config: RequestedIDDocumentComparisonCheckConfig + """ + self.__config = config + + @property + def type(self): + return ID_DOCUMENT_COMPARISON + + @property + def config(self): + return self.__config + + +class RequestedIDDocumentComparisonCheckBuilder(object): + """ + Builder to assist creation of :class:`RequestedIDDocumentComparisonCheck` + """ + + @staticmethod + def build(): + config = RequestedIDDocumentComparisonCheckConfig() + return RequestedIDDocumentComparisonCheck(config) diff --git a/yoti_python_sdk/doc_scan/session/retrieve/check_response.py b/yoti_python_sdk/doc_scan/session/retrieve/check_response.py index c5660ab0..52f78388 100644 --- a/yoti_python_sdk/doc_scan/session/retrieve/check_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/check_response.py @@ -148,7 +148,7 @@ class AuthenticityCheckResponse(CheckResponse): class FaceMatchCheckResponse(CheckResponse): """ - Represents a FaceMatch Check for a given session + Represents a FaceMatch check for a given session """ pass @@ -156,7 +156,7 @@ class FaceMatchCheckResponse(CheckResponse): class LivenessCheckResponse(CheckResponse): """ - Represents a Liveness Check for a given session + Represents a Liveness check for a given session """ pass @@ -168,3 +168,11 @@ class TextDataCheckResponse(CheckResponse): """ pass + + +class IDDocumentComparisonCheckResponse(CheckResponse): + """ + Represents an Identity Document Comparison check for a given session + """ + + pass diff --git a/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py b/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py index 7405b3e5..f7e735f0 100644 --- a/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py @@ -5,6 +5,7 @@ from .check_response import AuthenticityCheckResponse from .check_response import CheckResponse from .check_response import FaceMatchCheckResponse +from .check_response import IDDocumentComparisonCheckResponse from .check_response import LivenessCheckResponse from .check_response import TextDataCheckResponse from .resource_container import ResourceContainer @@ -48,6 +49,7 @@ def __parse_check(check): constants.ID_DOCUMENT_FACE_MATCH: FaceMatchCheckResponse, constants.ID_DOCUMENT_TEXT_DATA_CHECK: TextDataCheckResponse, constants.LIVENESS: LivenessCheckResponse, + constants.ID_DOCUMENT_COMPARISON: IDDocumentComparisonCheckResponse, } clazz = types.get(check.get("type", None), CheckResponse) return clazz(check) @@ -163,6 +165,16 @@ def liveness_checks(self): """ return self.__checks_of_type((LivenessCheckResponse,)) + @property + def id_document_comparison_checks(self): + """ + A filtered list of checks, returning only Identity Document Comparison checks + + :return: the Identity Document Comparison checks + :rtype: list[IDDocumentComparisonCheckResponse] + """ + return self.__checks_of_type((IDDocumentComparisonCheckResponse,)) + @property def resources(self): """ diff --git a/yoti_python_sdk/tests/doc_scan/session/create/check/test_requested_document_comparison_check.py b/yoti_python_sdk/tests/doc_scan/session/create/check/test_requested_document_comparison_check.py new file mode 100644 index 00000000..4b853bcf --- /dev/null +++ b/yoti_python_sdk/tests/doc_scan/session/create/check/test_requested_document_comparison_check.py @@ -0,0 +1,33 @@ +import json +import unittest + +from yoti_python_sdk.doc_scan.session.create.check import ( + RequestedIDDocumentComparisonCheckBuilder, +) +from yoti_python_sdk.doc_scan.session.create.check.document_comparison import ( + RequestedIDDocumentComparisonCheck, +) +from yoti_python_sdk.doc_scan.session.create.check.document_comparison import ( + RequestedIDDocumentComparisonCheckConfig, +) +from yoti_python_sdk.doc_scan.session.create.check.document_comparison import ( + RequestedIDDocumentComparisonCheckBuilder, +) +from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck +from yoti_python_sdk.utils import YotiEncoder + + +def test_should_build_correctly(): + result = RequestedIDDocumentComparisonCheckBuilder().build() + + assert isinstance(result, RequestedCheck) + assert isinstance(result, RequestedIDDocumentComparisonCheck) + assert isinstance(result.config, RequestedIDDocumentComparisonCheckConfig) + assert result.type == "ID_DOCUMENT_COMPARISON" + + +def test_should_serialize_to_json_without_error(): + result = RequestedIDDocumentComparisonCheckBuilder().build() + + s = json.dumps(result, cls=YotiEncoder) + assert s is not None and s != "" diff --git a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py index 0b259771..92b34f97 100644 --- a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py @@ -9,6 +9,9 @@ from yoti_python_sdk.doc_scan.session.retrieve.check_response import ( LivenessCheckResponse, ) +from yoti_python_sdk.doc_scan.session.retrieve.check_response import ( + IDDocumentComparisonCheckResponse, +) from yoti_python_sdk.doc_scan.session.retrieve.check_response import ( TextDataCheckResponse, ) @@ -31,6 +34,7 @@ class GetSessionResultTest(unittest.TestCase): {"type": "ID_DOCUMENT_TEXT_DATA_CHECK"}, {"type": "ID_DOCUMENT_FACE_MATCH"}, {"type": "LIVENESS"}, + {"type": "ID_DOCUMENT_COMPARISON"}, ] def test_should_parse_different_checks(self): @@ -52,11 +56,12 @@ def test_should_parse_different_checks(self): assert result.state is self.SOME_STATE assert result.user_tracking_id is self.SOME_USER_TRACKING_ID - assert len(result.checks) == 4 + assert len(result.checks) == 5 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.checks[4], IDDocumentComparisonCheckResponse) assert isinstance(result.resources, ResourceContainer) @@ -65,11 +70,12 @@ def test_should_filter_checks(self): result = GetSessionResult(data) - assert len(result.checks) == 4 + assert len(result.checks) == 5 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 + assert len(result.id_document_comparison_checks) == 1 if __name__ == "__main__": From 753e675882687abd31fa634e13ef9d8c83480c3a Mon Sep 17 00:00:00 2001 From: echarrod Date: Wed, 16 Sep 2020 12:12:21 +0100 Subject: [PATCH 24/35] NA: Only strings should be listed in "__all__" https://docs.python.org/3/tutorial/modules.html#importing-from-a-package --- yoti_python_sdk/doc_scan/__init__.py | 18 +++++++++--------- yoti_python_sdk/doc_scan/exception/__init__.py | 2 +- .../doc_scan/session/create/__init__.py | 12 ++++++------ .../doc_scan/session/create/check/__init__.py | 8 ++++---- .../doc_scan/session/create/filter/__init__.py | 8 ++++---- .../doc_scan/session/create/task/__init__.py | 2 +- yoti_python_sdk/doc_scan/support/__init__.py | 2 +- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/yoti_python_sdk/doc_scan/__init__.py b/yoti_python_sdk/doc_scan/__init__.py index 0d6318a1..fc9d5733 100644 --- a/yoti_python_sdk/doc_scan/__init__.py +++ b/yoti_python_sdk/doc_scan/__init__.py @@ -13,13 +13,13 @@ from .client import DocScanClient __all__ = [ - RequestedDocumentAuthenticityCheckBuilder, - RequestedLivenessCheckBuilder, - RequestedFaceMatchCheckBuilder, - RequestedIDDocumentComparisonCheckBuilder, - RequestedTextExtractionTaskBuilder, - SessionSpecBuilder, - NotificationConfigBuilder, - SdkConfigBuilder, - DocScanClient, + "RequestedDocumentAuthenticityCheckBuilder", + "RequestedLivenessCheckBuilder", + "RequestedFaceMatchCheckBuilder", + "RequestedIDDocumentComparisonCheckBuilder", + "RequestedTextExtractionTaskBuilder", + "SessionSpecBuilder", + "NotificationConfigBuilder", + "SdkConfigBuilder", + "DocScanClient", ] diff --git a/yoti_python_sdk/doc_scan/exception/__init__.py b/yoti_python_sdk/doc_scan/exception/__init__.py index aab9483e..b6a4424e 100644 --- a/yoti_python_sdk/doc_scan/exception/__init__.py +++ b/yoti_python_sdk/doc_scan/exception/__init__.py @@ -1,3 +1,3 @@ from .doc_scan_exception import DocScanException -__all__ = [DocScanException] +__all__ = ["DocScanException"] diff --git a/yoti_python_sdk/doc_scan/session/create/__init__.py b/yoti_python_sdk/doc_scan/session/create/__init__.py index 7b7896e6..eb0f1857 100644 --- a/yoti_python_sdk/doc_scan/session/create/__init__.py +++ b/yoti_python_sdk/doc_scan/session/create/__init__.py @@ -6,10 +6,10 @@ from .sdk_config import SdkConfigBuilder __all__ = [ - RequestedDocumentAuthenticityCheckBuilder, - RequestedFaceMatchCheckBuilder, - RequestedLivenessCheckBuilder, - NotificationConfigBuilder, - SessionSpecBuilder, - SdkConfigBuilder, + "RequestedDocumentAuthenticityCheckBuilder", + "RequestedFaceMatchCheckBuilder", + "RequestedLivenessCheckBuilder", + "NotificationConfigBuilder", + "SessionSpecBuilder", + "SdkConfigBuilder", ] diff --git a/yoti_python_sdk/doc_scan/session/create/check/__init__.py b/yoti_python_sdk/doc_scan/session/create/check/__init__.py index cca92358..c0c38292 100644 --- a/yoti_python_sdk/doc_scan/session/create/check/__init__.py +++ b/yoti_python_sdk/doc_scan/session/create/check/__init__.py @@ -4,8 +4,8 @@ from .liveness import RequestedLivenessCheckBuilder __all__ = [ - RequestedDocumentAuthenticityCheckBuilder, - RequestedIDDocumentComparisonCheckBuilder, - RequestedFaceMatchCheckBuilder, - RequestedLivenessCheckBuilder, + "RequestedDocumentAuthenticityCheckBuilder", + "RequestedIDDocumentComparisonCheckBuilder", + "RequestedFaceMatchCheckBuilder", + "RequestedLivenessCheckBuilder", ] diff --git a/yoti_python_sdk/doc_scan/session/create/filter/__init__.py b/yoti_python_sdk/doc_scan/session/create/filter/__init__.py index 222e9083..e1a088e6 100644 --- a/yoti_python_sdk/doc_scan/session/create/filter/__init__.py +++ b/yoti_python_sdk/doc_scan/session/create/filter/__init__.py @@ -6,8 +6,8 @@ from .required_id_document import RequiredIdDocumentBuilder __all__ = [ - DocumentRestrictionsFilterBuilder, - DocumentRestrictionBuilder, - OrthogonalRestrictionsFilterBuilder, - RequiredIdDocumentBuilder, + "DocumentRestrictionsFilterBuilder", + "DocumentRestrictionBuilder", + "OrthogonalRestrictionsFilterBuilder", + "RequiredIdDocumentBuilder", ] diff --git a/yoti_python_sdk/doc_scan/session/create/task/__init__.py b/yoti_python_sdk/doc_scan/session/create/task/__init__.py index 44755304..6ad73b42 100644 --- a/yoti_python_sdk/doc_scan/session/create/task/__init__.py +++ b/yoti_python_sdk/doc_scan/session/create/task/__init__.py @@ -1,3 +1,3 @@ from .text_extraction import RequestedTextExtractionTaskBuilder -__all__ = [RequestedTextExtractionTaskBuilder] +__all__ = ["RequestedTextExtractionTaskBuilder"] diff --git a/yoti_python_sdk/doc_scan/support/__init__.py b/yoti_python_sdk/doc_scan/support/__init__.py index eba661aa..a3f18751 100644 --- a/yoti_python_sdk/doc_scan/support/__init__.py +++ b/yoti_python_sdk/doc_scan/support/__init__.py @@ -1,3 +1,3 @@ from .supported_documents import SupportedDocumentsResponse -__all__ = [SupportedDocumentsResponse] +__all__ = ["SupportedDocumentsResponse"] From c75627cfe99fa768304277c0f4d0aae09bc1f634 Mon Sep 17 00:00:00 2001 From: nikhilPank Date: Mon, 21 Sep 2020 16:27:39 +0100 Subject: [PATCH 25/35] IN-3118: python Example QR --- .../yoti_example_django/yoti_example/static/index.css | 10 +++++----- .../yoti_example/templates/index.html | 8 ++++++-- examples/yoti_example_flask/static/index.css | 10 +++++----- examples/yoti_example_flask/templates/index.html | 8 ++++++-- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/examples/yoti_example_django/yoti_example/static/index.css b/examples/yoti_example_django/yoti_example/static/index.css index 979e3575..66bb6b61 100644 --- a/examples/yoti_example_django/yoti_example/static/index.css +++ b/examples/yoti_example_django/yoti_example/static/index.css @@ -26,8 +26,8 @@ font-size: 40px; font-weight: 700; line-height: 1.2; - - margin: 0; + margin-top: 0; + margin-bottom: 80px; text-align: center; @@ -45,8 +45,8 @@ } #yoti-share-button { - width: 130px; - height: 50px; + width: 250px; + height: 45px; } .yoti-login-or-separator { @@ -56,7 +56,7 @@ font-weight: bold; line-height: 1.5; text-align: center; - margin-bottom: 30px; + margin-top: 30px; } .yoti-login-dialog { diff --git a/examples/yoti_example_django/yoti_example/templates/index.html b/examples/yoti_example_django/yoti_example/templates/index.html index 26070ac6..f4ca812d 100644 --- a/examples/yoti_example_django/yoti_example/templates/index.html +++ b/examples/yoti_example_django/yoti_example/templates/index.html @@ -73,8 +73,12 @@

The Yoti app is free to download and use: diff --git a/examples/yoti_example_flask/static/index.css b/examples/yoti_example_flask/static/index.css index 979e3575..66bb6b61 100644 --- a/examples/yoti_example_flask/static/index.css +++ b/examples/yoti_example_flask/static/index.css @@ -26,8 +26,8 @@ font-size: 40px; font-weight: 700; line-height: 1.2; - - margin: 0; + margin-top: 0; + margin-bottom: 80px; text-align: center; @@ -45,8 +45,8 @@ } #yoti-share-button { - width: 130px; - height: 50px; + width: 250px; + height: 45px; } .yoti-login-or-separator { @@ -56,7 +56,7 @@ font-weight: bold; line-height: 1.5; text-align: center; - margin-bottom: 30px; + margin-top: 30px; } .yoti-login-dialog { diff --git a/examples/yoti_example_flask/templates/index.html b/examples/yoti_example_flask/templates/index.html index 26070ac6..f4ca812d 100644 --- a/examples/yoti_example_flask/templates/index.html +++ b/examples/yoti_example_flask/templates/index.html @@ -73,8 +73,12 @@

The Yoti app is free to download and use: From 3215a53e17c60a56cf2c2044df8051359e9ec8f1 Mon Sep 17 00:00:00 2001 From: nikhilPank Date: Thu, 24 Sep 2020 12:47:42 +0100 Subject: [PATCH 26/35] update dynamic share button config --- .../yoti_example/templates/dynamic-share.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/yoti_example_django/yoti_example/templates/dynamic-share.html b/examples/yoti_example_django/yoti_example/templates/dynamic-share.html index 38799e85..76c7921e 100644 --- a/examples/yoti_example_django/yoti_example/templates/dynamic-share.html +++ b/examples/yoti_example_django/yoti_example/templates/dynamic-share.html @@ -70,8 +70,12 @@

The Yoti app is free to download and use: From fc5975b674e895716f8199df3c99d2e83cf4de85 Mon Sep 17 00:00:00 2001 From: nikhilPank Date: Thu, 24 Sep 2020 12:48:22 +0100 Subject: [PATCH 27/35] update dynamic share button config --- examples/yoti_example_flask/templates/dynamic-share.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/yoti_example_flask/templates/dynamic-share.html b/examples/yoti_example_flask/templates/dynamic-share.html index 38799e85..76c7921e 100644 --- a/examples/yoti_example_flask/templates/dynamic-share.html +++ b/examples/yoti_example_flask/templates/dynamic-share.html @@ -70,8 +70,12 @@

The Yoti app is free to download and use: From d2eee0bfc903a2c1b2945aa50b1bf88929b896ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2020 05:27:02 +0000 Subject: [PATCH 28/35] Bump virtualenv from 20.0.31 to 20.0.32 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.0.31 to 20.0.32. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/20.0.32/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.0.31...20.0.32) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 516047fe..9b9238b3 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "python-coveralls==2.9.3", "coverage==4.5.4", "mock==2.0.0", - "virtualenv==20.0.31", + "virtualenv==20.0.32", ], }, classifiers=[ From 9c4b915640a5d3395fd1f094a588febac70b1fe9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2020 05:52:49 +0000 Subject: [PATCH 29/35] Bump virtualenv from 20.0.32 to 20.0.33 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.0.32 to 20.0.33. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.0.32...20.0.33) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9b9238b3..6ea005f6 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ "python-coveralls==2.9.3", "coverage==4.5.4", "mock==2.0.0", - "virtualenv==20.0.32", + "virtualenv==20.0.33", ], }, classifiers=[ From 0a8a5fc97dd636f23c37ee8a92ee67230b1d5e0c Mon Sep 17 00:00:00 2001 From: David Grayston Date: Fri, 2 Oct 2020 11:06:17 +0100 Subject: [PATCH 30/35] SDK-1732: Allow manual check to be configured on authenticity checks --- examples/doc_scan/app.py | 6 +- .../create/check/document_authenticity.py | 63 ++++++++++++++++--- ...t_requested_document_authenticity_check.py | 27 ++++++++ 3 files changed, 88 insertions(+), 8 deletions(-) diff --git a/examples/doc_scan/app.py b/examples/doc_scan/app.py index 54d36f53..1e8873b6 100644 --- a/examples/doc_scan/app.py +++ b/examples/doc_scan/app.py @@ -50,7 +50,11 @@ def create_session(): .with_client_session_token_ttl(600) .with_resources_ttl(90000) .with_user_tracking_id("some-user-tracking-id") - .with_requested_check(RequestedDocumentAuthenticityCheckBuilder().build()) + .with_requested_check( + RequestedDocumentAuthenticityCheckBuilder() + .with_manual_check_never() + .build() + ) .with_requested_check( RequestedLivenessCheckBuilder() .for_zoom_liveness() diff --git a/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py b/yoti_python_sdk/doc_scan/session/create/check/document_authenticity.py index 69fdc40c..43926725 100644 --- a/yoti_python_sdk/doc_scan/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.doc_scan.constants import ID_DOCUMENT_AUTHENTICITY +from yoti_python_sdk.doc_scan import constants from yoti_python_sdk.utils import YotiSerializable, remove_null_values from .requested_check import RequestedCheck @@ -11,8 +11,26 @@ class RequestedDocumentAuthenticityCheckConfig(YotiSerializable): The configuration applied when creating a Document Authenticity Check """ + def __init__(self, manual_check=None): + """ + :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 + Authenticity Check + + :return: the manual check value + :rtype: str + """ + return self.__manual_check + def to_json(self): - return remove_null_values({}) + return remove_null_values({"manual_check": self.manual_check}) class RequestedDocumentAuthenticityCheck(RequestedCheck): @@ -29,19 +47,50 @@ def __init__(self, config): @property def type(self): - return ID_DOCUMENT_AUTHENTICITY + return constants.ID_DOCUMENT_AUTHENTICITY @property def config(self): return self.__config -class RequestedDocumentAuthenticityCheckBuilder(object): +class RequestedDocumentAuthenticityCheckBuilder: """ Builder to assist creation of :class:`RequestedDocumentAuthenticityCheck` """ - @staticmethod - def build(): - config = RequestedDocumentAuthenticityCheckConfig() + def __init__(self): + self.__manual_check = None + + def with_manual_check_always(self): + """ + :return: the builder + :rtype: RequestedDocumentAuthenticityCheckBuilder + """ + self.__manual_check = constants.ALWAYS + return self + + def with_manual_check_fallback(self): + """ + :return: the builder + :rtype: RequestedDocumentAuthenticityCheckBuilder + """ + self.__manual_check = constants.FALLBACK + return self + + def with_manual_check_never(self): + """ + :return: the builder + :rtype: RequestedDocumentAuthenticityCheckBuilder + """ + self.__manual_check = constants.NEVER + return self + + def build(self=None): + if self is None: + manual_check = None + else: + manual_check = self.__manual_check + + config = RequestedDocumentAuthenticityCheckConfig(manual_check) return RequestedDocumentAuthenticityCheck(config) diff --git a/yoti_python_sdk/tests/doc_scan/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 index ae0d0596..404f0bfe 100644 --- a/yoti_python_sdk/tests/doc_scan/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 @@ -29,6 +29,33 @@ def test_should_serialize_to_json_without_error(self): s = json.dumps(result, cls=YotiEncoder) assert s is not None and s != "" + def test_should_build_with_manual_check_always(self): + result = ( + RequestedDocumentAuthenticityCheckBuilder() + .with_manual_check_always() + .build() + ) + + assert result.config.manual_check == "ALWAYS" + + def test_should_build_with_manual_check_fallback(self): + result = ( + RequestedDocumentAuthenticityCheckBuilder() + .with_manual_check_fallback() + .build() + ) + + assert result.config.manual_check == "FALLBACK" + + def test_should_build_with_manual_check_never(self): + result = ( + RequestedDocumentAuthenticityCheckBuilder() + .with_manual_check_never() + .build() + ) + + assert result.config.manual_check == "NEVER" + if __name__ == "__main__": unittest.main() From ed5cd62e7738d893bcc465801081127b8f58be2f Mon Sep 17 00:00:00 2001 From: David Grayston Date: Mon, 5 Oct 2020 17:49:54 +0100 Subject: [PATCH 31/35] SDK-1739: Add biometric consent support --- .../doc_scan/session/create/session_spec.py | 29 +++++++++++++++ .../session/retrieve/get_session_result.py | 37 +++++++++++++++++++ .../session/create/test_session_spec.py | 15 ++++++++ .../retrieve/test_get_session_result.py | 23 ++++++++++++ 4 files changed, 104 insertions(+) diff --git a/yoti_python_sdk/doc_scan/session/create/session_spec.py b/yoti_python_sdk/doc_scan/session/create/session_spec.py index 3b648644..ef3bbce4 100644 --- a/yoti_python_sdk/doc_scan/session/create/session_spec.py +++ b/yoti_python_sdk/doc_scan/session/create/session_spec.py @@ -20,6 +20,7 @@ def __init__( requested_checks=None, requested_tasks=None, required_documents=None, + block_biometric_consent=None, ): """ :param client_session_token_ttl: the client session token TTL @@ -38,6 +39,8 @@ def __init__( :type requested_tasks: list[RequestedTask] or None :param required_documents: the list of required documents :type required_documents: list[RequiredDocument] or None + :param block_biometric_consent: block the collection of biometric consent + :type block_biometric_consent: bool """ if requested_tasks is None: requested_tasks = [] @@ -54,6 +57,7 @@ def __init__( self.__requested_checks = requested_checks self.__requested_tasks = requested_tasks self.__required_documents = required_documents + self.__block_biometric_consent = block_biometric_consent @property def client_session_token_ttl(self): @@ -138,6 +142,16 @@ def required_documents(self): """ return self.__required_documents + @property + def block_biometric_consent(self): + """ + Whether or not to block the collection of biometric consent. + + :return: block biometric consent + :rtype: bool + """ + return self.__block_biometric_consent + def to_json(self): return remove_null_values( { @@ -149,6 +163,7 @@ def to_json(self): "requested_tasks": self.requested_tasks, "sdk_config": self.sdk_config, "required_documents": self.required_documents, + "block_biometric_consent": self.block_biometric_consent, } ) @@ -167,6 +182,7 @@ def __init__(self): self.__requested_checks = [] self.__requested_tasks = [] self.__required_documents = [] + self.__block_biometric_consent = None def with_client_session_token_ttl(self, value): """ @@ -264,6 +280,18 @@ def with_required_document(self, required_document): self.__required_documents.append(required_document) return self + def with_block_biometric_consent(self, block_biometric_consent): + """ + Sets whether or not to block the collection of biometric consent + + :param block_biometric_consent: block biometric consent + :type block_biometric_consent: bool + :return: the builder + :rtype: SessionSpecBuilder + """ + self.__block_biometric_consent = block_biometric_consent + return self + def build(self): """ Builds a :class:`SessionSpec` using the supplied values @@ -280,4 +308,5 @@ def build(self): self.__requested_checks, self.__requested_tasks, self.__required_documents, + self.__block_biometric_consent, ) diff --git a/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py b/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py index f7e735f0..71a69a34 100644 --- a/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py @@ -1,6 +1,11 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from iso8601 import ( + ParseError, + iso8601, +) + from yoti_python_sdk.doc_scan import constants from .check_response import AuthenticityCheckResponse from .check_response import CheckResponse @@ -29,10 +34,32 @@ def __init__(self, data): 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", [])] + self.__biometric_consent_timestamp = self.__parse_date( + data.get("biometric_consent", None) + ) resources = data.get("resources", None) self.__resources = ResourceContainer(resources) or 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 + @staticmethod def __parse_check(check): """ @@ -184,3 +211,13 @@ def resources(self): :rtype: ResourceContainer or None """ return self.__resources + + @property + def biometric_consent_timestamp(self): + """ + The biometric constent timestamp + + :return: the biometric constent timestamp + :rtype: datetime.datetime or None + """ + return self.__biometric_consent_timestamp diff --git a/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py b/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py index 9130a8ca..41f0a5e5 100644 --- a/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py @@ -104,6 +104,21 @@ def test_should_default_empty_arrays(self): assert len(result.requested_tasks) == 0 assert len(result.required_documents) == 0 + def test_should_build_correctly_with_block_biometric_consent_true(self): + result = SessionSpecBuilder().with_block_biometric_consent(True).build() + + assert result.block_biometric_consent is True + + def test_should_build_correctly_with_block_biometric_consent_false(self): + result = SessionSpecBuilder().with_block_biometric_consent(False).build() + + assert result.block_biometric_consent is False + + def test_should_build_correctly_without_block_biometric_consent_false(self): + result = SessionSpecBuilder().build() + + assert result.block_biometric_consent is None + if __name__ == "__main__": unittest.main() diff --git a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py index 92b34f97..b1634876 100644 --- a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_get_session_result.py @@ -1,5 +1,9 @@ import unittest +from datetime import datetime + +import pytz + from yoti_python_sdk.doc_scan.session.retrieve.check_response import ( AuthenticityCheckResponse, ) @@ -29,6 +33,7 @@ class GetSessionResultTest(unittest.TestCase): SOME_USER_TRACKING_ID = "someUserTrackingId" SOME_STATE = "someState" SOME_CLIENT_SESSION_TOKEN = "someClientSessionToken" + SOME_BIOMETRIC_CONSENT = "2019-05-01T05:01:48.000Z" SOME_CHECKS = [ {"type": "ID_DOCUMENT_AUTHENTICITY"}, {"type": "ID_DOCUMENT_TEXT_DATA_CHECK"}, @@ -37,6 +42,17 @@ class GetSessionResultTest(unittest.TestCase): {"type": "ID_DOCUMENT_COMPARISON"}, ] + EXPECTED_BIOMETRIC_CONSENT_DATETIME = datetime( + year=2019, + month=5, + day=1, + hour=5, + minute=1, + second=48, + microsecond=0, + tzinfo=pytz.utc, + ) + def test_should_parse_different_checks(self): data = { "client_session_token_ttl": self.SOME_CLIENT_SESSION_TOKEN_TTL, @@ -46,6 +62,7 @@ def test_should_parse_different_checks(self): "user_tracking_id": self.SOME_USER_TRACKING_ID, "checks": self.SOME_CHECKS, "resources": {}, + "biometric_consent": self.SOME_BIOMETRIC_CONSENT, } result = GetSessionResult(data) @@ -65,6 +82,12 @@ def test_should_parse_different_checks(self): assert isinstance(result.resources, ResourceContainer) + assert isinstance(result.biometric_consent_timestamp, datetime) + assert ( + result.biometric_consent_timestamp + == self.EXPECTED_BIOMETRIC_CONSENT_DATETIME + ) + def test_should_filter_checks(self): data = {"checks": self.SOME_CHECKS} From 236bc89c8ea4cf739f92b81e177b3d60eb9624df Mon Sep 17 00:00:00 2001 From: David Grayston Date: Mon, 5 Oct 2020 20:27:44 +0100 Subject: [PATCH 32/35] SDK-1739: Add biometric consent timestamp to demo --- examples/doc_scan/templates/success.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/doc_scan/templates/success.html b/examples/doc_scan/templates/success.html index d7afae86..76847b9f 100644 --- a/examples/doc_scan/templates/success.html +++ b/examples/doc_scan/templates/success.html @@ -34,6 +34,12 @@

Get Session Result

User Tracking ID {{ session_result.user_tracking_id }} + {% if session_result.biometric_consent_timestamp is not none %} + + Biometric Consent Timestamp + {{ session_result.biometric_consent_timestamp }} + + {% endif %} From 8149586280aa6ef88a45ccf718bbb88f96fed80e Mon Sep 17 00:00:00 2001 From: David Grayston Date: Wed, 7 Oct 2020 11:57:22 +0100 Subject: [PATCH 33/35] SDK-1639: Add support for chip data --- examples/doc_scan/app.py | 5 ++- examples/doc_scan/templates/success.html | 20 +++++++++ yoti_python_sdk/doc_scan/constants.py | 3 ++ .../session/create/task/text_extraction.py | 42 +++++++++++++++++-- .../retrieve/document_id_photo_response.py | 33 +++++++++++++++ .../retrieve/id_document_resource_response.py | 18 ++++++++ .../create/task/test_text_extraction_task.py | 42 ++++++++++++++++++- .../test_document_id_photo_response.py | 22 ++++++++++ .../test_id_document_resource_response.py | 7 ++++ 9 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 yoti_python_sdk/doc_scan/session/retrieve/document_id_photo_response.py create mode 100644 yoti_python_sdk/tests/doc_scan/session/retrieve/test_document_id_photo_response.py diff --git a/examples/doc_scan/app.py b/examples/doc_scan/app.py index 1e8873b6..0a862861 100644 --- a/examples/doc_scan/app.py +++ b/examples/doc_scan/app.py @@ -66,7 +66,10 @@ def create_session(): ) .with_requested_check(RequestedIDDocumentComparisonCheckBuilder().build()) .with_requested_task( - RequestedTextExtractionTaskBuilder().with_manual_check_never().build() + RequestedTextExtractionTaskBuilder() + .with_manual_check_never() + .with_chip_data_desired() + .build() ) .with_sdk_config(sdk_config) .with_required_document(build_required_id_document_restriction("PASSPORT")) diff --git a/examples/doc_scan/templates/success.html b/examples/doc_scan/templates/success.html index 76847b9f..6aaa0bc2 100644 --- a/examples/doc_scan/templates/success.html +++ b/examples/doc_scan/templates/success.html @@ -222,6 +222,26 @@
Media
{% endif %} + + {% if document.document_id_photo is not none %} +
+
+

+ +

+
+
+
+ {% if document.document_fields.media is not none %} + + {% endif %} +
+
+
+ {% endif %} + {% if document.text_extraction_tasks|length > 0 %}
diff --git a/yoti_python_sdk/doc_scan/constants.py b/yoti_python_sdk/doc_scan/constants.py index 0f4a4bab..b0f724c3 100644 --- a/yoti_python_sdk/doc_scan/constants.py +++ b/yoti_python_sdk/doc_scan/constants.py @@ -26,3 +26,6 @@ ALWAYS = "ALWAYS" FALLBACK = "FALLBACK" NEVER = "NEVER" + +DESIRED = "DESIRED" +IGNORE = "IGNORE" diff --git a/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py b/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py index ebcfdc7a..687b41a7 100644 --- a/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py +++ b/yoti_python_sdk/doc_scan/session/create/task/text_extraction.py @@ -7,12 +7,13 @@ class RequestedTextExtractionTaskConfig(YotiSerializable): - def __init__(self, manual_check): + def __init__(self, manual_check, chip_data=None): """ :param manual_check: the manual check value :type manual_check: str """ self.__manual_check = manual_check + self.__chip_data = chip_data @property def manual_check(self): @@ -23,8 +24,20 @@ def manual_check(self): """ return self.__manual_check + @property + def chip_data(self): + """ + Describes how to use chip data from an ID document if + it is available + + :return: the chip data usage + """ + return self.__chip_data + def to_json(self): - return remove_null_values({"manual_check": self.manual_check}) + return remove_null_values( + {"manual_check": self.manual_check, "chip_data": self.chip_data} + ) class RequestedTextExtractionTask(RequestedTask): @@ -55,6 +68,7 @@ class RequestedTextExtractionTaskBuilder(object): def __init__(self): self.__manual_check = None + self.__chip_data = None def with_manual_check_always(self): """ @@ -86,6 +100,28 @@ def with_manual_check_never(self): self.__manual_check = constants.NEVER return self + def with_chip_data_desired(self): + """ + The TextExtractionTask will use chip data if it is available + + :return: the builder + :rtype: RequestedTextExtractionTaskBuilder + """ + self.__chip_data = constants.DESIRED + return self + + def with_chip_data_ignore(self): + """ + The TextExtractionTask will ignore chip data + + :return: the builder + :rtype: RequestedTextExtractionTaskBuilder + """ + self.__chip_data = constants.IGNORE + return self + def build(self): - config = RequestedTextExtractionTaskConfig(self.__manual_check) + config = RequestedTextExtractionTaskConfig( + self.__manual_check, self.__chip_data + ) return RequestedTextExtractionTask(config) diff --git a/yoti_python_sdk/doc_scan/session/retrieve/document_id_photo_response.py b/yoti_python_sdk/doc_scan/session/retrieve/document_id_photo_response.py new file mode 100644 index 00000000..1c49648e --- /dev/null +++ b/yoti_python_sdk/doc_scan/session/retrieve/document_id_photo_response.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse + + +class DocumentIdPhotoResponse(object): + """ + Represents the document ID photo 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 ID photo + + :return: the media + :rtype: MediaResponse or None + """ + return self.__media diff --git a/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py b/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py index 44c72869..78f28d33 100644 --- a/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/id_document_resource_response.py @@ -4,6 +4,9 @@ from yoti_python_sdk.doc_scan.session.retrieve.document_fields_response import ( DocumentFieldsResponse, ) +from yoti_python_sdk.doc_scan.session.retrieve.document_id_photo_response import ( + DocumentIdPhotoResponse, +) from yoti_python_sdk.doc_scan.session.retrieve.page_response import PageResponse from yoti_python_sdk.doc_scan.session.retrieve.resource_response import ResourceResponse from yoti_python_sdk.doc_scan.session.retrieve.task_response import ( @@ -34,6 +37,11 @@ def __init__(self, data=None): if "document_fields" in data.keys() else None ) + self.__document_id_photo = ( + DocumentIdPhotoResponse(data["document_id_photo"]) + if "document_id_photo" in data.keys() + else None + ) @property def document_type(self): @@ -75,6 +83,16 @@ def document_fields(self): """ return self.__document_fields + @property + def document_id_photo(self): + """ + Returns the associated document ID photo + + :return: the document ID photo + :rtype: DocumentIdPhotoResponse + """ + return self.__document_id_photo + @property def text_extraction_tasks(self): """ diff --git a/yoti_python_sdk/tests/doc_scan/session/create/task/test_text_extraction_task.py b/yoti_python_sdk/tests/doc_scan/session/create/task/test_text_extraction_task.py index 2d20ae47..79806520 100644 --- a/yoti_python_sdk/tests/doc_scan/session/create/task/test_text_extraction_task.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/task/test_text_extraction_task.py @@ -15,14 +15,18 @@ class RequestedTextExtractionTaskTest(unittest.TestCase): - def test_should_build_with_manual_check_always(self): - result = RequestedTextExtractionTaskBuilder().with_manual_check_always().build() + def test_should_build_text_data_extraction_task(self): + result = RequestedTextExtractionTaskBuilder().build() assert isinstance(result, RequestedTask) assert isinstance(result, RequestedTextExtractionTask) assert isinstance(result.config, RequestedTextExtractionTaskConfig) assert result.type == "ID_DOCUMENT_TEXT_DATA_EXTRACTION" + + def test_should_build_with_manual_check_always(self): + result = RequestedTextExtractionTaskBuilder().with_manual_check_always().build() + assert result.config.manual_check == "ALWAYS" def test_should_build_with_manual_check_fallback(self): @@ -37,12 +41,46 @@ def test_should_build_with_manual_check_never(self): assert result.config.manual_check == "NEVER" + def test_should_build_with_chip_data_desired(self): + result = RequestedTextExtractionTaskBuilder().with_chip_data_desired().build() + + assert result.config.chip_data == "DESIRED" + + def test_should_build_with_chip_data_ignore(self): + result = RequestedTextExtractionTaskBuilder().with_chip_data_ignore().build() + + assert result.config.chip_data == "IGNORE" + 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 != "" + def test_to_json_should_return_correct_properties(self): + result = ( + RequestedTextExtractionTaskBuilder() + .with_manual_check_always() + .with_chip_data_desired() + .build() + ) + + json = result.to_json() + assert json is not None + + json_config = json.get("config").to_json() + assert json_config.get("manual_check") == "ALWAYS" + assert json_config.get("chip_data") == "DESIRED" + + def test_to_json_should_not_include_null_config_values(self): + result = RequestedTextExtractionTaskBuilder().build() + + json = result.to_json() + assert json is not None + + json_config = json.get("config").to_json() + assert json_config == {} + if __name__ == "__main__": unittest.main() diff --git a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_document_id_photo_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_document_id_photo_response.py new file mode 100644 index 00000000..3dd31f04 --- /dev/null +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_document_id_photo_response.py @@ -0,0 +1,22 @@ +import unittest + +from yoti_python_sdk.doc_scan.session.retrieve.document_id_photo_response import ( + DocumentIdPhotoResponse, +) +from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse + + +class DocumentIdPhotoResponseTest(unittest.TestCase): + def test_should_parse_correctly(self): + data = {"media": {}} + + result = DocumentIdPhotoResponse(data) + assert isinstance(result.media, MediaResponse) + + def test_should_not_throw_exception_for_none(self): + result = DocumentIdPhotoResponse(None) + assert result.media is None + + +if __name__ == "__main__": + unittest.main() diff --git a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_id_document_resource_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_id_document_resource_response.py index deeac750..6c9686c1 100644 --- a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_id_document_resource_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_id_document_resource_response.py @@ -3,6 +3,9 @@ from yoti_python_sdk.doc_scan.session.retrieve.document_fields_response import ( DocumentFieldsResponse, ) +from yoti_python_sdk.doc_scan.session.retrieve.document_id_photo_response import ( + DocumentIdPhotoResponse, +) from yoti_python_sdk.doc_scan.session.retrieve.id_document_resource_response import ( IdDocumentResourceResponse, ) @@ -22,6 +25,7 @@ class IdDocumentResourceResponseTest(unittest.TestCase): ] SOME_PAGES = [{"first": "page"}, {"second": "page"}] SOME_DOCUMENT_FIELDS = {"media": {}} + SOME_DOCUMENT_ID_PHOTO = {"media": {}} def test_should_parse_correctly(self): data = { @@ -31,6 +35,7 @@ def test_should_parse_correctly(self): "tasks": self.SOME_TASKS, "pages": self.SOME_PAGES, "document_fields": self.SOME_DOCUMENT_FIELDS, + "document_id_photo": self.SOME_DOCUMENT_ID_PHOTO, } result = IdDocumentResourceResponse(data) @@ -41,6 +46,7 @@ def test_should_parse_correctly(self): assert len(result.tasks) == 2 assert len(result.pages) == 2 assert isinstance(result.document_fields, DocumentFieldsResponse) + assert isinstance(result.document_id_photo, DocumentIdPhotoResponse) def test_should_parse_when_none(self): result = IdDocumentResourceResponse(None) @@ -51,6 +57,7 @@ def test_should_parse_when_none(self): assert len(result.tasks) == 0 assert len(result.pages) == 0 assert result.document_fields is None + assert result.document_id_photo is None def test_should_parse_tasks_with_type(self): data = { From deb6a81bd8b88b5cfb9a491a12f778cbbf33bf62 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Thu, 8 Oct 2020 13:57:05 +0100 Subject: [PATCH 34/35] SDK-1772: Add support for document page frames --- examples/doc_scan/templates/success.html | 60 ++++++++++++------- .../session/retrieve/page_response.py | 12 ++++ .../session/retrieve/test_page_response.py | 12 +++- 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/examples/doc_scan/templates/success.html b/examples/doc_scan/templates/success.html index 6aaa0bc2..646e3c5f 100644 --- a/examples/doc_scan/templates/success.html +++ b/examples/doc_scan/templates/success.html @@ -178,7 +178,7 @@

ID Documents

{% with doc_num=0 %} {% for document in session_result.resources.id_documents %} - {% set doc_num = loop.index + 1 %} + {% set doc_num = loop.index %}
@@ -305,36 +305,54 @@
Generated Media
{% endif %} {% if document.pages|length > 0 %} + {% with page_num=0 %} + {% for page in document.pages %} + {% set page_num = loop.index %}
-
+

-
-
-
- {% for page in document.pages %} - {% if page.media is not none %} -
- -
-

Method: {{ page.capture_method }}

-
+
+ +
+ {% if page.media is not none %} +
+ +
+

Method: {{ page.capture_method }}

+
+
+ {% endif %} +
+ + {% if page.frames|length > 0 %} +
+ {% for frame in page.frames %} + {% if frame.media is not none %} +
+ +
+
Frame
- {% endif %} - {% endfor %} -
+
+ {% endif %} + {% endfor %}
+ {% endif %} +
+ {% endfor %} + {% endwith %} {% endif %}
@@ -352,7 +370,7 @@

Zoom Liveness Resources

{% with liveness_num=0 %} {% for liveness in session_result.resources.zoom_liveness_resources %} - {% set liveness_num = loop.index + 1 %} + {% set liveness_num = loop.index %}
diff --git a/yoti_python_sdk/doc_scan/session/retrieve/page_response.py b/yoti_python_sdk/doc_scan/session/retrieve/page_response.py index 949ae345..25661660 100644 --- a/yoti_python_sdk/doc_scan/session/retrieve/page_response.py +++ b/yoti_python_sdk/doc_scan/session/retrieve/page_response.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from .media_response import MediaResponse +from .frame_response import FrameResponse class PageResponse(object): @@ -21,6 +22,7 @@ def __init__(self, data=None): data["capture_method"] if "capture_method" in data.keys() else None ) self.__media = MediaResponse(data["media"]) if "media" in data.keys() else None + self.__frames = [FrameResponse(frame) for frame in data.get("frames", [])] @property def capture_method(self): @@ -41,3 +43,13 @@ def media(self): :rtype: MediaResponse or None """ return self.__media + + @property + def frames(self): + """ + Returns the list of associated frames + + :return: the frames + :rtype: list[FrameResponse] + """ + return self.__frames diff --git a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_page_response.py b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_page_response.py index 07214d3e..22e66d18 100644 --- a/yoti_python_sdk/tests/doc_scan/session/retrieve/test_page_response.py +++ b/yoti_python_sdk/tests/doc_scan/session/retrieve/test_page_response.py @@ -2,24 +2,34 @@ from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse from yoti_python_sdk.doc_scan.session.retrieve.page_response import PageResponse +from yoti_python_sdk.doc_scan.session.retrieve.frame_response import FrameResponse class PageResponseTest(unittest.TestCase): SOME_CAPTURE_METHOD = "someCaptureMethod" + SOME_FRAMES = [{"first": "frame"}, {"second": "frame"}] def test_should_parse_correctly(self): - data = {"capture_method": self.SOME_CAPTURE_METHOD, "media": {}} + data = { + "capture_method": self.SOME_CAPTURE_METHOD, + "media": {}, + "frames": self.SOME_FRAMES, + } result = PageResponse(data) assert result.capture_method is self.SOME_CAPTURE_METHOD assert isinstance(result.media, MediaResponse) + assert len(result.frames) == 2 + assert isinstance(result.frames[0], FrameResponse) + assert isinstance(result.frames[1], FrameResponse) def test_should_parse_with_none(self): result = PageResponse(None) assert result.capture_method is None assert result.media is None + assert len(result.frames) == 0 if __name__ == "__main__": From 5931a555832cfe0da79af4c8c839b88ecc6a58fd Mon Sep 17 00:00:00 2001 From: David Grayston Date: Fri, 9 Oct 2020 14:29:49 +0100 Subject: [PATCH 35/35] Bump minor version --- 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 543119da..28191bca 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.12.2 +sonar.projectVersion = 2.13.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 953079f5..1947b0b3 100644 --- a/yoti_python_sdk/version.py +++ b/yoti_python_sdk/version.py @@ -1,2 +1,2 @@ # -*- coding: utf-8 -*- -__version__ = "2.12.2" +__version__ = "2.13.0"