Skip to content

Commit 3d934ba

Browse files
committed
Merge branch 'release-0.11.0'
2 parents 0a9e998 + 430f474 commit 3d934ba

21 files changed

+1817
-337
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bunq/sdk/model/generated linguist-generated=true

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.10.0
1+
0.11.0

bunq/sdk/client.py

Lines changed: 161 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import uuid
2-
3-
# Due to compatibility requirements, we are importing a class here.
4-
try:
5-
from json import JSONDecodeError
6-
except ImportError:
7-
from simplejson import JSONDecodeError
2+
from json import JSONDecodeError
3+
from urllib.parse import urlencode
84

95
import requests
106

11-
from bunq.sdk.json import converter
12-
from bunq.sdk import security
137
from bunq.sdk import context
148
from bunq.sdk import exception
9+
from bunq.sdk import security
10+
from bunq.sdk.json import converter
1511

1612

1713
class ApiClient(object):
@@ -35,7 +31,7 @@ class ApiClient(object):
3531
HEADER_AUTHENTICATION = 'X-Bunq-Client-Authentication'
3632

3733
# Default header values
38-
_USER_AGENT_BUNQ = 'bunq-sdk-python/0.10.0'
34+
_USER_AGENT_BUNQ = 'bunq-sdk-python/0.11.0'
3935
_GEOLOCATION_ZERO = '0 0 0 0 NL'
4036
_LANGUAGE_EN_US = 'en_US'
4137
_REGION_NL_NL = 'nl_NL'
@@ -47,6 +43,9 @@ class ApiClient(object):
4743
_METHOD_GET = 'GET'
4844
_METHOD_DELETE = 'DELETE'
4945

46+
# Delimiter between path and params in URL
47+
_DELIMITER_URL_QUERY = '?'
48+
5049
# Status code for successful execution
5150
_STATUS_CODE_OK = 200
5251

@@ -76,30 +75,35 @@ def post(self, uri_relative, request_bytes, custom_headers):
7675
self._METHOD_POST,
7776
uri_relative,
7877
request_bytes,
78+
{},
7979
custom_headers
8080
)
8181

82-
def _request(self, method, uri_relative, request_bytes, custom_headers):
82+
def _request(self, method, uri_relative, request_bytes, params,
83+
custom_headers):
8384
"""
8485
:type method: str
8586
:type uri_relative: str
8687
:type request_bytes: bytes
88+
:type params: dict[str, str]
8789
:type custom_headers: dict[str, str]
8890
8991
:return: BunqResponseRaw
9092
"""
9193

94+
uri_relative_with_params = self._append_params_to_uri(uri_relative,
95+
params)
9296
self._api_context.ensure_session_active()
9397
all_headers = self._get_all_headers(
9498
method,
95-
uri_relative,
99+
uri_relative_with_params,
96100
request_bytes,
97101
custom_headers
98102
)
99103

100104
response = requests.request(
101105
method,
102-
self._get_uri_full(uri_relative),
106+
self._get_uri_full(uri_relative_with_params),
103107
data=request_bytes,
104108
headers=all_headers,
105109
proxies={self._FIELD_PROXY_HTTPS: self._api_context.proxy_url}
@@ -117,6 +121,20 @@ def _request(self, method, uri_relative, request_bytes, custom_headers):
117121

118122
return self._create_bunq_response_raw(response)
119123

124+
@classmethod
125+
def _append_params_to_uri(cls, uri, params):
126+
"""
127+
:type uri: str
128+
:type params: dict[str, str]
129+
130+
:rtype: str
131+
"""
132+
133+
if params:
134+
return uri + cls._DELIMITER_URL_QUERY + urlencode(params)
135+
136+
return uri
137+
120138
def _get_all_headers(self, method, endpoint, request_bytes, custom_headers):
121139
"""
122140
:type method: str
@@ -242,12 +260,14 @@ def put(self, uri_relative, request_bytes, custom_headers):
242260
self._METHOD_PUT,
243261
uri_relative,
244262
request_bytes,
263+
{},
245264
custom_headers
246265
)
247266

248-
def get(self, uri_relative, custom_headers):
267+
def get(self, uri_relative, params, custom_headers):
249268
"""
250269
:type uri_relative: str
270+
:type params: dict[str, str]
251271
:type custom_headers: dict[str, str]
252272
253273
:rtype: BunqResponseRaw
@@ -257,6 +277,7 @@ def get(self, uri_relative, custom_headers):
257277
self._METHOD_GET,
258278
uri_relative,
259279
self._BYTES_EMPTY,
280+
params,
260281
custom_headers
261282
)
262283

