Skip to content
Snippets Groups Projects
Commit 8004ead9 authored by gbe's avatar gbe
Browse files

Track wines

... in a very rough fashion
parent af82a63f
No related branches found
No related tags found
No related merge requests found
......@@ -2,7 +2,6 @@ package main
import (
"embed"
"errors"
"fmt"
"io/fs"
"log"
......@@ -89,5 +88,5 @@ func initDB(db *sqlx.DB) error {
log.Println("it has", len(data), "bytes")
}
return errors.New("here be dragons")
return nil
}
......@@ -3,6 +3,7 @@ module git.c3pb.de/gbe/invinoveritas
go 1.16
require (
github.com/Masterminds/squirrel v1.5.0
github.com/jmoiron/sqlx v1.3.3
modernc.org/sqlite v1.10.2
)
github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8=
github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
......@@ -8,14 +12,22 @@ github.com/jmoiron/sqlx v1.3.3 h1:j82X0bf7oQ27XeqxicSZsTU5suPwKElg3oyxNn43iTk=
github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
......
......@@ -2,9 +2,12 @@ package main
import (
"embed"
"fmt"
"html/template"
"log"
"net/http"
"os"
"strconv"
"github.com/jmoiron/sqlx"
_ "modernc.org/sqlite" // Imported for side effects: registers DB driver
......@@ -31,19 +34,74 @@ func main() {
http.Handle("/static/", http.FileServer(http.FS(staticFS)))
http.HandleFunc("/debug/quit", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "invalid method:"+r.Method, http.StatusMethodNotAllowed)
return
}
log.Println("exit requested by", r.RemoteAddr)
os.Exit(0)
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Println("handling", r.Method, r.URL, "from", r.RemoteAddr)
if r.Method == "GET" {
wines, err := ListWines(r.Context(), db)
if err != nil {
http.Error(w, "can't list wines: "+err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("content-type", "text/html")
err := templates.ExecuteTemplate(w, "index.tpl", nil)
data := struct {
Wines []Vino
}{wines}
err = templates.ExecuteTemplate(w, "index.tpl", data)
if err != nil {
log.Println("can't execute index template:", err)
}
return
}
if r.Method != "POST" {
http.Error(w, "invalid method:"+r.Method, http.StatusMethodNotAllowed)
return
}
// TODO: load/store image
// TODO: Validate max. length
name := r.FormValue("name")
if name == "" {
http.Error(w, "name empty or missing", http.StatusBadRequest)
return
}
ratingVal := r.FormValue("rating")
rating, err := strconv.Atoi(ratingVal)
if err != nil {
http.Error(w, "can't convert rating:"+err.Error(), http.StatusBadRequest)
return
}
vino := Vino{
Name: name,
Rating: rating,
}
err = vino.Store(r.Context(), db)
if err != nil {
http.Error(w, fmt.Sprintf("can't store wine %q: %s", vino, err), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/", http.StatusSeeOther) // TODO: Is this the correct status?
})
const listenAddr = "127.0.0.1:7878"
......
......@@ -4,8 +4,28 @@
<title>In Vino Veritas</title>
</head>
<body>
Here there be dragons.
The DB contains {{ .Wines | len }} wine(s) right now:
<form method="POST" action="/add">
<input type="text" name="name" placeholder="Name"></input>
<input type="number" name="rating" placeholder="Rating"></input>
<span>TODO: Image</todo>
<button type="submit">+</button>
</form>
<table>
<tr><th>#</th><th>Name</th><th>Rating</th></tr>
{{ range .Wines }}
<tr><td>{{ .ID }}</td><td>{{ .Name }}</td><td>{{ .Rating }}</td></tr>
{{ end }}
</table>
<p>And a bit of foo.</p>
<hr/>
<form method="POST" action="/debug/quit">
<button type="submit">Kill server</button>
</form>
</body>
</html>
\ No newline at end of file
vino.go 0 → 100644
package main
import (
"context"
"fmt"
"log"
"github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
)
type Vino struct {
ID int `db:"rowid"`
Name string `db:"name"`
Rating int `db:"rating"`
Picture []byte `db:"picture"`
}
func (v Vino) String() string {
return fmt.Sprintf("{Name: %q, Rating: %d}", v.Name, v.Rating)
}
func (v Vino) Store(ctx context.Context, db *sqlx.DB) (err error) {
values := map[string]interface{}{
"name": v.Name,
"rating": v.Rating,
}
if v.Picture != nil {
values["picture"] = v.Picture
}
tx, err := db.Begin()
if err != nil {
return err
}
defer func() {
if err != nil {
log.Println("rolling back transaction:", err)
tx.Rollback()
return
}
err = tx.Commit()
if err != nil {
log.Println("can't commit transaction:", err)
}
}()
_, err = squirrel.Insert("wines").
SetMap(values).
RunWith(tx).
ExecContext(ctx)
if err != nil {
// Looks like we need to do an update instead
_, err = squirrel.Update("wines").
Where("name = ?", v.Name).
SetMap(values).
RunWith(tx).
ExecContext(ctx)
}
return err
}
func ListWines(ctx context.Context, db *sqlx.DB) ([]Vino, error) {
query := `SELECT rowid, name, rating FROM wines;`
var wines []Vino
err := db.SelectContext(ctx, &wines, query)
if err != nil {
return nil, err
}
return wines, nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment