Repository: litestar-org/litestar

Issue:
Endpoints 404 when using path parameters in Controller path
**Describe the bug**
When using a path parameter in the base path for a Controller, endpoints without further path parameters return 404. In the example provided, get_user functions as-is, while list_users only functions if get_user is deleted.

**To Reproduce**
Minimal example:

```python
from starlite import Controller, get
from pydantic import BaseModel, UUID4, Field
from uuid import uuid4


class User(BaseModel):
    id: UUID4 = Field(default_factory=uuid4)


class UserController(Controller):
    path = "/{realm:str}/users"

    @get()
    async def list_users(self, realm: str) -> list[User]:
        return [User()]

    @get(path="/{user_id:uuid}")
    async def get_user(self, realm: str, user_id: UUID4) -> User:
        return User()
```

**Additional context**
Version info:
```
$ python3 --version
Python 3.11.0

$ pip freeze
anyio==3.6.2
certifi==2022.9.24
click==8.1.3
dnspython==2.2.1
email-validator==1.3.0
Faker==15.3.2
h11==0.12.0
httpcore==0.15.0
httpx==0.23.0
idna==3.4
multidict==6.0.2
orjson==3.8.1
pydantic==1.10.2
pydantic-factories==1.15.0
pydantic-openapi-schema==1.3.0
python-dateutil==2.8.2
PyYAML==6.0
rfc3986==1.5.0
six==1.16.0
sniffio==1.3.0
starlite==1.39.0
starlite-multipart==1.2.0
typing_extensions==4.4.0
uvicorn==0.19.0
```



Discussion:
@all-contributors please add @sssssss340 for bug
@peterschutt 

I've put up [a pull request](https://github.com/starlite-api/starlite/pull/817) to add @sssssss340! :tada:

Resolving patch:
diff --git a/starlite/asgi/routing_trie/traversal.py b/starlite/asgi/routing_trie/traversal.py
--- a/starlite/asgi/routing_trie/traversal.py
+++ b/starlite/asgi/routing_trie/traversal.py
@@ -91,7 +91,7 @@ def traverse_route_map(
     has_path_param = PathParameterSentinel in current_node["child_keys"]
 
     if not path_components:
-        if has_path_param or not current_node["asgi_handlers"]:
+        if not current_node["asgi_handlers"]:
             raise NotFoundException()
         return current_node, path_params
 
@@ -171,7 +171,7 @@ def parse_scope_to_route(root_node: "RouteTrieNode", scope: "Scope", plain_route
         current_node, path_params = traverse_route_map(
             current_node=root_node,
             path=path,
-            path_components=deque([component for component in path.split("/") if component]),
+            path_components=deque(component for component in path.split("/") if component),
             path_params=[],
             scope=scope,
         )


Test patch:
diff --git a/tests/routing/test_path_resolution.py b/tests/routing/test_path_resolution.py
--- a/tests/routing/test_path_resolution.py
+++ b/tests/routing/test_path_resolution.py
@@ -1,4 +1,4 @@
-from typing import Any, Callable, Optional, Type
+from typing import Any, Callable, List, Optional, Type
 
 import pytest
 
@@ -206,3 +206,21 @@ def handler_fn(name: str) -> str:
 
         if response.status_code == HTTP_200_OK:
             assert response.text == expected_param
+
+
+def test_no_404_where_list_route_has_handlers_and_child_route_has_path_param() -> None:
+    # https://github.com/starlite-api/starlite/issues/816
+
+    # the error condition requires the path to not be a plain route, hence the prefixed path parameters
+    @get("/{a:str}/b")
+    def get_list() -> List[str]:
+        return ["ok"]
+
+    @get("/{a:str}/b/{c:int}")
+    def get_member() -> str:
+        return "ok"
+
+    with create_test_client(route_handlers=[get_list, get_member]) as client:
+        resp = client.get("/scope/b")
+        assert resp.status_code == 200
+        assert resp.json() == ["ok"]