oncall-engine/engine/common/api_helpers/paginators.py
Joey Orlando 767c5352fa
augment API response pagination attributes (#2471)
# What this PR does

This PR:
- adds a few attributes to paginated API responses
- removes channel filter "send demo alert" internal API endpoint + tests
(this endpoint was marked as deprecated + not consumed by the web UI)

With the new paginated API response schema, the web UI will no longer
need to:
- hardcode `ITEMS_PER_PAGE` for each table
- manually calculate total number of pages

(these two things ☝️ will be done in
https://github.com/grafana/oncall/issues/2476)

For `GET /api/internal/v1/alertgroups` the response will now look like
this:
```diff
{
    "next": <url> | None,
    "previous": <url> | None,
    "results": [],
++  "page_size": <int>
}
```

For all other paginated API responses, the response will now look like:
```diff
{
    "count": <int>,
    "next": <url> | None,
    "previous": <url> | None,
    "results": [],
++  "page_size": <int>,
++  "current_page_number": <int>,
++  "total_pages": <int>
}
```

## TODO
- [x] update public API docs to include these new attributes

## 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] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
2023-07-14 11:19:40 -04:00

85 lines
2.8 KiB
Python

import typing
from rest_framework.pagination import BasePagination, CursorPagination, PageNumberPagination
from rest_framework.response import Response
from common.api_helpers.utils import create_engine_url
PaginatedData = typing.List[typing.Any]
class BasePaginatedResponseData(typing.TypedDict):
next: str | None
previous: str | None
results: PaginatedData
page_size: int
class PageBasedPaginationResponseData(BasePaginatedResponseData):
count: int
current_page_number: int
total_pages: int
class BasePathPrefixedPagination(BasePagination):
max_page_size = 100
page_query_param = "page"
page_size_query_param = "perpage"
def paginate_queryset(self, queryset, request, view=None):
request.build_absolute_uri = lambda: create_engine_url(request.get_full_path())
# we're setting the request object explicitly here because the way the paginate_quersey works
# between PageNumberPagination and CursorPagination is slightly different. In the latter class,
# it does not set self.request in the paginate_queryset method, whereas in the former it does.
# this leads to an issue in _get_base_paginated_response_data where the self.request would not be set
self.request = request
return super().paginate_queryset(queryset, request, view)
def _get_base_paginated_response_data(self, data: PaginatedData) -> BasePaginatedResponseData:
return {
"next": self.get_next_link(),
"previous": self.get_previous_link(),
"results": data,
"page_size": self.get_page_size(self.request),
}
class PathPrefixedPagePagination(BasePathPrefixedPagination, PageNumberPagination):
def _get_paginated_response_data(self, data: PaginatedData) -> PageBasedPaginationResponseData:
return {
**self._get_base_paginated_response_data(data),
"count": self.page.paginator.count,
"current_page_number": self.page.number,
"total_pages": self.page.paginator.num_pages,
}
def get_paginated_response(self, data: PaginatedData) -> Response:
return Response(self._get_paginated_response_data(data))
class PathPrefixedCursorPagination(BasePathPrefixedPagination, CursorPagination):
def get_paginated_response(self, data: PaginatedData) -> Response:
return Response(self._get_base_paginated_response_data(data))
class HundredPageSizePaginator(PathPrefixedPagePagination):
page_size = 100
class FiftyPageSizePaginator(PathPrefixedPagePagination):
page_size = 50
class TwentyFivePageSizePaginator(PathPrefixedPagePagination):
page_size = 25
class FifteenPageSizePaginator(PathPrefixedPagePagination):
page_size = 15
class TwentyFiveCursorPaginator(PathPrefixedCursorPagination):
page_size = 25
ordering = "-pk"