Move database code to store package

This commit is contained in:
bronku 2025-03-21 23:35:55 +01:00
parent d1c393736a
commit e75468ce55
7 changed files with 79 additions and 75 deletions

View file

@ -2,9 +2,9 @@
## Critical Features ## Critical Features
- [ ] Database Implementation - [x] Database Implementation
- [ ] Init mode to create a new db - [x] Init mode to create a new db
- [x] Set up SQLite database - [x] Set up SQLite database
- [x] Create proper schemas - [x] Create proper schemas
- [x] Implement data persistence - [x] Implement data persistence

View file

@ -7,38 +7,40 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/Bronku/iroon/store"
) )
type handler struct { type handler struct {
tmpl *template.Template tmpl *template.Template
s *store s *store.Store
} }
func (h *handler) close() { func (h *handler) close() {
if h.s != nil { if h.s != nil {
h.s.close() h.s.Close()
} }
} }
func (h *handler) form(w http.ResponseWriter, r *http.Request) { func (h *handler) form(w http.ResponseWriter, r *http.Request) {
url := strings.Split(r.URL.String(), "/") url := strings.Split(r.URL.String(), "/")
o := order{ o := store.Order{
ID: -1, ID: -1,
Date: time.Now(), Date: time.Now(),
} }
id, err := strconv.Atoi(url[2]) id, err := strconv.Atoi(url[2])
if err == nil { if err == nil {
newOrder, err := h.s.getOrder(id) newOrder, err := h.s.GetOrder(id)
if err == nil { if err == nil {
o = newOrder o = newOrder
} }
} }
type formData struct { type formData struct {
Order order Order store.Order
Catalogue []cake Catalogue []store.Cake
} }
cakes, err := h.s.getCakes() cakes, err := h.s.GetCakes()
if err != nil { if err != nil {
fmt.Fprint(w, "server side error getting available cakes: ", err) fmt.Fprint(w, "server side error getting available cakes: ", err)
return return
@ -62,7 +64,7 @@ func (h *handler) addOrder(w http.ResponseWriter, r *http.Request) {
} }
fmt.Println("received form: ", r.Form) fmt.Println("received form: ", r.Form)
var n order var n store.Order
n.ID, err = strconv.Atoi(r.FormValue("id")) n.ID, err = strconv.Atoi(r.FormValue("id"))
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
@ -96,8 +98,8 @@ func (h *handler) addOrder(w http.ResponseWriter, r *http.Request) {
n.Accepted = time.Now() n.Accepted = time.Now()
n.Cakes = make([]cake, 0) n.Cakes = make([]store.Cake, 0)
cakes, err := h.s.getCakes() cakes, err := h.s.GetCakes()
if err != nil { if err != nil {
fmt.Fprint(w, "server side error getting available cakes: ", err) fmt.Fprint(w, "server side error getting available cakes: ", err)
return return
@ -117,7 +119,7 @@ func (h *handler) addOrder(w http.ResponseWriter, r *http.Request) {
fmt.Println("parsed order: ", n) fmt.Println("parsed order: ", n)
h.s.saveOrder(n) h.s.SaveOrder(n)
w.Header().Set("content-type", "text/html") w.Header().Set("content-type", "text/html")
w.WriteHeader(http.StatusAccepted) w.WriteHeader(http.StatusAccepted)
@ -126,7 +128,7 @@ func (h *handler) addOrder(w http.ResponseWriter, r *http.Request) {
func (h *handler) index(w http.ResponseWriter, r *http.Request) { func (h *handler) index(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "text/html") w.Header().Set("content-type", "text/html")
orders, err := h.s.getOrders() orders, err := h.s.GetOrders()
if err != nil { if err != nil {
fmt.Fprint(w, "server side error getting orders: ", err) fmt.Fprint(w, "server side error getting orders: ", err)
return return

View file

@ -6,7 +6,7 @@ import (
"net/http" "net/http"
"github.com/Bronku/iroon/auth" "github.com/Bronku/iroon/auth"
_ "github.com/mattn/go-sqlite3" "github.com/Bronku/iroon/store"
) )
func main() { func main() {
@ -20,7 +20,7 @@ func main() {
log.Fatal("can't parse templates: ", err) log.Fatal("can't parse templates: ", err)
} }
h.s, err = openStore("./foo.db") h.s, err = store.OpenStore("./foo.db")
if err != nil { if err != nil {
log.Fatal("can't open the databse", err) log.Fatal("can't open the databse", err)
} }

View file

@ -1,15 +1,15 @@
package main package store
import "time" import "time"
type cake struct { type Cake struct {
Name string Name string
ID int ID int
Price int Price int
Amount int Amount int
} }
type order struct { type Order struct {
ID int ID int
Name string Name string
Surname string Surname string
@ -19,5 +19,5 @@ type order struct {
Accepted time.Time Accepted time.Time
Status string Status string
Paid int // increments of 0.01 Paid int // increments of 0.01
Cakes []cake Cakes []Cake
} }

View file

@ -1,4 +1,4 @@
package main package store
import ( import (
"database/sql" "database/sql"
@ -7,17 +7,19 @@ import (
"os" "os"
"strconv" "strconv"
"time" "time"
_ "github.com/mattn/go-sqlite3"
) )
//go:embed schema.sql //go:embed schema.sql
var schema string var schema string
type store struct { type Store struct {
db *sql.DB db *sql.DB
} }
func openStore(filename string) (*store, error) { func OpenStore(filename string) (*Store, error) {
var out store var out Store
var err error var err error
_, err = os.Stat(filename) _, err = os.Stat(filename)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
@ -27,13 +29,13 @@ func openStore(filename string) (*store, error) {
return &out, err return &out, err
} }
func (s *store) close() { func (s *Store) Close() {
if s.db != nil { if s.db != nil {
s.db.Close() s.db.Close()
} }
} }
func (s *store) loadSchema() error { func (s *Store) loadSchema() error {
if s.db == nil { if s.db == nil {
return errors.New("database doesn't exist") return errors.New("database doesn't exist")
} }
@ -41,8 +43,8 @@ func (s *store) loadSchema() error {
return err return err
} }
func (s *store) getCake(id int) (cake, error) { func (s *Store) GetCake(id int) (Cake, error) {
out := cake{ID: id} out := Cake{ID: id}
row, err := s.db.Query("select name, price from cake where id = ?;", id) row, err := s.db.Query("select name, price from cake where id = ?;", id)
if err != nil { if err != nil {
return out, err return out, err
@ -53,15 +55,15 @@ func (s *store) getCake(id int) (cake, error) {
return out, err return out, err
} }
func (s *store) getCakes() ([]cake, error) { func (s *Store) GetCakes() ([]Cake, error) {
rows, err := s.db.Query("select id, name, price from cake") rows, err := s.db.Query("select id, name, price from cake")
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
cakes := make([]cake, 0) cakes := make([]Cake, 0)
for rows.Next() { for rows.Next() {
var c cake var c Cake
c.Amount = 0 c.Amount = 0
err = rows.Scan(&c.ID, &c.Name, &c.Price) err = rows.Scan(&c.ID, &c.Name, &c.Price)
if err != nil { if err != nil {
@ -72,7 +74,7 @@ func (s *store) getCakes() ([]cake, error) {
return cakes, err return cakes, err
} }
func (s *store) saveCake(newCake cake) (int, error) { func (s *Store) SaveCake(newCake Cake) (int, error) {
query := "insert into cake(name, price) values (?, ?) returning id;" query := "insert into cake(name, price) values (?, ?) returning id;"
if newCake.ID != -1 { if newCake.ID != -1 {
query = "update cake set name = ? , price = ? where id = " query = "update cake set name = ? , price = ? where id = "
@ -90,8 +92,8 @@ func (s *store) saveCake(newCake cake) (int, error) {
return newCake.ID, err return newCake.ID, err
} }
func (s *store) getOrder(id int) (order, error) { func (s *Store) GetOrder(id int) (Order, error) {
var out order var out Order
row, err := s.db.Query("select id, name, surname, phone, location, order_date, delivery_date, status, paid from customer_order where id = ?;", id) row, err := s.db.Query("select id, name, surname, phone, location, order_date, delivery_date, status, paid from customer_order where id = ?;", id)
defer row.Close() defer row.Close()
if err != nil { if err != nil {
@ -107,19 +109,19 @@ func (s *store) getOrder(id int) (order, error) {
out.Accepted, _ = time.Parse("2006-01-02 15:04", order_date) out.Accepted, _ = time.Parse("2006-01-02 15:04", order_date)
out.Date, _ = time.Parse("2006-01-02 15:04", delivery_date) out.Date, _ = time.Parse("2006-01-02 15:04", delivery_date)
out.Cakes = make([]cake, 0) out.Cakes = make([]Cake, 0)
rows, err := s.db.Query("select cake, amount from ordered_cake where customer_order = ?;", id) rows, err := s.db.Query("select cake, amount from ordered_cake where customer_order = ?;", id)
if err != nil { if err != nil {
return out, err return out, err
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var newCake cake var newCake Cake
err = rows.Scan(&newCake.ID, &newCake.Amount) err = rows.Scan(&newCake.ID, &newCake.Amount)
if err != nil { if err != nil {
return out, err return out, err
} }
cakeData, err := s.getCake(newCake.ID) cakeData, err := s.GetCake(newCake.ID)
if err != nil { if err != nil {
continue continue
} }
@ -131,8 +133,8 @@ func (s *store) getOrder(id int) (order, error) {
return out, nil return out, nil
} }
func (s *store) getOrders() ([]order, error) { func (s *Store) GetOrders() ([]Order, error) {
var out []order var out []Order
rows, err := s.db.Query("select id, name, surname, phone, location, order_date, delivery_date, status, paid from customer_order;") rows, err := s.db.Query("select id, name, surname, phone, location, order_date, delivery_date, status, paid from customer_order;")
if err != nil { if err != nil {
return out, err return out, err
@ -140,7 +142,7 @@ func (s *store) getOrders() ([]order, error) {
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var o order var o Order
var order_date, delivery_date string var order_date, delivery_date string
err = rows.Scan(&o.ID, &o.Name, &o.Surname, &o.Phone, &o.Location, &order_date, &delivery_date, &o.Status, &o.Paid) err = rows.Scan(&o.ID, &o.Name, &o.Surname, &o.Phone, &o.Location, &order_date, &delivery_date, &o.Status, &o.Paid)
if err != nil { if err != nil {
@ -149,7 +151,7 @@ func (s *store) getOrders() ([]order, error) {
o.Accepted, _ = time.Parse("2006-01-02 15:04", order_date) o.Accepted, _ = time.Parse("2006-01-02 15:04", order_date)
o.Date, _ = time.Parse("2006-01-02 15:04", delivery_date) o.Date, _ = time.Parse("2006-01-02 15:04", delivery_date)
o.Cakes = make([]cake, 0) o.Cakes = make([]Cake, 0)
rows, err := s.db.Query("select cake, amount from ordered_cake where customer_order = ?;", o.ID) rows, err := s.db.Query("select cake, amount from ordered_cake where customer_order = ?;", o.ID)
if err != nil { if err != nil {
@ -157,12 +159,12 @@ func (s *store) getOrders() ([]order, error) {
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var newCake cake var newCake Cake
err = rows.Scan(&newCake.ID, &newCake.Amount) err = rows.Scan(&newCake.ID, &newCake.Amount)
if err != nil { if err != nil {
return out, err return out, err
} }
cakeData, err := s.getCake(newCake.ID) cakeData, err := s.GetCake(newCake.ID)
if err != nil { if err != nil {
continue continue
} }
@ -177,7 +179,7 @@ func (s *store) getOrders() ([]order, error) {
return out, nil return out, nil
} }
func (s *store) saveOrder(newOrder order) (int, error) { func (s *Store) SaveOrder(newOrder Order) (int, error) {
query := "insert into customer_order(name, surname, phone, location, order_date, delivery_date, status, paid) values (?, ?, ?, ?, ?, ?, ?, ?) returning id;" query := "insert into customer_order(name, surname, phone, location, order_date, delivery_date, status, paid) values (?, ?, ?, ?, ?, ?, ?, ?) returning id;"
if newOrder.ID != -1 { if newOrder.ID != -1 {
query = "update customer_order set name = ?, surname = ?, phone = ?, location = ?, order_date = ?, delivery_date = ?, status = ?, paid = ? where id = " query = "update customer_order set name = ?, surname = ?, phone = ?, location = ?, order_date = ?, delivery_date = ?, status = ?, paid = ? where id = "

View file

@ -1,4 +1,4 @@
package main package store
import ( import (
"testing" "testing"
@ -6,21 +6,21 @@ import (
) )
func TestStore(t *testing.T) { func TestStore(t *testing.T) {
s, err := openStore("file:memdb1?mode=memory&cache=shared") s, err := OpenStore("file:memdb1?mode=memory&cache=shared")
if err != nil { if err != nil {
t.Fatalf("Failed to create store: %v", err) t.Fatalf("Failed to create store: %v", err)
} }
defer s.close() defer s.Close()
// ensure db is empty // ensure db is empty
cakes, err := s.getCakes() cakes, err := s.GetCakes()
if err != nil { if err != nil {
t.Errorf("Error getting cakes: %v", err) t.Errorf("Error getting cakes: %v", err)
} }
if len(cakes) != 0 { if len(cakes) != 0 {
t.Errorf("Expected empty cakes list, got: %v", cakes) t.Errorf("Expected empty cakes list, got: %v", cakes)
} }
orders, err := s.getOrders() orders, err := s.GetOrders()
if err != nil { if err != nil {
t.Errorf("Error getting orders: %v", err) t.Errorf("Error getting orders: %v", err)
} }
@ -29,8 +29,8 @@ func TestStore(t *testing.T) {
} }
// create new cake // create new cake
newCake := cake{Name: "Chocolate Cake", ID: -1, Price: 2500, Amount: -1} newCake := Cake{Name: "Chocolate Cake", ID: -1, Price: 2500, Amount: -1}
newCake.ID, err = s.saveCake(newCake) newCake.ID, err = s.SaveCake(newCake)
if err != nil { if err != nil {
t.Fatalf("Failed to save new cake: %v", err) t.Fatalf("Failed to save new cake: %v", err)
} }
@ -39,8 +39,8 @@ func TestStore(t *testing.T) {
} }
// create another cake // create another cake
anotherCake := cake{Name: "Another Cake", ID: -1, Price: 3000, Amount: -1} anotherCake := Cake{Name: "Another Cake", ID: -1, Price: 3000, Amount: -1}
anotherCake.ID, err = s.saveCake(anotherCake) anotherCake.ID, err = s.SaveCake(anotherCake)
if err != nil { if err != nil {
t.Fatalf("Failed to save new cake: %v", err) t.Fatalf("Failed to save new cake: %v", err)
} }
@ -51,7 +51,7 @@ func TestStore(t *testing.T) {
// Update Existing Cake // Update Existing Cake
newCake.Name = "Updated Cake" newCake.Name = "Updated Cake"
newCake.Price = 100 newCake.Price = 100
newID, err := s.saveCake(newCake) newID, err := s.SaveCake(newCake)
if newID != newCake.ID { if newID != newCake.ID {
t.Error("wrong id") t.Error("wrong id")
} }
@ -60,15 +60,15 @@ func TestStore(t *testing.T) {
} }
// Update Non-existing Cake // Update Non-existing Cake
var updatedCake cake var updatedCake Cake
updatedCake.ID = 10 updatedCake.ID = 10
_, err = s.saveCake(updatedCake) _, err = s.SaveCake(updatedCake)
if err == nil { if err == nil {
t.Error("did not return an error when attempted to update non existant cake") t.Error("did not return an error when attempted to update non existant cake")
} }
// get cakes // get cakes
cakes, err = s.getCakes() cakes, err = s.GetCakes()
if err != nil { if err != nil {
t.Fatalf("Failed to get all cakes after creation: %v", err) t.Fatalf("Failed to get all cakes after creation: %v", err)
} }
@ -96,7 +96,7 @@ func TestStore(t *testing.T) {
t.Errorf("Want %v\nGot %v", anotherCake, cakes) t.Errorf("Want %v\nGot %v", anotherCake, cakes)
} }
selectedCake, err := s.getCake(newCake.ID) selectedCake, err := s.GetCake(newCake.ID)
if err != nil { if err != nil {
t.Error("error getting a cake", err) t.Error("error getting a cake", err)
} }
@ -104,14 +104,14 @@ func TestStore(t *testing.T) {
t.Errorf("Want %v\nGot %v", newCake, selectedCake) t.Errorf("Want %v\nGot %v", newCake, selectedCake)
} }
selectedCake, err = s.getCake(-1) selectedCake, err = s.GetCake(-1)
if err == nil { if err == nil {
t.Error("no error getting a non-existant cake") t.Error("no error getting a non-existant cake")
} }
// create new order // create new order
now := time.Now() now := time.Now()
newOrder := order{ newOrder := Order{
ID: -1, ID: -1,
Name: "John", Name: "John",
Surname: "Doe", Surname: "Doe",
@ -121,9 +121,9 @@ func TestStore(t *testing.T) {
Date: now.Add(time.Hour * 24), Date: now.Add(time.Hour * 24),
Status: "Pending", Status: "Pending",
Paid: 1000, Paid: 1000,
Cakes: []cake{{ID: newCake.ID, Amount: 2}, {ID: anotherCake.ID, Amount: 10}}, Cakes: []Cake{{ID: newCake.ID, Amount: 2}, {ID: anotherCake.ID, Amount: 10}},
} }
newOrder.ID, err = s.saveOrder(newOrder) newOrder.ID, err = s.SaveOrder(newOrder)
if err != nil { if err != nil {
t.Fatalf("Failed to save new order: %v", err) t.Fatalf("Failed to save new order: %v", err)
} }
@ -132,7 +132,7 @@ func TestStore(t *testing.T) {
} }
// create another order // create another order
anotherOrder := order{ anotherOrder := Order{
ID: -1, ID: -1,
Name: "Jane", Name: "Jane",
Surname: "Doe", Surname: "Doe",
@ -142,9 +142,9 @@ func TestStore(t *testing.T) {
Date: now.Add(time.Hour * 192), Date: now.Add(time.Hour * 192),
Status: "Accepted", Status: "Accepted",
Paid: 1500, Paid: 1500,
Cakes: []cake{{ID: newCake.ID, Amount: 100}}, Cakes: []Cake{{ID: newCake.ID, Amount: 100}},
} }
anotherOrder.ID, err = s.saveOrder(anotherOrder) anotherOrder.ID, err = s.SaveOrder(anotherOrder)
if err != nil { if err != nil {
t.Fatalf("Failed to save new order: %v", err) t.Fatalf("Failed to save new order: %v", err)
} }
@ -156,7 +156,7 @@ func TestStore(t *testing.T) {
newOrder.Name = "James" newOrder.Name = "James"
newOrder.Status = "Done" newOrder.Status = "Done"
newOrder.Cakes = newOrder.Cakes[1:] newOrder.Cakes = newOrder.Cakes[1:]
newID, err = s.saveOrder(newOrder) newID, err = s.SaveOrder(newOrder)
if newID != newOrder.ID { if newID != newOrder.ID {
t.Error("wrong id") t.Error("wrong id")
} }
@ -165,15 +165,15 @@ func TestStore(t *testing.T) {
} }
// update non existing order // update non existing order
var updatedOrder order var updatedOrder Order
updatedOrder.ID = 10 updatedOrder.ID = 10
_, err = s.saveOrder(updatedOrder) _, err = s.SaveOrder(updatedOrder)
if err == nil { if err == nil {
t.Error("did not return an error when attempted to update non existant cake") t.Error("did not return an error when attempted to update non existant cake")
} }
// get orders // get orders
orders, err = s.getOrders() orders, err = s.GetOrders()
if err != nil { if err != nil {
t.Fatalf("Failed to get all orders after creation: %v", err) t.Fatalf("Failed to get all orders after creation: %v", err)
} }
@ -223,7 +223,7 @@ func TestStore(t *testing.T) {
t.Errorf("Want %v\nGot %v", anotherOrder.Cakes, orders[anotherOrderPos].Cakes) t.Errorf("Want %v\nGot %v", anotherOrder.Cakes, orders[anotherOrderPos].Cakes)
} }
selectedOrder, err := s.getOrder(newOrder.ID) selectedOrder, err := s.GetOrder(newOrder.ID)
if err != nil { if err != nil {
t.Error("error getting an order by id ", err) t.Error("error getting an order by id ", err)
} }
@ -241,20 +241,20 @@ func TestStore(t *testing.T) {
t.Errorf("Want %v\nGot %v", newOrder.Cakes, selectedOrder.Cakes) t.Errorf("Want %v\nGot %v", newOrder.Cakes, selectedOrder.Cakes)
} }
_, err = s.getOrder(-1) _, err = s.GetOrder(-1)
if err == nil { if err == nil {
t.Error("no error getting invalid order") t.Error("no error getting invalid order")
} }
} }
func TestSliceComparison(t *testing.T) { func TestSliceComparison(t *testing.T) {
a := []cake{ a := []Cake{
{Name: "ok", Price: 100, ID: 12, Amount: 10}, {Name: "ok", Price: 100, ID: 12, Amount: 10},
{Name: "ok", Price: 11, ID: 13, Amount: 1}, {Name: "ok", Price: 11, ID: 13, Amount: 1},
{Name: "ok", Price: 100, ID: 10, Amount: 11}, {Name: "ok", Price: 100, ID: 10, Amount: 11},
} }
b := []cake{ b := []Cake{
{Name: "okas", Price: 110, ID: 12, Amount: 10}, {Name: "okas", Price: 110, ID: 12, Amount: 10},
{Name: "oksa", Price: 11, ID: 13, Amount: 1}, {Name: "oksa", Price: 11, ID: 13, Amount: 1},
{Name: "oasdk", Price: 101, ID: 10, Amount: 11}, {Name: "oasdk", Price: 101, ID: 10, Amount: 11},
@ -264,11 +264,11 @@ func TestSliceComparison(t *testing.T) {
} }
} }
func areCakesEqual(a, b cake) bool { func areCakesEqual(a, b Cake) bool {
return a.ID == b.ID && a.Amount == b.Amount return a.ID == b.ID && a.Amount == b.Amount
} }
func areCakeSlicesEqual(a, b []cake) bool { func areCakeSlicesEqual(a, b []Cake) bool {
if len(a) != len(b) { if len(a) != len(b) {
return false return false
} }