Skip to content

Commit

Permalink
Merge branch 'release/1.14.18'
Browse files Browse the repository at this point in the history
  • Loading branch information
angelomelonas committed Sep 17, 2020
2 parents 78390d2 + 269aed4 commit f6202e4
Show file tree
Hide file tree
Showing 24 changed files with 263 additions and 258 deletions.
64 changes: 41 additions & 23 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**

Expand Down Expand Up @@ -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))
Expand All @@ -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)

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
)
Expand All @@ -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)
Expand Down
7 changes: 4 additions & 3 deletions bunq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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)

Expand Down
29 changes: 8 additions & 21 deletions bunq/sdk/context/api_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, UserPaymentServiceProvider
from bunq.sdk.model.generated.endpoint import UserCredentialPasswordIp, Session
from bunq.sdk.security import security

if typing.TYPE_CHECKING:
Expand Down Expand Up @@ -90,7 +89,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

Expand All @@ -104,8 +103,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(
Expand All @@ -117,7 +115,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

Expand Down Expand Up @@ -147,24 +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
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
token = session_server.token
expiry_time = self._get_expiry_timestamp(session_server)
user_id = session_server.get_referenced_user().id_
user = session_server.get_user_reference()

self._session_context = SessionContext(token, expiry_time, user_id)
self._session_context = SessionContext(token, expiry_time, user)

@classmethod
def _get_expiry_timestamp(cls, session_server: SessionServer) -> datetime.datetime:
Expand Down Expand Up @@ -237,7 +224,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:
Expand Down
5 changes: 4 additions & 1 deletion bunq/sdk/context/bunq_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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
Expand Down
87 changes: 80 additions & 7 deletions bunq/sdk/context/session_context.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
from __future__ import annotations

import datetime
from typing import Optional

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:
"""
: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
"""

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
# Error constants
_ERROR_ALL_FIELD_IS_NULL = 'All fields are null'
_ERROR_UNEXPECTED_USER_INSTANCE = '"{}" is unexpected user instance.'

@property
def token(self) -> str:
Expand All @@ -27,3 +35,68 @@ def expiry_time(self) -> datetime.datetime:
@property
def user_id(self) -> int:
return self._user_id

@property
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)
20 changes: 10 additions & 10 deletions bunq/sdk/context/user_context.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
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:
_ERROR_UNEXPECTED_USER_INSTANCE = '"{}" is unexpected user instance.'
_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
self._user_api_key = None
self._user_payment_service_provider = None
self._primary_monetary_account = None

self._set_user(self.__get_user_object())
self._set_user(user)

@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:
Expand All @@ -44,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:
Expand Down
Loading

0 comments on commit f6202e4

Please sign in to comment.