Skip to content

Commit

Permalink
Merge pull request #183 from getyoti/release-2.11.0
Browse files Browse the repository at this point in the history
Release 2.11.0
  • Loading branch information
MrBurtyyy authored Apr 20, 2020
2 parents d995f1b + db5c959 commit ec94e79
Show file tree
Hide file tree
Showing 88 changed files with 4,529 additions and 20 deletions.
6 changes: 5 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
omit =
yoti_python_sdk/tests/**
yoti_python_sdk/protobuf/**/*
examples/**
examples/**

[report]
exclude_lines =
raise NotImplementedError
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ requests>=2.20.0
urllib3>=1.24.2
deprecated==1.2.6
wheel==0.24.0
iso8601==0.1.12
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ cryptography==2.8 # via -r requirements.in, pyopenssl
deprecated==1.2.6 # via -r requirements.in
future==0.18.2 # via -r requirements.in
idna==2.7 # via requests
iso8601==0.1.12 # via -r requirements.in
itsdangerous==0.24 # via -r requirements.in
pbr==1.10.0 # via -r requirements.in
protobuf==3.11.3 # via -r requirements.in
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"future>=0.11.0",
"asn1==2.2.0",
"pyopenssl>=18.0.0",
"iso8601==0.1.12",
],
extras_require={
"examples": [
Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ sonar.host.url = https://sonarcloud.io
sonar.organization = getyoti
sonar.projectKey = getyoti:python
sonar.projectName = Python SDK
sonar.projectVersion = 2.10.2
sonar.projectVersion = 2.11.0
sonar.exclusions = yoti_python_sdk/tests/**,examples/**,yoti_python_sdk/protobuf/**/*

sonar.python.pylint.reportPath = coverage.out
Expand Down
28 changes: 19 additions & 9 deletions yoti_python_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"YOTI_API_URL": "https://api.yoti.com",
"YOTI_API_PORT": 443,
"YOTI_API_VERSION": "v1",
"YOTI_API_VERIFY_SSL": "true"
"YOTI_API_VERIFY_SSL": "true",
}

main_ns = {}
Expand All @@ -23,19 +23,29 @@

__version__ = main_ns["__version__"]
YOTI_API_URL = environ.get("YOTI_API_URL", DEFAULTS["YOTI_API_URL"])

YOTI_PROFILE_ENDPOINT = "/api/v1"
YOTI_DOC_SCAN_ENDPOINT = "/idverify/v1"

YOTI_API_PORT = environ.get("YOTI_API_PORT", DEFAULTS["YOTI_API_PORT"])
YOTI_API_VERSION = environ.get("YOTI_API_VERSION", DEFAULTS["YOTI_API_VERSION"])
YOTI_API_ENDPOINT = environ.get("YOTI_API_ENDPOINT", "{0}:{1}/api/{2}".format(
YOTI_API_URL, YOTI_API_PORT, YOTI_API_VERSION
))

YOTI_API_VERIFY_SSL = environ.get("YOTI_API_VERIFY_SSL", DEFAULTS["YOTI_API_VERIFY_SSL"])
# Fully formatted API URLs
YOTI_API_ENDPOINT = environ.get(
"YOTI_API_ENDPOINT",
"{0}:{1}{2}".format(YOTI_API_URL, YOTI_API_PORT, YOTI_PROFILE_ENDPOINT),
)
YOTI_DOC_SCAN_API_URL = environ.get(
"YOTI_DOC_SCAN_API_URL",
"{0}:{1}{2}".format(YOTI_API_URL, YOTI_API_PORT, YOTI_DOC_SCAN_ENDPOINT),
)

YOTI_API_VERIFY_SSL = environ.get(
"YOTI_API_VERIFY_SSL", DEFAULTS["YOTI_API_VERIFY_SSL"]
)
if YOTI_API_VERIFY_SSL.lower() == "false":
YOTI_API_VERIFY_SSL = False
else:
YOTI_API_VERIFY_SSL = True

