-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from RedHatProductSecurity/smart-pagination
Implement better pagination
- Loading branch information
Showing
7 changed files
with
192 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
""" | ||
component-registry-bindings exceptions | ||
""" | ||
|
||
|
||
class ComponentRegistryBindingsException(Exception): | ||
"""Base component-registry-bindings exception""" | ||
|
||
|
||
class OperationUnsupported(ComponentRegistryBindingsException): | ||
"""Session operation is unsupported exception""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
""" | ||
component-registry iterators | ||
""" | ||
|
||
import re | ||
from functools import partial | ||
from typing import Callable, Optional | ||
|
||
from .exceptions import ComponentRegistryBindingsException | ||
|
||
|
||
class Paginator: | ||
""" | ||
Iterator for handling API pagination. | ||
Receives either starting limit and offset together with the retreive list function | ||
or already existing response from which it should continue. | ||
It keeps calling `.next()` response until pages are exhausted. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
*args, | ||
retrieve_list_fn: Optional[Callable] = None, | ||
limit: int = 100, | ||
offset: int = 0, | ||
init_response=None, | ||
**kwargs, | ||
): | ||
|
||
if not init_response and not retrieve_list_fn: | ||
raise ComponentRegistryBindingsException( | ||
( | ||
"Paginator needs either initial response or session" | ||
"operation function to obtain the initial response." | ||
) | ||
) | ||
|
||
self.retrieve_list_fn = retrieve_list_fn | ||
|
||
# initial starting data | ||
self.__init_limit = limit | ||
self.__init_offset = offset | ||
self.__init_response = init_response | ||
|
||
# current response page | ||
self.current_response = init_response | ||
|
||
# request arguments | ||
self.args = args | ||
self.kwargs = kwargs | ||
|
||
def __iter__(self): | ||
# restore initial response | ||
self.current_response = self.__init_response | ||
return self | ||
|
||
def __next__(self): | ||
if self.current_response is None: | ||
|
||
# no current response page - obtain the first page | ||
response = self.retrieve_list_fn( | ||
*self.args, | ||
limit=self.__init_limit, | ||
offset=self.__init_offset, | ||
**self.kwargs, | ||
) | ||
response = self.make_response_iterable( | ||
response, self.retrieve_list_fn, *self.args, **self.kwargs | ||
) | ||
self.current_response = response | ||
return response | ||
else: | ||
|
||
# existing current response - call next page | ||
response = self.current_response.next() | ||
if response is not None: | ||
self.current_response = response | ||
return response | ||
else: | ||
raise StopIteration | ||
|
||
@staticmethod | ||
def make_response_iterable(response, retrieve_list_fn, *args, **kwargs): | ||
""" | ||
Populate next, prev and iterator helper methods for paginated responses | ||
""" | ||
|
||
response.iterator = Paginator( | ||
init_response=response, | ||
) | ||
|
||
for param_name, func_name in (("next_", "next"), ("previous", "prev")): | ||
kwargs.pop("limit", None) | ||
kwargs.pop("offset", None) | ||
param = getattr(response, param_name, None) | ||
if param is None: | ||
setattr(response, func_name, lambda: None) | ||
else: | ||
limit = re.search("limit=(\d+)", param) | ||
if limit is not None: | ||
kwargs["limit"] = limit.group(1) | ||
offset = re.search("offset=(\d+)", param) | ||
if offset is not None: | ||
kwargs["offset"] = offset.group(1) | ||
|
||
setattr(response, func_name, partial(retrieve_list_fn, *args, **kwargs)) | ||
|
||
return response |
Oops, something went wrong.