From a68b115e8fc8f3126eae0e67c720c7d0c0ddcaa2 Mon Sep 17 00:00:00 2001 From: Angelo Melonas Date: Thu, 10 Sep 2020 11:02:58 +0200 Subject: [PATCH 1/9] feature/sdk_python#149 Remove get_user_object call directly after UserContext creation. --- bunq/sdk/context/user_context.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bunq/sdk/context/user_context.py b/bunq/sdk/context/user_context.py index e467d41..efc812d 100644 --- a/bunq/sdk/context/user_context.py +++ b/bunq/sdk/context/user_context.py @@ -17,8 +17,6 @@ def __init__(self, user_id: int) -> None: self._user_payment_service_provider = None self._primary_monetary_account = None - self._set_user(self.__get_user_object()) - @staticmethod def __get_user_object() -> BunqModel: return endpoint.User.list().value[0].get_referenced_object() From d74f691421d83befa143f2c80d11e830cb632fbd Mon Sep 17 00:00:00 2001 From: Angelo Melonas Date: Thu, 10 Sep 2020 14:29:29 +0200 Subject: [PATCH 2/9] feature/sdk_python#149 Set user immediately after UserContext creation. --- bunq/sdk/context/api_context.py | 26 +++++--------------------- bunq/sdk/context/bunq_context.py | 2 +- bunq/sdk/context/session_context.py | 24 +++++++++++------------- bunq/sdk/context/user_context.py | 5 ++++- 4 files changed, 21 insertions(+), 36 deletions(-) diff --git a/bunq/sdk/context/api_context.py b/bunq/sdk/context/api_context.py index 4591d64..684cb38 100644 --- a/bunq/sdk/context/api_context.py +++ b/bunq/sdk/context/api_context.py @@ -13,7 +13,7 @@ from bunq.sdk.json import converter from bunq.sdk.model.core.payment_service_provider_credential_internal import PaymentServiceProviderCredentialInternal from bunq.sdk.model.generated import endpoint -from bunq.sdk.model.generated.endpoint import UserCredentialPasswordIp, UserPaymentServiceProvider +from bunq.sdk.model.generated.endpoint import UserCredentialPasswordIp from bunq.sdk.security import security if typing.TYPE_CHECKING: @@ -90,7 +90,7 @@ def create_for_psd2(cls, api_context._api_key = service_provider_credential.token_value api_context.__register_device(description, all_permitted_ip) - api_context.__initialize_session_for_psd2(service_provider_credential) + api_context.__initialize_session() return api_context @@ -104,8 +104,7 @@ def __initialize_installation(self) -> None: security.public_key_to_string(private_key_client.publickey()) ).value token = installation.token.token - public_key_server_string = \ - installation.server_public_key.server_public_key + public_key_server_string = installation.server_public_key.server_public_key public_key_server = RSA.import_key(public_key_server_string) self._installation_context = InstallationContext( @@ -117,7 +116,7 @@ def __initialize_installation(self) -> None: def __initialize_psd2_credential(self, certificate: str, private_key: str, - all_chain_certificate: List[str], ) -> UserCredentialPasswordIp: + all_chain_certificate: List[str]) -> UserCredentialPasswordIp: session_token = self.installation_context.token client_key_pair = self.installation_context.private_key_client @@ -149,22 +148,7 @@ def __initialize_session(self) -> None: from bunq.sdk.model.core.session_server import SessionServer session_server = SessionServer.create(self).value - token = session_server.token.token - expiry_time = self._get_expiry_timestamp(session_server) - user_id = session_server.get_referenced_user().id_ - - self._session_context = SessionContext(token, expiry_time, user_id) - - def __initialize_session_for_psd2(self, user_payment_service_provider: UserPaymentServiceProvider) -> None: - from bunq.sdk.model.core.session_server import SessionServer - - session_server = SessionServer.create(self).value - - token = session_server.token.token - expiry_time = self._get_expiry_timestamp(session_server) - user_id = session_server.get_referenced_user().id_ - - self._session_context = SessionContext(token, expiry_time, user_id) + self._session_context = SessionContext(session_server) @classmethod def _get_expiry_timestamp(cls, session_server: SessionServer) -> datetime.datetime: diff --git a/bunq/sdk/context/bunq_context.py b/bunq/sdk/context/bunq_context.py index 28beb7b..1f564af 100644 --- a/bunq/sdk/context/bunq_context.py +++ b/bunq/sdk/context/bunq_context.py @@ -17,7 +17,7 @@ def __init__(self) -> None: @classmethod def load_api_context(cls, api_context: ApiContext) -> None: cls._api_context = api_context - cls._user_context = UserContext(api_context.session_context.user_id) + cls._user_context = UserContext(api_context.session_context.user_id, api_context.session_context.user) cls._user_context.init_main_monetary_account() @classmethod diff --git a/bunq/sdk/context/session_context.py b/bunq/sdk/context/session_context.py index 88ac1e5..99e7bad 100644 --- a/bunq/sdk/context/session_context.py +++ b/bunq/sdk/context/session_context.py @@ -1,20 +1,14 @@ import datetime +from bunq.sdk.model.core.session_server import SessionServer -class SessionContext: - """ - :type _token: str - :type _expiry_time: datetime.datetime - :type _user_id: int - """ - def __init__(self, - token: str, - expiry_time: datetime.datetime, - user_id: int) -> None: - self._token = token - self._expiry_time = expiry_time - self._user_id = user_id +class SessionContext: + def __init__(self, session_server: SessionServer) -> None: + self._token = session_server.token.token + self._expiry_time = self._get_expiry_timestamp(session_server) + self._user_id = session_server.get_referenced_user().id_ + self._user = session_server.get_referenced_user() @property def token(self) -> str: @@ -27,3 +21,7 @@ def expiry_time(self) -> datetime.datetime: @property def user_id(self) -> int: return self._user_id + + @property + def user(self): + return self._user diff --git a/bunq/sdk/context/user_context.py b/bunq/sdk/context/user_context.py index efc812d..8a4507a 100644 --- a/bunq/sdk/context/user_context.py +++ b/bunq/sdk/context/user_context.py @@ -1,3 +1,4 @@ +from bunq import T from bunq.sdk.exception.bunq_exception import BunqException from bunq.sdk.model.core.bunq_model import BunqModel from bunq.sdk.model.generated import endpoint @@ -9,7 +10,7 @@ class UserContext: _ERROR_NO_ACTIVE_MONETARY_ACCOUNT_FOUND = 'No active monetary account found.' _STATUS_ACTIVE = 'ACTIVE' - def __init__(self, user_id: int) -> None: + def __init__(self, user_id: int, user: BunqModel) -> None: self._user_id = user_id self._user_person = None self._user_company = None @@ -17,6 +18,8 @@ def __init__(self, user_id: int) -> None: self._user_payment_service_provider = None self._primary_monetary_account = None + self._set_user(user) + @staticmethod def __get_user_object() -> BunqModel: return endpoint.User.list().value[0].get_referenced_object() From f23bdcee6b250a5f8abfbcc1e1f773a240ef132f Mon Sep 17 00:00:00 2001 From: Angelo Melonas Date: Mon, 14 Sep 2020 13:50:21 +0200 Subject: [PATCH 3/9] feature/sdk_python#151 Deprecated SandboxUser and replaced it with SandboxUserPerson/Company. Also cleaned up code. --- README.md | 6 +++--- bunq/__init__.py | 7 ++++--- bunq/sdk/context/api_context.py | 5 ++--- bunq/sdk/context/user_context.py | 17 ++++++++--------- .../monetary_account_reference_adapter.py | 8 ++------ bunq/sdk/json/session_server_adapter.py | 10 +++++----- bunq/sdk/json/share_detail_adapter.py | 14 +++++++------- bunq/sdk/util/util.py | 10 +++++----- .../endpoint/test_attachment_public.py | 6 +++--- .../model/generated/endpoint/test_payment.py | 19 +++++++++---------- .../generated/object/test_notification_url.py | 2 +- 11 files changed, 49 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index b5b887b..26fb9c7 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Creating objects through the API requires an `ApiContext`, a `requestMap` and id dependencies (such as User ID required for accessing a Monetary Account). Optionally, custom headers can be passed to requests. - payment_id = endpoint.Payment.create( + payment_id = Payment.create( amount=Amount(amount_string, self._CURRENCY_EURL), counterparty_alias=Pointer(self._POINTER_TYPE_EMAIL, recipient), description=description @@ -109,7 +109,7 @@ See [`tinker/list_all_payment`](https://github.com/bunq/tinker_python/blob/2182b Updating objects through the API goes the same way as creating objects, except that also the object to update identifier (ID or UUID) is needed. - endpoint.Card.update( + Card.update( card_id=int(card_id), monetary_account_current_id=int(account_id) ) @@ -130,7 +130,7 @@ passed to requests. Listing objects through the API requires an `ApiContext` and identifiers of all dependencies (such as User ID required for accessing a Monetary Account). Optionally, custom headers can be passed to requests. - users = endpoint.User.list(api_context) + users = User.list(api_context) ##### Example See [`UserListExample.py`](./examples/user_list_example.py) diff --git a/bunq/__init__.py b/bunq/__init__.py index 504ef53..d4e2515 100644 --- a/bunq/__init__.py +++ b/bunq/__init__.py @@ -5,6 +5,7 @@ from bunq.sdk.http.pagination import Pagination from bunq.sdk.json import converter from bunq.sdk.model.core.anchor_object_interface import AnchorObjectInterface +from bunq.sdk.model.generated.object_ import Geolocation, ShareDetail, MonetaryAccountReference from bunq.sdk.util.type_alias import T @@ -36,9 +37,9 @@ def initialize_converter() -> None: converter.register_adapter(InstallationContext, InstallationContextAdapter) converter.register_adapter(ApiEnvironmentType, ApiEnvironmentTypeAdapter) converter.register_adapter(float, FloatAdapter) - converter.register_adapter(object_.Geolocation, GeolocationAdapter) - converter.register_adapter(object_.MonetaryAccountReference, MonetaryAccountReferenceAdapter) - converter.register_adapter(object_.ShareDetail, ShareDetailAdapter) + converter.register_adapter(Geolocation, GeolocationAdapter) + converter.register_adapter(MonetaryAccountReference, MonetaryAccountReferenceAdapter) + converter.register_adapter(ShareDetail, ShareDetailAdapter) converter.register_adapter(datetime.datetime, DateTimeAdapter) converter.register_adapter(Pagination, PaginationAdapter) diff --git a/bunq/sdk/context/api_context.py b/bunq/sdk/context/api_context.py index 684cb38..fb9a9d1 100644 --- a/bunq/sdk/context/api_context.py +++ b/bunq/sdk/context/api_context.py @@ -12,8 +12,7 @@ from bunq.sdk.exception.bunq_exception import BunqException from bunq.sdk.json import converter from bunq.sdk.model.core.payment_service_provider_credential_internal import PaymentServiceProviderCredentialInternal -from bunq.sdk.model.generated import endpoint -from bunq.sdk.model.generated.endpoint import UserCredentialPasswordIp +from bunq.sdk.model.generated.endpoint import UserCredentialPasswordIp, Session from bunq.sdk.security import security if typing.TYPE_CHECKING: @@ -221,7 +220,7 @@ def close_session(self) -> None: self._drop_session_context() def _delete_session(self) -> None: - endpoint.Session.delete(self._SESSION_ID_DUMMY) + Session.delete(self._SESSION_ID_DUMMY) @property def environment_type(self) -> ApiEnvironmentType: diff --git a/bunq/sdk/context/user_context.py b/bunq/sdk/context/user_context.py index 8a4507a..64409fe 100644 --- a/bunq/sdk/context/user_context.py +++ b/bunq/sdk/context/user_context.py @@ -1,8 +1,7 @@ -from bunq import T from bunq.sdk.exception.bunq_exception import BunqException from bunq.sdk.model.core.bunq_model import BunqModel -from bunq.sdk.model.generated import endpoint -from bunq.sdk.model.generated.endpoint import UserPerson, UserCompany, UserApiKey, MonetaryAccountBank +from bunq.sdk.model.generated.endpoint import UserPerson, UserCompany, UserApiKey, MonetaryAccountBank, User, \ + UserPaymentServiceProvider class UserContext: @@ -22,19 +21,19 @@ def __init__(self, user_id: int, user: BunqModel) -> None: @staticmethod def __get_user_object() -> BunqModel: - return endpoint.User.list().value[0].get_referenced_object() + return User.list().value[0].get_referenced_object() def _set_user(self, user: BunqModel) -> None: - if isinstance(user, endpoint.UserPerson): + if isinstance(user, UserPerson): self._user_person = user - elif isinstance(user, endpoint.UserCompany): + elif isinstance(user, UserCompany): self._user_company = user - elif isinstance(user, endpoint.UserApiKey): + elif isinstance(user, UserApiKey): self._user_api_key = user - elif isinstance(user, endpoint.UserPaymentServiceProvider): + elif isinstance(user, UserPaymentServiceProvider): self._user_payment_service_provider = user else: @@ -45,7 +44,7 @@ def init_main_monetary_account(self) -> None: if self._user_payment_service_provider is not None: return - all_monetary_account = endpoint.MonetaryAccountBank.list().value + all_monetary_account = MonetaryAccountBank.list().value for account in all_monetary_account: if account.status == self._STATUS_ACTIVE: diff --git a/bunq/sdk/json/monetary_account_reference_adapter.py b/bunq/sdk/json/monetary_account_reference_adapter.py index 231a21b..b38c7ac 100644 --- a/bunq/sdk/json/monetary_account_reference_adapter.py +++ b/bunq/sdk/json/monetary_account_reference_adapter.py @@ -1,8 +1,7 @@ from typing import Type, Dict from bunq.sdk.json import converter -from bunq.sdk.model.generated import object_ -from bunq.sdk.model.generated.object_ import MonetaryAccountReference +from bunq.sdk.model.generated.object_ import MonetaryAccountReference, LabelMonetaryAccount class MonetaryAccountReferenceAdapter(converter.JsonAdapter): @@ -10,10 +9,7 @@ class MonetaryAccountReferenceAdapter(converter.JsonAdapter): def deserialize(cls, target_class: Type[MonetaryAccountReference], obj: Dict) -> MonetaryAccountReference: - label_monetary_account = converter.deserialize( - object_.LabelMonetaryAccount, - obj - ) + label_monetary_account = converter.deserialize(LabelMonetaryAccount, obj) return target_class.create_from_label_monetary_account(label_monetary_account) diff --git a/bunq/sdk/json/session_server_adapter.py b/bunq/sdk/json/session_server_adapter.py index 0a1a5d3..baf5857 100644 --- a/bunq/sdk/json/session_server_adapter.py +++ b/bunq/sdk/json/session_server_adapter.py @@ -5,7 +5,7 @@ from bunq.sdk.model.core.id import Id from bunq.sdk.model.core.session_server import SessionServer from bunq.sdk.model.core.session_token import SessionToken -from bunq.sdk.model.generated import endpoint +from bunq.sdk.model.generated.endpoint import UserCompany, UserPerson, UserApiKey, UserPaymentServiceProvider class SessionServerAdapter(converter.JsonAdapter): @@ -65,25 +65,25 @@ def deserialize(cls, if cls._FIELD_USER_COMPANY in user_dict_wrapped: session_server.__dict__[cls._ATTRIBUTE_USER_COMPANY] = \ converter.deserialize( - endpoint.UserCompany, + UserCompany, user_dict_wrapped[cls._FIELD_USER_COMPANY] ) elif cls._FIELD_USER_PERSON in user_dict_wrapped: session_server.__dict__[cls._ATTRIBUTE_USER_PERSON] = \ converter.deserialize( - endpoint.UserPerson, + UserPerson, user_dict_wrapped[cls._FIELD_USER_PERSON] ) elif cls._FIELD_USER_API_KEY in user_dict_wrapped: session_server.__dict__[cls._ATTRIBUTE_USER_API_KEY] = \ converter.deserialize( - endpoint.UserApiKey, + UserApiKey, user_dict_wrapped[cls._FIELD_USER_API_KEY] ) elif cls._FIELD_USER_PAYMENT_SERVER_PROVIDER in user_dict_wrapped: session_server.__dict__[cls._ATTRIBUTE_USER_PAYMENT_SERVER_PROVIDER] = \ converter.deserialize( - endpoint.UserPaymentServiceProvider, + UserPaymentServiceProvider, user_dict_wrapped[cls._FIELD_USER_PAYMENT_SERVER_PROVIDER] ) else: diff --git a/bunq/sdk/json/share_detail_adapter.py b/bunq/sdk/json/share_detail_adapter.py index 4245a63..f05a792 100644 --- a/bunq/sdk/json/share_detail_adapter.py +++ b/bunq/sdk/json/share_detail_adapter.py @@ -1,8 +1,8 @@ from typing import Dict, Type, Optional from bunq.sdk.json import converter -from bunq.sdk.model.generated import object_ -from bunq.sdk.model.generated.object_ import ShareDetail +from bunq.sdk.model.generated.object_ import ShareDetail, ShareDetailPayment, ShareDetailReadOnly, \ + ShareDetailDraftPayment class ShareDetailAdapter(converter.JsonAdapter): @@ -21,24 +21,24 @@ def deserialize(cls, target_class: Type[ShareDetail], obj: Dict) -> ShareDetail: """ - :type target_class: object_.ShareDetail|type + :type target_class: ShareDetail|type :type obj: dict - :rtype: object_.ShareDetail + :rtype: ShareDetail """ share_detail = target_class.__new__(target_class) share_detail.__dict__ = { cls._ATTRIBUTE_PAYMENT: converter.deserialize( - object_.ShareDetailPayment, + ShareDetailPayment, cls._get_field_or_none(cls._FIELD_DRAFT_PAYMENT, obj) ), cls._ATTRIBUTE_READ_ONLY: converter.deserialize( - object_.ShareDetailReadOnly, + ShareDetailReadOnly, cls._get_field_or_none(cls._FIELD_READ_ONLY, obj) ), cls._ATTRIBUTE_DRAFT_PAYMENT: converter.deserialize( - object_.ShareDetailDraftPayment, + ShareDetailDraftPayment, cls._get_field_or_none(cls._FIELD_DRAFT_PAYMENT, obj) ), } diff --git a/bunq/sdk/util/util.py b/bunq/sdk/util/util.py index 250eb80..e6a1567 100644 --- a/bunq/sdk/util/util.py +++ b/bunq/sdk/util/util.py @@ -9,13 +9,13 @@ from bunq.sdk.exception.bunq_exception import BunqException from bunq.sdk.http.api_client import ApiClient from bunq.sdk.model.generated import endpoint -from bunq.sdk.model.generated.endpoint import SandboxUser +from bunq.sdk.model.generated.endpoint import SandboxUserPerson __UNIQUE_REQUEST_ID = "uniqueness-is-required" __FIELD_API_KEY = "ApiKey" __INDEX_FIRST = 0 __FIELD_RESPONSE = "Response" -__ENDPOINT_SANDBOX_USER = "sandbox-user" +__ENDPOINT_SANDBOX_USER_PERSON = "sandbox-user-person" _ERROR_COULD_NOT_CREATE_NEW_SANDBOX_USER = "Could not create new sandbox user." @@ -29,8 +29,8 @@ def automatic_sandbox_install() -> ApiContext: ) -def __generate_new_sandbox_user() -> SandboxUser: - url = ApiEnvironmentType.SANDBOX.uri_base + __ENDPOINT_SANDBOX_USER +def __generate_new_sandbox_user() -> SandboxUserPerson: + url = ApiEnvironmentType.SANDBOX.uri_base + __ENDPOINT_SANDBOX_USER_PERSON headers = { ApiClient.HEADER_REQUEST_ID: __UNIQUE_REQUEST_ID, @@ -44,7 +44,7 @@ def __generate_new_sandbox_user() -> SandboxUser: if response.status_code is ApiClient.STATUS_CODE_OK: response_json = json.loads(response.text) - return endpoint.SandboxUser.from_json( + return SandboxUserPerson.from_json( json.dumps(response_json[__FIELD_RESPONSE][__INDEX_FIRST][ __FIELD_API_KEY])) diff --git a/tests/model/generated/endpoint/test_attachment_public.py b/tests/model/generated/endpoint/test_attachment_public.py index fc9155a..092d78e 100644 --- a/tests/model/generated/endpoint/test_attachment_public.py +++ b/tests/model/generated/endpoint/test_attachment_public.py @@ -1,7 +1,7 @@ from typing import AnyStr from bunq.sdk.http.api_client import ApiClient -from bunq.sdk.model.generated import endpoint +from bunq.sdk.model.generated.endpoint import AttachmentPublicContent, AttachmentPublic from tests.bunq_test import BunqSdkTestCase @@ -25,8 +25,8 @@ def test_file_upload_and_retrieval(self): self._ATTACHMENT_DESCRIPTION, } - attachment_uuid = endpoint.AttachmentPublic.create(self.attachment_contents, custom_headers).value - contents_from_response = endpoint.AttachmentPublicContent.list(attachment_uuid).value + attachment_uuid = AttachmentPublic.create(self.attachment_contents, custom_headers).value + contents_from_response = AttachmentPublicContent.list(attachment_uuid).value self.assertEqual(self.attachment_contents, contents_from_response) diff --git a/tests/model/generated/endpoint/test_payment.py b/tests/model/generated/endpoint/test_payment.py index 68af654..7806a4a 100644 --- a/tests/model/generated/endpoint/test_payment.py +++ b/tests/model/generated/endpoint/test_payment.py @@ -1,7 +1,6 @@ from typing import List -from bunq.sdk.model.generated import endpoint -from bunq.sdk.model.generated.endpoint import Payment +from bunq.sdk.model.generated.endpoint import Payment, PaymentBatch, BunqResponseInt, BunqResponsePaymentBatch from bunq.sdk.model.generated.object_ import Amount, Pointer from tests.bunq_test import BunqSdkTestCase @@ -29,7 +28,7 @@ def test_payment_to_other_user(self): without errors """ - endpoint.Payment.create( + Payment.create( Amount(self._PAYMENT_AMOUNT_EUR, self._PAYMENT_CURRENCY), self._get_pointer_bravo(), self._PAYMENT_DESCRIPTION @@ -43,21 +42,21 @@ def test_payment_to_other_account(self): without errors """ - endpoint.Payment.create( + Payment.create( Amount(self._PAYMENT_AMOUNT_EUR, self._PAYMENT_CURRENCY), self._get_alias_second_account(), self._PAYMENT_DESCRIPTION ) def test_payment_batch(self): - response_create = endpoint.PaymentBatch.create(self.__create_payment_list()) + response_create = PaymentBatch.create(self.__create_payment_list()) - self.assertIsInstance(response_create, endpoint.BunqResponseInt) + self.assertIsInstance(response_create, BunqResponseInt) self.assertIsNotNone(response_create) - response_get = endpoint.PaymentBatch.get(response_create.value) + response_get = PaymentBatch.get(response_create.value) - self.assertIsInstance(response_get, endpoint.BunqResponsePaymentBatch) + self.assertIsInstance(response_get, BunqResponsePaymentBatch) self.assertIsNotNone(response_get) self.assertFalse(response_get.value.is_all_field_none()) @@ -66,13 +65,13 @@ def __create_payment_list(self) -> List[Payment]: while len(all_payment) < self._MAXIMUM_PAYMENT_IN_BATCH: all_payment.append( - endpoint.Payment( + Payment( Amount(self._PAYMENT_AMOUNT_EUR, self._PAYMENT_CURRENCY), Pointer(self._POINTER_EMAIL, self._EMAIL_BRAVO), self._PAYMENT_DESCRIPTION ) ) self.assertIsInstance(all_payment, List) - self.assertIsInstance(all_payment[0], endpoint.Payment) + self.assertIsInstance(all_payment[0], Payment) return all_payment diff --git a/tests/model/generated/object/test_notification_url.py b/tests/model/generated/object/test_notification_url.py index e70b599..9a0d11b 100644 --- a/tests/model/generated/object/test_notification_url.py +++ b/tests/model/generated/object/test_notification_url.py @@ -116,7 +116,7 @@ def get_notification_url(self, file_path: str) -> NotificationUrl: self.assertTrue(self._KEY_NOTIFICATION_URL_MODEL in json_object) - return object_.NotificationUrl.from_json(json_string) + return NotificationUrl.from_json(json_string) def test_mutation_model(self): self.execute_notification_url_test( From a4999381bbcdfab436a1bb525dffb11b9a170c70 Mon Sep 17 00:00:00 2001 From: Angelo Melonas Date: Mon, 14 Sep 2020 16:02:46 +0200 Subject: [PATCH 4/9] feature/sdk_python#151 Small circular import fix. --- bunq/sdk/model/core/bunq_model.py | 2 +- setup.py | 2 +- tests/context/test_user_context.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bunq/sdk/model/core/bunq_model.py b/bunq/sdk/model/core/bunq_model.py index 5b2ac00..d19dffe 100644 --- a/bunq/sdk/model/core/bunq_model.py +++ b/bunq/sdk/model/core/bunq_model.py @@ -3,7 +3,7 @@ import typing from typing import Dict, List -from bunq import T +from bunq.sdk.util.type_alias import T from bunq.sdk.http.bunq_response import BunqResponse from bunq.sdk.http.bunq_response_raw import BunqResponseRaw from bunq.sdk.json import converter diff --git a/setup.py b/setup.py index 8f640c0..c4eec83 100644 --- a/setup.py +++ b/setup.py @@ -62,7 +62,7 @@ ], # Require Python version equal or higher than the requested version. - python_requires='>=3.5.3', + python_requires='>=3.7.0', # Keywords related to the project keywords='open-banking sepa bunq finance api payment', diff --git a/tests/context/test_user_context.py b/tests/context/test_user_context.py index 3a5945b..168c403 100644 --- a/tests/context/test_user_context.py +++ b/tests/context/test_user_context.py @@ -1,4 +1,3 @@ -from bunq.sdk.context.user_context import UserContext from tests.bunq_test import BunqSdkTestCase @@ -14,7 +13,8 @@ def setUpClass(cls): cls._API_CONTEXT = cls._get_api_context() def test_build_user_context(self): - user_context = UserContext(self._API_CONTEXT.session_context.user_id) + from bunq.sdk.context.user_context import UserContext + user_context = UserContext(self._API_CONTEXT.session_context.user_id, self._API_CONTEXT.session_context.user) user_context.refresh_user_context() self.assertIsNotNone(user_context.user_id) From b784552ff978f3efd47aeade20de2b53d7afaf55 Mon Sep 17 00:00:00 2001 From: Angelo Melonas Date: Wed, 16 Sep 2020 12:12:50 +0200 Subject: [PATCH 5/9] feature/sdk_python#149 Clean up. --- bunq/sdk/util/util.py | 13 +++++-------- tests/context/test_api_context.py | 9 ++++----- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/bunq/sdk/util/util.py b/bunq/sdk/util/util.py index e6a1567..c2bf4f7 100644 --- a/bunq/sdk/util/util.py +++ b/bunq/sdk/util/util.py @@ -8,7 +8,6 @@ from bunq.sdk.context.api_context import ApiContext, ApiEnvironmentType from bunq.sdk.exception.bunq_exception import BunqException from bunq.sdk.http.api_client import ApiClient -from bunq.sdk.model.generated import endpoint from bunq.sdk.model.generated.endpoint import SandboxUserPerson __UNIQUE_REQUEST_ID = "uniqueness-is-required" @@ -17,16 +16,15 @@ __FIELD_RESPONSE = "Response" __ENDPOINT_SANDBOX_USER_PERSON = "sandbox-user-person" +# Error constants _ERROR_COULD_NOT_CREATE_NEW_SANDBOX_USER = "Could not create new sandbox user." +_ERROR_ALL_FIELD_IS_NULL = 'All fields are null' def automatic_sandbox_install() -> ApiContext: sandbox_user = __generate_new_sandbox_user() - return ApiContext.create(ApiEnvironmentType.SANDBOX, - sandbox_user.api_key, - socket.gethostname() - ) + return ApiContext.create(ApiEnvironmentType.SANDBOX, sandbox_user.api_key, socket.gethostname()) def __generate_new_sandbox_user() -> SandboxUserPerson: @@ -44,8 +42,7 @@ def __generate_new_sandbox_user() -> SandboxUserPerson: if response.status_code is ApiClient.STATUS_CODE_OK: response_json = json.loads(response.text) - return SandboxUserPerson.from_json( - json.dumps(response_json[__FIELD_RESPONSE][__INDEX_FIRST][ - __FIELD_API_KEY])) + + return SandboxUserPerson.from_json(json.dumps(response_json[__FIELD_RESPONSE][__INDEX_FIRST][__FIELD_API_KEY])) raise BunqException(_ERROR_COULD_NOT_CREATE_NEW_SANDBOX_USER) diff --git a/tests/context/test_api_context.py b/tests/context/test_api_context.py index 9cec7f9..b3af76d 100644 --- a/tests/context/test_api_context.py +++ b/tests/context/test_api_context.py @@ -23,11 +23,10 @@ class TestApiContext(BunqSdkTestCase): def setUpClass(cls) -> None: super().setUpClass() cls._FILE_MODE_READ = ApiContext._FILE_MODE_READ - cls._API_CONTEXT = cls._get_api_context() - cls._TMP_FILE_PATH_FULL = (cls._get_directory_test_root() + - cls._TMP_FILE_PATH) + cls._API_CONTEXT: ApiContext = cls._get_api_context() + cls._TMP_FILE_PATH_FULL = (cls._get_directory_test_root() + cls._TMP_FILE_PATH) - def test_api_context_save(self): + def test_api_context_save(self) -> None: """ Converts an ApiContext to JSON data, saves the same ApiContext to a temporary file, and compares whether the JSON data is equal to the @@ -47,7 +46,7 @@ def test_api_context_save(self): self.assertEqual(context_retrieved, context_json) - def test_api_context_restore(self): + def test_api_context_restore(self) -> None: """ Saves an ApiContext to a temporary file, restores an ApiContext from that file, and compares whether the api_keys, tokens, and environment From d84182480581a783af4106bf55619a6e6306fe91 Mon Sep 17 00:00:00 2001 From: Angelo Melonas Date: Wed, 16 Sep 2020 16:13:55 +0200 Subject: [PATCH 6/9] feature/sdk_python#149 ApiContext is now saved and restored correctly. --- bunq/sdk/context/api_context.py | 8 ++- bunq/sdk/context/bunq_context.py | 5 +- bunq/sdk/context/session_context.py | 91 ++++++++++++++++++++++++--- bunq/sdk/json/converter.py | 17 +++-- bunq/sdk/json/installation_adapter.py | 2 +- bunq/sdk/model/core/session_server.py | 73 ++++++++++----------- bunq/sdk/security/security.py | 12 ++-- tests/context/test_user_context.py | 5 +- 8 files changed, 149 insertions(+), 64 deletions(-) diff --git a/bunq/sdk/context/api_context.py b/bunq/sdk/context/api_context.py index fb9a9d1..1f0fed0 100644 --- a/bunq/sdk/context/api_context.py +++ b/bunq/sdk/context/api_context.py @@ -145,9 +145,13 @@ def __register_device(self, def __initialize_session(self) -> None: from bunq.sdk.model.core.session_server import SessionServer - session_server = SessionServer.create(self).value - self._session_context = SessionContext(session_server) + + token = session_server.token + expiry_time = self._get_expiry_timestamp(session_server) + user = session_server.get_user_reference() + + self._session_context = SessionContext(token, expiry_time, user) @classmethod def _get_expiry_timestamp(cls, session_server: SessionServer) -> datetime.datetime: diff --git a/bunq/sdk/context/bunq_context.py b/bunq/sdk/context/bunq_context.py index 1f564af..76b3af0 100644 --- a/bunq/sdk/context/bunq_context.py +++ b/bunq/sdk/context/bunq_context.py @@ -17,7 +17,10 @@ def __init__(self) -> None: @classmethod def load_api_context(cls, api_context: ApiContext) -> None: cls._api_context = api_context - cls._user_context = UserContext(api_context.session_context.user_id, api_context.session_context.user) + cls._user_context = UserContext( + api_context.session_context.user_id, + api_context.session_context.get_user_reference() + ) cls._user_context.init_main_monetary_account() @classmethod diff --git a/bunq/sdk/context/session_context.py b/bunq/sdk/context/session_context.py index 99e7bad..44f3adf 100644 --- a/bunq/sdk/context/session_context.py +++ b/bunq/sdk/context/session_context.py @@ -1,14 +1,28 @@ +from __future__ import annotations + import datetime +from typing import Optional -from bunq.sdk.model.core.session_server import SessionServer +from bunq.sdk.exception.bunq_exception import BunqException +from bunq.sdk.model.core.bunq_model import BunqModel +from bunq.sdk.model.core.session_token import SessionToken +from bunq.sdk.model.generated.endpoint import UserPerson, UserCompany, UserApiKey, UserPaymentServiceProvider class SessionContext: - def __init__(self, session_server: SessionServer) -> None: - self._token = session_server.token.token - self._expiry_time = self._get_expiry_timestamp(session_server) - self._user_id = session_server.get_referenced_user().id_ - self._user = session_server.get_referenced_user() + """ + :type _token: str + :type _expiry_time: datetime.datetime + :type _user_id: int + :type _user_person: UserPerson|None + :type _user_company: UserCompany|None + :type _user_api_key: UserApiKey|None + :type _user_payment_service_provider: UserPaymentServiceProvider|None + """ + + # Error constants + _ERROR_ALL_FIELD_IS_NULL = 'All fields are null' + _ERROR_UNEXPECTED_USER_INSTANCE = '"{}" is unexpected user instance.' @property def token(self) -> str: @@ -23,5 +37,66 @@ def user_id(self) -> int: return self._user_id @property - def user(self): - return self._user + def user_person(self) -> Optional[UserPerson]: + return self._user_person + + @property + def user_company(self) -> Optional[UserCompany]: + return self._user_company + + @property + def user_api_key(self) -> Optional[UserApiKey]: + return self._user_api_key + + @property + def user_payment_service_provider(self) -> Optional[UserPaymentServiceProvider]: + return self._user_payment_service_provider + + def __init__(self, token: SessionToken, expiry_time: datetime.datetime, user: BunqModel) -> None: + self._user_person = None + self._user_company = None + self._user_api_key = None + self._user_payment_service_provider = None + self._token = token.token + self._expiry_time = expiry_time + self._user_id = self.__get_user_id(user) + self.__set_user(user) + + def __get_user_id(self, user: BunqModel) -> int: + if isinstance(user, UserPerson): + return user.id_ + + if isinstance(user, UserCompany): + return user.id_ + + if isinstance(user, UserApiKey): + return user.id_ + + if isinstance(user, UserPaymentServiceProvider): + return user.id_ + + raise BunqException(self._ERROR_UNEXPECTED_USER_INSTANCE) + + def __set_user(self, user: BunqModel): + if isinstance(user, UserPerson): + self._user_person = user + elif isinstance(user, UserCompany): + self._user_company = user + elif isinstance(user, UserApiKey): + self._user_api_key = user + elif isinstance(user, UserPaymentServiceProvider): + self._user_payment_service_provider = user + else: + raise BunqException(self._ERROR_UNEXPECTED_USER_INSTANCE) + + def get_user_reference(self) -> BunqModel: + if self.user_person is not None: + return self.user_person + elif self.user_company is not None: + return self.user_company + elif self.user_api_key is not None: + return self.user_api_key + elif self.user_payment_service_provider is not None: + return self.user_payment_service_provider + else: + raise BunqException(self._ERROR_ALL_FIELD_IS_NULL) diff --git a/bunq/sdk/json/converter.py b/bunq/sdk/json/converter.py index 73b8412..7e04f26 100644 --- a/bunq/sdk/json/converter.py +++ b/bunq/sdk/json/converter.py @@ -7,7 +7,7 @@ import typing import warnings from types import ModuleType -from typing import Type, Optional, Callable, Generator, Dict, Match, List, Union, Generic +from typing import Type, Optional, Callable, Generator, Dict, Match, List, Union, Generic, Any from bunq.sdk.exception.bunq_exception import BunqException from bunq.sdk.util.type_alias import T, JsonValue @@ -22,6 +22,7 @@ class JsonAdapter(Generic[T]): # Error constants _ERROR_COULD_NOT_FIND_CLASS = 'Could not find class: {}' + _ERROR_MISSING_DOC_COMMENT = 'A doc :type is missing for {} in class {}' # Maps to store custom serializers and deserializers _custom_serializers = {} @@ -41,15 +42,14 @@ class JsonAdapter(Generic[T]): _PREFIX_KEY_PROTECTED = '_' # Constants to fetch param types from the docstrings - _TEMPLATE_PATTERN_PARAM_TYPES = ':type (_?{}):[\s\n\r]+([\w.]+)(?:\[([\w.]+)\])?' - _PATTERN_PARAM_NAME_TYPED_ANY = ':type (\w+):' + _TEMPLATE_PATTERN_PARAM_TYPES = ':type (_?{}):[\\s\\n\\r]+([\\w.]+)(?:\\[([\\w.]+)\\])?' + _PATTERN_PARAM_NAME_TYPED_ANY = ':type (\\w+):' _SUBMATCH_INDEX_NAME = 1 _SUBMATCH_INDEX_TYPE_MAIN = 2 _SUBMATCH_INDEX_TYPE_SUB = 3 # List of builtin type names - _TYPE_NAMES_BUILTIN = {'int', 'bool', 'float', 'str', 'list', 'dict', - 'bytes', 'unicode'} + _TYPE_NAMES_BUILTIN = {'int', 'bool', 'float', 'str', 'list', 'dict', 'bytes', 'unicode'} # Delimiter between modules in class paths _DELIMITER_MODULE = '.' @@ -187,7 +187,12 @@ def _fetch_attribute_specs_from_doc(cls, cls_in: Type[T], attribute_name: str) -> Optional[ValueSpecs]: pattern = cls._TEMPLATE_PATTERN_PARAM_TYPES.format(attribute_name) - match = re.search(pattern, cls_in.__doc__) + doc_type = cls_in.__doc__ + + if doc_type is None: + raise BunqException(cls._ERROR_MISSING_DOC_COMMENT.format(attribute_name, cls_in)) + + match = re.search(pattern, doc_type) if match is not None: return ValueSpecs( diff --git a/bunq/sdk/json/installation_adapter.py b/bunq/sdk/json/installation_adapter.py index 6df63ea..d0d93b8 100644 --- a/bunq/sdk/json/installation_adapter.py +++ b/bunq/sdk/json/installation_adapter.py @@ -51,5 +51,5 @@ def serialize(cls, installation: Installation) -> List: return [ {cls._FIELD_ID: converter.serialize(installation.id_)}, {cls._FIELD_TOKEN: converter.serialize(installation.token)}, - {cls._FIELD_SERVER_PUBLIC_KEY: converter.serialize(installation.server_public_key), }, + {cls._FIELD_SERVER_PUBLIC_KEY: converter.serialize(installation.server_public_key)}, ] diff --git a/bunq/sdk/model/core/session_server.py b/bunq/sdk/model/core/session_server.py index c49ed4b..286b89d 100644 --- a/bunq/sdk/model/core/session_server.py +++ b/bunq/sdk/model/core/session_server.py @@ -20,7 +20,7 @@ class SessionServer(BunqModel): :type _user_person: UserPerson|None :type _user_company: UserCompany|None :type _user_api_key: UserApiKey|None - :type _user_payment_service_provider:UserPaymentServiceProvider|None + :type _user_payment_service_provider: UserPaymentServiceProvider|None """ # Endpoint name. @@ -32,28 +32,20 @@ class SessionServer(BunqModel): # Error constants _ERROR_ALL_FIELD_IS_NULL = 'All fields are null' - def __init__(self) -> None: - self._id_ = None - self._token = None - self._user_person = None - self._user_company = None - self._user_api_key = None - self._user_payment_service_provider = None - @property - def id_(self) -> Id: + def id_(self) -> Optional[Id]: return self._id_ @property - def token(self) -> SessionToken: + def token(self) -> Optional[SessionToken]: return self._token @property - def user_person(self) -> UserPerson: + def user_person(self) -> Optional[UserPerson]: return self._user_person @property - def user_company(self) -> UserCompany: + def user_company(self) -> Optional[UserCompany]: return self._user_company @property @@ -61,11 +53,20 @@ def user_api_key(self) -> Optional[UserApiKey]: return self._user_api_key @property - def user_payment_service_provider(self) -> UserPaymentServiceProvider: + def user_payment_service_provider(self) -> Optional[UserPaymentServiceProvider]: return self._user_payment_service_provider + def __init__(self) -> None: + self._user_person = None + self._user_company = None + self._user_api_key = None + self._user_payment_service_provider = None + self._token = None + self._id_ = None + @classmethod def create(cls, api_context: ApiContext) -> BunqResponse[SessionServer]: + cls.__init__(cls) api_client = ApiClient(api_context) body_bytes = cls.generate_request_body_bytes(api_context.api_key) response_raw = api_client.post(cls._ENDPOINT_URL_POST, body_bytes, {}) @@ -79,35 +80,27 @@ def generate_request_body_bytes(cls, secret: str) -> bytes: def is_all_field_none(self) -> bool: if self.id_ is not None: return False - - if self.token is not None: + elif self.token is not None: return False - - if self.user_person is not None: + elif self.user_person is not None: return False - - if self.user_company is not None: + elif self.user_company is not None: return False - - if self.user_payment_service_provider is not None: + elif self.user_api_key is not None: return False - - if self.user_api_key is not None: + elif self.user_payment_service_provider is not None: return False + else: + return True - return True - - def get_referenced_user(self) -> BunqModel: - if self._user_person is not None: - return self._user_person - - if self._user_company is not None: - return self._user_company - - if self._user_payment_service_provider is not None: - return self._user_payment_service_provider - - if self._user_api_key is not None: - return self._user_api_key - - raise BunqException(self._ERROR_ALL_FIELD_IS_NULL) + def get_user_reference(self) -> BunqModel: + if self.user_person is not None: + return self.user_person + elif self.user_company is not None: + return self.user_company + elif self.user_api_key is not None: + return self.user_api_key + elif self.user_payment_service_provider is not None: + return self.user_payment_service_provider + else: + raise BunqException(self._ERROR_ALL_FIELD_IS_NULL) diff --git a/bunq/sdk/security/security.py b/bunq/sdk/security/security.py index 1febf65..c658cb1 100644 --- a/bunq/sdk/security/security.py +++ b/bunq/sdk/security/security.py @@ -16,6 +16,7 @@ from Cryptodome.PublicKey import RSA from Cryptodome.PublicKey.RSA import RsaKey from Cryptodome.Signature import PKCS1_v1_5 +from requests.structures import CaseInsensitiveDict from bunq.sdk.exception.bunq_exception import BunqException @@ -58,6 +59,7 @@ _HEADER_CLIENT_ENCRYPTION_HMAC = 'X-Bunq-Client-Encryption-Hmac' _HEADER_SERVER_SIGNATURE = 'X-Bunq-Server-Signature' + def generate_rsa_private_key() -> RsaKey: return RSA.generate(_RSA_KEY_SIZE) @@ -177,7 +179,7 @@ def _add_header_client_encryption_hmac(request_bytes: bytes, def validate_response(public_key_server: RsaKey, status_code: int, body_bytes: bytes, - headers: Dict[str, str]) -> None: + headers: CaseInsensitiveDict[str, str]) -> None: if is_valid_response_header_with_body(public_key_server, status_code, body_bytes, headers): return elif is_valid_response_body(public_key_server, body_bytes, headers): @@ -189,7 +191,7 @@ def validate_response(public_key_server: RsaKey, def is_valid_response_header_with_body(public_key_server: RsaKey, status_code: int, body_bytes: bytes, - headers: Dict[str, str]) -> bool: + headers: CaseInsensitiveDict[str, str]) -> bool: head_bytes = _generate_response_head_bytes(status_code, headers) bytes_signed = head_bytes + body_bytes signer = PKCS1_v1_5.pkcs1_15.new(public_key_server) @@ -205,8 +207,8 @@ def is_valid_response_header_with_body(public_key_server: RsaKey, def is_valid_response_body(public_key_server: RsaKey, - body_bytes: bytes, - headers: Dict[str, str]) -> bool: + body_bytes: bytes, + headers: CaseInsensitiveDict[str, str]) -> bool: signer = PKCS1_v1_5.pkcs1_15.new(public_key_server) digest = SHA256.new() digest.update(body_bytes) @@ -220,7 +222,7 @@ def is_valid_response_body(public_key_server: RsaKey, def _generate_response_head_bytes(status_code: int, - headers: Dict[str, str]) -> bytes: + headers: CaseInsensitiveDict[str, str]) -> bytes: head_string = str(status_code) + _DELIMITER_NEWLINE header_tuples = sorted((k, headers[k]) for k in headers) diff --git a/tests/context/test_user_context.py b/tests/context/test_user_context.py index 168c403..0a35573 100644 --- a/tests/context/test_user_context.py +++ b/tests/context/test_user_context.py @@ -14,7 +14,10 @@ def setUpClass(cls): def test_build_user_context(self): from bunq.sdk.context.user_context import UserContext - user_context = UserContext(self._API_CONTEXT.session_context.user_id, self._API_CONTEXT.session_context.user) + user_context = UserContext( + self._API_CONTEXT.session_context.user_id, + self._API_CONTEXT.session_context.get_user_reference() + ) user_context.refresh_user_context() self.assertIsNotNone(user_context.user_id) From 68b13948b4e5f3f3e59eff0ff92a0f6d5665a8d6 Mon Sep 17 00:00:00 2001 From: Angelo Melonas Date: Thu, 17 Sep 2020 12:44:45 +0200 Subject: [PATCH 7/9] Regenerated code based on the latest tag. --- bunq/sdk/model/generated/endpoint.py | 95 ++++------------------------ 1 file changed, 12 insertions(+), 83 deletions(-) diff --git a/bunq/sdk/model/generated/endpoint.py b/bunq/sdk/model/generated/endpoint.py index 019321d..2af7c01 100644 --- a/bunq/sdk/model/generated/endpoint.py +++ b/bunq/sdk/model/generated/endpoint.py @@ -8629,6 +8629,8 @@ class Company(BunqModel): :type _chamber_of_commerce_number: str :param _legal_form: The company's legal form. :type _legal_form: str + :param _subscription_type: The subscription type for the company. + :type _subscription_type: str :param _avatar_uuid: The public UUID of the company's avatar. :type _avatar_uuid: str :param _UserCompany: @@ -8649,6 +8651,7 @@ class Company(BunqModel): FIELD_UBO = "ubo" FIELD_CHAMBER_OF_COMMERCE_NUMBER = "chamber_of_commerce_number" FIELD_LEGAL_FORM = "legal_form" + FIELD_SUBSCRIPTION_TYPE = "subscription_type" FIELD_AVATAR_UUID = "avatar_uuid" # Object type. @@ -8662,10 +8665,11 @@ class Company(BunqModel): _ubo_field_for_request = None _chamber_of_commerce_number_field_for_request = None _legal_form_field_for_request = None + _subscription_type_field_for_request = None _avatar_uuid_field_for_request = None def __init__(self, name, address_main, address_postal, country, legal_form, ubo=None, - chamber_of_commerce_number=None, avatar_uuid=None): + chamber_of_commerce_number=None, subscription_type=None, avatar_uuid=None): """ :param name: The company name. :type name: str @@ -8682,6 +8686,8 @@ def __init__(self, name, address_main, address_postal, country, legal_form, ubo= :type ubo: list[object_.Ubo] :param chamber_of_commerce_number: The company's chamber of commerce number. :type chamber_of_commerce_number: str + :param subscription_type: The subscription type for the company. + :type subscription_type: str :param avatar_uuid: The public UUID of the company's avatar. :type avatar_uuid: str """ @@ -8693,11 +8699,12 @@ def __init__(self, name, address_main, address_postal, country, legal_form, ubo= self._legal_form_field_for_request = legal_form self._ubo_field_for_request = ubo self._chamber_of_commerce_number_field_for_request = chamber_of_commerce_number + self._subscription_type_field_for_request = subscription_type self._avatar_uuid_field_for_request = avatar_uuid @classmethod def create(cls, name, address_main, address_postal, country, legal_form, ubo=None, chamber_of_commerce_number=None, - avatar_uuid=None, custom_headers=None): + subscription_type=None, avatar_uuid=None, custom_headers=None): """ :type user_id: int :param name: The company name. @@ -8716,6 +8723,8 @@ def create(cls, name, address_main, address_postal, country, legal_form, ubo=Non :param chamber_of_commerce_number: The company's chamber of commerce number. :type chamber_of_commerce_number: str + :param subscription_type: The subscription type for the company. + :type subscription_type: str :param avatar_uuid: The public UUID of the company's avatar. :type avatar_uuid: str :type custom_headers: dict[str, str]|None @@ -8734,6 +8743,7 @@ def create(cls, name, address_main, address_postal, country, legal_form, ubo=Non cls.FIELD_UBO: ubo, cls.FIELD_CHAMBER_OF_COMMERCE_NUMBER: chamber_of_commerce_number, cls.FIELD_LEGAL_FORM: legal_form, + cls.FIELD_SUBSCRIPTION_TYPE: subscription_type, cls.FIELD_AVATAR_UUID: avatar_uuid } request_map_string = converter.class_to_json(request_map) @@ -34408,77 +34418,6 @@ def from_json(json_str): return converter.json_to_class(SandboxUserPerson, json_str) -class SandboxUser(BunqModel): - """ - Used to create a sandbox user. - - :param _api_key: The API key of the newly created sandbox user. - :type _api_key: str - """ - - # Endpoint constants. - _ENDPOINT_URL_CREATE = "sandbox-user" - - # Object type. - _OBJECT_TYPE_POST = "ApiKey" - - _api_key = None - - @classmethod - def create(cls, custom_headers=None): - """ - :type custom_headers: dict[str, str]|None - - :rtype: BunqResponseSandboxUser - """ - - if custom_headers is None: - custom_headers = {} - - request_map = { - - } - request_map_string = converter.class_to_json(request_map) - request_map_string = cls._remove_field_for_request(request_map_string) - - api_client = ApiClient(cls._get_api_context()) - request_bytes = request_map_string.encode() - endpoint_url = cls._ENDPOINT_URL_CREATE - response_raw = api_client.post(endpoint_url, request_bytes, custom_headers) - - return BunqResponseSandboxUser.cast_from_bunq_response( - cls._from_json(response_raw, cls._OBJECT_TYPE_POST) - ) - - @property - def api_key(self): - """ - :rtype: str - """ - - return self._api_key - - def is_all_field_none(self): - """ - :rtype: bool - """ - - if self._api_key is not None: - return False - - return True - - @staticmethod - def from_json(json_str): - """ - :type json_str: str - - :rtype: SandboxUser - """ - - return converter.json_to_class(SandboxUser, json_str) - - class ScheduleUser(BunqModel): """ view for reading the scheduled definitions. @@ -41014,16 +40953,6 @@ def value(self): return super().value -class BunqResponseSandboxUser(BunqResponse): - @property - def value(self): - """ - :rtype: SandboxUser - """ - - return super().value - - class BunqResponseScheduleUserList(BunqResponse): @property def value(self): From 57afde3401d31110079d3167a9dbc4b3ec74e8ac Mon Sep 17 00:00:00 2001 From: Angelo Melonas Date: Thu, 17 Sep 2020 12:45:33 +0200 Subject: [PATCH 8/9] Regenerated changelog. --- CHANGELOG.md | 64 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1205346..8f18749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,25 @@ ## [Unreleased](https://github.com/bunq/sdk_python/tree/HEAD) -[Full Changelog](https://github.com/bunq/sdk_python/compare/1.14.0...HEAD) +[Full Changelog](https://github.com/bunq/sdk_python/compare/1.14.1...HEAD) + +**Implemented enhancements:** + +- Replace /sandbox-user with /sandbox-user-person and /sandbox-user-company [\#151](https://github.com/bunq/sdk_python/issues/151) + +**Fixed bugs:** + +- Remove \_\_get\_user\_object call immediately after UserContext creation \(reduce rate-limit hits\) [\#149](https://github.com/bunq/sdk_python/issues/149) +- Feature/sdk python\#149 save/restore User in SessionContext from file [\#153](https://github.com/bunq/sdk_python/pull/153) ([angelomelonas](https://github.com/angelomelonas)) + +**Merged pull requests:** + +- feature/sdk\_python\#151 Deprecated SandboxUser and replaced it with SandboxUserPerson/Company [\#152](https://github.com/bunq/sdk_python/pull/152) ([angelomelonas](https://github.com/angelomelonas)) +- feature/sdk\_python\#149 Remove get\_user\_object call directly after UserContext creation [\#150](https://github.com/bunq/sdk_python/pull/150) ([angelomelonas](https://github.com/angelomelonas)) + +## [1.14.1](https://github.com/bunq/sdk_python/tree/1.14.1) (2020-08-20) + +[Full Changelog](https://github.com/bunq/sdk_python/compare/1.14.0...1.14.1) **Implemented enhancements:** @@ -126,32 +144,18 @@ **Implemented enhancements:** -- \[python\] Update examples in readme [\#87](https://github.com/bunq/sdk_python/issues/87) -- It is not possible to refresh userContext data [\#79](https://github.com/bunq/sdk_python/issues/79) -- Optimise test framework [\#78](https://github.com/bunq/sdk_python/issues/78) -- Add more example scripts [\#68](https://github.com/bunq/sdk_python/issues/68) -- Auto save the session after automatic session reset has been executed  [\#65](https://github.com/bunq/sdk_python/issues/65) -- Add optional parameters to constructor  [\#55](https://github.com/bunq/sdk_python/issues/55) -- Monetary account joint cannot be retrieved. [\#52](https://github.com/bunq/sdk_python/issues/52) -- Endpoint HTTP methods should not be static [\#41](https://github.com/bunq/sdk_python/issues/41) -- Name field for pointer counterparty [\#29](https://github.com/bunq/sdk_python/issues/29) - Added method to refresh user context data. \(bunq/sdk\_python\#79\) [\#80](https://github.com/bunq/sdk_python/pull/80) ([OGKevin](https://github.com/OGKevin)) -**Fixed bugs:** - -- bunq-sdk 0.12.4 package on PyPI contains code from 0.13.0 [\#88](https://github.com/bunq/sdk_python/issues/88) -- Can not construct a BunqMeTabEntry to create a BunqMeTab [\#77](https://github.com/bunq/sdk_python/issues/77) -- ImportError: cannot import name 'JSONDecodeError' [\#72](https://github.com/bunq/sdk_python/issues/72) - **Closed issues:** +- Add oath support. [\#102](https://github.com/bunq/sdk_python/issues/102) - Update Sandbox API key procedure [\#100](https://github.com/bunq/sdk_python/issues/100) -- Python 3.4.2: ImportError: cannot import name 'context' \(cyclic import?\) [\#73](https://github.com/bunq/sdk_python/issues/73) **Merged pull requests:** - Oauth bunq/sdk\_python\#102 [\#103](https://github.com/bunq/sdk_python/pull/103) ([OGKevin](https://github.com/OGKevin)) - Update Sandbox API key procedure. \(bunq/sdk\_python\#100\) [\#101](https://github.com/bunq/sdk_python/pull/101) ([sandervdo](https://github.com/sandervdo)) +- Move to new sandbox bunq/sdk\_python\#98 [\#99](https://github.com/bunq/sdk_python/pull/99) ([OGKevin](https://github.com/OGKevin)) - Updated readme to point to tinker for examples. \(bunq/sdk\_python\#87\) [\#95](https://github.com/bunq/sdk_python/pull/95) ([OGKevin](https://github.com/OGKevin)) - Fix monetary account joint retrieval bunq/sdk\_python\#52 [\#94](https://github.com/bunq/sdk_python/pull/94) ([OGKevin](https://github.com/OGKevin)) - Fix supperfouls fields error bunq/sdk\_python\#77 [\#91](https://github.com/bunq/sdk_python/pull/91) ([OGKevin](https://github.com/OGKevin)) @@ -165,19 +169,33 @@ [Full Changelog](https://github.com/bunq/sdk_python/compare/0.13.0...0.13.1) +**Implemented enhancements:** + +- \[python\] Update examples in readme [\#87](https://github.com/bunq/sdk_python/issues/87) +- It is not possible to refresh userContext data [\#79](https://github.com/bunq/sdk_python/issues/79) +- Optimise test framework [\#78](https://github.com/bunq/sdk_python/issues/78) +- Add more example scripts [\#68](https://github.com/bunq/sdk_python/issues/68) +- Auto save the session after automatic session reset has been executed  [\#65](https://github.com/bunq/sdk_python/issues/65) +- Add optional parameters to constructor  [\#55](https://github.com/bunq/sdk_python/issues/55) +- Monetary account joint cannot be retrieved. [\#52](https://github.com/bunq/sdk_python/issues/52) +- Endpoint HTTP methods should not be static [\#41](https://github.com/bunq/sdk_python/issues/41) +- Name field for pointer counterparty [\#29](https://github.com/bunq/sdk_python/issues/29) + +**Fixed bugs:** + +- bunq-sdk 0.12.4 package on PyPI contains code from 0.13.0 [\#88](https://github.com/bunq/sdk_python/issues/88) +- Can not construct a BunqMeTabEntry to create a BunqMeTab [\#77](https://github.com/bunq/sdk_python/issues/77) +- ImportError: cannot import name 'JSONDecodeError' [\#72](https://github.com/bunq/sdk_python/issues/72) + **Closed issues:** +- Move to new sandbox env. [\#98](https://github.com/bunq/sdk_python/issues/98) - Bunq sdk release request [\#97](https://github.com/bunq/sdk_python/issues/97) - reopening of question \#12 generated.Payment.FIELD\_COUNTERPARTY\_ALIAS not working with IBAN [\#96](https://github.com/bunq/sdk_python/issues/96) - Update samples and readme [\#93](https://github.com/bunq/sdk_python/issues/93) - bunq.sdk.exception.BunqException: ApiContext has not been loaded. Please load ApiContext in BunqContext [\#92](https://github.com/bunq/sdk_python/issues/92) - Sample for reading/using shared accounts [\#90](https://github.com/bunq/sdk_python/issues/90) -- Add oath support. [\#102](https://github.com/bunq/sdk_python/issues/102) -- Move to new sandbox env. [\#98](https://github.com/bunq/sdk_python/issues/98) - -**Merged pull requests:** - -- Move to new sandbox bunq/sdk\_python\#98 [\#99](https://github.com/bunq/sdk_python/pull/99) ([OGKevin](https://github.com/OGKevin)) +- Python 3.4.2: ImportError: cannot import name 'context' \(cyclic import?\) [\#73](https://github.com/bunq/sdk_python/issues/73) ## [0.13.0](https://github.com/bunq/sdk_python/tree/0.13.0) (2018-03-20) From 269aed472197f63612213564618dfbf5d7896d21 Mon Sep 17 00:00:00 2001 From: Angelo Melonas Date: Thu, 17 Sep 2020 12:46:27 +0200 Subject: [PATCH 9/9] Version bump to 1.14.18. --- bunq/sdk/http/api_client.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bunq/sdk/http/api_client.py b/bunq/sdk/http/api_client.py index 5864a2f..234385f 100644 --- a/bunq/sdk/http/api_client.py +++ b/bunq/sdk/http/api_client.py @@ -59,7 +59,7 @@ class ApiClient: HEADER_RESPONSE_ID_LOWER_CASED = 'x-bunq-client-response-id' # Default header values - USER_AGENT_BUNQ = 'bunq-sdk-python/1.14.1' + USER_AGENT_BUNQ = 'bunq-sdk-python/1.14.18' GEOLOCATION_ZERO = '0 0 0 0 NL' LANGUAGE_EN_US = 'en_US' REGION_NL_NL = 'nl_NL' diff --git a/setup.py b/setup.py index c4eec83..4722654 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ # Versions should comply with PEP440. For a discussion on single-sourcing # the version across setup.py and the project code, see # https://packaging.python.org/en/latest/single_source_version.html - version='1.14.1', + version='1.14.18', description='bunq Python SDK', long_description=long_description,