Coverage for src / lexigram / contracts / admin / contributor.py: 100%
46 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
1"""Convenience base class for admin contributor implementations."""
3from __future__ import annotations
5from collections.abc import Sequence
6from typing import TYPE_CHECKING, cast
8from lexigram.contracts.admin.types import (
9 AdminActionDefinition,
10 AdminHealthDefinition,
11 AdminRouteSpec,
12 DashboardWidgetDefinition,
13 ManagementPageDefinition,
14 NavigationContribution,
15 SettingsPanelDefinition,
16 WidgetParams,
17)
19if TYPE_CHECKING:
20 from lexigram.contracts.admin.errors import AdminError
21 from lexigram.contracts.admin.health_payload import HealthCheckPayload
22 from lexigram.contracts.admin.types import WidgetViewModel
23 from lexigram.contracts.core.di import ContainerResolverProtocol
24 from lexigram.contracts.core.result import Result
27class BaseAdminContributor:
28 """Convenience base class for admin contributors.
30 Provides no-op defaults for all ``AdminContributorProtocol`` methods.
31 Subclasses override only the methods they need.
32 """
34 name: str = ""
35 display_name: str = ""
36 group: str = "framework"
37 depends_on: tuple[str, ...] = ()
38 icon: str = "box"
39 priority: int = 100
40 version: str = "0.0.0"
41 package_source: str = "built-in"
42 required_permissions: frozenset[str] = frozenset()
44 @property
45 def contributor_id(self) -> str:
46 """Stable identifier for RBAC lookup — equals ``name``."""
47 return self.name
49 def get_resources(self) -> Sequence[type]:
50 """Return an empty sequence by default."""
51 return []
53 def get_routes(self) -> Sequence[AdminRouteSpec]:
54 """Return an empty sequence by default."""
55 return []
57 def get_dashboard_widgets(self) -> Sequence[DashboardWidgetDefinition]:
58 """Return an empty list by default."""
59 return []
61 def get_navigation_items(self) -> Sequence[NavigationContribution]:
62 """Return an empty list by default."""
63 return []
65 def get_management_pages(self) -> Sequence[ManagementPageDefinition]:
66 """Return an empty list by default."""
67 return []
69 def get_settings_panels(self) -> Sequence[SettingsPanelDefinition]:
70 """Return an empty list by default."""
71 return []
73 def get_health_definitions(self) -> Sequence[AdminHealthDefinition]:
74 """Return an empty list by default."""
75 return []
77 def get_actions(self) -> Sequence[AdminActionDefinition]:
78 """Return an empty list by default."""
79 return []
81 async def on_admin_boot(self, container: ContainerResolverProtocol) -> None:
82 """No-op boot hook."""
84 async def on_admin_shutdown(self) -> None:
85 """No-op shutdown hook."""
87 async def render_widget(
88 self,
89 widget_name: str,
90 params: WidgetParams,
91 resolver: ContainerResolverProtocol | None = None,
92 ) -> Result[WidgetViewModel, AdminError]:
93 """Return a not-found error by default — override in subclasses."""
94 from lexigram.contracts.admin.errors import WidgetNotFoundError
95 from lexigram.contracts.core.result import Err
97 result: Result[WidgetViewModel, AdminError] = cast(
98 "Result[WidgetViewModel, AdminError]",
99 Err(WidgetNotFoundError(self.name, widget_name)),
100 )
101 return result
103 async def render_health_check(
104 self,
105 check_name: str,
106 ) -> Result[HealthCheckPayload, AdminError]:
107 """Default: this contributor does not serve the requested health check.
109 Args:
110 check_name: Name of the health check requested.
112 Returns:
113 ``Err(HealthCheckNotFoundError)`` — contributor does not provide this check.
114 """
115 from lexigram.contracts.admin.errors import HealthCheckNotFoundError
116 from lexigram.contracts.core.result import Err
118 result: Result[HealthCheckPayload, AdminError] = cast(
119 "Result[HealthCheckPayload, AdminError]",
120 Err(HealthCheckNotFoundError(self.name, check_name)),
121 )
122 return result
125__all__ = ["BaseAdminContributor"]