Skip to content

Commit 512e57e

Browse files
committed
Merge remote-tracking branch 'origin/develop' into feature/sdk_python#136_complete_oauth_psd2_implementation
2 parents f655915 + 15cb353 commit 512e57e

26 files changed

+216
-479
lines changed

.github/ISSUE_TEMPLATE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
[//]: # (If there is a traceback please share it in a quote! You can do this by pasting the traceback text, highlighting it and pressing the quote button.)
1212

1313
## SDK version and environment
14-
- Tested on [0.12.4](https://github.com/bunq/sdk_python/releases/tag/0.12.4)
14+
- Tested on [1.14.0](https://github.com/bunq/sdk_python/releases/tag/1.14.0)
1515
- [ ] Sandbox
1616
- [ ] Production
1717

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44

55
## This PR closes/fixes the following issues:
66
[//]: # (If for some reason your pull request does not require a test case you can just mark this box as checked and explain why it does not require a test case.)
7-
- Closes bunq/sdk_php#
7+
- Closes bunq/sdk_python#
88
- [ ] Tested

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828

2929
[Full Changelog](https://github.com/bunq/sdk_python/compare/1.10.16...1.13.0)
3030

31+
**Breaking Changes:**
32+
- Breaking changes since v1.13.0 [\#142](https://github.com/bunq/sdk_python/issues/142) ([angelomelonas](https://github.com/angelomelonas))
33+
3134
**Implemented enhancements:**
3235

3336
- Python SDK Refactor [\#117](https://github.com/bunq/sdk_python/pull/117) ([angelomelonas](https://github.com/angelomelonas))

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
### How to contribute to the bunq Python SDK 😎
22

33
#### Want to add a new amazing feature to our SDK? 🚀
4-
- First let’s discuss the feature that you would like to add. [Open a new issue](https://github.com/bunq/bunq-Python/issues/new), describe the feature and explain why you think it should be added.
4+
- First let’s discuss the feature that you would like to add. [Open a new issue](https://github.com/bunq/sdk_python/issues/new), describe the feature and explain why you think it should be added.
55
- Once we agree on the new feature, open a new GitHub pull request and include all the relevant information to get your code approved!
66

77
#### Did you find a bug? 🐛
8-
- Before opening a new issue check if the bug hasn't already been reported by searching on GitHub under [issues](https://github.com/bunq/bunq-Python/issues).
9-
- If it hasn't already been reported you can [open a new issue](https://github.com/bunq/bunq-Python/issues/new). Make sure you include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behaviour that is not occurring.
8+
- Before opening a new issue check if the bug hasn't already been reported by searching on GitHub under [issues](https://github.com/bunq/sdk_python/issues).
9+
- If it hasn't already been reported you can [open a new issue](https://github.com/bunq/sdk_python/issues/new). Make sure you include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behaviour that is not occurring.
1010
- If you wrote a patch that fixes a bug, open a new GitHub pull request and make sure to clearly describe the problem and your awesome solution.
1111

1212
#### Do you have questions about the source code?

bunq/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Type, Any
1+
from typing import Type
22

33
from bunq.sdk.context.api_environment_type import ApiEnvironmentType
44
from bunq.sdk.context.installation_context import InstallationContext
@@ -42,11 +42,11 @@ def initialize_converter() -> None:
4242
converter.register_adapter(datetime.datetime, DateTimeAdapter)
4343
converter.register_adapter(Pagination, PaginationAdapter)
4444

45-
def register_anchor_adapter(class_to_register: Type[Any]) -> None:
45+
def register_anchor_adapter(class_to_register: Type[T]) -> None:
4646
if issubclass(class_to_register, AnchorObjectInterface):
4747
converter.register_adapter(class_to_register, AnchorObjectAdapter)
4848

49-
def get_class(class_string_to_get: str) -> Type[Any]:
49+
def get_class(class_string_to_get: str) -> Type[T]:
5050
if hasattr(object_, class_string_to_get):
5151
return getattr(object_, class_string_to_get)
5252

bunq/sdk/context/api_context.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ def create_for_psd2(cls,
8484
service_provider_credential = api_context.__initialize_psd2_credential(
8585
certificate,
8686
private_key,
87-
all_chain_certificate)
87+
all_chain_certificate
88+
)
8889

8990
api_context._api_key = service_provider_credential.token_value
9091

bunq/sdk/json/converter.py

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import typing
88
import warnings
99
from types import ModuleType
10-
from typing import Type, Optional, Callable, Generator, Any, Dict, Match, List, Union, Generic
10+
from typing import Type, Optional, Callable, Generator, Dict, Match, List, Union, Generic
1111

1212
from bunq.sdk.exception.bunq_exception import BunqException
1313
from bunq.sdk.util.type_alias import T, JsonValue
@@ -74,14 +74,14 @@ def register_custom_adapter(cls,
7474
cls._custom_deserializers[class_name] = adapter
7575

7676
@classmethod
77-
def _get_serializer(cls, cls_for: Type[Any]) -> type:
77+
def _get_serializer(cls, cls_for: Type[T]) -> type:
7878
if cls_for.__name__ in cls._custom_serializers:
7979
return cls._custom_serializers[cls_for.__name__]
8080

8181
return JsonAdapter
8282

8383
@classmethod
84-
def _get_deserializer(cls, cls_for: Type[Any]) -> Type[JsonAdapter]:
84+
def _get_deserializer(cls, cls_for: Type[T]) -> Type[JsonAdapter]:
8585
if cls_for.__name__ in cls._custom_deserializers:
8686
return cls._custom_deserializers[cls_for.__name__]
8787

@@ -121,7 +121,7 @@ def _deserialize_default(cls,
121121
@classmethod
122122
def _is_deserialized(cls,
123123
cls_target: Type[T],
124-
obj: Any) -> bool:
124+
obj: T) -> bool:
125125
if cls_target is None:
126126
return True
127127

@@ -151,7 +151,7 @@ def _deserialize_dict(cls,
151151

152152
@classmethod
153153
def _deserialize_dict_attributes(cls,
154-
cls_context: Type[Any],
154+
cls_context: Type[T],
155155
dict_: Dict) -> Dict:
156156
dict_deserialized = {}
157157

@@ -243,7 +243,7 @@ def _str_to_type(cls,
243243
@classmethod
244244
def _str_to_type_from_member_module(cls,
245245
module_: ModuleType,
246-
string: str) -> Type[Any]:
246+
string: str) -> Type[T]:
247247
"""
248248
249249
:raise: BunqException when could not find the class for the string.
@@ -307,7 +307,7 @@ def can_serialize(cls) -> bool:
307307
return True
308308

309309
@classmethod
310-
def serialize(cls, obj: Any) -> JsonValue:
310+
def serialize(cls, obj: T) -> JsonValue:
311311
cls._initialize()
312312
serializer = cls._get_serializer(type(obj))
313313

@@ -317,7 +317,7 @@ def serialize(cls, obj: Any) -> JsonValue:
317317
return serializer.serialize(obj)
318318

319319
@classmethod
320-
def _serialize_default(cls, obj: Any) -> JsonValue:
320+
def _serialize_default(cls, obj: T) -> JsonValue:
321321
if obj is None or cls._is_primitive(obj):
322322
return obj
323323
elif cls._is_bytes(obj):
@@ -330,19 +330,19 @@ def _serialize_default(cls, obj: Any) -> JsonValue:
330330
return cls._serialize_dict(dict_)
331331

332332
@classmethod
333-
def _is_primitive(cls, obj: Any) -> bool:
333+
def _is_primitive(cls, obj: T) -> bool:
334334
return cls._is_type_primitive(type(obj))
335335

336336
@classmethod
337-
def _is_type_primitive(cls, type_: Type[Any]) -> bool:
337+
def _is_type_primitive(cls, type_: Type[T]) -> bool:
338338
return type_ in {int, str, bool, float}
339339

340340
@classmethod
341-
def _is_bytes(cls, obj: Any) -> bool:
341+
def _is_bytes(cls, obj: T) -> bool:
342342
return cls._is_bytes_type(type(obj))
343343

344344
@classmethod
345-
def _is_bytes_type(cls, type_: Type[Any]) -> bool:
345+
def _is_bytes_type(cls, type_: Type[T]) -> bool:
346346
return type_.__name__ in cls._TYPE_NAMES_BYTES
347347

348348
@classmethod
@@ -356,7 +356,7 @@ def _serialize_list(cls, list_: List) -> List:
356356
return list_serialized
357357

358358
@classmethod
359-
def _get_obj_raw(cls, obj: Any) -> Dict:
359+
def _get_obj_raw(cls, obj: T) -> Dict:
360360
return obj if type(obj) == dict else obj.__dict__
361361

362362
@classmethod
@@ -375,32 +375,22 @@ def _serialize_dict(cls, dict_: Dict) -> Dict:
375375

376376

377377
class ValueTypes:
378-
"""
379-
:type _main: type|None
380-
:type _sub: type|None
381-
"""
382-
383378
def __init__(self,
384-
main: Type[Any] = None,
385-
sub: Type[Any] = None) -> None:
379+
main: Type[T] = None,
380+
sub: Type[T] = None) -> None:
386381
self._main = main
387382
self._sub = sub
388383

389384
@property
390-
def main(self) -> Type[Any]:
385+
def main(self) -> Type[T]:
391386
return self._main
392387

393388
@property
394-
def sub(self) -> Type[Any]:
389+
def sub(self) -> Type[T]:
395390
return self._sub
396391

397392

398393
class ValueSpecs:
399-
"""
400-
:type _name: str|None
401-
:type _types: ValueTypes|None
402-
"""
403-
404394
def __init__(self,
405395
name: str = None,
406396
types: ValueTypes = None) -> None:
@@ -444,11 +434,11 @@ def deserialize(cls: Type[T], obj_raw: JsonValue) -> T:
444434
return JsonAdapter.deserialize(cls, obj_raw)
445435

446436

447-
def class_to_json(obj: Any) -> JsonValue:
437+
def class_to_json(obj: T) -> JsonValue:
448438
obj_raw = serialize(obj)
449439

450440
return json.dumps(obj_raw, indent=_JSON_INDENT, sort_keys=True)
451441

452442

453-
def serialize(obj_cls: Any) -> JsonValue:
443+
def serialize(obj_cls: T) -> JsonValue:
454444
return JsonAdapter.serialize(obj_cls)

setup.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@
5858

5959
# Specify the Python versions you support here. In particular, ensure
6060
# that you indicate whether you support Python 2, Python 3 or both.
61-
'Programming Language :: Python :: 3.5',
62-
'Programming Language :: Python :: 3.6',
6361
'Programming Language :: Python :: 3.7',
6462
],
6563

tests/bunq_test.py

Lines changed: 28 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import os
22
import time
33
import unittest
4+
from typing import AnyStr
45

6+
from bunq.sdk.context.api_context import ApiContext
57
from bunq.sdk.context.bunq_context import BunqContext
68
from bunq.sdk.exception.bunq_exception import BunqException
79
from bunq.sdk.http.api_client import ApiClient
10+
from bunq.sdk.model.generated.endpoint import MonetaryAccountBank, RequestInquiry, AttachmentPublic, Avatar, \
11+
CashRegister
12+
from bunq.sdk.model.generated.object_ import Amount, Pointer
813
from bunq.sdk.util import util
9-
from bunq.sdk.model.generated import endpoint
10-
from bunq.sdk.model.generated import object_
1114

1215

1316
class BunqSdkTestCase(unittest.TestCase):
1417
"""
15-
:type _second_monetary_account: endpoint.MonetaryAccountBank
16-
:type _cash_register: endpoint.CashRegister
18+
:type _second_monetary_account: MonetaryAccountBank
19+
:type _cash_register: CashRegister
1720
"""
1821

1922
# Error constants.
@@ -61,31 +64,25 @@ def setUp(self):
6164
BunqContext.user_context().refresh_user_context()
6265

6366
def __set_second_monetary_account(self):
64-
response = endpoint.MonetaryAccountBank.create(
67+
response = MonetaryAccountBank.create(
6568
self.__CURRENCY_EUR,
6669
self.__SECOND_MONETARY_ACCOUNT_DESCRIPTION
6770
)
6871

69-
self._second_monetary_account = endpoint.MonetaryAccountBank.get(
72+
self._second_monetary_account = MonetaryAccountBank.get(
7073
response.value
7174
).value
7275

7376
def __request_spending_money(self):
74-
endpoint.RequestInquiry.create(
75-
object_.Amount(self.__SPENDING_MONEY_AMOUNT, self.__CURRENCY_EUR),
76-
object_.Pointer(
77-
self._POINTER_EMAIL,
78-
self.__SPENDING_MONEY_RECIPIENT
79-
),
77+
RequestInquiry.create(
78+
Amount(self.__SPENDING_MONEY_AMOUNT, self.__CURRENCY_EUR),
79+
Pointer(self._POINTER_EMAIL, self.__SPENDING_MONEY_RECIPIENT),
8080
self.__REQUEST_SPENDING_DESCRIPTION,
8181
False
8282
)
83-
endpoint.RequestInquiry.create(
84-
object_.Amount(self.__SPENDING_MONEY_AMOUNT, self.__CURRENCY_EUR),
85-
object_.Pointer(
86-
self._POINTER_EMAIL,
87-
self.__SPENDING_MONEY_RECIPIENT
88-
),
83+
RequestInquiry.create(
84+
Amount(self.__SPENDING_MONEY_AMOUNT, self.__CURRENCY_EUR),
85+
Pointer(self._POINTER_EMAIL, self.__SPENDING_MONEY_RECIPIENT),
8986
self.__REQUEST_SPENDING_DESCRIPTION,
9087
False,
9188
self._second_monetary_account.id_
@@ -98,55 +95,38 @@ def _get_cash_register_id(self):
9895
return self._cash_register.id_
9996

10097
@classmethod
101-
def _get_api_context(cls):
102-
"""
103-
:rtype: ApiContext
104-
"""
105-
98+
def _get_api_context(cls) -> ApiContext:
10699
return util.automatic_sandbox_install()
107100

108-
def _get_pointer_bravo(self):
109-
"""
110-
:rtype: object_.Pointer
111-
"""
112-
113-
return object_.Pointer(self._POINTER_EMAIL, self._EMAIL_BRAVO)
114-
115-
def _get_alias_second_account(self):
116-
"""
117-
:rtype: object_.Pointer
118-
"""
101+
def _get_pointer_bravo(self) -> Pointer:
102+
return Pointer(self._POINTER_EMAIL, self._EMAIL_BRAVO)
119103

104+
def _get_alias_second_account(self) -> Pointer:
120105
return self._second_monetary_account.alias[self._FIRST_INDEX]
121106

122107
@staticmethod
123108
def _get_directory_test_root():
124109
return os.path.dirname(os.path.abspath(__file__))
125110

126111
def _set_cash_register(self):
127-
attachment_uuid = endpoint.AttachmentPublic.create(
112+
attachment_uuid = AttachmentPublic.create(
128113
self._attachment_contents,
129114
{
130115
ApiClient.HEADER_CONTENT_TYPE: self._CONTENT_TYPE,
131-
ApiClient.HEADER_ATTACHMENT_DESCRIPTION:
132-
self._ATTACHMENT_DESCRIPTION,
116+
ApiClient.HEADER_ATTACHMENT_DESCRIPTION: self._ATTACHMENT_DESCRIPTION,
133117
}
134118
)
135-
avatar_uuid = endpoint.Avatar.create(attachment_uuid.value)
136-
cash_register_id = endpoint.CashRegister.create(
119+
avatar_uuid = Avatar.create(attachment_uuid.value)
120+
cash_register_id = CashRegister.create(
137121
self.__CASH_REGISTER_DESCRIPTION,
138122
self.__CASH_REGISTER_STATUS,
139123
avatar_uuid.value
140124
)
141125

142-
self._cash_register = endpoint.CashRegister.get(cash_register_id.value)
126+
self._cash_register = CashRegister.get(cash_register_id.value)
143127

144128
@property
145-
def _attachment_contents(self):
146-
"""
147-
:rtype: bytes
148-
"""
149-
129+
def _attachment_contents(self) -> AnyStr:
150130
with open(
151131
self._get_directory_test_root() +
152132
self._PATH_ATTACHMENT +
@@ -156,17 +136,11 @@ def _attachment_contents(self):
156136
return file.read()
157137

158138
@property
159-
def alias_first(self):
160-
"""
161-
:rtype: Pointer
162-
"""
163-
139+
def alias_first(self) -> Pointer:
164140
if BunqContext.user_context().is_only_user_company_set():
165-
return BunqContext.user_context().user_company.alias[
166-
self._FIRST_INDEX]
141+
return BunqContext.user_context().user_company.alias[self._FIRST_INDEX]
167142

168143
if BunqContext.user_context().is_only_user_person_set():
169-
return BunqContext.user_context().user_person.alias[
170-
self._FIRST_INDEX]
144+
return BunqContext.user_context().user_person.alias[self._FIRST_INDEX]
171145

172146
raise BunqException(self.__ERROR_COULD_NOT_DETERMINE_USER)

0 commit comments

Comments
 (0)