diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-02-17 14:43:42 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-02-17 14:43:42 -1000 |
| commit | ec7d895c00c571b37ad9255b99b2e1756776c9e1 (patch) | |
| tree | 31f8a925375fd5b00ee5febfe5d83f35487b1dd3 /internal/store/sqlite.go | |
| parent | 44fa97ce901bbfc5957e6d9ba90a53086bb5950b (diff) | |
Add calendar cache layer, incremental sync tests, completion assertions
- Google Calendar events now cached via CacheFetcher pattern with
stale-cache fallback on API errors (new migration 015, store methods,
fetchCalendarEvents handler, BuildTimeline reads from store)
- Todoist incremental sync path covered by 5 new tests
- Task completion tests assert response body, headers, and template data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/store/sqlite.go')
| -rw-r--r-- | internal/store/sqlite.go | 88 |
1 files changed, 85 insertions, 3 deletions
diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go index 24a24d7..158febc 100644 --- a/internal/store/sqlite.go +++ b/internal/store/sqlite.go @@ -19,9 +19,10 @@ import ( // Cache key constants const ( - CacheKeyTodoistTasks = "todoist_tasks" - CacheKeyTrelloBoards = "trello_boards" - CacheKeyPlanToEatMeals = "plantoeat_meals" + CacheKeyTodoistTasks = "todoist_tasks" + CacheKeyTrelloBoards = "trello_boards" + CacheKeyPlanToEatMeals = "plantoeat_meals" + CacheKeyGoogleCalendar = "google_calendar" ) type Store struct { @@ -800,6 +801,87 @@ func (s *Store) GetCardsByDateRange(start, end time.Time) ([]models.Card, error) return cards, rows.Err() } +// Calendar event operations + +// SaveCalendarEvents replaces all cached calendar events +func (s *Store) SaveCalendarEvents(events []models.CalendarEvent) error { + tx, err := s.db.Begin() + if err != nil { + return err + } + defer func() { _ = tx.Rollback() }() + + _, err = tx.Exec(`DELETE FROM calendar_events`) + if err != nil { + return err + } + + stmt, err := tx.Prepare(` + INSERT INTO calendar_events (id, summary, description, start_time, end_time, html_link) + VALUES (?, ?, ?, ?, ?, ?) + `) + if err != nil { + return err + } + defer func() { _ = stmt.Close() }() + + for _, e := range events { + _, err = stmt.Exec(e.ID, e.Summary, e.Description, e.Start, e.End, e.HTMLLink) + if err != nil { + return err + } + } + + return tx.Commit() +} + +// GetCalendarEvents retrieves all cached calendar events +func (s *Store) GetCalendarEvents() ([]models.CalendarEvent, error) { + rows, err := s.db.Query(` + SELECT id, summary, description, start_time, end_time, html_link + FROM calendar_events + ORDER BY start_time ASC + `) + if err != nil { + return nil, err + } + defer func() { _ = rows.Close() }() + + var events []models.CalendarEvent + for rows.Next() { + var e models.CalendarEvent + if err := rows.Scan(&e.ID, &e.Summary, &e.Description, &e.Start, &e.End, &e.HTMLLink); err != nil { + return nil, err + } + events = append(events, e) + } + return events, rows.Err() +} + +// GetCalendarEventsByDateRange retrieves cached calendar events within a date range +func (s *Store) GetCalendarEventsByDateRange(start, end time.Time) ([]models.CalendarEvent, error) { + rows, err := s.db.Query(` + SELECT id, summary, description, start_time, end_time, html_link + FROM calendar_events + WHERE start_time >= ? AND start_time <= ? + ORDER BY start_time ASC + `, start, end) + if err != nil { + return nil, err + } + defer func() { _ = rows.Close() }() + + var events []models.CalendarEvent + for rows.Next() { + var e models.CalendarEvent + if err := rows.Scan(&e.ID, &e.Summary, &e.Description, &e.Start, &e.End, &e.HTMLLink); err != nil { + return nil, err + } + events = append(events, e) + } + return events, rows.Err() +} + // Agent operations // CreateAgentSession creates a new pending agent session |
