mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 10:15:27 -04:00
* feat: CalDAV write-back — push local event create/update/delete to the remote (#800) CalDAV sync was pull-only (src/caldav_sync.py), so events created, edited, or deleted in Odysseus on a CalDAV-backed calendar only changed local SQLite and never reached the server — they silently vanished on the next pull and never appeared on the user's phone (iCloud, etc.). This adds the missing write half: - src/caldav_writeback.py builds the VEVENT, re-discovers the remote calendar by the same URL-hash the local id was derived from (the remote URL isn't stored), and PUTs/DELETEs the event by UID via the caldav lib. The pure pieces (build_event_ical, find_remote_calendar, push_event) take inputs by argument so they unit-test against a fake client with no network. - create/update/delete event handlers (routes/calendar_routes.py) call it best-effort for caldav-sourced calendars only: the local DB stays the source of truth, a remote failure is logged, never fatal, and local calendars are untouched. Tests: tests/test_caldav_writeback.py (9, pure logic incl. iCal serialization, hash discovery, create/update/delete orchestration) and tests/test_caldav_writeback_route.py (3, route-level: a caldav calendar pushes, a local one does not, delete pushes a delete). 12 passed. Note: write-back re-discovers the remote calendar per write (the URL isn't persisted locally); a follow-up could cache it. Live-iCloud verification needs a real account — flagging for a maintainer pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: drive #800 route regression without TestClient (fixes local hang) Same fix as the document route test: the CalDAV write-back route regression used Starlette TestClient (middleware app + threadpool) which hung in the maintainer's environment. Rework it to call the async create/delete calendar handlers directly — extracted from the router — with a minimal fake request, temp-SQLite-patched SessionLocal, and writeback_event stubbed to record calls. Same coverage (a caldav calendar pushes, a local one does not, delete pushes a delete), completes in ~0.3s with no TestClient. Verified the maintainer's exact batch: pytest tests/test_caldav_writeback.py tests/test_caldav_writeback_route.py -> 12 passed Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -758,6 +758,16 @@ def setup_calendar_routes() -> APIRouter:
|
||||
)
|
||||
db.add(ev)
|
||||
db.commit()
|
||||
if cal.source == "caldav":
|
||||
# Push the new event to the remote so it appears on the user's
|
||||
# other devices — the sync is otherwise pull-only (#800).
|
||||
from src.caldav_writeback import writeback_event
|
||||
await writeback_event(owner, cal.source, cal.id, {
|
||||
"uid": uid, "summary": data.summary, "description": data.description,
|
||||
"location": data.location, "dtstart": dtstart, "dtend": dtend,
|
||||
"all_day": data.all_day, "is_utc": _is_utc and not data.all_day,
|
||||
"rrule": data.rrule or "",
|
||||
})
|
||||
return {"ok": True, "uid": uid}
|
||||
except HTTPException:
|
||||
raise
|
||||
@@ -804,6 +814,14 @@ def setup_calendar_routes() -> APIRouter:
|
||||
if data.color is not None:
|
||||
ev.color = data.color if data.color else None
|
||||
db.commit()
|
||||
cal = db.query(CalendarCal).filter(CalendarCal.id == ev.calendar_id).first()
|
||||
if cal and cal.source == "caldav":
|
||||
from src.caldav_writeback import writeback_event
|
||||
await writeback_event(owner, cal.source, cal.id, {
|
||||
"uid": ev.uid, "summary": ev.summary, "description": ev.description,
|
||||
"location": ev.location, "dtstart": ev.dtstart, "dtend": ev.dtend,
|
||||
"all_day": ev.all_day, "is_utc": ev.is_utc, "rrule": ev.rrule or "",
|
||||
})
|
||||
return {"ok": True}
|
||||
except HTTPException:
|
||||
raise
|
||||
@@ -824,8 +842,15 @@ def setup_calendar_routes() -> APIRouter:
|
||||
db = SessionLocal()
|
||||
try:
|
||||
ev = _get_or_404_event(db, base_uid, owner)
|
||||
# Capture what the remote push needs BEFORE the row is gone.
|
||||
_cal = db.query(CalendarCal).filter(CalendarCal.id == ev.calendar_id).first()
|
||||
_is_caldav = bool(_cal and _cal.source == "caldav")
|
||||
_cal_id, _ev_uid = ev.calendar_id, ev.uid
|
||||
db.delete(ev)
|
||||
db.commit()
|
||||
if _is_caldav:
|
||||
from src.caldav_writeback import writeback_event
|
||||
await writeback_event(owner, "caldav", _cal_id, {"uid": _ev_uid}, delete=True)
|
||||
return {"ok": True}
|
||||
except HTTPException:
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user