Push local changes up to Google, completing the Google leg
Mirrors are created with events.import, updated and deleted with sendUpdates=none, so a write to an aggregate cannot mail anyone about a meeting that was already invited from its source. The import question the plan flagged is settled from Google's own API discovery document rather than by guesswork: events.import accepts no sendUpdates parameter at all, while insert, update and delete all do, and it is documented as adding "a private copy of an existing event". A method with notification behaviour would need that control. Confirmed live that attendees survive an import with their response statuses intact. Testing against the real API caught a defect that unit tests could not: the first push dropped VALARM entirely and Google substituted the calendar's default reminders. Alarms are never stripped by decision, so they now map to and from Google's reminder overrides in both directions, with relative TRIGGER durations converted to whole minutes. A trigger Google cannot express — absolute, or after the start — is dropped rather than guessed at. Known gaps recorded in TODO.md rather than papered over: a series' exceptions are not pushed, since Google models those as separate events against an existing series; and a failed Google pull still aborts the cycle, which is safe but stops the CalDAV side too. 121 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
edb16204fa
commit
bc64f19211
6 changed files with 675 additions and 14 deletions
27
TODO.md
27
TODO.md
|
|
@ -32,17 +32,17 @@ Core modules:
|
|||
parser does not always match its documentation
|
||||
- [x] `google/auth.rs` — OAuth loopback flow with PKCE, refresh, keyring-sourced secrets
|
||||
- [x] `google/convert.rs` — JSON to iCalendar, including recurrence and timezones
|
||||
- [~] `google/api.rs` — incremental pull with syncToken done; the push direction
|
||||
(import / update / delete) is still outstanding, so a Google endpoint is
|
||||
currently read-only
|
||||
- [x] `google/api.rs` — incremental pull by syncToken, and push by import / update
|
||||
/ delete with notification suppressed
|
||||
- [x] Reintroduce `SchedulingSuppression` in `config.rs` (removed in M0 as dead code)
|
||||
|
||||
Safety-critical behaviour:
|
||||
|
||||
- [ ] **`events.import` gate — do this first.** Import an attendee-bearing event whose
|
||||
guests are on a mail sink we control and confirm no mail is emitted; repeat for
|
||||
update and delete under `sendUpdates=none`. The Google attendee path depends on
|
||||
it. Fallback if it fails: the same demotion transform used for CalDAV.
|
||||
- [x] **`events.import` gate** — settled from Google's own API discovery document:
|
||||
`events.import` accepts no `sendUpdates` parameter at all, while `insert`,
|
||||
`update` and `delete` all do, and it is documented as adding "a private copy of
|
||||
an existing event". Confirmed live that attendees and alarms survive an import.
|
||||
Creation goes through `import`; update and delete pass `sendUpdates=none`.
|
||||
- [x] `sync` refuses to run on aggregate target drift, before reconciliation
|
||||
- [x] `aggregate retarget` — flush unrouted creations against the old target, then
|
||||
re-materialise; keep old orphans by default
|
||||
|
|
@ -63,6 +63,19 @@ Tests:
|
|||
- [x] Retarget: drift makes `sync` exit non-zero having written nothing and losing no source
|
||||
event; purge is bounded by the derivation; an unrouted creation reaches a sink first
|
||||
|
||||
### Known gaps carried out of M1
|
||||
|
||||
- [ ] A recurring series' *exceptions* are not pushed to Google. Google models them
|
||||
as separate events against an already existing series, so they need
|
||||
`events.instances` plus a patch per exception. Reported per sync rather than
|
||||
dropped silently.
|
||||
- [ ] `push` deleting an event remotely is exercised only when the reconciler removes
|
||||
a mirror mid-cycle; it has no end-to-end test yet, because a pull legitimately
|
||||
resurrects anything deleted from a vdir before the cycle runs.
|
||||
- [ ] A failed Google pull aborts the whole cycle, including the CalDAV side. Safe —
|
||||
reconciling against a stale snapshot could read as mass deletion — but it means
|
||||
a lapsed token stops everything.
|
||||
|
||||
## M2 — interface and packaging
|
||||
|
||||
- [ ] axum configuration UI, bound to 127.0.0.1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue