From 5bafda072b95d23f16c823ff75ee78c6e5b35424 Mon Sep 17 00:00:00 2001 From: Erik Winter Date: Thu, 24 Oct 2024 07:29:32 +0200 Subject: [PATCH] done --- plan/main.go | 1 + plan/storage/sqlite/sqlite.go | 11 +++++- plan/storage/sqlite/sync.go | 73 ++++++++++++++++++++++++++++++++--- 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/plan/main.go b/plan/main.go index 78d52c3..49d677f 100644 --- a/plan/main.go +++ b/plan/main.go @@ -30,6 +30,7 @@ func main() { os.Exit(1) } + fmt.Printf("%+v\n", conf) syncClient := client.New(conf.SyncURL, conf.ApiKey) app := &cli.App{ diff --git a/plan/storage/sqlite/sqlite.go b/plan/storage/sqlite/sqlite.go index 4b8e951..f976280 100644 --- a/plan/storage/sqlite/sqlite.go +++ b/plan/storage/sqlite/sqlite.go @@ -18,6 +18,13 @@ var migrations = []string{ `PRAGMA synchronous=NORMAL`, `PRAGMA cache_size=2000`, `CREATE TABLE localids ("id" TEXT UNIQUE, "local_id" INTEGER)`, + `CREATE TABLE items ( + id TEXT PRIMARY KEY NOT NULL, + kind TEXT NOT NULL, + updated TIMESTAMP NOT NULL, + deleted BOOLEAN NOT NULL, + body TEXT NOT NULL +)`, } var ( @@ -39,7 +46,9 @@ func NewSqlites(dbPath string) (*LocalID, *SqliteEvent, *SqliteSync, error) { se := &SqliteEvent{ db: db, } - ss := &SqliteSync{} + ss := &SqliteSync{ + db: db, + } if err := migrate(db, migrations); err != nil { return nil, nil, nil, err diff --git a/plan/storage/sqlite/sync.go b/plan/storage/sqlite/sync.go index d0ef0e5..76669d6 100644 --- a/plan/storage/sqlite/sync.go +++ b/plan/storage/sqlite/sync.go @@ -1,29 +1,92 @@ package sqlite import ( + "database/sql" + "fmt" "time" "go-mod.ewintr.nl/planner/item" ) -type SqliteSync struct{} +type SqliteSync struct { + db *sql.DB +} -func NewSqliteSync() *SqliteSync { - return &SqliteSync{} +func NewSqliteSync(db *sql.DB) *SqliteSync { + return &SqliteSync{db: db} } func (s *SqliteSync) FindAll() ([]item.Item, error) { - return nil, nil + rows, err := s.db.Query("SELECT id, kind, updated, deleted, body FROM items") + if err != nil { + return nil, fmt.Errorf("%w: failed to query items: %v", ErrSqliteFailure, err) + } + defer rows.Close() + + var items []item.Item + for rows.Next() { + var i item.Item + var updatedStr string + err := rows.Scan(&i.ID, &i.Kind, &updatedStr, &i.Deleted, &i.Body) + if err != nil { + return nil, fmt.Errorf("%w: failed to scan item: %v", ErrSqliteFailure, err) + } + i.Updated, err = time.Parse(time.RFC3339, updatedStr) + if err != nil { + return nil, fmt.Errorf("%w: failed to parse updated time: %v", ErrSqliteFailure, err) + } + items = append(items, i) + } + + if err = rows.Err(); err != nil { + return nil, fmt.Errorf("%w: error iterating over rows: %v", ErrSqliteFailure, err) + } + + return items, nil } func (s *SqliteSync) Store(i item.Item) error { + // Ensure we have a valid time + if i.Updated.IsZero() { + i.Updated = time.Now() + } + + _, err := s.db.Exec( + "INSERT OR REPLACE INTO items (id, kind, updated, deleted, body) VALUES (?, ?, ?, ?, ?)", + i.ID, + i.Kind, + i.Updated.UTC().Format(time.RFC3339), + i.Deleted, + sql.NullString{String: i.Body, Valid: i.Body != ""}, // This allows empty string but not NULL + ) + if err != nil { + return fmt.Errorf("%w: failed to store item: %v", ErrSqliteFailure, err) + } return nil } func (s *SqliteSync) DeleteAll() error { + _, err := s.db.Exec("DELETE FROM items") + if err != nil { + return fmt.Errorf("%w: failed to delete all items: %v", ErrSqliteFailure, err) + } return nil } func (s *SqliteSync) LastUpdate() (time.Time, error) { - return time.Time{}, nil + var updatedStr sql.NullString + err := s.db.QueryRow("SELECT MAX(updated) FROM items").Scan(&updatedStr) + if err != nil { + return time.Time{}, fmt.Errorf("%w: failed to get last update: %v", ErrSqliteFailure, err) + } + + if !updatedStr.Valid { + return time.Time{}, nil // Return zero time if NULL or no rows + } + + lastUpdate, err := time.Parse(time.RFC3339, updatedStr.String) + if err != nil { + return time.Time{}, fmt.Errorf("%w: failed to parse last update time: %v", ErrSqliteFailure, err) + } + return lastUpdate, nil }