summaryrefslogtreecommitdiff
path: root/internal/store/sqlite.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-02-17 14:43:42 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-02-17 14:43:42 -1000
commitec7d895c00c571b37ad9255b99b2e1756776c9e1 (patch)
tree31f8a925375fd5b00ee5febfe5d83f35487b1dd3 /internal/store/sqlite.go
parent44fa97ce901bbfc5957e6d9ba90a53086bb5950b (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.go88
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