From afecf9628c1d3af80db96a8d1cfaa896a2f0804f Mon Sep 17 00:00:00 2001 From: isthisnagee Date: Mon, 27 Dec 2021 11:12:24 -0800 Subject: [PATCH] Add notes to diary entries --- db/db.go | 45 +++++++++++++++++------- model/note.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++ model/note_test.go | 69 ++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 13 deletions(-) create mode 100644 model/note.go create mode 100644 model/note_test.go diff --git a/db/db.go b/db/db.go index 42bd429..302b868 100644 --- a/db/db.go +++ b/db/db.go @@ -10,33 +10,36 @@ import ( _ "github.com/mattn/go-sqlite3" ) -var __version = 1 +var migrations = []func(*sql.Tx) error{migration0, migration1} +var __version = len(migrations) type DbCtx struct { Db *sql.DB Version int } -func initVersion(tx *sql.Tx) (int, error) { +func initVersion(tx *sql.Tx) (int, int, error) { var version int // Check the version if err := tx.QueryRow("PRAGMA user_version").Scan(&version); err != nil { tx.Rollback() - return 0, fmt.Errorf("Could not select user_version. %w", err) + return 0, 0, fmt.Errorf("Could not select user_version. %w", err) } if version == 0 { _, err := tx.Exec(fmt.Sprintf("PRAGMA user_version=%d", __version)) if err != nil { tx.Rollback() - return 0, fmt.Errorf("Could not update pragma version. %w", err) + return 0, 0, fmt.Errorf("Could not update pragma version. %w", err) } - version = __version + // Start from 1 so that all migrations are applied + return 0, __version, nil } else if version != __version { tx.Rollback() - return 0, errors.New(fmt.Sprintf("Wrong version. Expected %d got %d", __version, version)) + return 0, 0, errors.New(fmt.Sprintf("Wrong version. Expected %d got %d", __version, version)) } - return version, nil + // User is on the latest version, migrations do not need to be ran. + return version, version, nil } func migration0(tx *sql.Tx) error { @@ -54,12 +57,28 @@ func migration0(tx *sql.Tx) error { return nil } -func initMigrations(tx *sql.Tx, start_from_versiono int) error { - var migrations = []func(*sql.Tx) error{migration0} +func migration1(tx *sql.Tx) error { + if _, err := tx.Exec(` + create table diary_log_note ( + id integer not null primary key, + log_id integer, + body text, + created_at int not null default (strftime('%s','now')), + version int not null default 0, + foreign key(log_id) references diary_log(id) + ) + `); err != nil { + tx.Rollback() + return fmt.Errorf("Could not create diary_log_note. %w", err) + } + return nil +} + +func initMigrations(tx *sql.Tx, start_from_version int) error { for migration_idx, migration := range migrations { // Version is 1 indexed, while the migration_idx is 0 indexed var migration_num = migration_idx + 1 - if migration_num < start_from_versiono { + if migration_num < start_from_version { continue } err := migration(tx) @@ -87,17 +106,17 @@ func Init(db_location string) (*DbCtx, error) { return nil, err } - version, err := initVersion(tx) + old_version, new_version, err := initVersion(tx) if err != nil { return nil, err } - err = initMigrations(tx, version) + err = initMigrations(tx, old_version) if err != nil { return nil, err } tx.Commit() - return &DbCtx{db, version}, nil + return &DbCtx{db, new_version}, nil } diff --git a/model/note.go b/model/note.go new file mode 100644 index 0000000..7f0e009 --- /dev/null +++ b/model/note.go @@ -0,0 +1,87 @@ +package model + +type DiaryEntryNote struct { + Id int `json:"id"` + Body string `json:"body"` + CreatedAt int `json:"created_at"` + Version int `json:"version"` + LogId int `json:"log_id"` +} + +func (app *App) NewDiaryEntryNote(entry_id int, body string) *DiaryEntryNote { + var diary_entry_note DiaryEntryNote + + var result, err = app.Db.Exec("INSERT INTO diary_log_note (body, log_id) values (?, ?);", body, entry_id) + if err != nil { + panic(err) + } + + id, err := result.LastInsertId() + if err != nil { + panic(err) + } + + if err := app.Db.QueryRow( + "SELECT id, body, created_at, version, log_id FROM diary_log_note where id=?", + id, + ).Scan( + &diary_entry_note.Id, + &diary_entry_note.Body, + &diary_entry_note.CreatedAt, + &diary_entry_note.Version, + &diary_entry_note.LogId, + ); err != nil { + panic(err) + } + + return &diary_entry_note +} + +func (app *App) GetDiaryEntryNotes(entry_id int) []*DiaryEntryNote { + // not sure if it should be descending. + rows, err := app.Db.Query( + // The second ordering is necessary to break ties. + `SELECT id, body, created_at, version, log_id FROM diary_log_note WHERE log_id=? + ORDER BY created_at desc, + id desc`, + entry_id, + ) + if err != nil { + panic(err) + } + defer rows.Close() + + var result []*DiaryEntryNote + for rows.Next() { + var id, created_at, version, log_id int + var body string + err := rows.Scan(&id, &body, &created_at, &version, &log_id) + if err != nil { + panic(err) + } + result = append(result, &DiaryEntryNote{ + Id: id, Body: body, CreatedAt: created_at, Version: version, LogId: log_id, + }) + } + if err := rows.Err(); err != nil { + panic(err) + } + + return result +} + +func (app *App) DeleteDiaryEntryNote(note_id int) (bool, error) { + result, err := app.Db.Exec("DELETE FROM diary_log_note where id=?", note_id) + if err != nil { + return false, err + } + + num_rows_affected, err := result.RowsAffected() + if err != nil { + return false, err + } + if num_rows_affected == 0 { + return false, &NotFoundError{given_id: note_id} + } + return true, nil +} diff --git a/model/note_test.go b/model/note_test.go new file mode 100644 index 0000000..0044c63 --- /dev/null +++ b/model/note_test.go @@ -0,0 +1,69 @@ +package model + +import ( + "testing" +) + +func TestNewEntryNote(t *testing.T) { + var app = setup() + + var entry = app.NewDiaryEntry("Met with Nagee @ 1PM") + var note = app.NewDiaryEntryNote(entry.Id, "A note") + + assert_string(t, "A note", note.Body) + assert_exists(t, note.Id) + assert_exists(t, note.CreatedAt) + assert_exists(t, note.Version) + + teardown(app) +} + +func TestGetDiaryEntryNotes(t *testing.T) { + var app = setup() + + var entry = app.NewDiaryEntry("Met with Nagee @ 1PM") + var note_1 = app.NewDiaryEntryNote(entry.Id, "A note") + var note_2 = app.NewDiaryEntryNote(entry.Id, "Another note") + + // The first note should be the latest one created + var notes = app.GetDiaryEntryNotes(entry.Id) + + assert_string(t, notes[0].Body, note_2.Body) + assert_int(t, notes[0].Id, note_2.Id) + assert_int(t, notes[0].CreatedAt, note_2.CreatedAt) + assert_int(t, notes[0].Version, note_2.Version) + + assert_string(t, notes[1].Body, note_1.Body) + assert_int(t, notes[1].Id, note_1.Id) + assert_int(t, notes[1].CreatedAt, note_1.CreatedAt) + assert_int(t, notes[1].Version, note_1.Version) + + teardown(app) +} + +func DeleteDiaryEntryNote(t *testing.T) { + var app = setup() + + var entry = app.NewDiaryEntry("Met with Nagee @ 1PM") + var note = app.NewDiaryEntryNote(entry.Id, "A note") + + is_deleted, _ := app.DeleteDiaryEntry(note.Id) + assert_bool(t, true, is_deleted) + + teardown(app) +} + +func DeleteDiaryEntryNoteNotFound(t *testing.T) { + var app = setup() + + _, err := app.DeleteDiaryEntryNote(-1) + switch err_type := err.(type) { + case *NotFoundError: + // Do nothing + break + default: + t.Fatalf("Expected NotFoundError, got %s", err_type) + } + + teardown(app) +}