diff options
Diffstat (limited to 'model')
-rw-r--r-- | model/recipe.go | 106 | ||||
-rw-r--r-- | model/recipe_test.go | 2 | ||||
-rw-r--r-- | model/sql/migrate.sql | 2 | ||||
-rw-r--r-- | model/step.go | 96 |
4 files changed, 157 insertions, 49 deletions
diff --git a/model/recipe.go b/model/recipe.go index b1ad781..c4f0d47 100644 --- a/model/recipe.go +++ b/model/recipe.go @@ -24,6 +24,10 @@ func (r Recipe) String() string { } func (r *Recipe) Create(tx *sql.Tx) error { + if r.Id != "" { + return fmt.Errorf("Cannot create recipe if ID is given") + } + cmd := ` INSERT INTO recipes (title, portions, url, notes, created, last_changed) @@ -44,19 +48,60 @@ VALUES r.Id = fmt.Sprint(id) - err = r.CreateSteps(tx) - if err != nil { - return err + for i := range r.Steps { + r.Steps[i].Recipe = r.Id + r.Steps[i].Index = fmt.Sprint(i) + err = r.Steps[i].Create(tx) + if err != nil { + return err + } } return nil } +func (r *Recipe) getStepIds(tx *sql.Tx) ([]Step, error) { + retval := make([]Step, 0) + + cmd := ` +SELECT + id +FROM + steps +WHERE + recipe = ? +ORDER BY + 'index' ASC +` + + rows, err := tx.Query(cmd, r.Id) + if err != nil { + return retval, err + } + defer rows.Close() + + for rows.Next() { + s := Step{} + + err = rows.Scan(&s.Id) + if err != nil { + return retval, err + } + + retval = append(retval, s) + } + + return retval, nil +} + func (r *Recipe) Read(tx *sql.Tx) error { cmd := ` -SELECT id, title, portions, url, notes, created, last_changed -FROM recipes -WHERE id = ? +SELECT + title, portions, url, notes, created, last_changed +FROM + recipes +WHERE + id = ? ` rows, err := tx.Query(cmd, r.Id) @@ -70,7 +115,6 @@ WHERE id = ? } err = rows.Scan( - &r.Id, &r.Title, &r.Portions, &r.Url, @@ -82,32 +126,57 @@ WHERE id = ? return err } - err = r.ReadSteps(tx) + r.Steps, err = r.getStepIds(tx) if err != nil { return err } + for i := range r.Steps { + err = r.Steps[i].Read(tx) + if err != nil { + return err + } + } + return nil } func (r *Recipe) Update(tx *sql.Tx) error { - err := r.UpdateSteps(tx) + oldSteps, err := r.getStepIds(tx) if err != nil { return err } - query := `UPDATE + for i := range oldSteps { + err = oldSteps[i].Delete(tx) + if err != nil { + return err + } + } + + for i := range r.Steps { + r.Steps[i].Index = fmt.Sprint(i) + r.Steps[i].Recipe = r.Id + r.Steps[i].Create(tx) + if err != nil { + return err + } + } + + cmd := ` +UPDATE recipes SET title = ?, portions = ?, url = ?, notes = ?, + created = ?, last_changed = ? WHERE id = ?` - res, err := tx.Exec(query, r.Title, r.Portions, r.Url, r.Notes, + res, err := tx.Exec(cmd, r.Title, r.Portions, r.Url, r.Notes, r.Created, r.LastChanged, r.Id) if err != nil { return err @@ -125,19 +194,26 @@ WHERE } func (r *Recipe) Delete(tx *sql.Tx) error { - err := r.DeleteSteps(tx) + oldSteps, err := r.getStepIds(tx) if err != nil { return err } - query := ` + for i := range oldSteps { + err = oldSteps[i].Delete(tx) + if err != nil { + return err + } + } + + cmd := ` DELETE FROM recipes WHERE id = ? ` - result, err := tx.Exec(query, r.Id) + result, err := tx.Exec(cmd, r.Id) if err != nil { return err } @@ -153,7 +229,6 @@ WHERE func RecipeTestData() []Recipe { return []Recipe{ { - Id: "1", Title: "Pancakes", Portions: "4", Url: "https://example.org", @@ -167,7 +242,6 @@ func RecipeTestData() []Recipe { }, }, { - Id: "2", Title: "Burger", Portions: "2", Url: "https://xengineering.eu/git/ceres", diff --git a/model/recipe_test.go b/model/recipe_test.go index b9a44e1..508cf53 100644 --- a/model/recipe_test.go +++ b/model/recipe_test.go @@ -31,7 +31,7 @@ func TestRecipeCrud(t *testing.T) { readback.Id = original.Id err = readback.Read(tx) if err != nil { - t.Fatalf("Failed to create test recipe in DB: %v\n", err) + t.Fatalf("Failed to read test recipe from DB: %v\n", err) } if !reflect.DeepEqual(original, readback) { diff --git a/model/sql/migrate.sql b/model/sql/migrate.sql index 9577dab..f931af6 100644 --- a/model/sql/migrate.sql +++ b/model/sql/migrate.sql @@ -12,8 +12,8 @@ CREATE TABLE recipes ( CREATE TABLE steps ( id INTEGER PRIMARY KEY, - recipe INTEGER NOT NULL, 'index' INTEGER NOT NULL, text TEXT NOT NULL, + recipe INTEGER NOT NULL, FOREIGN KEY(recipe) REFERENCES recipes(id) ); diff --git a/model/step.go b/model/step.go index 24a11f4..f689fb9 100644 --- a/model/step.go +++ b/model/step.go @@ -1,89 +1,123 @@ package model import ( + "errors" + "encoding/json" "database/sql" + "fmt" ) type Step struct { + Id string `json:"id"` + Index string `json:"index"` Text string `json:"text"` + Recipe string `json:"recipe"` } -func (r *Recipe) CreateSteps(tx *sql.Tx) error { - err := r.DeleteSteps(tx) - if err != nil { - return err +func (s Step) String() string { + b, _ := json.MarshalIndent(s, "", " ") + return string(b) +} + +func (s *Step) Create(tx *sql.Tx) error { + if s.Id != "" { + return fmt.Errorf("Cannot create step if ID is given") } cmd := ` INSERT INTO steps - (recipe, 'index', text) + ('index', text, recipe) VALUES (?, ?, ?) ` - for i, s := range r.Steps { - _, err := tx.Exec(cmd, r.Id, i, s.Text) - if err != nil { - return err - } + result, err := tx.Exec(cmd, s.Index, s.Text, s.Recipe) + if err != nil { + return err } + id, err := result.LastInsertId() + if err != nil { + return err + } + + s.Id = fmt.Sprint(id) + return nil } -func (r *Recipe) ReadSteps(tx *sql.Tx) error { +func (s *Step) Read(tx *sql.Tx) error { cmd := ` SELECT - text + "index", text, recipe FROM steps WHERE - recipe = ? -ORDER BY - 'index' ASC + id = ? ` - rows, err := tx.Query(cmd, r.Id) + rows, err := tx.Query(cmd, s.Id) if err != nil { return err } + defer rows.Close() - r.Steps = make([]Step, 0) - for rows.Next() { - s := Step{} - - err = rows.Scan(&s.Text) - if err != nil { - return err - } + if !rows.Next() { + return sql.ErrNoRows + } - r.Steps = append(r.Steps, s) + err = rows.Scan(&s.Index, &s.Text, &s.Recipe) + if err != nil { + return err } return nil } -func (r *Recipe) UpdateSteps(tx *sql.Tx) error { - err := r.DeleteSteps(tx) +func (s *Step) Update(tx *sql.Tx) error { + cmd := ` +UPDATE + steps +SET + index = ?, + text = ?, + recipe = ? +WHERE + id = ?` + + res, err := tx.Exec(cmd, s.Index, s.Text, s.Recipe, s.Id) if err != nil { return err } - return r.CreateSteps(tx) + affected, err := res.RowsAffected() + if err != nil { + return err + } + if affected != 1 { + return fmt.Errorf("Recipe update affected %d rows instead of 1", affected) + } + + return nil } -func (r *Recipe) DeleteSteps(tx *sql.Tx) error { +func (s *Step) Delete(tx *sql.Tx) error { cmd := ` DELETE FROM steps WHERE - recipe = ? + id = ? ` - _, err := tx.Exec(cmd, r.Id) + result, err := tx.Exec(cmd, s.Id) if err != nil { return err } + rows, err := result.RowsAffected() + if rows != 1 { + return errors.New("Recipe deletion did not affect exactly one row") + } + return nil } |