Skip to content

Commit f4ec9af

Browse files
committed
SDK-2758-python-add-support-for-requesting-and-retrieving-share-code-resources-tasks
1 parent eab6d64 commit f4ec9af

10 files changed

Lines changed: 557 additions & 0 deletions

yoti_python_sdk/doc_scan/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION = (
1414
"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION"
1515
)
16+
VERIFY_SHARE_CODE_TASK = "VERIFY_SHARE_CODE_TASK"
1617

1718
CAMERA = "CAMERA"
1819
CAMERA_AND_UPLOAD = "CAMERA_AND_UPLOAD"

yoti_python_sdk/doc_scan/session/retrieve/resource_container.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
from yoti_python_sdk.doc_scan.session.retrieve.static_liveness_resource_response import (
1515
StaticLivenessResourceResponse,
1616
)
17+
from yoti_python_sdk.doc_scan.session.retrieve.share_code_resource_response import (
18+
ShareCodeResourceResponse,
19+
)
1720

1821

1922
class ResourceContainer(object):
@@ -45,6 +48,11 @@ def __init__(self, data=None):
4548
for liveness in data.get("liveness_capture", [])
4649
]
4750

51+
self.__share_codes = [
52+
ShareCodeResourceResponse(share_code)
53+
for share_code in data.get("share_codes", [])
54+
]
55+
4856
@staticmethod
4957
def __parse_liveness_capture(liveness_capture):
5058
"""
@@ -122,3 +130,13 @@ def static_liveness_resources(self):
122130
for liveness in self.__liveness_capture
123131
if isinstance(liveness, StaticLivenessResourceResponse)
124132
]
133+
134+
@property
135+
def share_codes(self):
136+
"""
137+
Return a list of share code resources
138+
139+
:return: list of share codes
140+
:rtype: list[ShareCodeResourceResponse]
141+
"""
142+
return self.__share_codes

yoti_python_sdk/doc_scan/session/retrieve/resource_response.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
TextExtractionTaskResponse,
55
SupplementaryDocumentTextExtractionTaskResponse,
66
)
7+
from .verify_share_code_task_response import VerifyShareCodeTaskResponse
78

89

910
class ResourceResponse(object):
@@ -35,6 +36,7 @@ def __parse_task(task):
3536
types = {
3637
constants.ID_DOCUMENT_TEXT_DATA_EXTRACTION: TextExtractionTaskResponse,
3738
constants.SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION: SupplementaryDocumentTextExtractionTaskResponse,
39+
constants.VERIFY_SHARE_CODE_TASK: VerifyShareCodeTaskResponse,
3840
}
3941
clazz = types.get(
4042
task.get("type", None), TaskResponse # Default fallback for task type
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse
2+
3+
4+
class ShareCodeMediaResponse(object):
5+
"""
6+
Wraps a MediaResponse inside a share code resource field
7+
"""
8+
9+
def __init__(self, data=None):
10+
if data is None:
11+
data = dict()
12+
13+
if "media" in data and data["media"] is not None:
14+
self.__media = MediaResponse(data["media"])
15+
else:
16+
self.__media = None
17+
18+
@property
19+
def media(self):
20+
"""
21+
The media object
22+
23+
:return: the media
24+
:rtype: MediaResponse or None
25+
"""
26+
return self.__media
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
from yoti_python_sdk.doc_scan.session.retrieve.resource_response import (
2+
ResourceResponse,
3+
)
4+
from yoti_python_sdk.doc_scan.session.retrieve.share_code_media_response import (
5+
ShareCodeMediaResponse,
6+
)
7+
from yoti_python_sdk.doc_scan.session.retrieve.verify_share_code_task_response import (
8+
VerifyShareCodeTaskResponse,
9+
)
10+
11+
12+
class ShareCodeResourceResponse(ResourceResponse):
13+
"""
14+
Represents a share code resource for a given session
15+
"""
16+
17+
def __init__(self, data=None):
18+
if data is None:
19+
data = dict()
20+
21+
ResourceResponse.__init__(self, data)
22+
23+
source = data.get("source", None)
24+
if isinstance(source, str):
25+
self.__source = source
26+
elif isinstance(source, dict):
27+
self.__source = source.get("type", str(source))
28+
else:
29+
self.__source = None
30+
31+
self.__created_at = data.get("created_at", None)
32+
self.__last_updated = data.get("last_updated", None)
33+
34+
self.__lookup_profile = (
35+
ShareCodeMediaResponse(data["lookup_profile"])
36+
if "lookup_profile" in data and data["lookup_profile"] is not None
37+
else None
38+
)
39+
self.__returned_profile = (
40+
ShareCodeMediaResponse(data["returned_profile"])
41+
if "returned_profile" in data and data["returned_profile"] is not None
42+
else None
43+
)
44+
self.__id_photo = (
45+
ShareCodeMediaResponse(data["id_photo"])
46+
if "id_photo" in data and data["id_photo"] is not None
47+
else None
48+
)
49+
self.__file = (
50+
ShareCodeMediaResponse(data["file"])
51+
if "file" in data and data["file"] is not None
52+
else None
53+
)
54+
55+
@property
56+
def source(self):
57+
"""
58+
The source of the share code
59+
60+
:return: the source
61+
:rtype: str or None
62+
"""
63+
return self.__source
64+
65+
@property
66+
def created_at(self):
67+
"""
68+
The date the share code was created
69+
70+
:return: the created at date
71+
:rtype: str or None
72+
"""
73+
return self.__created_at
74+
75+
@property
76+
def last_updated(self):
77+
"""
78+
The date the share code was last updated
79+
80+
:return: the last updated date
81+
:rtype: str or None
82+
"""
83+
return self.__last_updated
84+
85+
@property
86+
def lookup_profile(self):
87+
"""
88+
The lookup profile media
89+
90+
:return: the lookup profile
91+
:rtype: ShareCodeMediaResponse or None
92+
"""
93+
return self.__lookup_profile
94+
95+
@property
96+
def returned_profile(self):
97+
"""
98+
The returned profile media
99+
100+
:return: the returned profile
101+
:rtype: ShareCodeMediaResponse or None
102+
"""
103+
return self.__returned_profile
104+
105+
@property
106+
def id_photo(self):
107+
"""
108+
The ID photo media
109+
110+
:return: the ID photo
111+
:rtype: ShareCodeMediaResponse or None
112+
"""
113+
return self.__id_photo
114+
115+
@property
116+
def file(self):
117+
"""
118+
The file media
119+
120+
:return: the file
121+
:rtype: ShareCodeMediaResponse or None
122+
"""
123+
return self.__file
124+
125+
@property
126+
def verify_share_code_tasks(self):
127+
"""
128+
Returns a list of verify share code tasks
129+
130+
:return: list of verify share code tasks
131+
:rtype: list[VerifyShareCodeTaskResponse]
132+
"""
133+
return [
134+
task
135+
for task in self.tasks
136+
if isinstance(task, VerifyShareCodeTaskResponse)
137+
]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from yoti_python_sdk.doc_scan.session.retrieve.task_response import TaskResponse
2+
3+
4+
class VerifyShareCodeTaskResponse(TaskResponse):
5+
"""
6+
Represents a Verify Share Code task response
7+
"""
8+
9+
pass

yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_container.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from yoti_python_sdk.doc_scan.session.retrieve.resource_container import (
1010
ResourceContainer,
1111
)
12+
from yoti_python_sdk.doc_scan.session.retrieve.share_code_resource_response import (
13+
ShareCodeResourceResponse,
14+
)
1215

1316

1417
class ResourceContainerTest(unittest.TestCase):
@@ -62,6 +65,32 @@ def test_should_filter_static_liveness_resources(self):
6265
assert len(result.liveness_capture) == 2
6366
assert len(result.static_liveness_resources) == 1
6467

68+
def test_should_parse_share_codes(self):
69+
data = {
70+
"share_codes": [
71+
{"id": "share-code-1", "source": "END_USER", "tasks": []},
72+
{"id": "share-code-2", "source": "END_USER", "tasks": []},
73+
]
74+
}
75+
76+
result = ResourceContainer(data)
77+
78+
assert len(result.share_codes) == 2
79+
assert isinstance(result.share_codes[0], ShareCodeResourceResponse)
80+
assert isinstance(result.share_codes[1], ShareCodeResourceResponse)
81+
82+
def test_should_parse_with_empty_share_codes(self):
83+
data = {"share_codes": []}
84+
85+
result = ResourceContainer(data)
86+
87+
assert len(result.share_codes) == 0
88+
89+
def test_should_parse_with_missing_share_codes(self):
90+
result = ResourceContainer({})
91+
92+
assert len(result.share_codes) == 0
93+
6594

6695
if __name__ == "__main__":
6796
unittest.main()
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
import unittest
5+
6+
from yoti_python_sdk.doc_scan.session.retrieve.share_code_media_response import (
7+
ShareCodeMediaResponse,
8+
)
9+
10+
11+
class ShareCodeMediaResponseTest(unittest.TestCase):
12+
def test_should_parse_media(self):
13+
data = {
14+
"media": {
15+
"id": "some-media-id",
16+
"type": "JSON",
17+
"created": "2026-02-05T11:33:46Z",
18+
"last_updated": "2026-02-05T11:33:50Z",
19+
}
20+
}
21+
22+
result = ShareCodeMediaResponse(data)
23+
24+
assert result.media is not None
25+
assert result.media.id == "some-media-id"
26+
assert result.media.type == "JSON"
27+
assert result.media.created is not None
28+
assert result.media.last_updated is not None
29+
30+
def test_should_return_none_media_when_key_missing(self):
31+
result = ShareCodeMediaResponse({})
32+
33+
assert result.media is None
34+
35+
def test_should_return_none_media_when_value_is_null(self):
36+
data = {"media": None}
37+
38+
result = ShareCodeMediaResponse(data)
39+
40+
assert result.media is None
41+
42+
def test_should_parse_empty_media_object(self):
43+
data = {"media": {}}
44+
45+
result = ShareCodeMediaResponse(data)
46+
47+
assert result.media is not None
48+
assert result.media.id is None
49+
assert result.media.type is None
50+
51+
def test_should_parse_when_none(self):
52+
result = ShareCodeMediaResponse(None)
53+
54+
assert result.media is None
55+
56+
57+
if __name__ == "__main__":
58+
unittest.main()

0 commit comments

Comments
 (0)