Skip to content

Commit 49d306c

Browse files
committed
Added test for ForwardRefModule
1 parent ce922c4 commit 49d306c

File tree

11 files changed

+234
-72
lines changed

11 files changed

+234
-72
lines changed

ellar/app/core_module.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from ellar.auth import AppIdentitySchemes, IdentityAuthenticationService
44
from ellar.auth.session import SessionServiceNullStrategy, SessionStrategy
55
from ellar.common import (
6+
GlobalGuard,
7+
GuardCanActivate,
68
IApplicationReady,
79
IApplicationShutdown,
810
IApplicationStartup,
@@ -29,7 +31,7 @@
2931
from ellar.core.guards import GuardConsumer
3032
from ellar.core.interceptors import EllarInterceptorConsumer
3133
from ellar.core.services import Reflector, reflector
32-
from ellar.di import EllarInjector, ProviderConfig, request_scope
34+
from ellar.di import EllarInjector, ProviderConfig, injectable, request_scope
3335
from ellar.di.injector.tree_manager import ModuleTreeManager
3436

3537
if t.TYPE_CHECKING: # pragma: no cover
@@ -40,6 +42,14 @@ def _raise_unavailable_exception() -> None:
4042
raise Exception("Service is only available during request")
4143

4244

45+
@injectable
46+
class GlobalCanActivatePlaceHolder(GuardCanActivate):
47+
placeholder: bool = True
48+
49+
async def can_activate(self, context: IExecutionContext) -> bool:
50+
return True
51+
52+
4353
def get_core_module(app_module: t.Union[t.Type, t.Any], config: Config) -> t.Type:
4454
@Module(
4555
modules=[app_module],
@@ -49,6 +59,11 @@ def get_core_module(app_module: t.Union[t.Type, t.Any], config: Config) -> t.Typ
4959
use_value=config,
5060
export=True,
5161
),
62+
ProviderConfig(
63+
GlobalGuard,
64+
use_class=GlobalCanActivatePlaceHolder,
65+
export=True,
66+
),
5267
ProviderConfig(
5368
ModuleTreeManager,
5469
use_value=lambda: config.MODULE_TREE_MANAGER_CLASS(

ellar/app/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ def use_global_guards(
127127
self, *guards: t.Union["GuardCanActivate", t.Type["GuardCanActivate"]]
128128
) -> None:
129129
self._global_guards.extend(guards)
130+
self._ensure_available_in_providers(*guards)
130131

131132
def use_global_interceptors(
132133
self, *interceptors: t.Union[EllarInterceptor, t.Type[EllarInterceptor]]
@@ -332,6 +333,9 @@ def __global_guard(
332333
) -> t.List[t.Union[t.Type[GuardCanActivate], GuardCanActivate]]:
333334
try:
334335
guard = self.injector.get(GlobalGuard)
336+
if guard.__class__.__name__ == "GlobalCanActivatePlaceHolder":
337+
return []
338+
335339
return [guard]
336340
except Exception:
337341
return []

ellar/core/modules/config.py

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ def setup(cls, param1: Any, param2: Any, foo: str) -> DynamicModule:
9797
return DynamicModule(cls, providers=[], controllers=[], routers=[])
9898
9999
100-
def module_a_configuration_factory(module: Type[MyModule], config: Config, foo: Foo):
101-
return module.setup(param1=config.param1, param2=config.param2, foo=foo.foo)
100+
def module_a_configuration_factory(module_ref: ModuleRefBase, config: Config, foo: Foo):
101+
return module_ref.module.setup(param1=config.param1, param2=config.param2, foo=foo.foo)
102102
103103
104104
@Module(modules=[ModuleSetup(MyModule, inject=[Config, Foo], factory=module_a_configuration_factory), ])
@@ -162,39 +162,28 @@ def providers(self) -> t.Dict[t.Type, t.Type]:
162162
def is_ready(self) -> bool:
163163
raise Exception(f"{self.module} is not ready")
164164

165-
# @property
166-
# def has_factory_function(self) -> bool:
167-
# if self.factory is not None:
168-
# # if we have a factory function, we need to check if the services to inject is just config
169-
# # if so, then we can go ahead and have the configuration executed since at this level,
170-
# # the config service is available to be injected.
171-
# inject_size = len(self.inject)
172-
# if inject_size == 0:
173-
# return False
174-
#
175-
# if inject_size == 1 and self.inject[0] == Config:
176-
# return False
177-
# return True
178-
179-
def get_module_ref(
180-
self, config: "Config", container: Container
181-
) -> t.Union["ModuleRefBase", "ModuleSetup"]:
182-
# if self.has_factory_function or self.ref_type == MODULE_REF_TYPES.APP_DEPENDENT:
183-
# return self
184-
165+
def get_module_ref(self, config: "Config", container: Container) -> ModuleRefBase:
185166
if self.factory is not None:
186-
ref = self.configure_with_factory(config, container)
167+
ref = self._configure_with_factory(config, container)
187168
else:
188169
ref = create_module_ref_factor(
189170
self.module, config, container, **self.init_kwargs
190171
)
191172

173+
assert isinstance(
174+
ref, ModuleRefBase
175+
), f"{ref.module} is not properly configured."
176+
192177
ref.initiate_module_build()
193178
return ref
194179

195-
def configure_with_factory(
180+
def _configure_with_factory(
196181
self, config: "Config", container: Container
197182
) -> "ModuleRefBase":
183+
if self.ref_type == MODULE_REF_TYPES.APP_DEPENDENT:
184+
app = fail_silently(container.injector.get, interface="App")
185+
assert app, "Application is not ready"
186+
198187
services = self._get_services(container.injector)
199188
init_kwargs = dict(self.init_kwargs)
200189

@@ -226,32 +215,12 @@ def _get_services(self, injector: EllarInjector) -> t.List:
226215
res.append(injector.get(service))
227216
return res
228217

229-
def build(
230-
self,
231-
injector: EllarInjector,
232-
config: "Config",
233-
) -> "ModuleRefBase":
234-
# routes = []
235-
236-
if self.ref_type == MODULE_REF_TYPES.APP_DEPENDENT:
237-
app = fail_silently(injector.get, interface="App")
238-
assert app, "Application is not ready"
239-
240-
ref = self.get_module_ref(container=injector.container, config=config)
241-
assert isinstance(
242-
ref, ModuleRefBase
243-
), f"{ref.module} is not properly configured."
244-
245-
# ref.initiate_module_build()
246-
# ref.build_modules(ref_type)
247-
return ref
248-
249218
def build_and_get_routes(
250219
self,
251220
injector: EllarInjector,
252221
config: "Config",
253222
) -> t.List[BaseRoute]:
254-
ref = self.build(injector, config)
223+
ref = self.get_module_ref(config, injector.container)
255224
ref.build_dependencies()
256225

257226
return ref.get_routes()
@@ -319,7 +288,7 @@ def get_module_dependency_by_type(
319288
)
320289
except Exception as ex:
321290
raise ImproperConfiguration(
322-
f'Unable to import "{self.module}" registered in "{parent_module_ref.module}"'
291+
f"Unable to import '{self.module}' registered in '{parent_module_ref.module}'"
323292
) from ex
324293
else:
325294
module_cls = t.cast(t.Type["ModuleBase"], self.module)
@@ -328,8 +297,9 @@ def get_module_dependency_by_type(
328297

329298
if not node:
330299
raise ImproperConfiguration(
331-
f"ForwardRefModule module='{self.module_name}' "
332-
f"defined in {parent_module_ref.module} could not be found."
300+
f"ForwardRefModule module='{self.module}' "
301+
f"defined in {parent_module_ref.module} could not be found.\n"
302+
f"Please kindly ensure a {self.module} is decorated with @Module() is registered"
333303
)
334304

335305
return node.value.module

ellar/core/modules/ref/base.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
MODULE_WATERMARK,
1313
ROUTE_INTERCEPTORS,
1414
)
15-
from ellar.common.exceptions import ImproperConfiguration
1615
from ellar.core.conf import Config
1716
from ellar.core.context import ApplicationContext
1817
from ellar.core.modules.base import ModuleBase
@@ -144,12 +143,9 @@ def build_dependencies(self, step: int = -1) -> None:
144143
if step != -1 and idx + 1 > step:
145144
break
146145

147-
ref = module_config
146+
ref = module_config.value
148147
if not isinstance(module_config.value, ModuleRefBase):
149-
ref = module_config.value.build(self.container.injector, self.config)
150-
151-
if not isinstance(ref, ModuleRefBase): # pragma: no cover
152-
raise ImproperConfiguration(f"Expected 'ModuleRefBase' but got '{ref}'")
148+
ref = module_config.value.get_module_ref(self.config, self.container)
153149

154150
ref.build_dependencies(step - 1 if step != -1 else step)
155151

@@ -202,8 +198,8 @@ def add_provider(
202198
self._providers.update({provider_type: provider_type})
203199
provider.register(self.container)
204200

205-
if provider.export or export:
206-
self.add_exports(provider_type)
201+
if provider.export or export:
202+
self.add_exports(provider_type)
207203

208204
def _build_module_parameters(self) -> None:
209205
from ellar.core.modules.config import ForwardRefModule

ellar/testing/module.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,19 @@ def override_provider(
3939
*,
4040
use_value: t.Optional[T] = None,
4141
use_class: t.Optional[t.Union[t.Type[T], t.Any]] = None,
42+
core: bool = False,
43+
export: bool = True,
4244
) -> "TestingModule":
4345
"""
4446
Overrides Service at module level.
4547
Use this function before creating an application instance.
4648
"""
4749
provider_config = ProviderConfig(
48-
base_type, use_class=use_class, use_value=use_value, export=True
50+
base_type,
51+
use_class=use_class,
52+
use_value=use_value,
53+
export=export,
54+
core=core,
4955
)
5056
reflect.define_metadata(
5157
constants.MODULE_METADATA.PROVIDERS, [provider_config], self._testing_module

samples/03-auth-with-guards/auth_project/auth/module.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@ def register_providers(self, container: Container) -> None:
1818
1919
"""
2020

21-
from datetime import timedelta
22-
2321
from ellar.common import GlobalGuard, Module
22+
from ellar.core import ForwardRefModule, ModuleBase
2423
from ellar.core import LazyModuleImport as lazyLoad
25-
from ellar.core import ModuleBase
2624
from ellar.di import ProviderConfig
2725
from ellar_jwt import JWTModule
2826

@@ -34,14 +32,12 @@ def register_providers(self, container: Container) -> None:
3432
@Module(
3533
modules=[
3634
lazyLoad("auth_project.users.module:UsersModule"),
37-
JWTModule.setup(
38-
signing_secret_key="my_poor_secret_key_lol", lifetime=timedelta(minutes=5)
39-
),
35+
ForwardRefModule(JWTModule),
4036
],
4137
controllers=[AuthController],
4238
providers=[
4339
AuthService,
44-
ProviderConfig(GlobalGuard, use_class=AuthGuard, export=True),
40+
ProviderConfig(GlobalGuard, use_class=AuthGuard, core=True),
4541
],
4642
)
4743
class AuthModule(ModuleBase):

samples/03-auth-with-guards/auth_project/auth/tests/test_auth_controllers.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
from datetime import timedelta
12
from unittest.mock import Mock
23

34
from ellar.common import Identity
45
from ellar.di import ProviderConfig
56
from ellar.testing import Test
6-
from ellar_jwt import JWTService
7+
from ellar_jwt import JWTModule, JWTService
78

89
from ..controllers import AuthController
910
from ..module import AuthModule
@@ -48,7 +49,15 @@ async def test_get_all_action(self, anyio_backend):
4849

4950
class TestAuthControllerE2E:
5051
def setup_method(self):
51-
self.test_module = Test.create_test_module(modules=[AuthModule])
52+
self.test_module = Test.create_test_module(
53+
modules=[
54+
AuthModule,
55+
JWTModule.setup(
56+
signing_secret_key="my_poor_secret_key_lol",
57+
lifetime=timedelta(minutes=5),
58+
),
59+
]
60+
)
5261
jwt_service: JWTService = self.test_module.get(JWTService)
5362
self.token = jwt_service.sign({"username": "john", "id": 23, "sub": "john"})
5463

samples/03-auth-with-guards/auth_project/root_module.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from datetime import timedelta
2+
13
from ellar.common import (
24
IExecutionContext,
35
JSONResponse,
@@ -8,9 +10,18 @@
810
from ellar.core import LazyModuleImport as lazyLoad
911
from ellar.core import ModuleBase
1012
from ellar.samples.modules import HomeModule
13+
from ellar_jwt import JWTModule
1114

1215

13-
@Module(modules=[HomeModule, lazyLoad("auth_project.auth.module:AuthModule")])
16+
@Module(
17+
modules=[
18+
HomeModule,
19+
lazyLoad("auth_project.auth.module:AuthModule"),
20+
JWTModule.setup(
21+
signing_secret_key="my_poor_secret_key_lol", lifetime=timedelta(minutes=5)
22+
),
23+
]
24+
)
1425
class ApplicationModule(ModuleBase):
1526
@exception_handler(404)
1627
def exception_404_handler(self, ctx: IExecutionContext, exc: Exception) -> Response:

tests/test_application/test_execution_context.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def lookup_status_code_exception_handler(self, status_code: int):
8383

8484
def test_can_replace_host_context():
8585
tm = Test.create_test_module(controllers=[ExampleController]).override_provider(
86-
IHostContextFactory, use_class=NewHostContextFactory
86+
IHostContextFactory, use_class=NewHostContextFactory, core=True
8787
)
8888

8989
assert hasattr(NewHostContext, "worked") is False
@@ -127,7 +127,7 @@ def exception_400(self, context: IHostContext, exc: Exception):
127127

128128
def test_can_replace_execution_context():
129129
tm = Test.create_test_module(controllers=[ExampleController]).override_provider(
130-
IExecutionContextFactory, use_class=NewExecutionHostFactory
130+
IExecutionContextFactory, use_class=NewExecutionHostFactory, core=True
131131
)
132132

133133
assert hasattr(NewExecutionContext, "worked") is False

tests/test_auth/test_guards/test_auth_guards.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def _auth_demo_endpoint(request: Inject[Request]):
287287

288288
def test_global_guard_case_2_works():
289289
_app = AppFactory.create_app(
290-
providers=[ProviderConfig(GlobalGuard, use_class=DigestAuth)]
290+
providers=[ProviderConfig(GlobalGuard, use_class=DigestAuth, core=True)]
291291
)
292292

293293
@get("/global")
@@ -326,7 +326,7 @@ def test_if_an_auth_guard_return_converts_to_identity(
326326
input_type, is_authenticated, expect_result
327327
):
328328
_app = AppFactory.create_app(
329-
providers=[ProviderConfig(GlobalGuard, use_class=DigestAuth)]
329+
providers=[ProviderConfig(GlobalGuard, use_class=DigestAuth, core=True)]
330330
)
331331

332332
@get("/global")

0 commit comments

Comments
 (0)