diff --git a/api/schemas/pagination.py b/api/schemas/pagination.py index 8e510da..29c5717 100644 --- a/api/schemas/pagination.py +++ b/api/schemas/pagination.py @@ -1,7 +1,7 @@ from enum import Enum -from typing import Any +from typing import Annotated, Any -from pydantic import BaseModel, Field +from pydantic import BaseModel, BeforeValidator, Field from api.environment import settings @@ -13,10 +13,22 @@ class PaginationOrderOptions(str, Enum): desc = "desc" +def limit_to_max_pagination_value(value: Any) -> int: + """Ensures that users cannot exceed the maximum pagination limit + + Needs to be run prior to normal validation to prevent a validation error from getting thrown + by the framework. + """ + value = int(value) + if value > settings.pagination_max_limit: + value = settings.pagination_max_limit + return value + + class PaginationOptions(BaseModel): """Query string options to adjust pagination""" - limit: int = Field( + limit: Annotated[int, BeforeValidator(limit_to_max_pagination_value)] = Field( settings.pagination_default_limit, gt=0, le=settings.pagination_max_limit ) offset: int = 0 diff --git a/api/tests/cards/test_cards.py b/api/tests/cards/test_cards.py index e3d4c77..e0fd2f8 100644 --- a/api/tests/cards/test_cards.py +++ b/api/tests/cards/test_cards.py @@ -6,7 +6,7 @@ from api.models.release import Release, UserRelease from api.services.card import create_card -from ..utils import create_user_token +from ..utils import create_user_token, monkeypatch_settings def names_from_results(response): @@ -210,6 +210,19 @@ def test_pagination_paging(client: TestClient, session: db.Session): assert data["next"] is not None +def test_pagination_exceed_limit(client: TestClient, session: db.Session, monkeypatch): + """Exceeding the max pagination limit results in getting the max limit back""" + monkeypatch_settings( + monkeypatch, {"pagination_max_limit": 4, "pagination_default_limit": 2} + ) + response = client.get("/v2/cards", params={"limit": 5}) + assert response.status_code == 200 + data = response.json() + assert data["count"] == 10 + assert len(data["results"]) == 4 + assert data["next"] is not None + + def test_pagination_negative_offsets(client: TestClient, session: db.Session): """A number that would result in a negative offset must result in no offset""" # Verify that previous links work properly with arbitrary offsets that would make them negative