Skip to content

Commit 75a55ec

Browse files
committed
refactored route operation
1 parent d5a8035 commit 75a55ec

File tree

18 files changed

+170
-127
lines changed

18 files changed

+170
-127
lines changed

ellar/common/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,14 @@
3737
IExceptionMiddlewareService,
3838
IExecutionContext,
3939
IExecutionContextFactory,
40+
IGuardsConsumer,
4041
IHostContext,
4142
IHostContextFactory,
4243
IHTTPConnectionContextFactory,
4344
IHTTPHostContext,
45+
IInterceptorsConsumer,
4446
IModuleSetup,
4547
IModuleTemplateLoader,
46-
InterceptorConsumer,
4748
IResponseModel,
4849
IWebSocketContextFactory,
4950
IWebSocketHostContext,
@@ -194,7 +195,8 @@
194195
"IModuleSetup",
195196
"IResponseModel",
196197
"IModuleTemplateLoader",
197-
"InterceptorConsumer",
198+
"IInterceptorsConsumer",
199+
"IGuardsConsumer",
198200
"EllarInterceptor",
199201
"interceptors",
200202
]

ellar/common/interfaces/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
IWebSocketHostContext,
1010
)
1111
from .exceptions import IExceptionHandler, IExceptionMiddlewareService
12-
from .interceptor_consumer import InterceptorConsumer
12+
from .guard_consumer import IGuardsConsumer
13+
from .interceptor_consumer import IInterceptorsConsumer
1314
from .module import IModuleSetup
1415
from .response_model import IResponseModel
1516
from .templating import IModuleTemplateLoader
@@ -28,5 +29,6 @@
2829
"IModuleSetup",
2930
"IResponseModel",
3031
"IModuleTemplateLoader",
31-
"InterceptorConsumer",
32+
"IInterceptorsConsumer",
33+
"IGuardsConsumer",
3234
]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import typing as t
2+
from abc import ABC, abstractmethod
3+
4+
from .context import IExecutionContext
5+
6+
if t.TYPE_CHECKING: # pragma: no cover
7+
from ellar.common.routing import RouteOperationBase
8+
9+
10+
class IGuardsConsumer(ABC):
11+
@abstractmethod
12+
async def execute(
13+
self, context: IExecutionContext, route_operation: "RouteOperationBase"
14+
) -> t.Any:
15+
"""implementation goes here"""

ellar/common/interfaces/interceptor_consumer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ellar.common.routing import RouteOperationBase
88

99

10-
class InterceptorConsumer(ABC):
10+
class IInterceptorsConsumer(ABC):
1111
@abstractmethod
1212
async def execute(
1313
self, context: IExecutionContext, route_operation: "RouteOperationBase"

ellar/common/routing/base.py

Lines changed: 22 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@
55

66
from ellar.common.constants import (
77
CONTROLLER_CLASS_KEY,
8-
GUARDS_KEY,
98
SCOPE_API_VERSIONING_RESOLVER,
109
SCOPE_SERVICE_PROVIDER,
1110
VERSIONING_KEY,
1211
)
1312
from ellar.common.interfaces import (
1413
IExecutionContext,
1514
IExecutionContextFactory,
16-
InterceptorConsumer,
15+
IGuardsConsumer,
16+
IInterceptorsConsumer,
1717
)
18-
from ellar.common.models import GuardCanActivate
1918
from ellar.common.types import TReceive, TScope, TSend
2019
from ellar.reflect import reflect
2120

2221
if t.TYPE_CHECKING: # pragma: no cover
22+
from ellar.common import ControllerBase
2323
from ellar.core.versioning.resolver import BaseAPIVersioningResolver
2424
from ellar.di import EllarInjector
2525

@@ -30,10 +30,17 @@
3030

3131

3232
class RouteOperationBase:
33-
path: str
34-
endpoint: t.Callable
3533
methods: t.Set[str]
3634

35+
def __init__(self, endpoint: t.Callable) -> None:
36+
self.endpoint = endpoint
37+
_controller_type: t.Type = reflect.get_metadata( # type: ignore[assignment]
38+
CONTROLLER_CLASS_KEY, self.endpoint
39+
)
40+
self._controller_type: t.Union[t.Type, t.Type["ControllerBase"]] = t.cast(
41+
t.Union[t.Type, t.Type["ControllerBase"]], _controller_type
42+
)
43+
3744
@t.no_type_check
3845
def __call__(
3946
self, context: IExecutionContext, *args: t.Any, **kwargs: t.Any
@@ -44,55 +51,32 @@ def __call__(
4451
def _load_model(self) -> None:
4552
"""compute route models"""
4653

47-
@t.no_type_check
48-
async def run_route_guards(self, context: IExecutionContext) -> None:
49-
app = context.get_app()
50-
reflector = app.reflector
51-
52-
targets = [self.endpoint, self.get_control_type()]
53-
54-
_guards: t.Optional[
55-
t.List[t.Union[t.Type["GuardCanActivate"], "GuardCanActivate"]]
56-
] = reflector.get_all_and_override(GUARDS_KEY, *targets)
57-
58-
if not _guards:
59-
_guards = app.get_guards()
60-
61-
if _guards:
62-
for guard in _guards:
63-
if isinstance(guard, type):
64-
guard = context.get_service_provider().get(guard)
65-
66-
result = await guard.can_activate(context)
67-
if not result:
68-
guard.raise_exception()
69-
7054
async def app(self, scope: TScope, receive: TReceive, send: TSend) -> None:
7155
service_provider: "EllarInjector" = scope[SCOPE_SERVICE_PROVIDER]
7256

7357
execution_context_factory = service_provider.get(IExecutionContextFactory)
7458
context = execution_context_factory.create_context(
7559
operation=self, scope=scope, receive=receive, send=send
7660
)
77-
interceptor_consumer = service_provider.get(InterceptorConsumer)
61+
interceptor_consumer = service_provider.get(IInterceptorsConsumer)
62+
guard_consumer = service_provider.get(IGuardsConsumer)
7863

79-
await self.run_route_guards(context=context)
80-
# await self._handle_request(context=context)
64+
await guard_consumer.execute(context, self)
8165
await interceptor_consumer.execute(context, self)
8266

83-
def get_control_type(self) -> t.Type:
67+
def get_controller_type(self) -> t.Type:
8468
"""
8569
For operation under a controller, `get_control_type` and `get_class` will return the same result
8670
For operation under ModuleRouter, this will return a unique type created for the router for tracking some properties
8771
:return: a type that wraps the operation
8872
"""
89-
if not hasattr(self, "_control_type"):
73+
if not self._controller_type:
9074
_control_type = reflect.get_metadata(CONTROLLER_CLASS_KEY, self.endpoint)
9175
if _control_type is None:
9276
raise Exception("Operation must have a single control type.")
93-
self._control_type = t.cast(t.Type, _control_type)
77+
self._controller_type = t.cast(t.Type, _control_type)
9478

95-
return self._control_type
79+
return self._controller_type
9680

9781
@abstractmethod
9882
async def handle_request(self, *, context: IExecutionContext) -> t.Any:
@@ -108,9 +92,10 @@ def get_allowed_version(self) -> t.Set[t.Union[int, float, str]]:
10892
versions = reflect.get_metadata(VERSIONING_KEY, self.endpoint) or set()
10993
if not versions:
11094
versions = (
111-
reflect.get_metadata(VERSIONING_KEY, self.get_control_type()) or set()
95+
reflect.get_metadata(VERSIONING_KEY, self.get_controller_type())
96+
or set()
11297
)
113-
return t.cast(t.Set[t.Union[int, float, str]], versions)
98+
return versions
11499

115100
@classmethod
116101
def get_methods(cls, methods: t.Optional[t.List[str]] = None) -> t.Set[str]:

ellar/common/routing/controller/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
class ControllerRouteOperationBase:
88
endpoint: t.Callable
9-
get_control_type: t.Callable
9+
get_controller_type: t.Callable
1010

1111
def _get_controller_instance(self, ctx: IExecutionContext) -> ControllerBase:
12-
controller_type: t.Optional[t.Type[ControllerBase]] = self.get_control_type()
12+
controller_type: t.Optional[t.Type[ControllerBase]] = self.get_controller_type()
1313

1414
service_provider = ctx.get_service_provider()
1515

ellar/common/routing/route.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def __init__(
4545
name: t.Optional[str] = None,
4646
include_in_schema: bool = True,
4747
) -> None:
48+
super().__init__(endpoint=endpoint)
4849
self._is_coroutine = inspect.iscoroutinefunction(endpoint)
4950
self._defined_responses: t.Dict[int, t.Type] = dict(response)
5051

@@ -53,7 +54,6 @@ def __init__(
5354
self.path_regex, self.path_format, self.param_convertors = compile_path(
5455
self.path
5556
)
56-
self.endpoint = endpoint # type: ignore
5757

5858
self.name = get_name(endpoint) if name is None else name
5959
self.include_in_schema = include_in_schema

ellar/common/routing/route_collections.py

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,8 @@
44

55
from starlette.routing import BaseRoute, Host, Mount
66

7-
from ellar.common.constants import CONTROLLER_CLASS_KEY
8-
from ellar.common.helper import (
9-
generate_controller_operation_unique_id,
10-
get_unique_control_type,
11-
)
7+
from ellar.common.helper import generate_controller_operation_unique_id
128
from ellar.common.logger import logger
13-
from ellar.reflect import reflect
14-
15-
from .base import RouteOperationBase
16-
from .route import RouteOperation
17-
from .websocket.route import WebsocketRouteOperation
189

1910

2011
class RouteCollection(t.Sequence[BaseRoute]):
@@ -59,21 +50,12 @@ def sort_routes(self) -> None:
5950
key=lambda e: e.host if isinstance(e, Host) else e.path # type: ignore
6051
)
6152

62-
def _add_operation(
63-
self, operation: t.Union[RouteOperation, WebsocketRouteOperation, BaseRoute]
64-
) -> None:
53+
def _add_operation(self, operation: t.Union[BaseRoute]) -> None:
6554

6655
if not isinstance(operation, BaseRoute):
6756
logger.warning("Tried Adding an operation that is not supported.")
6857
return
6958

70-
if isinstance(operation, RouteOperationBase) and not reflect.has_metadata(
71-
CONTROLLER_CLASS_KEY, operation.endpoint
72-
):
73-
reflect.define_metadata(
74-
CONTROLLER_CLASS_KEY, get_unique_control_type(), operation.endpoint
75-
)
76-
7759
_methods = getattr(operation, "methods", {"WS"})
7860
_versioning = list(
7961
operation.get_allowed_version() # type: ignore

ellar/common/routing/websocket/route.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def __init__(
4545
extra_handler_type: t.Optional[t.Type[WebSocketExtraHandler]] = None,
4646
**handlers_kwargs: t.Any,
4747
) -> None:
48+
super().__init__(endpoint=endpoint)
4849
assert path.startswith("/"), "Routed paths must start with '/'"
4950
self._handlers_kwargs: t.Dict[str, t.Any] = dict(
5051
encoding=encoding,
@@ -62,7 +63,6 @@ def __init__(
6263
self.path_regex, self.path_format, self.param_convertors = compile_path(
6364
self.path
6465
)
65-
self.endpoint = endpoint # type: ignore
6666
self.name = get_name(endpoint) if name is None else name
6767

6868
self.endpoint_parameter_model: WebsocketEndpointArgsModel = NOT_SET

ellar/core/context/factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def create_context(
7676
receive=receive,
7777
send=send,
7878
operation_handler=operation.endpoint,
79-
operation_handler_type=operation.get_control_type(),
79+
operation_handler_type=operation.get_controller_type(),
8080
reflector=self.reflector,
8181
)
8282

0 commit comments

Comments
 (0)