Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/fastapi_toolsets/crud/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ def _decode_cursor(cursor: str) -> tuple[str, list[str] | None, _CursorDirection

def _page_size_query(default: int, max_size: int) -> int:
"""Return a FastAPI ``Query`` for the ``items_per_page`` parameter."""
# FastAPI >= 0.148 no longer validates defaults, so ge/le would not catch this.
if not 1 <= default <= max_size:
raise ValueError(
f"default_page_size must be between 1 and max_page_size ({max_size}), "
f"got {default}"
)
return Query(
default,
ge=1,
Expand Down Expand Up @@ -683,7 +689,7 @@ def _build_paginate_params(
default=Query(
default=None,
description="Restrict search to a single column",
enum=search_keys,
json_schema_extra={"enum": search_keys},
),
),
]
Expand Down Expand Up @@ -729,7 +735,7 @@ def _build_paginate_params(
default=Query(
None,
description=f"Field to order by. Valid values: {order_valid_keys}",
enum=order_valid_keys,
json_schema_extra={"enum": order_valid_keys},
),
),
inspect.Parameter(
Expand Down Expand Up @@ -982,7 +988,8 @@ def paginate_params(
inspect.Parameter.KEYWORD_ONLY,
annotation=PaginationType,
default=Query(
default_pagination_type, description="Pagination strategy"
PaginationType(default_pagination_type),
description="Pagination strategy",
),
),
inspect.Parameter(
Expand Down
24 changes: 24 additions & 0 deletions tests/test_crud_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -1715,6 +1715,30 @@ def test_each_dependency_exposes_its_pagination_params(self, factory, fixed, nam
assert size.default == 42
assert next(m.le for m in size.metadata if hasattr(m, "le")) == 50

@pytest.mark.parametrize(
"factory",
["offset_paginate_params", "cursor_paginate_params", "paginate_params"],
)
@pytest.mark.parametrize("default_page_size", [0, 51])
def test_default_page_size_outside_bounds_is_rejected(
self, factory, default_page_size
):
with pytest.raises(ValueError, match="default_page_size"):
getattr(RoleCursorCrud, factory)(
default_page_size=default_page_size, max_page_size=50
)

def test_default_pagination_type_is_coerced_to_the_enum(self):
dep = RoleCursorCrud.paginate_params(
default_pagination_type="cursor", # ty:ignore[invalid-argument-type]
search=False,
filter=False,
order=False,
)
default = inspect.signature(dep).parameters["pagination_type"].default

assert default.default is PaginationType.CURSOR

@pytest.mark.anyio
async def test_fixed_options_are_forwarded_not_exposed(self):
offset = RoleCrud.offset_paginate_params(
Expand Down
6 changes: 3 additions & 3 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading