Files
govoplan-calendar/docs/CALENDAR_INTEGRATION_CONCEPT.md

202 lines
9.9 KiB
Markdown

# GovOPlaN Calendar Integration Concept
## Module Boundary
`govoplan-calendar` owns calendar primitives:
- calendar collections
- VEVENT storage and iCalendar import/export
- event recurrence data, recurrence exceptions, and future recurrence expansion
- availability and free/busy semantics
- resources such as rooms, shared equipment, and service desks
- groupware calendar adapter boundaries, including CalDAV and Open-Xchange
Calendar does not own meeting-poll decisions, bookable service appointments, task lifecycle, mail transport, or document lifecycle. Those modules should reference calendar events through stable IDs, iCalendar UIDs, capabilities, or API contracts.
## First Implementation
The first standalone module provides:
- `CalendarCollection` records for tenant calendars
- `CalendarEvent` records for VEVENT data
- normalized query fields: start, end, summary, location, all-day flag, status, transparency, classification, calendar ID, UID, recurrence ID, sequence, source, and ETag
- iCalendar preservation: raw VEVENT properties, parameters, and generated `text/calendar` export
- API endpoints for listing calendars, creating/updating/deleting events, importing iCalendar, and exporting event ICS
- free/busy API primitive with recurrence expansion for scheduling and appointment conflict checks
- calendar-owned CalDAV sync sources with credential references, scheduled due-sync metadata, full/incremental inbound sync, two-way PUT/DELETE writes, and ETag conflict handling
- a Calendar-owned durable desired-state outbox for two-way CalDAV writes. Event
state and the exact external resource snapshot commit atomically; workers use
deterministic hrefs, conditional requests, expiring leases, bounded
exponential retry, and semantic GET reconciliation after ambiguous outcomes
- WebUI views: month, week, workweek, day, and continuous week-row scrolling
The first implementation is not yet a full CalDAV network server. It is the internal calendar storage, sync, availability, and UI foundation on which Open-Xchange integration, richer recurrence editing, scheduling inbox/outbox behavior, and CalDAV server endpoints can be built.
## Outbound delivery operations
No event API or cross-module capability performs CalDAV network I/O inside the
caller's transaction. A local mutation instead creates a
`calendar_outbox_operations` row containing the exact desired ICS resource,
target href, expected ETag, conflict-policy snapshot, and idempotency key.
The registered `govoplan.calendar.dispatch_outbox` worker commits an expiring
lease before doing network I/O and commits each outcome independently. If a
worker loses the database connection after a successful remote write, the next
attempt reads the remote object and compares semantic ICS fingerprints. A
matching PUT or an already absent DELETE completes successfully without a blind
duplicate write. Pending updates for one href supersede older never-attempted
rows; attempted predecessors are reconciled first, and updates queued behind an
in-flight write inherit the ETag produced by that write. Delivery and inbound
REPORT application take the same source-row lock,
and only one operation per source is leased at a time. This deliberately trades
per-source throughput for a simple ordering guarantee: a stale inbound report
cannot land after a newer outbound result, and queued leases do not expire while
waiting behind another network request for the same source.
Operators can list, dispatch, retry, reconcile, and discard tenant operations
through the `/calendar/caldav/outbox` administration endpoints. Terminal ETag
conflicts and exhausted retries remain the current local desired state and
shield that resource from inbound overwrite until explicitly resolved.
Discarding is the administrator's accept-remote transition: it is allowed only
for the latest generation, atomically cancels its unresolved predecessor chain,
marks the event projection as discarded, clears the sync token, and schedules a
full inbound reconciliation so an unchanged remote object is not missed.
Disabling outbound delivery or switching to inbound-only is rejected while
unresolved desired state exists; retirement is the explicit exception and
cancels unresolved work. Endpoint/calendar changes also require no active event
bindings or unresolved delivery. Credential rotation does not discard committed
desired state. Public event mutation cannot set sync-owned source hrefs, kinds,
or ETags. Deleting a synchronized collection or retiring its source is a local
unlink: it never deletes the remote collection or its remaining remote events.
The current singular ownership model permits one active sync source per
calendar; multi-source fan-in will require per-event source routing. Celery beat
triggers recovery every minute, while root-transaction after-commit dispatch
provides the normal low-latency path.
### Collection retirement and bulk event moves
Collection deletion accepts two explicit, non-destructive external actions for
`event_action="move"`:
- `detach_keep_remote` applies only when the source collection is synchronized
and the target is local. It moves the local event projections, clears their
sync-owned source kind, href, ETag, and CalDAV projection metadata, retires
the source locally, and cancels undelivered outbox state. An active delivery
lease blocks the transaction. The action never enqueues a remote DELETE, so
the remote collection and remote events remain unchanged.
- `copy_to_remote` applies only when the source is local and the target has an
active, enabled, two-way CalDAV source. It moves the local events and commits
destination PUT desired states in the durable outbox in the same database
transaction. Remote failures therefore remain visible and retryable without
rolling back or hiding the local move.
Local-to-local moves retain their existing behavior and do not accept an
`external_action`. Implicit or mismatched actions, inbound-only destinations,
and synchronized-to-synchronized moves are rejected. `remote_move` is a known
but deliberately unsupported action: implementing it requires a separately
approved saga for destination reconciliation, conditional source deletion,
collision handling, and concurrent-edit policy. The WebUI does not offer that
destructive mode.
Resolved terminal rows (`succeeded`, `superseded`, and `cancelled`) are removed
in bounded batches after `CALENDAR_OUTBOX_TERMINAL_RETENTION_DAYS` (90 days by
default; `0` disables cleanup). `conflict` and `dead` rows are never removed by
this cleanup because they still carry unresolved local desired state. Each
periodic or after-commit dispatch also performs one bounded cleanup batch for
the same tenant scope.
## Integration Points
### Scheduling
`govoplan-scheduling` should use calendar for:
- organizer availability lookup
- candidate slot conflict checks
- final event creation after a poll decision
- participant invitation state as VEVENT attendees when a meeting becomes real
Scheduling remains owner of polls, candidate ranking, external participation links, and decision audit.
### Appointments
`govoplan-appointments` should use calendar for:
- confirmed appointment placement
- staff/resource conflict checks
- room and desk calendars
- cancellation or rescheduling events
Appointments remains owner of public booking flows, service definitions, capacity rules, queues, and no-show handling.
### Tasks And Workflow
`govoplan-tasks` and `govoplan-workflow` should use calendar for:
- due-date and reminder calendar overlays
- workflow deadlines
- escalation dates
- optional event creation for hearings, reviews, inspections, and internal meetings
Tasks/workflow remain owners of assignment, status, SLA logic, and completion semantics.
### Mail And Notifications
`govoplan-mail` and `govoplan-notifications` should use calendar for:
- outbound iCalendar invites and updates
- inbound invite parsing from messages
- RSVP state changes
- reminders and digest notifications
Mail remains owner of SMTP/IMAP profiles and mailbox transport. Notifications remains owner of delivery channels and delivery policy.
### Documents And DMS
`govoplan-dms` can link documents to events for:
- agendas
- minutes
- attachments
- legal deadlines
- retention and audit context
DMS remains owner of versions, locks, approvals, collaboration, legal hold, and document retention.
### Cases, Campaigns, Forms, And Portal
Domain modules may attach calendar references to domain objects:
- cases: hearings, inspections, review meetings
- campaigns: send windows and review deadlines
- forms: submission windows and office-hour sessions
- portal: citizen-facing event or appointment visibility
Calendar should expose reusable event and availability APIs instead of importing those modules.
### Connectors And Public-Sector Groupware
`govoplan-connectors` should own generic connector catalogue behavior, connection setup patterns, and external-system inventory. Calendar owns the actual calendar semantics for:
- CalDAV
- Open-Xchange calendar
- OpenDesk calendar stack integration
- resource-calendar mapping
- free/busy sync
- recurrence and exception mapping
- conflict handling
The connector boundary should hand calendar a configured profile and credentials reference, not a domain-specific event model.
## Follow-Up Work
- Full recurrence expansion for RRULE, RDATE, EXDATE, RECURRENCE-ID, overridden instances, and detached instances.
- User-facing recurrence editing for RRULE, RDATE, EXDATE, RECURRENCE-ID, overridden instances, and detached instances.
- UI management for CalDAV credentials, sync status, sync direction, and conflict resolution.
- CalDAV server endpoints if GovOPlaN should expose calendars to external clients rather than only syncing remote collections.
- Open-Xchange adapter that maps OX calendars, attendees, resources, recurrence, and free/busy to the internal calendar model.
- Attendee RSVP workflow and mail/notification bridge.
- Resource calendars for rooms, equipment, counters, and service desks.
- Calendar ACLs beyond tenant-wide permissions.