From d0c115b02591c95ad72fdfc4dea4aa0c4ca16016 Mon Sep 17 00:00:00 2001 From: FAbrickA Date: Wed, 30 Apr 2025 16:14:07 +0300 Subject: [PATCH] Fix #85 issue --- fastapi_utils/cbv.py | 11 +++++++++++ tests/test_cbv.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/fastapi_utils/cbv.py b/fastapi_utils/cbv.py index 231325c5..9428b7ae 100644 --- a/fastapi_utils/cbv.py +++ b/fastapi_utils/cbv.py @@ -29,6 +29,15 @@ RETURN_TYPES_FUNC_KEY = "__return_types_func__" +class AlwaysTrueString(str): + """ + String that always return True then casted to boolean + """ + + def __bool__(self): + return True + + def cbv(router: APIRouter, *urls: str) -> Callable[[Type[T]], Type[T]]: """ This function returns a decorator that converts the decorated into a class-based view for the provided router. @@ -128,6 +137,8 @@ def _register_endpoints(router: APIRouter, cls: Type[Any], *urls: str) -> None: for route in cbv_routes: router.routes.remove(route) route.path = route.path[prefix_length:] + if not route.path: + route.path = AlwaysTrueString("") _update_cbv_route_endpoint_signature(cls, route) route.name = cls.__name__ + "." + route.name cbv_router.routes.append(route) diff --git a/tests/test_cbv.py b/tests/test_cbv.py index b5b9f646..89fae94c 100644 --- a/tests/test_cbv.py +++ b/tests/test_cbv.py @@ -131,6 +131,45 @@ def root(self) -> str: assert response.status_code == 200 assert response.json() == "hello" + def test_empty_path(self) -> None: + router_root = APIRouter(prefix="/api") + router = APIRouter(prefix="/item") + + @cbv(router) + class CBV: + @router.get("") + def root(self) -> str: + return "hello" + + router_root.include_router(router) + + client = TestClient(router_root) + response = client.get("/api/item") + assert response.status_code == 200 + assert response.json() == "hello" + + def test_route_outside_cbv(self): + router = APIRouter(prefix="/api") + + @cbv(router) + class CBV: + @router.get("/item") + def root(self) -> str: + return "Response: item" + + @router.get("/another_item") + def get_another_item() -> str: + return "Response: another item" + + client = TestClient(router) + response = client.get("/api/item") + assert response.status_code == 200 + assert response.json() == "Response: item" + + response = client.get("/api/another_item") + assert response.status_code == 200 + assert response.json() == "Response: another item" + def test_url_for(self, router: APIRouter) -> None: @cbv(router) class Foo: