From 68ab718d68f9e7c2385eb1f979ec916c1f662264 Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Thu, 21 Mar 2024 22:40:47 -0700 Subject: [PATCH 1/5] my spell checker yells about words --- .vscode/settings.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..263bf0b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "cSpell.words": [ + "glebarez", + "gocook", + "huandu", + "sqlbuilder" + ] +} \ No newline at end of file From 7cf3e7943ca349c5a9ae6ec47133a84fab1e7248 Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Thu, 21 Mar 2024 22:41:05 -0700 Subject: [PATCH 2/5] created the initial model --- api/models/users.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/api/models/users.go b/api/models/users.go index 730153c..e47dc2b 100644 --- a/api/models/users.go +++ b/api/models/users.go @@ -1,6 +1,11 @@ package models +import "time" + type UserModel struct { - Name string - Hash string -} \ No newline at end of file + Id int + Name string + Hash string + CreatedAt time.Time + LastUpdated time.Time +} From d6f2fc3585923467aad2b1164f0f3d88561ce90e Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Thu, 21 Mar 2024 22:41:25 -0700 Subject: [PATCH 3/5] having goose run the first migration --- api/migrations/20240321194227_user_table.sql | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 api/migrations/20240321194227_user_table.sql diff --git a/api/migrations/20240321194227_user_table.sql b/api/migrations/20240321194227_user_table.sql new file mode 100644 index 0000000..ffc627b --- /dev/null +++ b/api/migrations/20240321194227_user_table.sql @@ -0,0 +1,16 @@ +-- +goose Up +-- +goose StatementBegin +SELECT 'up SQL query'; +CREATE TABLE USERS ( + ID INTEGER PRIMARY KEY AUTOINCREMENT, + Name TEXT NOT NULL, + Hash TEXT NOT NULL, + CreatedAt DATETIME NOT NULL, + LastUpdated DATETIME NOT NULL +) +-- +goose StatementEnd +-- +goose Down +-- +goose StatementBegin +SELECT 'down SQL query'; +DROP TABLE IF EXISTS USERS; +-- +goose StatementEnd \ No newline at end of file From 3c145d66af5ba845839fef907e34ae623abe2881 Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Thu, 21 Mar 2024 22:41:52 -0700 Subject: [PATCH 4/5] the table accepts my record and I can rebuild it without a ORM! --- api/repositories/users.go | 74 +++++++++++++++++++++++++++++----- api/repositories/users_test.go | 41 +++++++++++++++++-- 2 files changed, 100 insertions(+), 15 deletions(-) diff --git a/api/repositories/users.go b/api/repositories/users.go index c629fcc..8d52e30 100644 --- a/api/repositories/users.go +++ b/api/repositories/users.go @@ -2,9 +2,9 @@ package repositories import ( "database/sql" - "go-cook/api/models" - "errors" "fmt" + "go-cook/api/models" + "time" "github.com/huandu/go-sqlbuilder" "golang.org/x/crypto/bcrypt" @@ -25,25 +25,77 @@ func (ur UserRepository) GetByName(name string) (models.UserModel, error) { builder := sqlbuilder.NewSelectBuilder() builder.Select("*").From("users").Where( builder.E("Name", name), - ); + ) + query, args := builder.Build() + rows, err := ur.connection.Query(query, args...) + if err != nil { + return models.UserModel{}, err + } - return models.UserModel{}, errors.New("user was not found") + return ur.processRows(rows)[0], nil } -func (ur UserRepository) NewUser(name string, password string) (int, error) { +func (ur UserRepository) NewUser(name, password string) (int64, error) { passwordBytes := []byte(password) hash, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost) if err != nil { return 0, err } - fmt.Println(hash) - query := sqlbuilder.NewInsertBuilder() - query.InsertInto("users") - query.Cols("name", "hash") - query.Values(name, string(hash)) - ur.connection.Query(query.Build()) + dt := time.Now() + queryBuilder := sqlbuilder.NewInsertBuilder() + queryBuilder.InsertInto("users") + queryBuilder.Cols("Name", "Hash", "LastUpdated", "CreatedAt") + queryBuilder.Values(name, string(hash), dt, dt) + query, args := queryBuilder.Build() + + _, err = ur.connection.Exec(query, args...) + if err != nil { + return 0, err + } return 1, nil } + +// If the hash matches what we have in the database, an error will not be returned. +// If the user does not exist or the hash does not match, an error will be returned +func (ur UserRepository) CheckUserHash(name, password string) error { + record, err := ur.GetByName(name) + if err != nil { + return err + } + + err = bcrypt.CompareHashAndPassword([]byte(record.Hash), []byte(password)) + if err != nil { + return err + } + + return nil +} + +func (ur UserRepository) processRows(rows *sql.Rows) []models.UserModel { + items := []models.UserModel{} + + for rows.Next() { + var id int + var name string + var hash string + var createdAt time.Time + var lastUpdated time.Time + err := rows.Scan(&id, &name, &hash, &createdAt, &lastUpdated) + if err != nil { + fmt.Println(err) + } + + items = append(items, models.UserModel{ + Id: id, + Name: name, + Hash: hash, + CreatedAt: createdAt, + LastUpdated: lastUpdated, + }) + } + + return items +} diff --git a/api/repositories/users_test.go b/api/repositories/users_test.go index f72d491..c86cd89 100644 --- a/api/repositories/users_test.go +++ b/api/repositories/users_test.go @@ -1,7 +1,40 @@ package repositories_test -import "testing" +import ( + "database/sql" + "go-cook/api/repositories" + "log" + "testing" -func TestNewUser(t *testing.T) { - -} \ No newline at end of file + _ "github.com/glebarez/go-sqlite" +) + +func TestCanCreateNewUser(t *testing.T) { + db, err := sql.Open("sqlite", "../../gocook.db") + if err != nil { + log.Println("unable to open connection") + t.FailNow() + } + defer db.Close() + + repo := repositories.NewUserRepository(db) + updated, err := repo.NewUser("testing", "NotSecure") + if err != nil { + log.Println(err) + t.FailNow() + } + log.Println(updated) +} + +func TestCanFindUserInTable(t *testing.T) { + db, err := sql.Open("sqlite", "../../gocook.db") + if err != nil { + log.Println("unable to open connection") + t.FailNow() + } + defer db.Close() + + repo := repositories.NewUserRepository(db) + repo.GetByName("testing") + +} From ab8a674988e00c50f80875c8d7d787404d64b3eb Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Thu, 21 Mar 2024 22:42:03 -0700 Subject: [PATCH 5/5] helper files --- .gitignore | 1 + Justfile | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 Justfile diff --git a/.gitignore b/.gitignore index d1ce888..ddc0925 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ *.so *.dylib go-cook +*.db # Test binary, built with `go test -c` *.test diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..264ab7f --- /dev/null +++ b/Justfile @@ -0,0 +1,5 @@ +migrate-up: + GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./gocook.db goose -dir ./api/migrations up + +migrate-down: + GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./gocook.db goose -dir ./api/migrations down \ No newline at end of file