commit ba9b17964ac7c30dea319c7b0cac5fff1fa37c0f
parent 7784691dbae237cb0e44b42c69c18659cb6eee0e
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Tue, 13 Aug 2024 15:43:44 -0700
Separate floorplan data manipulation functions into their own file
I'm going to design it a little differently than I had originally
intended, so a couple api endpoints which may or may not be back
have been removed for now.
(Also implemented a generic *sql.Rows collector function.)
Diffstat:
7 files changed, 79 insertions(+), 130 deletions(-)
diff --git a/cmd/api/floorplans.go b/cmd/api/floorplans.go
@@ -122,51 +122,6 @@ func (e *Env) GetFloorplan(c *gin.Context) {
}
}
-func (e *Env) AddFloorplanPoints(c *gin.Context) {
- user := c.Param("user")
- floorplan := c.Param("floorplan")
-
- var points []Point
- if err := c.ShouldBind(&points); err != nil {
- RespondError(c, 400, "%s: Unable to read points", err.Error())
- return
- }
-
- tx, err := e.backend.DB.Begin()
- if err != nil {
- RespondError(c, 400, "Unable to begin transaction")
- return
- }
-
- var rpoints []backend.Point
- for _, point := range points {
- rp, err := e.backend.AddFloorplanPoint(tx, user, floorplan, backend.Point{ X: *point.X, Y: *point.Y })
- if err != nil {
- tx.Rollback()
- RespondError(c, 400, "%s: Unable to add point", err.Error())
- return
- }
- rpoints = append(rpoints, rp)
- }
- if err := tx.Commit(); err != nil {
- RespondError(c, 400, "Unable to commit")
- } else {
- Respond(c, 200, rpoints)
- }
-}
-
-func (e *Env) GetFloorplanPoints(c *gin.Context) {
- user := c.Param("user")
- floorplan := c.Param("floorplan")
-
- points, err := e.backend.GetFloorplanPoints(nil, user, floorplan)
- if err != nil {
- RespondError(c, 400, "Unable to get points")
- } else {
- Respond(c, 200, points)
- }
-}
-
func patchableToSettable(data map[string]interface{}) (*SettableFloorplan, error) {
// Seems stupidly inefficient, but I can't find a better way at the moment
jsondata, err := json.Marshal(data)
diff --git a/cmd/api/main.go b/cmd/api/main.go
@@ -95,10 +95,6 @@ func setAuthenticatedRoutes(env *Env, r *gin.RouterGroup) {
fp.PATCH("", env.UpdateFloorplan)
fp.DELETE("", env.DeleteFloorplan)
fp.GET("", env.GetFloorplan)
-
- pt := fp.Group("/points")
- pt.GET("", env.GetFloorplanPoints)
- pt.POST("", env.AddFloorplanPoints)
}
func noRoute(c *gin.Context) {
diff --git a/internal/backend/db.go b/internal/backend/db.go
@@ -0,0 +1,21 @@
+package backend
+
+import "database/sql"
+
+type Scanner interface {
+ Scan(dest ...any) error
+}
+
+func collectRows[T any](rows *sql.Rows, collector func(Scanner) (T, error)) ([]T, error) {
+ var things []T
+
+ for rows.Next() {
+ thing, err := collector(rows)
+ if err != nil {
+ return nil, err
+ }
+ things = append(things, thing)
+ }
+
+ return things, nil
+}
diff --git a/internal/backend/floorplan.go b/internal/backend/floorplan.go
@@ -15,17 +15,6 @@ type Floorplan struct {
Created time.Time `json:"created"`
}
-type Point struct {
- //floorplan_id int
- Id int `json:"id"`
- X int `json:"x"`
- Y int `json:"y"`
-}
-
-type Scanner interface {
- Scan(dest ...any) error
-}
-
func (e *Env) CreateFloorplan(tx *sql.Tx, template *Floorplan) (*Floorplan, error) {
stmt, err := e.CacheTxStmt(tx, "create_floorplan", `INSERT INTO spaceplanner.floorplans (owner, name, address, synopsis)
VALUES ($1, $2, $3, $4) RETURNING *`)
@@ -55,26 +44,20 @@ func (e *Env) GetFloorplan(tx *sql.Tx, user string, name string) (*Floorplan, er
return scanFloorplan(stmt.QueryRow(user, name))
}
-func (e *Env) GetFloorplans(tx *sql.Tx, user string) ([]Floorplan, error) {
+func (e *Env) GetFloorplans(tx *sql.Tx, user string) ([]*Floorplan, error) {
stmt, err := e.CacheTxStmt(tx, "get_floorplans", `SELECT * FROM spaceplanner.floorplans WHERE owner = $1
ORDER BY updated DESC, created DESC`)
if err != nil {
return nil, err
}
- var floorplans []Floorplan
rows, err := stmt.Query(user)
if err != nil {
return nil, err
}
- for rows.Next() {
- t, err := scanFloorplan(rows)
- if err != nil {
- return nil, err
- }
- floorplans = append(floorplans, *t)
- }
- return floorplans, nil
+ defer rows.Close()
+
+ return collectRows(rows, scanFloorplan)
}
func (e *Env) DeleteFloorplan(tx *sql.Tx, user string, name string) (*Floorplan, error) {
@@ -86,68 +69,8 @@ func (e *Env) DeleteFloorplan(tx *sql.Tx, user string, name string) (*Floorplan,
return scanFloorplan(stmt.QueryRow(user, name))
}
-func (e *Env) AddFloorplanPoint(tx *sql.Tx, user string, floorplan string, point Point) (Point, error) {
- stmt, err := e.CacheTxStmt(tx, "add_point", `INSERT INTO floorplan_points VALUES (
- spaceplanner.floorplan_id($1, $2), (SELECT coalesce(max(id), 0) FROM
- floorplan_points WHERE floorplan = spaceplanner.floorplan_id($1, $2)) + 1, $3, $4) RETURNING id, x, y`)
- if err != nil {
- return Point{}, err
- }
-
- row := stmt.QueryRow(user, floorplan, point.X, point.Y)
- return scanPoint(row)
-
-}
-
-func (e *Env) InsertFloorplanPoint(tx *sql.Tx, user string, floorplan string, point Point) error {
- stmt, err := e.CacheTxStmt(tx, "ins_point", `WITH t AS (
- UPDATE floorplan_points
- WHERE floorplan = spaceplanner.floorplan_id($1, $2) AND id > $3 SET id = id + 1
- ) INSERT INTO floorplan_points id, x, y VALUES (spaceplanner.floorplan_id($1, $2), $3, $4, $5)`)
-
- if err != nil {
- return err
- }
- _, err = stmt.Exec(user, floorplan, point.Id, point.X, point.Y)
- return err
-}
-
-func (e *Env) GetFloorplanPoints(tx *sql.Tx, user string, floorplan string) ([]Point, error) {
- stmt, err := e.CacheTxStmt(tx, "get_points", "SELECT id, x, y FROM floorplan_points WHERE floorplan = spaceplanner.floorplan_id($1, $2)")
- if err != nil {
- return nil, err
- }
-
- rows, err := stmt.Query(user, floorplan)
- if err != nil {
- return nil, err
- }
- return collectPoints(rows)
-}
-
func scanFloorplan(row Scanner) (*Floorplan, error) {
var f Floorplan
err := row.Scan(&f.id, &f.User, &f.Name, &f.Address, &f.Synopsis, &f.Updated, &f.Created)
return &f, err
}
-
-func collectPoints(rows *sql.Rows) ([]Point, error) {
- var points []Point
-
- for rows.Next() {
- point, err := scanPoint(rows)
- if err != nil {
- return nil, err
- }
- points = append(points, point)
- }
-
- return points, nil
-}
-
-func scanPoint(row Scanner) (Point, error) {
- var point Point
-
- err := row.Scan(&point.Id, &point.X, &point.Y)
- return point, err
-}
diff --git a/internal/backend/floorplan_data.go b/internal/backend/floorplan_data.go
@@ -0,0 +1,50 @@
+package backend
+
+import (
+ "database/sql"
+)
+
+type Point struct {
+ Id int `json:"id" binding:"required"`
+ X int `json:"x" binding:"required"`
+ Y int `json:"y" binding:"required"`
+}
+
+type FloorplanData struct {
+ points []Point
+}
+
+func (e *Env) GetFloorplanData(tx *sql.Tx, user string, floorplan string) (FloorplanData, error) {
+ var data FloorplanData
+ var err error
+
+ data.points, err = e.getFloorplanPoints(tx, user, floorplan)
+ if err != nil {
+ return data, err
+ }
+
+ return data, nil
+}
+
+func (e *Env) getFloorplanPoints(tx *sql.Tx, user string, floorplan string) ([]Point, error) {
+ stmt, err := e.CacheTxStmt(tx, "get_points",
+ `SELECT (id, x, y) FROM spaceplanner.floorplan_points WHERE
+ floorplan = spaceplanner.floorplan_id($1, $2)`)
+ if err != nil {
+ return nil, err
+ }
+
+ rows, err := stmt.Query(user, floorplan)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ return collectRows(rows, scanPoint)
+}
+
+func scanPoint(row Scanner) (Point, error) {
+ var point Point
+
+ err := row.Scan(&point.Id, &point.X, &point.Y)
+ return point, err
+}
diff --git a/internal/backend/go.mod b/internal/backend/go.mod
@@ -3,3 +3,5 @@ module jacobedwards.org/spaceplanner.app/internal/backend
go 1.22.1
require golang.org/x/crypto v0.25.0
+
+require github.com/lib/pq v1.10.9 // indirect
diff --git a/internal/backend/go.sum b/internal/backend/go.sum
@@ -1,2 +1,4 @@
+github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
+github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=