Coverage for src/lexigram/admin/di/mount/core.py: 0%
52 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:18 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:18 +0800
1"""Core mount phases: resource resolution and the admin settings service."""
3from __future__ import annotations
5from typing import TYPE_CHECKING, Any
7from lexigram.logging import get_logger
9if TYPE_CHECKING:
10 from lexigram.admin.di.bundle_provider import AdminProvider
11 from lexigram.admin.di.mount.context import MountContext
13_log = get_logger(__name__)
16class AdminMountCoreMixin:
17 """Mount phases that resolve admin resources and settings plumbing."""
19 # Host attributes provided by AdminProvider.
20 _config: Any
21 _resources: list[Any]
22 _controllers: list[Any]
23 _mount_failures: dict[str, str]
24 _authorizer_service: Any
26 _get_csrf_service: Any
29 async def _mount_resources(
30 self, resolver: Any, ctx: MountContext
31 ) -> None:
32 """Resolve registered resource classes into named instances.
34 Failures are recorded in ``_mount_failures`` and re-raised only when
35 strict resource resolution is configured.
37 Args:
38 resolver: The DI resolver for resource resolution.
39 ctx: Mount pipeline state (``resources`` populated in place).
40 """
41 from lexigram.admin.rbac.inventory import PermissionInventoryService
43 for resource_cls in self._resources:
44 name = (
45 getattr(resource_cls, "name", None)
46 or resource_cls.__name__.replace("Resource", "").lower()
47 )
48 try:
49 ctx.resources[name] = await resolver.resolve(
50 resource_cls,
51 bypass_visibility=True,
52 )
53 except Exception as exc:
54 _log.error(
55 "admin.resource_resolution_failed",
56 resource=resource_cls.__name__,
57 error=str(exc),
58 strict=self._config.strict_resource_resolution,
59 )
60 self._mount_failures[f"resource:{resource_cls.__name__}"] = str(exc)
61 if self._config.strict_resource_resolution:
62 raise
64 # Populate the RBAC permission inventory from registered resources
65 try:
66 inventory = await resolver.resolve(
67 PermissionInventoryService,
68 bypass_visibility=True,
69 )
70 inventory.register_resources(ctx.resources.keys())
71 except Exception as exc: # noqa: BLE001 — discovery is best-effort
72 _log.warning(
73 "admin.rbac_inventory_discovery_failed",
74 error=str(exc),
75 )
77 async def _mount_settings_service(
78 self, resolver: Any, ctx: MountContext
79 ) -> None:
80 """Create the shared settings service for runtime theme overrides.
82 Best-effort: without a database provider the service falls back to an
83 in-memory instance so the panel still mounts.
85 Args:
86 resolver: The DI resolver for settings dependencies.
87 ctx: Mount pipeline state (``settings_service`` populated).
88 """
89 admin_settings_service: Any = None
90 try:
91 from lexigram.admin.services.settings_service import (
92 AdminSettingsDbProvider,
93 AdminSettingsService,
94 )
95 from lexigram.contracts.data import DatabaseProviderProtocol
97 db_provider = await resolver.resolve(
98 DatabaseProviderProtocol,
99 bypass_visibility=True,
100 )
101 config_provider = AdminSettingsDbProvider(db=db_provider)
102 # Ensure the tenant_configs table is created eagerly at startup
103 try:
104 await config_provider._ensure_table()
105 except Exception:
106 _log.warning("admin.tenant_config_table_creation_failed")
107 admin_settings_service = AdminSettingsService(
108 config_provider=config_provider,
109 )
110 from lexigram.admin.settings.panel.registry import ConfigRegistry
111 from lexigram.admin.settings.store import TenantConfigStore
113 try:
114 registry = await resolver.resolve(
115 ConfigRegistry,
116 bypass_visibility=True,
117 )
118 registry.register_store("db", TenantConfigStore(admin_settings_service))
119 except Exception:
120 _log.warning("admin.settings_store_registration_failed")
121 except Exception:
122 try:
123 admin_settings_service = AdminSettingsService()
124 except Exception as exc: # noqa: BLE001 — fallback is best-effort
125 _log.warning("admin.settings_service_fallback_failed", reason=str(exc))
126 ctx.settings_service = admin_settings_service