feat: re-write get all routes to use pagination (#1424)

rewrite get_all routes to use a pagination pattern to allow for better implementations of search, filter, and sorting on the frontend or by any client without fetching all the data. Additionally we added a CI check for running the Nuxt built to confirm that no TS errors were present. Finally, I had to remove the header support for the Shopping lists as the browser caching based off last_updated header was not allowing it to read recent updates due to how we're handling the updated_at property in the database with nested fields. This will have to be looked at in the future to reimplement. I'm unsure how many other routes have a similar issue. 

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
This commit is contained in:
Michael Genson
2022-06-25 14:39:38 -05:00
committed by GitHub
parent c158672d12
commit cb15db2d27
55 changed files with 683 additions and 197 deletions

View File

@@ -1,9 +1,13 @@
import enum
from typing import Generic, TypeVar
from typing import Any, Generic, TypeVar
from urllib.parse import parse_qs, urlencode, urlsplit, urlunsplit
from humps import camelize
from pydantic import BaseModel
from pydantic.generics import GenericModel
from mealie.schema._mealie import MealieModel
DataT = TypeVar("DataT", bound=BaseModel)
@@ -12,11 +16,11 @@ class OrderDirection(str, enum.Enum):
desc = "desc"
class PaginationQuery(BaseModel):
class PaginationQuery(MealieModel):
page: int = 1
per_page: int = 50
order_by: str = "created_at"
order_direction: OrderDirection = OrderDirection.desc
per_page: int = 50
class PaginationBase(GenericModel, Generic[DataT]):
@@ -24,4 +28,45 @@ class PaginationBase(GenericModel, Generic[DataT]):
per_page: int = 10
total: int = 0
total_pages: int = 0
data: list[DataT]
items: list[DataT]
next: str | None
previous: str | None
def _set_next(self, route: str, query_params: dict[str, Any]) -> None:
if self.page >= self.total_pages:
self.next = None
return
# combine params with base route
query_params["page"] = self.page + 1
self.next = PaginationBase.merge_query_parameters(route, query_params)
def _set_prev(self, route: str, query_params: dict[str, Any]) -> None:
if self.page <= 1:
self.previous = None
return
# combine params with base route
query_params["page"] = self.page - 1
self.previous = PaginationBase.merge_query_parameters(route, query_params)
def set_pagination_guides(self, route: str, query_params: dict[str, Any] | None) -> None:
if not query_params:
query_params = {}
query_params = camelize(query_params)
# sanitize user input
self.page = max(self.page, 1)
self._set_next(route, query_params)
self._set_prev(route, query_params)
@staticmethod
def merge_query_parameters(url: str, params: dict[str, Any]):
scheme, netloc, path, query_string, fragment = urlsplit(url)
query_params = parse_qs(query_string)
query_params.update(params)
new_query_string = urlencode(query_params, doseq=True)
return urlunsplit((scheme, netloc, path, new_query_string, fragment))