From 435b924fd9dd18d12dd8b133b3557f5a1f1c2659 Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Sun, 2 Aug 2026 16:38:34 +0200 Subject: [PATCH] Extend calendar invitation capability contract --- src/govoplan_core/core/calendar.py | 55 ++++++++++++++++++++++++++++++ tests/test_module_system.py | 8 +++++ 2 files changed, 63 insertions(+) diff --git a/src/govoplan_core/core/calendar.py b/src/govoplan_core/core/calendar.py index 045673d..51c7f73 100644 --- a/src/govoplan_core/core/calendar.py +++ b/src/govoplan_core/core/calendar.py @@ -91,6 +91,16 @@ class CalendarInvitationRef: degraded_reasons: tuple[str, ...] = () +@dataclass(frozen=True, slots=True) +class CalendarInvitationCalendarRef: + id: str + name: str + color: str | None = None + timezone: str = "UTC" + source_kind: str = "local" + writable: bool = True + + @dataclass(frozen=True, slots=True) class CalendarExternalProfileRequest: """Connector-neutral request for a Calendar-owned external profile.""" @@ -165,6 +175,20 @@ class CalendarOutboxProvider(Protocol): class CalendarInvitationProvider(Protocol): """Correlation-aware invitation boundary for Campaign and Mail adapters.""" + def list_calendars( + self, + session: object, + *, + tenant_id: str, + user_id: str | None = None, + group_ids: Sequence[str] = (), + can_admin: bool = False, + ) -> Sequence[CalendarInvitationCalendarRef]: + ... + + def render_invitation(self, request: CalendarInvitationRequest) -> str: + ... + def upsert_invitation( self, session: object, @@ -184,6 +208,26 @@ class CalendarInvitationProvider(Protocol): ) -> CalendarInvitationRef | None: ... + def get_invitations( + self, + session: object, + *, + tenant_id: str, + correlation_ids: Sequence[str], + ) -> Mapping[str, CalendarInvitationRef]: + ... + + def summarize_invitations( + self, + session: object, + *, + tenant_id: str, + source_module: str, + source_resource_type: str, + source_resource_id: str | None, + ) -> Mapping[str, object]: + ... + def record_response( self, session: object, @@ -198,6 +242,17 @@ class CalendarInvitationProvider(Protocol): ) -> CalendarInvitationRef: ... + def record_icalendar_reply( + self, + session: object, + *, + tenant_id: str, + icalendar: str, + received_at: datetime | None = None, + evidence: Mapping[str, object] | None = None, + ) -> Sequence[CalendarInvitationRef]: + ... + @runtime_checkable class CalendarExternalProfileProvider(Protocol): diff --git a/tests/test_module_system.py b/tests/test_module_system.py index e35dad5..da2652f 100644 --- a/tests/test_module_system.py +++ b/tests/test_module_system.py @@ -239,6 +239,7 @@ class ModuleSystemTests(unittest.TestCase): "addresses", "dist_lists", "templates", + "calendar", "postbox", "approvals", "reporting", @@ -3469,6 +3470,12 @@ finally: "version_min": "0.1.0", "version_max_exclusive": "0.2.0", }, modules["campaigns"]["requires_interfaces"]) + self.assertIn({ + "name": "calendar.invitations", + "optional": True, + "version_min": "0.2.0", + "version_max_exclusive": "0.3.0", + }, modules["campaigns"]["requires_interfaces"]) self.assertEqual( [ "files", @@ -3477,6 +3484,7 @@ finally: "addresses", "dist_lists", "templates", + "calendar", "postbox", "approvals", "reporting",