Skip to content

Commit f122b4c

Browse files
authored
✨ New Resources - File and FileBatch (#226)
* first approach of File resource * Updatable and comments * changed user_id approach * changed how the user_id is uploaded * different validation and sending form as str * encode uploaded file and use user_id * added tests * updated cuenca-validation file types * added bin dependency * updated dependencies ? * added FileBatch resource * send is_back on requests * remove line for identify * type for batch metadata * added clabe for users * lint * fixed tests * dev dependency magic lib * fixed cassette to current response * generic links, removed not mine and bump validations * remove unused stuff and bring back lines
1 parent f22c4ba commit f122b4c

17 files changed

+662
-5
lines changed

cuenca/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
'CurpValidation',
1313
'Commission',
1414
'Deposit',
15+
'File',
16+
'FileBatch',
1517
'Identity',
1618
'IdentityEvent',
1719
'LoginToken',
@@ -46,6 +48,8 @@
4648
Commission,
4749
CurpValidation,
4850
Deposit,
51+
File,
52+
FileBatch,
4953
Identity,
5054
IdentityEvent,
5155
LoginToken,

cuenca/http/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ def get(
8585
) -> DictStrAny:
8686
return self._request_json('get', endpoint, params=params)
8787

88-
def post(self, endpoint: str, data: DictStrAny) -> DictStrAny:
89-
return self._request_json('post', endpoint, data=data)
88+
def post(self, endpoint: str, data: DictStrAny, **kwargs) -> DictStrAny:
89+
return self._request_json('post', endpoint, data=data, **kwargs)
9090

9191
def patch(self, endpoint: str, data: DictStrAny, **kwargs) -> DictStrAny:
9292
return self._request_json('patch', endpoint, data=data)

cuenca/resources/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
'Commission',
1212
'CurpValidation',
1313
'Deposit',
14+
'File',
15+
'FileBatch',
1416
'Identity',
1517
'IdentityEvent',
1618
'LoginToken',
@@ -38,6 +40,8 @@
3840
from .commissions import Commission
3941
from .curp_validations import CurpValidation
4042
from .deposits import Deposit
43+
from .file_batches import FileBatch
44+
from .files import File
4145
from .identities import Identity
4246
from .identity_events import IdentityEvent
4347
from .login_tokens import LoginToken
@@ -68,6 +72,8 @@
6872
CurpValidation,
6973
Commission,
7074
Deposit,
75+
File,
76+
FileBatch,
7177
Identity,
7278
IdentityEvent,
7379
LoginToken,

cuenca/resources/base.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import base64
12
import datetime as dt
3+
import json
24
from dataclasses import asdict, dataclass, fields
35
from io import BytesIO
46
from typing import ClassVar, Dict, Generator, Optional, Union
@@ -121,6 +123,30 @@ def xml(self) -> bytes:
121123
return self.download(self, file_format=FileFormat.xml).read()
122124

123125

126+
@dataclass
127+
class Uploadable(Resource):
128+
@classmethod
129+
def _upload(
130+
cls,
131+
file: bytes,
132+
user_id: str,
133+
*,
134+
session: Session = global_session,
135+
**data,
136+
) -> Resource:
137+
encoded_file = base64.b64encode(file)
138+
resp = session.request(
139+
'post',
140+
cls._resource,
141+
files=dict(
142+
file=(None, encoded_file),
143+
user_id=(None, user_id),
144+
**{k: (None, v) for k, v in data.items()},
145+
),
146+
)
147+
return cls._from_dict(json.loads(resp))
148+
149+
124150
@dataclass
125151
class Queryable(Resource):
126152
_query_params: ClassVar = QueryParams

cuenca/resources/file_batches.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from typing import ClassVar, Dict, List, cast
2+
3+
from cuenca_validations.types import BatchFileMetadata, FileBatchUploadRequest
4+
from pydantic.dataclasses import dataclass
5+
6+
from ..http import Session, session as global_session
7+
from .base import Creatable, Queryable
8+
9+
10+
@dataclass
11+
class FileBatch(Creatable, Queryable):
12+
_resource: ClassVar = 'file_batches'
13+
14+
received_files: List[BatchFileMetadata]
15+
uploaded_files: List[BatchFileMetadata]
16+
user_id: str
17+
18+
@classmethod
19+
def create(
20+
cls,
21+
files: List[Dict],
22+
user_id: str,
23+
*,
24+
session: Session = global_session,
25+
) -> 'FileBatch':
26+
req = FileBatchUploadRequest(files=files, user_id=user_id)
27+
return cast('FileBatch', cls._create(session=session, **req.dict()))

cuenca/resources/files.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from io import BytesIO
2+
from typing import ClassVar, Optional, cast
3+
4+
from cuenca_validations.types import (
5+
FileFormat,
6+
FileQuery,
7+
FileUploadRequest,
8+
KYCFileType,
9+
)
10+
from pydantic import HttpUrl
11+
from pydantic.dataclasses import dataclass
12+
13+
from ..http import Session, session as global_session
14+
from .base import Downloadable, Queryable, Uploadable
15+
16+
17+
@dataclass
18+
class File(Downloadable, Queryable, Uploadable):
19+
_resource: ClassVar = 'files'
20+
_query_params: ClassVar = FileQuery
21+
22+
extension: str
23+
type: KYCFileType
24+
url: HttpUrl
25+
user_id: str
26+
27+
@classmethod
28+
def upload(
29+
cls,
30+
file: BytesIO,
31+
file_type: KYCFileType,
32+
extension: Optional[str],
33+
is_back: bool = False,
34+
user_id: str = 'me',
35+
*,
36+
session: Session = global_session,
37+
) -> 'File':
38+
"""
39+
Stores an encrypted version of the file,
40+
only users with permissions can access it.
41+
42+
:param file:
43+
:param user_id:
44+
:param session:
45+
:return: New encrypted file object
46+
"""
47+
req = FileUploadRequest(
48+
file=file.read(),
49+
type=file_type,
50+
extension=extension,
51+
is_back=is_back,
52+
user_id=user_id,
53+
)
54+
return cast(
55+
'File',
56+
cls._upload(
57+
session=session,
58+
**req.dict(),
59+
),
60+
)
61+
62+
@property
63+
def file(self) -> bytes:
64+
"""
65+
Bytes of the decrypted file.
66+
Format of the file is found on `file_type` property.
67+
"""
68+
return self.download(self, file_format=FileFormat.any).read()
69+
70+
@property
71+
def pdf(self) -> bytes:
72+
"""
73+
Override from `Downloadable`, this property does not apply.
74+
"""
75+
raise NotImplementedError
76+
77+
@property
78+
def xml(self) -> bytes:
79+
"""
80+
Override from `Downloadable`, this property does not apply.
81+
"""
82+
raise NotImplementedError

cuenca/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
__version__ = '0.7.14'
1+
__version__ = '0.7.15'
22
CLIENT_VERSION = __version__
33
API_VERSION = '2020-03-19'

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
requests==2.27.1
2-
cuenca-validations==0.9.18
2+
cuenca-validations==0.9.20
33
dataclasses>=0.7;python_version<"3.7"

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
python_requires='>=3.6',
2525
install_requires=[
2626
'requests>=2.24,<28',
27-
'cuenca-validations>=0.9.18,<0.9.19',
2827
'dataclasses>=0.7;python_version<"3.7"',
28+
'cuenca-validations>=0.9.18,<0.9.22',
2929
],
3030
classifiers=[
3131
'Programming Language :: Python :: 3',

tests/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import datetime as dt
2+
from io import BytesIO
23
from typing import Dict
34

45
import pytest
@@ -60,3 +61,9 @@ def user_request() -> Dict:
6061
),
6162
)
6263
return user_dict
64+
65+
66+
@pytest.fixture
67+
def file() -> BytesIO:
68+
with open('tests/data/test_file.jpeg', 'rb') as image_file:
69+
return BytesIO(image_file.read())

0 commit comments

Comments
 (0)