Push the week's plan to Google Calendar (Feature 16)
CI / ci (push) Successful in 3m52s
CI / ci (pull_request) Successful in 3m52s

POST /api/v1/plans/{weekStart}/push-calendar writes one event per plan
day to the chosen calendar: "Dinner: {name}" from the household dinner
time for 60 minutes in the household zone, with the dinner name,
"Start by {time}" for crock-pot dinners and the
{MEALQUEST_PUBLIC_URL}/dinner/{id} link. Event ids are the lowercase
base32hex SHA-256 of "mealquest|{weekStart}|{date}"; each event is
inserted, and a 409 updates it instead.

gcal.Service.PushEvents does the Calendar writes under the account lock,
with the same redaction as listing. plan.Service.Push records a push only
when every event was written: the plan becomes synced with
calendar_synced_at and each day stores its event id. A partial failure
records nothing; a plan edited mid-push is 409 plan_changed.

A swap, or a redraft to another dinner, clears the day's
calendar_event_id, so the next push writes just those days; a push with
none pending writes every day again.

No calendar chosen is 409 no_calendar; a deleted or read-only chosen
calendar is 409 calendar_unavailable; a refused grant is 502
reconnect_required, leaving the connection as it is.

Maestro task 5885d2b1-cf06-44ff-abf3-242d7763c3c9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 22:12:44 -04:00
co-authored by Claude Opus 5.5
parent 482bec9727
commit 691c1ff34a
20 changed files with 1765 additions and 71 deletions
+4 -4
View File
@@ -109,8 +109,8 @@ type Config struct {
CalendarAPI string
}
// Service connects and disconnects the household's Google account and
// chooses the calendar MealQuest pushes to.
// Service connects and disconnects the household's Google account,
// chooses the calendar MealQuest pushes to and writes events to it.
// MealQuest runs as one process, so the service's in-memory lock and pending
// states see every request.
type Service struct {
@@ -128,8 +128,8 @@ type Service struct {
// account serializes every use of and change to the connected account —
// a connect's exchange and store, a disconnect's read, revoke and delete,
// a calendar listing's read and Calendar API calls, a calendar choice's
// listing and store — across the requests to Google and the database
// write. Google revokes the whole grant, so a revoke overlapping a
// listing and store, an event push's read and event writes — across the
// requests to Google and the database write. Google revokes the whole grant, so a revoke overlapping a
// connect would also kill the token the connect stores.
account sync.Mutex