Coverage for src/lexigram/notification/config/top_level.py: 90%
21 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 02:26 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 02:26 +0800
1"""Top-level notification configuration."""
3from __future__ import annotations
5from dataclasses import dataclass
6from typing import ClassVar
8from lexigram.config.base import BaseConfig
9from lexigram.notification.config.push import NamedPushConfig
10from lexigram.notification.config.sms import NamedSMSConfig
11from lexigram.notification.constants import (
12 ENV_NESTED_DELIMITER,
13 ENV_PREFIX,
14)
15from lexigram.validation import ConfigDict, Field
18@dataclass(init=False)
19class NotificationConfig(BaseConfig):
20 """Top-level notification configuration.
22 Loaded from the ``notification:`` key in application.yaml, with environment
23 variable overrides via ``LEX_NOTIFICATION__*`` prefix.
24 """
26 config_section: ClassVar[str] = "notification"
28 model_config: ClassVar[ConfigDict] = ConfigDict( # type: ignore[typeddict-unknown-key]
29 env_prefix=ENV_PREFIX,
30 env_nested_delimiter=ENV_NESTED_DELIMITER,
31 extra="ignore",
32 )
34 sms_backends: list[NamedSMSConfig] = Field(
35 default_factory=list,
36 description=(
37 "Named SMS backends for multi-backend support. "
38 "When non-empty, the provider registers each backend under "
39 "Annotated[SMSChannelProtocol, Named(entry.name)]. "
40 "The first entry (or the one with primary=True) also receives "
41 "the unnamed SMSChannelProtocol binding for backward compatibility."
42 ),
43 )
44 push_backends: list[NamedPushConfig] = Field(
45 default_factory=list,
46 description=(
47 "Named push notification backends for multi-backend support. "
48 "When non-empty, the provider registers each backend under "
49 "Annotated[PushChannelProtocol, Named(entry.name)]. "
50 "The first entry (or the one with primary=True) also receives "
51 "the unnamed PushChannelProtocol binding for backward compatibility."
52 ),
53 )
55 @classmethod
56 def from_named_sms(cls, entry: NamedSMSConfig) -> NotificationConfig:
57 """Build a single-SMS-backend NotificationConfig from a NamedSMSConfig entry.
59 Used internally by NotificationProvider to create per-backend configs
60 from a multi-backend declaration.
62 Args:
63 entry: The named SMS backend entry to materialise.
65 Returns:
66 A NotificationConfig configured for the single named SMS backend.
67 """
68 return cls(sms_backends=[entry])
70 @classmethod
71 def from_named_push(cls, entry: NamedPushConfig) -> NotificationConfig:
72 """Build a single-push-backend NotificationConfig from a NamedPushConfig entry.
74 Used internally by NotificationProvider to create per-backend configs
75 from a multi-backend declaration.
77 Args:
78 entry: The named push backend entry to materialise.
80 Returns:
81 A NotificationConfig configured for the single named push backend.
82 """
83 return cls(push_backends=[entry])
86__all__ = [
87 "NotificationConfig",
88]