@@ -272,6 +293,7 @@ def delete(self, uri_relative, custom_headers):
272293
self._METHOD_DELETE,
273294
uri_relative,
274295
self._BYTES_EMPTY,
296+
{},
275297
custom_headers
276298
)
277299

@@ -312,16 +334,19 @@ class BunqResponse(object):
312334
"""
313335
:type _value: T
314336
:type _headers: dict[str, str]
337+
:type _pagination: Pagination|None
315338
"""
316339

317-
def __init__(self, value, headers):
340+
def __init__(self, value, headers, pagination=None):
318341
"""
319342
:type value: T
320343
:type headers: dict[str, str]
344+
:type pagination Pagination|None
321345
"""
322346

323347
self._value = value
324348
self._headers = headers
349+
self._pagination = pagination
325350

326351
@property
327352
def value(self):
@@ -338,3 +363,125 @@ def headers(self):
338363
"""
339364

340365
return self._headers
366+
367+
@property
368+
def pagination(self):
369+
"""
370+
:rtype: Pagination
371+
"""
372+
373+
return self._pagination
374+
375+
376+
class Pagination(object):
377+
"""
378+
:type older_id: int|None
379+
:type newer_id: int|None
380+
:type future_id: int|None
381+
:type count: int|None
382+
"""
383+
384+
# Error constants
385+
_ERROR_NO_PREVIOUS_PAGE = 'Could not generate previous page URL params: ' \
386+
'there is no previous page.'
387+
_ERROR_NO_NEXT_PAGE = 'Could not generate next page URL params: ' \
388+
'there is no next page.'
389+
390+
# URL Param constants
391+
PARAM_OLDER_ID = 'older_id'
392+
PARAM_NEWER_ID = 'newer_id'
393+
PARAM_COUNT = 'count'
394+
395+
def __init__(self):
396+
self.older_id = None
397+
self.newer_id = None
398+
self.future_id = None
399+
self.count = None
400+
401+
@property
402+
def url_params_previous_page(self):
403+
"""
404+
:rtype: dict[str, str]
405+
"""
406+
407+
self.assert_has_previous_page()
408+
409+
params = {self.PARAM_OLDER_ID: str(self.older_id)}
410+
self._add_count_to_params_if_needed(params)
411+
412+
return params
413+
414+
def assert_has_previous_page(self):
415+
"""
416+
:raise: exception.BunqException
417+
"""
418+
419+
if not self.has_previous_page():
420+
raise exception.BunqException(self._ERROR_NO_PREVIOUS_PAGE)
421+
422+
def has_previous_page(self):
423+
"""
424+
:rtype: bool
425+
"""
426+
427+
return self.older_id is not None
428+
429+
@property
430+
def url_params_count_only(self):
431+
"""
432+
:rtype: dict[str, str]
433+
"""
434+
435+
params = {}
436+
self._add_count_to_params_if_needed(params)
437+
438+
return params
439+
440+
def _add_count_to_params_if_needed(self, params):
441+
"""
442+
:type params: dict[str, str]
443+
444+
:rtype: None
445+
"""
446+
447+
if self.count is not None:
448+
params[self.PARAM_COUNT] = str(self.count)
449+
450+
def has_next_page_assured(self):
451+
"""
452+
:rtype: bool
453+
"""
454+
455+
return self.newer_id is not None
456+
457+
@property
458+
def url_params_next_page(self):
459+
"""
460+
:rtype: dict[str, str]
461+
"""
462+
463+
self.assert_has_next_page()
464+
465+
params = {self.PARAM_NEWER_ID: str(self._next_id)}
466+
self._add_count_to_params_if_needed(params)
467+
468+
return params
469+
470+
def assert_has_next_page(self):
471+
"""
472+
:raise: exception.BunqException
473+
"""
474+
475+
if self._next_id is None:
476+
raise exception.BunqException(self._ERROR_NO_NEXT_PAGE)
477+
478+
@property
479+
def _next_id(self):
480+
"""
481+
:rtype: int
482+
"""
483+
484+
if self.has_next_page_assured():
485+
return self.newer_id
486+
487+
return self.future_id

0 commit comments

Comments
 (0)