Coverage for src / lexigram / admin / di / sub_providers / core.py: 91%

33 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-13 22:14 +0800

1"""Admin core sub-provider — config, routing, Starlette sub-app, static files.""" 

2 

3from __future__ import annotations 

4 

5from typing import TYPE_CHECKING 

6 

7from lexigram.contracts.core.health import HealthCheckResult, HealthStatus 

8 

9if TYPE_CHECKING: 

10 from lexigram.admin.config import AdminConfig 

11 from lexigram.contracts.core.di import ( 

12 ContainerRegistrarProtocol, 

13 ContainerResolverProtocol, 

14 ) 

15 

16 

17class AdminCoreSubProvider: 

18 """Manages core admin infrastructure: config, routing, templates, ASGI mounting. 

19 

20 This is a focused helper class, NOT a Provider subclass. 

21 It is orchestrated by AdminProvider. 

22 """ 

23 

24 def __init__(self, config: AdminConfig, **kwargs: object) -> None: 

25 self._config = config 

26 self._kwargs = kwargs 

27 self._mounted = False 

28 

29 @property 

30 def config(self) -> AdminConfig: 

31 """Return current admin config.""" 

32 return self._config 

33 

34 async def register(self, container: ContainerRegistrarProtocol) -> None: 

35 """Register core admin services: config, routing, rendering, feature flags.""" 

36 from lexigram.admin.config import AdminConfig 

37 from lexigram.admin.core.rendering import AdminRenderer 

38 from lexigram.admin.engine.renderer import AdminRenderer as EngineAdminRenderer 

39 from lexigram.admin.services.feature_flags import AdminFeatureFlagService 

40 from lexigram.admin.settings.panel.registry import ConfigRegistry 

41 from lexigram.admin.state.dependency_tracker import DependencyTracker 

42 

43 container.singleton(AdminConfig, self._config) 

44 

45 # AdminRouter is built in AdminProvider.mount_to_app() with the 

46 # resolved resource and controller instances — do NOT register as class here 

47 # (its constructor has resources/controllers that are not container-resolvable). 

48 

49 # Register the engine AdminRenderer (all-optional deps) — this is what 

50 # AdminController subclasses use for rendering admin pages. 

51 from lexigram.admin.engine.renderer import AdminRendererConfig 

52 

53 renderer_config = AdminRendererConfig( 

54 primary_color=self._config.ui.primary_color or "#6b7280", 

55 ) 

56 container.singleton(EngineAdminRenderer, EngineAdminRenderer(renderer_config)) 

57 

58 # Register the core AdminRenderer as well (used internally) 

59 container.singleton(AdminRenderer, AdminRenderer) 

60 

61 container.singleton(DependencyTracker, DependencyTracker) 

62 container.singleton(ConfigRegistry, ConfigRegistry.with_defaults) 

63 

64 # Feature flags: register the admin service only. 

65 # The concrete FlagManagerProtocol implementation is expected to be 

66 # registered by the application's feature-flags provider. 

67 container.singleton(AdminFeatureFlagService, AdminFeatureFlagService) 

68 

69 async def boot(self, container: ContainerResolverProtocol) -> None: 

70 """Initialize Starlette sub-app and mount routes.""" 

71 self._mounted = True 

72 

73 async def shutdown(self) -> None: 

74 """Clean up core resources.""" 

75 self._mounted = False 

76 

77 async def health_check(self, timeout: float = 5.0) -> HealthCheckResult: 

78 """Return core health status.""" 

79 return HealthCheckResult( 

80 component="admin_core", 

81 status=HealthStatus.HEALTHY if self._mounted else HealthStatus.UNKNOWN, 

82 message="Admin core operational" if self._mounted else "Not yet mounted", 

83 ) 

84 

85 

86__all__ = ["AdminCoreSubProvider"]