__all__ = [
"Client",
__version__
]
__all__ = ["Client", __version__]
21 changes: 21 additions & 0 deletions yoti_python_sdk/doc_scan/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from .session.create.check.document_authenticity import (
RequestedDocumentAuthenticityCheckBuilder,
)
from .session.create.check.face_match import RequestedFaceMatchCheckBuilder
from .session.create.check.liveness import RequestedLivenessCheckBuilder
from .session.create.task.text_extraction import RequestedTextExtractionTaskBuilder
from .session.create.notification_config import NotificationConfigBuilder
from .session.create.sdk_config import SdkConfigBuilder
from .session.create.session_spec import SessionSpecBuilder
from .client import DocScanClient

__all__ = [
RequestedDocumentAuthenticityCheckBuilder,
RequestedLivenessCheckBuilder,
RequestedFaceMatchCheckBuilder,
RequestedTextExtractionTaskBuilder,
SessionSpecBuilder,
NotificationConfigBuilder,
SdkConfigBuilder,
DocScanClient,
]
171 changes: 171 additions & 0 deletions yoti_python_sdk/doc_scan/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import json

import yoti_python_sdk
from yoti_python_sdk.doc_scan.endpoint import Endpoint
from yoti_python_sdk.doc_scan.session.retrieve.create_session_result import (
CreateSessionResult,
)
from yoti_python_sdk.doc_scan.session.retrieve.get_session_result import (
GetSessionResult,
)
from yoti_python_sdk.doc_scan.session.retrieve.media_value import MediaValue
from yoti_python_sdk.http import SignedRequest
from yoti_python_sdk.utils import YotiEncoder
from .exception import DocScanException


class DocScanClient(object):
"""
Client used for communication with the Yoti Doc Scan service where any
signed request is required
"""

def __init__(self, sdk_id, key, api_url=None):
self.__sdk_id = sdk_id
self.__key = key
if api_url is not None:
self.__api_url = api_url
else:
self.__api_url = yoti_python_sdk.YOTI_DOC_SCAN_ENDPOINT

def create_session(self, session_spec):
"""
Creates a Doc Scan session using the supplied session specification
:param session_spec: the session specification
:type session_spec: SessionSpec
:return: the create session result
:rtype: CreateSessionResult
:raises DocScanException: if there was an error creating the session
"""
payload = json.dumps(session_spec, cls=YotiEncoder).encode("utf-8")

request = (
SignedRequest.builder()
.with_post()
.with_pem_file(self.__key)
.with_base_url(self.__api_url)
.with_endpoint(Endpoint.create_docs_session_path())
.with_param("sdkId", self.__sdk_id)
.with_payload(payload)
.with_header("Content-Type", "application/json")
.build()
)
response = request.execute()

if response.status_code != 201:
raise DocScanException("Failed to create session", response)

data = json.loads(response.text)
return CreateSessionResult(data)

def get_session(self, session_id):
"""
Retrieves the state of a previously created Yoti Doc Scan session
:param session_id: the session ID
:type session_id: str
:return: the session state
:rtype: GetSessionResult
:raises DocScanException: if there was an error retrieving the session
"""
request = (
SignedRequest.builder()
.with_get()
.with_pem_file(self.__key)
.with_base_url(self.__api_url)
.with_endpoint(Endpoint.retrieve_docs_session_path(session_id))
.with_param("sdkId", self.__sdk_id)
.build()
)
response = request.execute()

if response.status_code != 200:
raise DocScanException("Failed to retrieve session", response)

data = json.loads(response.text)
return GetSessionResult(data)

def delete_session(self, session_id):
"""
Deletes a previously created Yoti Doc Scan session and
all of its related resources
:param session_id: the session id to delete
:type session_id: str
:rtype: None
:raises DocScanException: if there was an error deleting the session
"""
request = (
SignedRequest.builder()
.with_http_method("DELETE")
.with_pem_file(self.__key)
.with_base_url(self.__api_url)
.with_endpoint(Endpoint.delete_docs_session_path(session_id))
.with_param("sdkId", self.__sdk_id)
.build()
)
response = request.execute()

if response.status_code < 200 or response.status_code >= 300:
raise DocScanException("Failed to delete session", response)

def get_media_content(self, session_id, media_id):
"""
Retrieves media related to a Yoti Doc Scan session
based on the supplied media ID
:param session_id: the session ID
:type session_id: str
:param media_id: the media ID
:type media_id: str
:return: the media
:rtype: MediaValue
:raises DocScanException: if there was an error retrieving the media content
"""
request = (
SignedRequest.builder()
.with_get()
.with_pem_file(self.__key)
.with_base_url(self.__api_url)
.with_endpoint(Endpoint.get_media_content_path(session_id, media_id))
.with_param("sdkId", self.__sdk_id)
.build()
)
response = request.execute()

