From bb3d9abb7e935acd1aad2a17317900611323af80 Mon Sep 17 00:00:00 2001 From: Michael Thomson Date: Thu, 6 Feb 2025 16:33:49 -0500 Subject: [PATCH] todo sqlite and inmemory repo --- cmd/main.go | 54 ++++++++++- internal/migrate/migrate.go | 11 +-- internal/models/todo.go | 7 ++ .../todo/inmemorytodorepository.go | 58 ++++++++++++ .../repositories/todo/sqlitetodorepository.go | 92 +++++++++++++++++++ internal/repositories/todo/todorepository.go | 18 ++++ migrations/1-init_db.sql | 5 +- 7 files changed, 233 insertions(+), 12 deletions(-) create mode 100644 internal/models/todo.go create mode 100644 internal/repositories/todo/inmemorytodorepository.go create mode 100644 internal/repositories/todo/sqlitetodorepository.go create mode 100644 internal/repositories/todo/todorepository.go diff --git a/cmd/main.go b/cmd/main.go index 0a93328..cd4d088 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,9 +1,61 @@ package main import ( + "database/sql" + "fmt" + "log" + "gitea.michaelthomson.dev/mthomson/habits/internal/migrate" + "gitea.michaelthomson.dev/mthomson/habits/internal/models" + repositories "gitea.michaelthomson.dev/mthomson/habits/internal/repositories/todo" ) func main() { - migrate.Migrate(); + db, err := sql.Open("sqlite3", "./habits.db") + if err != nil { + log.Fatal("Failed to open db pool") + } + + defer db.Close() + + // run migrations + migrate.Migrate(db); + + repo := repositories.NewSqliteTodoRepository(db) + + // create todo + newTodo := &models.Todo{Name: "clean dishes", Done: false} + + err = repo.Create(newTodo) + + if err != nil { + log.Fatal(err) + } + + fmt.Printf("id of created todo: %d\n", newTodo.Id) + + // update todo + newTodo.Done = true + + err = repo.Update(newTodo) + + if err != nil { + log.Fatal(err) + } + + fmt.Printf("todo is now done: %t\n", newTodo.Done) + + // delete todo + err = repo.Delete(newTodo.Id) + + if err != nil { + log.Fatal(err) + } + + // get todo + _, err = repo.GetById(newTodo.Id) + + if err != nil { + log.Fatal(err) + } } diff --git a/internal/migrate/migrate.go b/internal/migrate/migrate.go index a613a30..0b14c7c 100644 --- a/internal/migrate/migrate.go +++ b/internal/migrate/migrate.go @@ -14,21 +14,14 @@ type Migration struct { Name string } -func Migrate() { - db, err := sql.Open("sqlite3", "./habits.db") - if err != nil { - log.Fatal("Failed to open db pool") - } - - defer db.Close() - +func Migrate(db *sql.DB) { migrationTableSql := ` CREATE TABLE IF NOT EXISTS migrations( version INTEGER PRIMARY KEY, name VARCHAR(50) );` - _, err = db.Exec(migrationTableSql) + _, err := db.Exec(migrationTableSql) if err != nil { log.Fatal(err) } diff --git a/internal/models/todo.go b/internal/models/todo.go new file mode 100644 index 0000000..e7ff132 --- /dev/null +++ b/internal/models/todo.go @@ -0,0 +1,7 @@ +package models + +type Todo struct { + Id int64 + Name string + Done bool +} diff --git a/internal/repositories/todo/inmemorytodorepository.go b/internal/repositories/todo/inmemorytodorepository.go new file mode 100644 index 0000000..25bab4a --- /dev/null +++ b/internal/repositories/todo/inmemorytodorepository.go @@ -0,0 +1,58 @@ +package repositories + +import "gitea.michaelthomson.dev/mthomson/habits/internal/models" + +type InMemoryTodoRepository struct { + db map[int64]*models.Todo + id int64 +} + +func NewInMemoryTodoRepository() InMemoryTodoRepository { + return InMemoryTodoRepository{ + db: make(map[int64]*models.Todo), + id: 1, + } +} + + +func (r *InMemoryTodoRepository) GetById(id int64) (*models.Todo, error) { + todo, found := r.db[id] + + if !found { + return todo, ErrNotFound + } + + return todo, nil +} + +func (r *InMemoryTodoRepository) Create(todo *models.Todo) error { + todo.Id = r.id + r.db[r.id] = todo + r.id++ + + return nil +} + +func (r *InMemoryTodoRepository) Update(todo *models.Todo) error { + _, found := r.db[todo.Id] + + if !found { + return ErrNotFound + } + + r.db[todo.Id] = todo + + return nil +} + +func (r *InMemoryTodoRepository) Delete(id int64) error { + _, found := r.db[id] + + if !found { + return ErrNotFound + } + + delete(r.db, id) + + return nil +} diff --git a/internal/repositories/todo/sqlitetodorepository.go b/internal/repositories/todo/sqlitetodorepository.go new file mode 100644 index 0000000..d8fbff7 --- /dev/null +++ b/internal/repositories/todo/sqlitetodorepository.go @@ -0,0 +1,92 @@ +package repositories + +import ( + "database/sql" + + "gitea.michaelthomson.dev/mthomson/habits/internal/models" +) + +type SqliteTodoRepository struct { + db *sql.DB +} + +func NewSqliteTodoRepository(db *sql.DB) SqliteTodoRepository { + return SqliteTodoRepository{ + db: db, + } +} + +func (r *SqliteTodoRepository) GetById(id int64) (*models.Todo, error) { + todo := &models.Todo{} + + row := r.db.QueryRow("SELECT * FROM todo WHERE id = ?;", id) + err := row.Scan(todo.Id, todo.Name, todo.Done) + + if err != nil { + if err == sql.ErrNoRows { + return todo, ErrNotFound + } + + return todo, err + } + + return todo, nil +} + +func (r *SqliteTodoRepository) Create(todo *models.Todo) error { + result, err := r.db.Exec("INSERT INTO todo (name, done) VALUES (?, ?)", todo.Name, todo.Done) + + if err != nil { + return err + } + + id, err := result.LastInsertId() + + if err != nil { + return err + } + + todo.Id = id + + return nil +} + +func (r *SqliteTodoRepository) Update(todo *models.Todo) error { + result, err := r.db.Exec("UPDATE todo SET name = ?, done = ? WHERE id = ?", todo.Name, todo.Done, todo.Id) + + if err != nil { + return err + } + + rowsAffected, err := result.RowsAffected() + + if err != nil { + return err + } + + if rowsAffected == 0 { + return ErrNotFound + } + + return nil +} + +func (r *SqliteTodoRepository) Delete(id int64) error { + result, err := r.db.Exec("DELETE FROM todo WHERE id = ?", id) + + if err != nil { + return err + } + + rowsAffected, err := result.RowsAffected() + + if err != nil { + return err + } + + if rowsAffected == 0 { + return ErrNotFound + } + + return nil +} diff --git a/internal/repositories/todo/todorepository.go b/internal/repositories/todo/todorepository.go new file mode 100644 index 0000000..d2c637e --- /dev/null +++ b/internal/repositories/todo/todorepository.go @@ -0,0 +1,18 @@ +package repositories + +import ( + "errors" + + "gitea.michaelthomson.dev/mthomson/habits/internal/models" +) + +var ( + ErrNotFound error = errors.New("Todo cannot be found") +) + +type TodoRepository interface { + Create(todo *models.Todo) error + GetById(id int64) (*models.Todo, error) + Update(todo *models.Todo) error + Delete(id int64) error +} diff --git a/migrations/1-init_db.sql b/migrations/1-init_db.sql index d197aef..c2a1c5c 100644 --- a/migrations/1-init_db.sql +++ b/migrations/1-init_db.sql @@ -1,4 +1,5 @@ CREATE TABLE todo( - id INT PRIMARY KEY, - name VARCHAR(50) + id INTEGER PRIMARY KEY, + name VARCHAR(50), + done INTEGER );