Fix pagination behavior when page # exceeds search results (#4817)

# What this PR does
Change pagination to return last available page if the page number
exceeds the pages available instead of returning 404. This came up from
if the user is on a page other than the first and they enter a search
and the number of search results are smaller than what would be needed
to reach the current page number it would give a blank page and 404.

## Which issue(s) this PR closes

Related to [issue link here]

<!--
*Note*: If you want the issue to be auto-closed once the PR is merged,
change "Related to" to "Closes" in the line above.
If you have more than one GitHub issue that this PR closes, be sure to
preface
each issue link with a [closing
keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue).
This ensures that the issue(s) are auto-closed once the PR has been
merged.
-->

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
    show up in the autogenerated release notes.

---------

Co-authored-by: Rares Mardare <rares.mardare@grafana.com>
This commit is contained in:
Michael Derynck 2024-08-19 07:59:01 -06:00 committed by GitHub
parent 7112be8116
commit f79445fbcb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 46 additions and 6 deletions

View file

@ -447,10 +447,9 @@ def test_pagination(settings, alert_group_public_api_setup):
token, alert_groups, _, _ = alert_group_public_api_setup
client = APIClient()
url = reverse("api-public:alert_groups-list")
url = "{}?perpage=1".format(reverse("api-public:alert_groups-list"))
with patch("common.api_helpers.paginators.PathPrefixedPagePagination.get_page_size", return_value=1):
response = client.get(url, HTTP_AUTHORIZATION=token)
response = client.get(url, HTTP_AUTHORIZATION=token)
assert response.status_code == status.HTTP_200_OK
result = response.json()

View file

@ -1,5 +1,6 @@
import typing
from django.core.paginator import EmptyPage
from rest_framework.pagination import BasePagination, CursorPagination, PageNumberPagination
from rest_framework.response import Response
@ -54,6 +55,44 @@ class PathPrefixedPagePagination(BasePathPrefixedPagination, PageNumberPaginatio
)
return paginated_schema
def paginate_queryset(self, queryset, request, view=None):
request.build_absolute_uri = lambda: create_engine_url(request.get_full_path())
per_page = request.query_params.get(self.page_size_query_param, self.page_size)
try:
per_page = int(per_page)
except ValueError:
per_page = self.page_size
if per_page < 1:
per_page = self.page_size
paginator = self.django_paginator_class(queryset, per_page)
page_number = request.query_params.get(self.page_query_param, 1)
try:
page_number = int(page_number)
except ValueError:
page_number = 1
if page_number < 1:
page_number = 1
try:
self.page = self.get_page(page_number, paginator)
except EmptyPage:
self.page = paginator.page(paginator.num_pages)
if paginator.num_pages > 1 and self.template is not None:
self.display_page_controls = True
self.request = request
return list(self.page)
def get_page(self, page_number, paginator):
try:
return paginator.page(page_number)
except EmptyPage:
return paginator.page(paginator.num_pages)
class PathPrefixedCursorPagination(BasePathPrefixedPagination, CursorPagination):
def get_paginated_response(self, data: PaginatedData) -> Response:

View file

@ -660,6 +660,7 @@ class _IntegrationsPage extends React.Component<IntegrationsProps, IntegrationsS
invalidateRequestFn = (requestedPage: number) => {
const { store } = this.props;
return requestedPage !== store.filtersStore.currentTablePageNum[PAGE.Integrations];
};
@ -696,6 +697,10 @@ class _IntegrationsPage extends React.Component<IntegrationsProps, IntegrationsS
const { alertReceiveChannelStore } = store;
const newPage = isOnMount ? store.filtersStore.currentTablePageNum[PAGE.Integrations] : 1;
runInAction(() => {
store.filtersStore.currentTablePageNum[PAGE.Integrations] = newPage;
});
await alertReceiveChannelStore.fetchPaginatedItems({
filters: this.getFiltersBasedOnCurrentTab(),
page: newPage,
@ -703,9 +708,6 @@ class _IntegrationsPage extends React.Component<IntegrationsProps, IntegrationsS
invalidateFn: () => this.invalidateRequestFn(newPage),
});
runInAction(() => {
store.filtersStore.currentTablePageNum[PAGE.Integrations] = newPage;
});
LocationHelper.update({ p: newPage }, 'partial');
};