if response.status_code != 200:
raise DocScanException("Failed to retrieve media content", response)

media_mime_type = response.headers["Content-Type"]
media_content = response.content
return MediaValue(media_mime_type, media_content)

def delete_media_content(self, session_id, media_id):
"""
Deletes media related to a Yoti Doc Scan session
based on the supplied media ID
:param session_id: the session ID
:type session_id: str
:param media_id: the media ID
:type media_id: str
:rtype: None
:raises DocScanException: if there was an error deleting the media content
"""
request = (
SignedRequest.builder()
.with_http_method("DELETE")
.with_pem_file(self.__key)
.with_base_url(self.__api_url)
.with_endpoint(Endpoint.delete_media_path(session_id, media_id))
.with_param("sdkId", self.__sdk_id)
.build()
)

response = request.execute()
if response.status_code < 200 or response.status_code >= 300:
raise DocScanException("Failed to delete media content", response)
21 changes: 21 additions & 0 deletions yoti_python_sdk/doc_scan/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

ID_DOCUMENT_AUTHENTICITY = "ID_DOCUMENT_AUTHENTICITY"
ID_DOCUMENT_TEXT_DATA_CHECK = "ID_DOCUMENT_TEXT_DATA_CHECK"
ID_DOCUMENT_TEXT_DATA_EXTRACTION = "ID_DOCUMENT_TEXT_DATA_EXTRACTION"
ID_DOCUMENT_FACE_MATCH = "ID_DOCUMENT_FACE_MATCH"
LIVENESS = "LIVENESS"
ZOOM = "ZOOM"

CAMERA = "CAMERA"
CAMERA_AND_UPLOAD = "CAMERA_AND_UPLOAD"

RESOURCE_UPDATE = "RESOURCE_UPDATE"
TASK_COMPLETION = "TASK_COMPLETION"
CHECK_COMPLETION = "CHECK_COMPLETION"
SESSION_COMPLETION = "SESSION_COMPLETION"

ALWAYS = "ALWAYS"
FALLBACK = "FALLBACK"
NEVER = "NEVER"
22 changes: 22 additions & 0 deletions yoti_python_sdk/doc_scan/endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Endpoint(object):
@staticmethod
def create_docs_session_path():
return "/sessions"

@staticmethod
def retrieve_docs_session_path(session_id):
return "/sessions/{sessionId}".format(sessionId=session_id)

@staticmethod
def delete_docs_session_path(session_id):
return Endpoint.retrieve_docs_session_path(session_id)

@staticmethod
def get_media_content_path(session_id, media_id):
return "/sessions/{sessionId}/media/{mediaId}/content".format(
sessionId=session_id, mediaId=media_id
)

@staticmethod
def delete_media_path(session_id, media_id):
return Endpoint.get_media_content_path(session_id, media_id)
3 changes: 3 additions & 0 deletions yoti_python_sdk/doc_scan/exception/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .doc_scan_exception import DocScanException

__all__ = [DocScanException]
60 changes: 60 additions & 0 deletions yoti_python_sdk/doc_scan/exception/doc_scan_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class DocScanException(Exception):
"""
Exception thrown by the Yoti Doc Scan client
when an error has occurred when communicating with the API
"""

def __init__(self, message, response):
"""
:param message: the exception message
:type message: str
:param response: the http response
:type response: requests.Response
"""
Exception.__init__(self)

self.__message = message
self.__response = response

@property
def message(self):
"""
Get the specific exception message
:return: the exception message
:rtype: str
"""
return self.__message

@property
def status_code(self):
"""
Get the status code of the HTTP response
:return: the status code
:rtype: int or None
"""
return self.__response.status_code

@property
def text(self):
"""
Return the HTTP response body as text
:return: the body as text
:rtype: str
"""
return self.__response.text

@property
def content(self):
"""
Return the HTTP response body as bytes
:return: the body as bytes
:rtype: bytearray or None
"""
return self.__response.content

def __str__(self):
return self.__message
Empty file.
Loading

0 comments on commit ec94e79

Please sign in to comment.