feat: reconcile scheduling calendar holds

This commit is contained in:
2026-08-20 07:44:15 +02:00
parent 4bbf80e634
commit 93fb8aeff8
3 changed files with 170 additions and 6 deletions
@@ -11,6 +11,7 @@ from sqlalchemy.orm import Session
from govoplan_core.core.calendar import (
CalendarCapabilityError,
CalendarEventRef,
CalendarEventReleaseRef,
CalendarEventRequest,
CalendarExternalProfileProvider,
CalendarExternalProfileRef,
@@ -37,6 +38,7 @@ from govoplan_calendar.backend.service import (
CalendarError,
create_sync_source,
create_event,
delete_event,
list_calendars as list_calendar_collections,
list_freebusy,
update_event,
@@ -312,6 +314,96 @@ class SqlCalendarSchedulingProvider(CalendarSchedulingProvider):
outbox_operation_id=outbox_operation_id,
)
def promote_event(
self,
session: object,
*,
tenant_id: str,
user_id: str | None,
event_id: str,
request: CalendarEventRequest,
) -> CalendarEventRef:
try:
payload = CalendarEventUpdateRequest(
calendar_id=request.calendar_id,
summary=request.summary,
description=request.description,
location=request.location,
status=request.status,
transparency=request.transparency,
classification=request.classification,
start_at=request.start_at,
end_at=request.end_at,
timezone=request.timezone,
attendees=[dict(item) for item in request.attendees],
categories=list(request.categories),
related_to=[dict(item) for item in request.related_to],
metadata=dict(request.metadata),
)
event = update_event(
session,
tenant_id=tenant_id,
user_id=user_id,
event_id=event_id,
payload=payload,
)
except (CalendarError, TypeError, ValueError) as exc:
raise CalendarCapabilityError(str(exc)) from exc
external_state, outbox_operation_id = _external_state(event)
return CalendarEventRef(
id=event.id,
calendar_id=event.calendar_id,
uid=event.uid,
external_state=external_state,
outbox_operation_id=outbox_operation_id,
)
def release_event(
self,
session: object,
*,
tenant_id: str,
user_id: str | None,
event_id: str,
) -> CalendarEventReleaseRef:
if not isinstance(session, Session):
raise CalendarCapabilityError("Calendar release requires a database session")
event = (
session.query(CalendarEvent)
.filter(
CalendarEvent.tenant_id == tenant_id,
CalendarEvent.id == event_id,
)
.first()
)
if event is None:
return CalendarEventReleaseRef(
event_id=event_id,
already_released=True,
external_state="not_found",
)
was_released = event.deleted_at is not None
if not was_released:
try:
delete_event(
session,
tenant_id=tenant_id,
event_id=event_id,
user_id=user_id,
)
except CalendarError as exc:
raise CalendarCapabilityError(str(exc)) from exc
session.flush()
external_state, outbox_operation_id = _external_state(event)
return CalendarEventReleaseRef(
event_id=event_id,
already_released=was_released,
external_state=(
external_state if external_state != "local" else "local_released"
),
outbox_operation_id=outbox_operation_id,
)
class SqlCalendarExternalProfileProvider(CalendarExternalProfileProvider):
"""Configure groupware profiles while Calendar retains event semantics."""
+2 -2
View File
@@ -504,7 +504,7 @@ manifest = ModuleManifest(
optional_dependencies=("mail", "tasks", "scheduling", "appointments", "workflow_engine", "notifications", "dms", "connectors", "search"),
provides_interfaces=(
ModuleInterfaceProvider(name="calendar.outbox", version="0.1.8"),
ModuleInterfaceProvider(name="calendar.scheduling", version="0.1.8"),
ModuleInterfaceProvider(name="calendar.scheduling", version="0.1.9"),
ModuleInterfaceProvider(name="calendar.invitations", version="0.2.0"),
ModuleInterfaceProvider(name="calendar.external_profiles", version="0.1.0"),
),
@@ -608,7 +608,7 @@ manifest = ModuleManifest(
id="calendar.manage-calendars-and-events",
title="Use calendars and events",
summary="Create calendar collections and work with all-day or timed events in continuous, month, week, workweek, and day views.",
body="Calendar remembers the selected view and preferences. Events can be created, edited, moved, resized, repeated, imported, exported, or deleted when the current account has the corresponding permission. All-day events use dates rather than local clock times; timed events retain their timezone-aware start and end values.",
body="Calendar remembers the selected view and preferences. Events can be created, edited, moved, resized, repeated, imported, exported, or deleted when the current account has the corresponding permission. All-day events use dates rather than local clock times; timed events retain their timezone-aware start and end values. Scheduling may promote a selected tentative hold in place and submit unused holds for idempotent release through the neutral Calendar capability. Calendar owns the resulting local tombstone, synchronized outbox operation, retry and reconciliation evidence; an already released or absent event is an accepted replay rather than a duplicate failure.",
documentation_types=("user",),
audience=("user", "calendar_manager"),
related_modules=("scheduling", "notifications"),