-
Notifications
You must be signed in to change notification settings - Fork 4
add cursor based pagination #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
d26d9be
1ead0f0
7fa88a2
8484692
f5e2bcd
55814f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,10 @@ def __init__(self): | |
| self.next_page_url_base = None | ||
| self.skip_key = None | ||
| self.next_page_url = None | ||
| self.cursor_next_token_path = None | ||
| self.cursor_query_param = None | ||
| self.cursor_initial_token = None | ||
| self.cursor_query_param_value = None | ||
| self.records_to_skip = None | ||
| self.pagination_type = "" | ||
| self.counting_key = None | ||
|
|
@@ -25,28 +29,33 @@ def __init__(self): | |
| self.update_next_page = self.update_next_page_default | ||
|
|
||
| def configure_paging(self, config=None, skip_key=None, | ||
| next_page_key=None, next_page_url_base=None, pagination_type="na"): | ||
| next_page_key=None, next_page_url_base=None, | ||
| cursor_next_token_path=None, cursor_query_param=None, | ||
| cursor_initial_token=None, pagination_type="na"): | ||
| config = {} if config is None else config | ||
| self.pagination_type = config.get("pagination_type", pagination_type) | ||
| self.update_next_page = self.update_next_page_default | ||
| if self.pagination_type == "next_page": | ||
| self.next_page_key = config.get("next_page_key", next_page_key) | ||
| self.next_page_key = None if self.next_page_key == '' else self.next_page_key | ||
| if next_page_url_base: | ||
| next_page_url_base = next_page_url_base.strip('/') | ||
| self.next_page_url_base = next_page_url_base | ||
| elif self.pagination_type in ["offset", "page"]: | ||
| self.skip_key = config.get("skip_key", skip_key) | ||
| logger.info("configure_paging: self.pagination_type='{}', self.next_page_key='{}', self.next_page_url_base='{}', self.skip_key='{}'".format( | ||
| self.pagination_type, self.next_page_key, self.next_page_url_base, self.skip_key | ||
| )) | ||
| if self.pagination_type == "next_page": | ||
| self.update_next_page = self.update_next_page_link | ||
| elif self.pagination_type == "cursor": | ||
| self.cursor_next_token_path = config.get("cursor_next_token_path", cursor_next_token_path) | ||
| self.cursor_query_param = config.get("cursor_query_param", cursor_query_param) | ||
| self.cursor_initial_token = config.get("cursor_initial_token", cursor_initial_token) | ||
| self.update_next_page = self.update_next_page_cursor | ||
| elif self.pagination_type == "offset": | ||
| self.skip_key = config.get("skip_key", skip_key) | ||
| self.update_next_page = self.update_next_page_offset | ||
| elif self.pagination_type == "page": | ||
| self.skip_key = config.get("skip_key", skip_key) | ||
| self.update_next_page = self.update_next_page_per_page | ||
| else: | ||
| self.update_next_page = self.update_next_page_default | ||
| logger.info("configure_paging: self.pagination_type='{}', self.next_page_key='{}', self.next_page_url_base='{}', self.skip_key='{}', self.cursor_next_token_path='{}', self.cursor_query_param='{}', self.cursor_initial_token='{}'".format( | ||
| self.pagination_type, self.next_page_key, self.next_page_url_base, self.skip_key, self.cursor_next_token_path, self.cursor_query_param, self.cursor_initial_token | ||
| )) | ||
|
|
||
| def reset_paging(self, counting_key=None, url=None): | ||
| self.records_to_skip = 0 | ||
|
|
@@ -57,6 +66,7 @@ def reset_paging(self, counting_key=None, url=None): | |
| else: | ||
| self.next_page_number = 0 | ||
| self.next_page_url = url | ||
| self.cursor_query_param_value = self.cursor_initial_token | ||
| self.is_last_batch_empty = False | ||
| self.is_first_batch = True | ||
| self.is_paging_started = True | ||
|
|
@@ -127,6 +137,16 @@ def update_next_page_link(self, data, response_links=None): | |
| next_link, self.next_page_url, self.params_must_be_blanked, self.next_page_number, self.counter | ||
| )) | ||
|
|
||
| def update_next_page_cursor(self, data, response_links=None): | ||
| self.is_first_batch = False | ||
| self.counter += 1 | ||
| self.cursor_query_param_value = None | ||
| self.data_is_list = False | ||
|
|
||
| if self.cursor_next_token_path and self.cursor_query_param: | ||
| self.cursor_query_param_value = extract_key_using_json_path(data, self.cursor_next_token_path) | ||
| logger.info("update_next_page_cursor:{}".format({self.cursor_query_param_value})) | ||
|
|
||
| def update_next_page_default(self, data, response_links=None): | ||
| self.is_first_batch = False | ||
|
|
||
|
|
@@ -145,6 +165,15 @@ def has_next_page(self): | |
| ret | ||
| )) | ||
| return ret | ||
| if self.pagination_type == "cursor": | ||
| ret = (self.cursor_query_param_value is not None) and (self.cursor_query_param_value != "") | ||
| logger.info("has_next_page:cursor_next_token_path={} cursor_query_param={} cursor_query_param_value={} -> {}".format( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, I would put
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same than previous |
||
| self.cursor_next_token_path, | ||
| self.cursor_query_param, | ||
| self.cursor_query_param_value, | ||
| ret | ||
| )) | ||
| return ret | ||
| if self.pagination_type in ["page", "offset"]: | ||
| if self.counting_key: | ||
| # There is a counting key and we already know the last batch was not empty | ||
|
|
@@ -168,6 +197,10 @@ def get_params(self): | |
| ret.update({ | ||
| self.skip_key: self.next_page_number if self.pagination_type == "page" else self.records_to_skip | ||
| }) | ||
| if self.cursor_query_param and self.cursor_query_param_value not in [None, ""]: | ||
| ret.update({ | ||
| self.cursor_query_param: self.cursor_query_param_value | ||
| }) | ||
| return ret | ||
|
|
||
| def get_next_page_url(self): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -89,13 +89,19 @@ def __init__(self, credential, secure_credentials, endpoint, custom_key_values={ | |
| next_page_url_base = endpoint.get("next_page_url_base", None) if is_next_page_url_relative else None | ||
| next_page_url_base = format_template(next_page_url_base, **self.presets_variables) | ||
| skip_key = endpoint.get("skip_key") | ||
| cursor_next_token_path = endpoint.get("cursor_next_token_path") | ||
| cursor_query_param = endpoint.get("cursor_query_param") | ||
| cursor_initial_token = format_template(endpoint.get("cursor_initial_token"), **self.presets_variables) | ||
|
Comment on lines
+92
to
+94
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason why only
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not see how it would be useful for the others, initial token has a good chance to be stored within contextual variables. |
||
| pagination_type = endpoint.get("pagination_type", "na") | ||
| if pagination_type == "next_page" and is_next_page_url_relative and not next_page_url_base: | ||
| raise RestAPIClientError("Pagination's 'Next page URL' is relative but no 'Base URL to next page' has been set") | ||
| self.pagination.configure_paging( | ||
| skip_key=skip_key, | ||
| next_page_key=next_page_url_key, | ||
| next_page_url_base=next_page_url_base, | ||
| cursor_next_token_path=cursor_next_token_path, | ||
| cursor_query_param=cursor_query_param, | ||
| cursor_initial_token=cursor_initial_token, | ||
| pagination_type=pagination_type | ||
| ) | ||
| self.last_interaction = None | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest
debugThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's consistent with the other pagination type, I would agree conceptually but it was useful when debugging the issue initially