commit 59bf1940dc5d523335e62b534092eb4d71740b22
parent 907da057dcee96aa2e342a85d4ca602f1109ffdb
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Fri, 4 Oct 2024 15:55:04 -0700
Use IDs to reference floorplans
While I like referencing things by name, using an ID will prevent
some unwelcome behavior for free (say, when the name of a floorplan
gets changed while you're editing it.)
A basic permission system was also half-implemented so the user
doesn't have to be passed around so much which has the added benifit
of providing a more helpful error than "no results found" on lack
of permissions.
Diffstat:
5 files changed, 261 insertions(+), 112 deletions(-)
diff --git a/cmd/api/floorplans.go b/cmd/api/floorplans.go
@@ -18,6 +18,11 @@ type Point struct {
Y *int `json:"y" binding:"required"`
}
+type FloorplanURI struct {
+ User string `uri:"user" binding:"required"`
+ Floorplan backend.ObjectID `uri:"floorplan" binding:"required"`
+}
+
func (e *Env) FurnitureTypes(c *gin.Context) {
types, err := e.backend.FurnitureTypes(nil)
if err != nil {
@@ -45,17 +50,20 @@ func (e *Env) CreateFloorplan(c *gin.Context) {
}
func (e *Env) UpdateFloorplan(c *gin.Context) {
+ var uri FloorplanURI
var req SettableFloorplan
+ if err := c.ShouldBindUri(&uri); err != nil {
+ RespondError(c, 400, err.Error())
+ return
+ }
if err := c.ShouldBind(&req); err != nil {
RespondError(c, 400, "%s", err.Error())
return
}
- user := c.Param("user")
- name := c.Param("floorplan")
- fp, err := e.backend.UpdateFloorplan(nil, user, name,
- &backend.Floorplan{User: user, Name: req.Name, Address: req.Address, Synopsis: req.Synopsis})
+ fp, err := e.backend.UpdateFloorplan(nil, uri.User, uri.Floorplan,
+ &backend.Floorplan{User: uri.User, Name: req.Name, Address: req.Address, Synopsis: req.Synopsis})
if err != nil {
RespondError(c, 500, "Unable to update floorplan")
} else {
@@ -64,10 +72,14 @@ func (e *Env) UpdateFloorplan(c *gin.Context) {
}
func (e *Env) DeleteFloorplan(c *gin.Context) {
- user := c.Param("user")
- floorplan := c.Param("floorplan")
+ var uri FloorplanURI
- fp, err := e.backend.DeleteFloorplan(nil, user, floorplan)
+ if err := c.ShouldBindUri(&uri); err != nil {
+ RespondError(c, 400, err.Error())
+ return
+ }
+
+ fp, err := e.backend.DeleteFloorplan(nil, uri.User, uri.Floorplan)
if err != nil {
RespondError(c, 400, "%s", err.Error())
} else {
@@ -87,10 +99,14 @@ func (e *Env) GetFloorplans(c *gin.Context) {
}
func (e *Env) GetFloorplan(c *gin.Context) {
- user := c.Param("user")
- name := c.Param("floorplan")
+ var uri FloorplanURI
- fp, err := e.backend.GetFloorplan(nil, user, name)
+ if err := c.ShouldBindUri(&uri); err != nil {
+ RespondError(c, 400, err.Error())
+ return
+ }
+
+ fp, err := e.backend.GetFloorplan(nil, uri.User, uri.Floorplan)
if err != nil {
RespondError(c, 400, "%s", err.Error())
} else {
@@ -99,10 +115,14 @@ func (e *Env) GetFloorplan(c *gin.Context) {
}
func (e *Env) GetFloorplanData(c *gin.Context) {
- user := c.Param("user")
- floorplan := c.Param("floorplan")
+ var uri FloorplanURI
+
+ if err := c.ShouldBindUri(&uri); err != nil {
+ RespondError(c, 400, err.Error())
+ return
+ }
- data, err := e.backend.GetFloorplanData(nil, user, floorplan)
+ data, err := e.backend.GetFloorplanData(nil, uri.User, uri.Floorplan)
if err != nil {
RespondError(c, 400, "%s", err.Error())
} else {
@@ -111,16 +131,19 @@ func (e *Env) GetFloorplanData(c *gin.Context) {
}
func (e *Env) PatchFloorplanData(c *gin.Context) {
+ var uri FloorplanURI
var patch []backend.Patch
- user := c.Param("user")
- floorplan := c.Param("floorplan")
+ if err := c.ShouldBindUri(&uri); err != nil {
+ RespondError(c, 400, err.Error())
+ return
+ }
if err := c.ShouldBind(&patch); err != nil {
RespondError(c, 400, "%s: Unable to get patch", err.Error())
return
}
- data, err := e.backend.PatchFloorplanData(nil, user, floorplan, patch)
+ data, err := e.backend.PatchFloorplanData(nil, uri.User, uri.Floorplan, patch)
if err != nil {
RespondError(c, 400, "%s: Unable to patch floorplan", err.Error())
} else {
@@ -129,16 +152,19 @@ func (e *Env) PatchFloorplanData(c *gin.Context) {
}
func (e *Env) ReplaceFloorplanData(c *gin.Context) {
+ var uri FloorplanURI
var data *backend.FloorplanData
- user := c.Param("user")
- floorplan := c.Param("floorplan")
+ if err := c.ShouldBindUri(&uri); err != nil {
+ RespondError(c, 400, err.Error())
+ return
+ }
if err := c.ShouldBind(&data); err != nil {
RespondError(c, 400, "%s: Couldn't get floorplan data", err.Error())
return
}
- newdata, err := e.backend.ReplaceFloorplanData(nil, user, floorplan, data)
+ newdata, err := e.backend.ReplaceFloorplanData(nil, uri.User, uri.Floorplan, data)
if err != nil {
RespondError(c, 500, "%s: Unable to update floorplan data", err.Error())
return
diff --git a/internal/backend/floorplan.go b/internal/backend/floorplan.go
@@ -2,11 +2,12 @@ package backend
import (
"database/sql"
+ "errors"
"time"
)
type Floorplan struct {
- id int
+ ID ObjectID `json:"id"`
User string `json:"user"`
Name string `json:"name"`
Address *string `json:"address"`
@@ -17,7 +18,7 @@ type Floorplan struct {
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 *`)
+ VALUES ($1, $2, $3, $4) RETURNING id, owner, name, address, synopsis, updated, created`)
if err != nil {
return nil, err
}
@@ -25,27 +26,37 @@ func (e *Env) CreateFloorplan(tx *sql.Tx, template *Floorplan) (*Floorplan, erro
return scanFloorplan(stmt.QueryRow(template.User, template.Name, template.Address, template.Synopsis))
}
-func (e *Env) UpdateFloorplan(tx *sql.Tx, user string, name string, updated *Floorplan) (*Floorplan, error) {
+func (e *Env) UpdateFloorplan(tx *sql.Tx, user string, id ObjectID, updated *Floorplan) (*Floorplan, error) {
+ if id.Type != IDTypeFloorplan {
+ return nil, errors.New("Expected floorplan ID")
+ }
+
stmt, err := e.CacheTxStmt(tx, "update_floorplan", `UPDATE spaceplanner.floorplans SET (name, address, synopsis) =
- ($3, $4, $5) WHERE owner = $1 AND name = $2 RETURNING *`)
+ ($3, $4, $5) WHERE owner = $1 AND id = $2 RETURNING id, owner, name, address, synopsis, updated, created`)
if err != nil {
return nil, err
}
- return scanFloorplan(stmt.QueryRow(user, name, updated.Name, updated.Address, updated.Synopsis))
+ return scanFloorplan(stmt.QueryRow(user, id.Seq, updated.Name, updated.Address, updated.Synopsis))
}
-func (e *Env) GetFloorplan(tx *sql.Tx, user string, name string) (*Floorplan, error) {
- stmt, err := e.CacheTxStmt(tx, "get_floorplan", "SELECT * FROM spaceplanner.floorplans WHERE owner = $1 AND name = $2")
+func (e *Env) GetFloorplan(tx *sql.Tx, user string, id ObjectID) (*Floorplan, error) {
+ stmt, err := e.CacheTxStmt(tx, "get_floorplan", `SELECT id, owner, name, address, synopsis, updated, created
+ FROM spaceplanner.floorplans
+ WHERE owner = $1 AND id = $2`)
if err != nil {
return nil, err
}
- return scanFloorplan(stmt.QueryRow(user, name))
+ if id.Type != IDTypeFloorplan {
+ return nil, errors.New("Expected floorplan ID")
+ }
+ return scanFloorplan(stmt.QueryRow(user, id.Seq))
}
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
+ stmt, err := e.CacheTxStmt(tx, "get_floorplans", `SELECT id, owner, name, address, synopsis, updated, created
+ FROM spaceplanner.floorplans WHERE owner = $1
ORDER BY updated DESC, created DESC`)
if err != nil {
return nil, err
@@ -60,17 +71,25 @@ func (e *Env) GetFloorplans(tx *sql.Tx, user string) ([]*Floorplan, error) {
return collectRows(rows, scanFloorplan)
}
-func (e *Env) DeleteFloorplan(tx *sql.Tx, user string, name string) (*Floorplan, error) {
- stmt, err := e.CacheTxStmt(tx, "del_floorplan", "DELETE FROM spaceplanner.floorplans WHERE owner = $1 AND name = $2 RETURNING *")
+func (e *Env) DeleteFloorplan(tx *sql.Tx, user string, id ObjectID) (*Floorplan, error) {
+ if id.Type != IDTypeFloorplan {
+ return nil, errors.New("Expected floorplan ID")
+ }
+
+ stmt, err := e.CacheTxStmt(tx, "del_floorplan", `DELETE FROM spaceplanner.floorplans
+ WHERE owner = $1 AND id = $2
+ RETURNING id, owner, name, address, synopsis, updated, created`)
if err != nil {
return nil, err
}
- return scanFloorplan(stmt.QueryRow(user, name))
+ return scanFloorplan(stmt.QueryRow(user, id.Seq))
}
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)
+ var id int64
+ err := row.Scan(&id, &f.User, &f.Name, &f.Address, &f.Synopsis, &f.Updated, &f.Created)
+ f.ID = makeID(IDTypeFloorplan, id)
return &f, err
}
diff --git a/internal/backend/floorplan_data.go b/internal/backend/floorplan_data.go
@@ -78,9 +78,9 @@ type DBObject interface {
* NOTE: these all take user and floorplan arguments solely for authorization, but
* I want a cleaner method
*/
- Create(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error)
- Update(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error)
- Delete(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error)
+ Create(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error)
+ Update(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error)
+ Delete(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error)
}
type FurnitureType struct {
@@ -166,26 +166,38 @@ func (e *Env) FurnitureTypes(tx *sql.Tx) (map[string]FurnitureType, error) {
return data, nil
}
-func (e *Env) GetFloorplanData(tx *sql.Tx, user string, floorplan string) (FloorplanData, error) {
+func (e *Env) GetFloorplanData(tx *sql.Tx, user string, floorplan ObjectID) (FloorplanData, error) {
var data FloorplanData
var err error
- data.Points, err = e.getFloorplanPoints(tx, user, floorplan)
+ if floorplan.Type != IDTypeFloorplan {
+ return data, errors.New("Expected floorplan id")
+ }
+
+ a, err := e.userFloorplanAccess(tx, user, floorplan)
+ if err != nil {
+ return FloorplanData{}, err
+ }
+ if !a.Read() {
+ return FloorplanData{}, errors.New("You do not have read permission on this resource")
+ }
+
+ data.Points, err = e.getFloorplanPoints(tx, floorplan)
if err != nil {
return data, err
}
- data.Pointmaps, err = e.getFloorplanPointMaps(tx, user, floorplan)
+ data.Pointmaps, err = e.getFloorplanPointMaps(tx, floorplan)
if err != nil {
return data, err
}
- data.Furniture, err = e.getFloorplanFurnitureDefs(tx, user, floorplan)
+ data.Furniture, err = e.getFloorplanFurnitureDefs(tx, floorplan)
if err != nil {
return data, err
}
- data.FurnitureMaps, err = e.getFloorplanFurnitureMaps(tx, user, floorplan)
+ data.FurnitureMaps, err = e.getFloorplanFurnitureMaps(tx, floorplan)
if err != nil {
return data, err
}
@@ -193,15 +205,15 @@ func (e *Env) GetFloorplanData(tx *sql.Tx, user string, floorplan string) (Floor
return data, nil
}
-func (e *Env) getFloorplanPoints(tx *sql.Tx, user string, floorplan string) (map[ObjectID]Point, error) {
+func (e *Env) getFloorplanPoints(tx *sql.Tx, floorplan ObjectID) (map[ObjectID]Point, error) {
stmt, err := e.CacheTxStmt(tx, "get_points",
- `SELECT id, x, y FROM spaceplanner.floorplan_points WHERE
- floorplan = spaceplanner.floorplan_id($1, $2)`)
+ `SELECT id, x, y FROM spaceplanner.floorplan_points
+ WHERE floorplan = $1`)
if err != nil {
return nil, err
}
- rows, err := stmt.Query(user, floorplan)
+ rows, err := stmt.Query(floorplan.Seq)
if err != nil {
return nil, err
}
@@ -214,15 +226,15 @@ func (e *Env) getFloorplanPoints(tx *sql.Tx, user string, floorplan string) (map
return mapArray(points, mapPoint)
}
-func (e *Env) getFloorplanPointMaps(tx *sql.Tx, user string, floorplan string) (map[ObjectID]PointMap, error) {
+func (e *Env) getFloorplanPointMaps(tx *sql.Tx, floorplan ObjectID) (map[ObjectID]PointMap, error) {
stmt, err := e.CacheTxStmt(tx, "get_pointmaps",
`SELECT id, type, a, b, door_swing FROM spaceplanner.floorplan_pointmaps WHERE
- floorplan = spaceplanner.floorplan_id($1, $2)`)
+ floorplan = $1`)
if err != nil {
return nil, err
}
- rows, err := stmt.Query(user, floorplan)
+ rows, err := stmt.Query(floorplan.Seq)
if err != nil {
return nil, err
}
@@ -234,16 +246,16 @@ func (e *Env) getFloorplanPointMaps(tx *sql.Tx, user string, floorplan string) (
return mapArray(pointmaps, mapPointMap)
}
-func (e *Env) getFloorplanFurnitureDefs(tx *sql.Tx, user string, floorplan string) (map[ObjectID]Furniture, error) {
+func (e *Env) getFloorplanFurnitureDefs(tx *sql.Tx, floorplan ObjectID) (map[ObjectID]Furniture, error) {
defsStmt, err := e.CacheTxStmt(tx, "furniture_defs",
`SELECT id, type, name, width, depth
FROM spaceplanner.furniture
- WHERE floorplan = spaceplanner.floorplan_id($1, $2)`)
+ WHERE floorplan = $1`)
if err != nil {
return nil, err
}
- rows, err := defsStmt.Query(user, floorplan)
+ rows, err := defsStmt.Query(floorplan.Seq)
if err != nil {
return nil, err
}
@@ -258,20 +270,20 @@ func (e *Env) getFloorplanFurnitureDefs(tx *sql.Tx, user string, floorplan strin
}
-func (e *Env) getFloorplanFurnitureMaps(tx *sql.Tx, user string, floorplan string) (map[ObjectID]FurnitureMap, error) {
+func (e *Env) getFloorplanFurnitureMaps(tx *sql.Tx, floorplan ObjectID) (map[ObjectID]FurnitureMap, error) {
mapsStmt, err := e.CacheTxStmt(tx, "furniture_maps",
`SELECT id, furniture_id, layout, x, y, angle
FROM spaceplanner.furniture_maps
WHERE furniture_id IN (
SELECT id
FROM spaceplanner.furniture
- WHERE floorplan = spaceplanner.floorplan_id($1, $2)
+ WHERE floorplan = $1
)`)
if err != nil {
return nil, err
}
- rows, err := mapsStmt.Query(user, floorplan)
+ rows, err := mapsStmt.Query(floorplan.Seq)
if err != nil {
return nil, err
}
@@ -284,7 +296,11 @@ func (e *Env) getFloorplanFurnitureMaps(tx *sql.Tx, user string, floorplan strin
return mapArray(maps, mapFurnitureMap)
}
-func (e *Env) ReplaceFloorplanData(tx *sql.Tx, user string, floorplan string, data *FloorplanData) (FloorplanData, error) {
+func (e *Env) ReplaceFloorplanData(tx *sql.Tx, user string, floorplan ObjectID, data *FloorplanData) (FloorplanData, error) {
+ if floorplan.Type != IDTypeFloorplan {
+ return FloorplanData{}, errors.New("Expected floorplan id")
+ }
+
mytx := false
if (tx == nil) {
mytx = true
@@ -333,9 +349,12 @@ func (e *Env) ReplaceFloorplanData(tx *sql.Tx, user string, floorplan string, da
return newdata, nil
}
-func (e *Env) DeleteFloorplanData(tx *sql.Tx, user string, floorplan string) error {
- var mytx bool
+func (e *Env) DeleteFloorplanData(tx *sql.Tx, user string, floorplan ObjectID) error {
+ if floorplan.Type != IDTypeFloorplan {
+ return errors.New("Expected floorplan id")
+ }
+ var mytx bool
if tx == nil {
mytx = true
var err error
@@ -345,26 +364,34 @@ func (e *Env) DeleteFloorplanData(tx *sql.Tx, user string, floorplan string) err
}
defer tx.Rollback()
}
-
+
+ a, err := e.userFloorplanAccess(tx, user, floorplan)
+ if err != nil {
+ return err
+ }
+ if !a.Write() {
+ return errors.New("You do not have write permission on this resource")
+ }
+
delPnt, err := e.CacheTxStmt(tx, "del_floorplan_pnt",
`DELETE FROM spaceplanner.floorplan_points
- WHERE floorplan = spaceplanner.floorplan_id($1, $2)`)
+ WHERE floorplan = $1)`)
if err != nil {
return err
}
delFur, err := e.CacheTxStmt(tx, "del_floorplan_fur",
`DELETE FROM spaceplanner.furniture
- WHERE floorplan = spaceplanner.floorplan_id($1, $2)`)
+ WHERE floorplan = $1`)
if err != nil {
return err
}
- _, err = delPnt.Exec(user, floorplan)
+ _, err = delPnt.Exec(floorplan.Seq)
if err != nil {
return err
}
- _, err = delFur.Exec(user, floorplan)
+ _, err = delFur.Exec(floorplan.Seq)
if err != nil {
return err
}
@@ -375,10 +402,14 @@ func (e *Env) DeleteFloorplanData(tx *sql.Tx, user string, floorplan string) err
return nil
}
-func (e *Env) PatchFloorplanData(tx *sql.Tx, user string, floorplan string, patches []Patch) (FloorplanData, error) {
+func (e *Env) PatchFloorplanData(tx *sql.Tx, user string, floorplan ObjectID, patches []Patch) (FloorplanData, error) {
var err error
mytx := false
-
+
+ if floorplan.Type != IDTypeFloorplan {
+ return FloorplanData{}, errors.New("Expected floorplan id")
+ }
+
if (tx == nil) {
tx, err = e.DB.Begin()
if err != nil {
@@ -388,6 +419,14 @@ func (e *Env) PatchFloorplanData(tx *sql.Tx, user string, floorplan string, patc
mytx = true
}
+ a, err := e.userFloorplanAccess(tx, user, floorplan)
+ if err != nil {
+ return FloorplanData{}, err
+ }
+ if !a.Write() {
+ return FloorplanData{}, errors.New("You do not have write permission on this resource")
+ }
+
newIDs := make(map[ObjectID]ObjectID)
data := FloorplanData{}
@@ -409,7 +448,7 @@ func (e *Env) PatchFloorplanData(tx *sql.Tx, user string, floorplan string, patc
}
if (id.Type == IDTypePoint) {
- point, err := applyPatch[Point](e, tx, user, floorplan, &patch, id, newIDs)
+ point, err := applyPatch[Point](e, tx, floorplan, &patch, id, newIDs)
if err != nil {
return data, id.Error("", err)
}
@@ -417,7 +456,7 @@ func (e *Env) PatchFloorplanData(tx *sql.Tx, user string, floorplan string, patc
data.Points[point.id] = point
}
} else if (id.Type == IDTypePointMap) {
- pointmap, err := applyPatch[PointMap](e, tx, user, floorplan, &patch, id, newIDs)
+ pointmap, err := applyPatch[PointMap](e, tx, floorplan, &patch, id, newIDs)
if err != nil {
return data, id.Error("", err)
}
@@ -425,7 +464,7 @@ func (e *Env) PatchFloorplanData(tx *sql.Tx, user string, floorplan string, patc
data.Pointmaps[pointmap.id] = pointmap
}
} else if (id.Type == IDTypeFurniture) {
- def, err := applyPatch[Furniture](e, tx, user, floorplan, &patch, id, newIDs)
+ def, err := applyPatch[Furniture](e, tx, floorplan, &patch, id, newIDs)
if err != nil {
return data, id.Error("", err)
}
@@ -433,7 +472,7 @@ func (e *Env) PatchFloorplanData(tx *sql.Tx, user string, floorplan string, patc
data.Furniture[def.id] = def
}
} else if (id.Type == IDTypeFurnitureMap) {
- fm, err := applyPatch[FurnitureMap](e, tx, user, floorplan, &patch, id, newIDs)
+ fm, err := applyPatch[FurnitureMap](e, tx, floorplan, &patch, id, newIDs)
if err != nil {
return data, id.Error("", err)
}
@@ -455,7 +494,25 @@ func (e *Env) PatchFloorplanData(tx *sql.Tx, user string, floorplan string, patc
return data, nil
}
-func applyPatch[T DBObject](e *Env, tx *sql.Tx, user, floorplan string, patch *Patch, id ObjectID,
+func (e *Env) userFloorplanAccess(tx *sql.Tx, user string, floorplan ObjectID) (AccessPermission, error) {
+ checkPerms, err := e.CacheTxStmt(tx, "check_perms",
+ "SELECT owner FROM spaceplanner.floorplans WHERE id = $1")
+ if err != nil {
+ return NoAccess, err
+ }
+
+ var owner string
+ r := checkPerms.QueryRow(floorplan.Seq)
+ if err := r.Scan(&owner); err != nil {
+ return NoAccess, err
+ }
+ if owner == user {
+ return ReadAccess | WriteAccess, nil
+ }
+ return NoAccess, nil
+}
+
+func applyPatch[T DBObject](e *Env, tx *sql.Tx, floorplan ObjectID, patch *Patch, id ObjectID,
newIDs map[ObjectID]ObjectID) (T, error) {
inputID := id
id, exists := newIDs[inputID]
@@ -479,11 +536,11 @@ func applyPatch[T DBObject](e *Env, tx *sql.Tx, user, floorplan string, patch *P
var dbo DBObject
switch patch.Op {
case "new":
- dbo, err = thing.Create(e, tx, user, floorplan)
+ dbo, err = thing.Create(e, tx, floorplan)
case "replace":
- dbo, err = thing.Update(e, tx, user, floorplan)
+ dbo, err = thing.Update(e, tx, floorplan)
case "remove":
- dbo, err = thing.Delete(e, tx, user, floorplan)
+ dbo, err = thing.Delete(e, tx, floorplan)
default:
return thing, inputID.Error("Unsupported operation", nil)
}
@@ -515,151 +572,151 @@ func remarshal[T any](value interface{}, result *T) (error) {
* these functions return an interface instead of their value
* Not sure exactly how this is suppost to be done.
*/
-func (p Point) Create(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error) {
+func (p Point) Create(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
stmt, err := e.CacheTxStmt(tx, "add_point",`INSERT INTO spaceplanner.floorplan_points (floorplan, x, y)
- VALUES (spaceplanner.floorplan_id($1, $2), $3, $4) RETURNING id, x, y`)
+ VALUES ($1, $2, $3) RETURNING id, x, y`)
if err != nil {
return Point{}, err
}
- return scanPoint(stmt.QueryRow(user, floorplan, p.X, p.Y))
+ return scanPoint(stmt.QueryRow(floorplan.Seq, p.X, p.Y))
}
-func (p Point) Update(e *Env, tx *sql.Tx, user string, floorplan string) (DBObject, error) {
- stmt, err := e.CacheTxStmt(tx, "repl_point", `UPDATE spaceplanner.floorplan_points SET (x, y) = ($4, $5)
- WHERE floorplan = spaceplanner.floorplan_id($1, $2) AND id = $3 RETURNING id, x, y`)
+func (p Point) Update(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
+ stmt, err := e.CacheTxStmt(tx, "repl_point", `UPDATE spaceplanner.floorplan_points SET (x, y) = ($3, $4)
+ WHERE floorplan = $1 AND id = $2 RETURNING id, x, y`)
if err != nil {
return Point{}, err
}
- return scanPoint(stmt.QueryRow(user, floorplan, p.id.Seq, p.X, p.Y))
+ return scanPoint(stmt.QueryRow(floorplan.Seq, p.id.Seq, p.X, p.Y))
}
-func (p Point) Delete(e *Env, tx *sql.Tx, user string, floorplan string) (DBObject, error) {
- return e.DeletePoint(tx, user, floorplan, p.id)
+func (p Point) Delete(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
+ return e.DeletePoint(tx, floorplan, p.id)
}
-func (e *Env) DeletePoint(tx *sql.Tx, user string, floorplan string, id ObjectID) (Point, error) {
+func (e *Env) DeletePoint(tx *sql.Tx, floorplan, id ObjectID) (Point, error) {
stmt, err := e.CacheTxStmt(tx, "dele_point",
- `DELETE FROM spaceplanner.floorplan_points WHERE floorplan = spaceplanner.floorplan_id($1, $2) AND id = $3
+ `DELETE FROM spaceplanner.floorplan_points WHERE floorplan = $1 AND id = $2
RETURNING id, x, y`)
if err != nil {
return Point{}, err
}
- return scanPoint(stmt.QueryRow(user, floorplan, id.Seq))
+ return scanPoint(stmt.QueryRow(floorplan.Seq, id.Seq))
}
-func (pm PointMap) Create(e *Env, tx *sql.Tx, user string, floorplan string) (DBObject, error) {
+func (pm PointMap) Create(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
stmt, err := e.CacheTxStmt(tx, "add_pointmap",
`INSERT INTO spaceplanner.floorplan_pointmaps (floorplan, type, a, b, door_swing) VALUES (
- spaceplanner.floorplan_id($1, $2), $3, $4, $5, $6
+ $1, $2, $3, $4, $5
) RETURNING id, type, a, b, door_swing`)
if err != nil {
return PointMap{}, err
}
- return scanPointMap(stmt.QueryRow(user, floorplan, pm.Type, pm.A.Seq, pm.B.Seq, pm.DoorSwing))
+ return scanPointMap(stmt.QueryRow(floorplan.Seq, pm.Type, pm.A.Seq, pm.B.Seq, pm.DoorSwing))
}
-func (pm PointMap) Update(e *Env, tx *sql.Tx, user string, floorplan string) (DBObject, error) {
+func (pm PointMap) Update(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
stmt, err := e.CacheTxStmt(tx, "repl_pointmap",
`UPDATE spaceplanner.floorplan_pointmaps SET (type, a, b, door_swing) =
- ($4, $5, $6, $7) WHERE floorplan = spaceplanner.floorplan_id($1, $2) AND id = $3
+ ($3, $4, $5, $6) WHERE floorplan = $1 AND id = $2
RETURNING id, type, a, b, door_swing`)
if err != nil {
return PointMap{}, err
}
- return scanPointMap(stmt.QueryRow(user, floorplan, pm.id.Seq, pm.Type, pm.A.Seq, pm.B.Seq, pm.DoorSwing))
+ return scanPointMap(stmt.QueryRow(floorplan.Seq, pm.id.Seq, pm.Type, pm.A.Seq, pm.B.Seq, pm.DoorSwing))
}
-func (pm PointMap) Delete(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error) {
- return e.DeletePointMap(tx, user, floorplan, pm.id)
+func (pm PointMap) Delete(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
+ return e.DeletePointMap(tx, floorplan, pm.id)
}
-func (e *Env) DeletePointMap(tx *sql.Tx, user string, floorplan string, id ObjectID) (PointMap, error) {
+func (e *Env) DeletePointMap(tx *sql.Tx, floorplan, id ObjectID) (PointMap, error) {
stmt, err := e.CacheTxStmt(tx, "dele_pointmap",
`DELETE FROM spaceplanner.floorplan_pointmaps
- WHERE floorplan = spaceplanner.floorplan_id($1, $2) AND id = $3
+ WHERE floorplan = $1 AND id = $2
RETURNING id, type, a, b, door_swing`)
if err != nil {
return PointMap{}, err
}
- return scanPointMap(stmt.QueryRow(user, floorplan, id.Seq))
+ return scanPointMap(stmt.QueryRow(floorplan.Seq, id.Seq))
}
-func (f Furniture) Create(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error) {
+func (f Furniture) Create(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
ins, err := e.CacheTxStmt(tx, "add_furn",
`INSERT INTO spaceplanner.furniture (floorplan, type, name, width, depth)
- VALUES (spaceplanner.floorplan_id($1, $2), $3, $4, $5, $6)
+ VALUES ($1, $2, $3, $4, $5)
RETURNING id, type, name, width, depth`)
if err != nil {
return f, err
}
- return scanFurniture(ins.QueryRow(user, floorplan, f.Type, f.Name, f.Width, f.Depth))
+ return scanFurniture(ins.QueryRow(floorplan.Seq, f.Type, f.Name, f.Width, f.Depth))
}
-func (f Furniture) Update(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error) {
+func (f Furniture) Update(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
update, err := e.CacheTxStmt(tx, "update_furn",
`UPDATE spaceplanner.furniture SET (type, name, width, depth) =
- ($4, $5, $6, $7) WHERE floorplan = spaceplanner.floorplan_id($1, $2) AND id = $3
+ ($3, $4, $5, $6) WHERE floorplan = $1 AND id = $2
RETURNING id, type, name, width, depth`)
if err != nil {
return f, err
}
- return scanFurniture(update.QueryRow(user, floorplan, f.id.Seq, f.Type, f.Name, f.Width, f.Depth))
+ return scanFurniture(update.QueryRow(floorplan.Seq, f.id.Seq, f.Type, f.Name, f.Width, f.Depth))
}
-func (f Furniture) Delete(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error) {
+func (f Furniture) Delete(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
del, err := e.CacheTxStmt(tx, "dele_furn",
`DELETE FROM spaceplanner.furniture
- WHERE floorplan = spaceplanner.floorplan_id($1, $2) AND id = $3
+ WHERE floorplan = $1 AND id = $2
RETURNING id, type, name, width, depth`)
if err != nil {
return f, err
}
- return scanFurniture(del.QueryRow(user, floorplan, f.id.Seq))
+ return scanFurniture(del.QueryRow(floorplan.Seq, f.id.Seq))
}
-func (f FurnitureMap) Create(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error) {
+func (f FurnitureMap) Create(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
ins, err := e.CacheTxStmt(tx, "add_furnmap",
`INSERT INTO spaceplanner.furniture_maps (floorplan, furniture_id, layout, x, y, angle)
- VALUES (spaceplanner.floorplan_id($1, $2), $3, $4, $5, $6, $7)
+ VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, furniture_id, layout, x, y, angle`)
if err != nil {
return f, err
}
- return scanFurnitureMap(ins.QueryRow(user, floorplan, f.FurnitureID.Seq, f.Layout, f.X, f.Y, f.Angle))
+ return scanFurnitureMap(ins.QueryRow(floorplan.Seq, f.FurnitureID.Seq, f.Layout, f.X, f.Y, f.Angle))
}
-func (fm FurnitureMap) Update(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error) {
+func (fm FurnitureMap) Update(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
update, err := e.CacheTxStmt(tx, "update_furnmap",
`UPDATE spaceplanner.furniture_maps SET (furniture_id, layout, x, y, angle) =
- ($4, $5, $6, $7, $8) WHERE floorplan = spaceplanner.floorplan_id($1, $2) AND id = $3
+ ($3, $4, $5, $6, $7) WHERE floorplan = $1 AND id = $2
RETURNING id, furniture_id, layout, x, y, angle`)
if err != nil {
return fm, err
}
- return scanFurnitureMap(update.QueryRow(user, floorplan, fm.id.Seq, fm.FurnitureID.Seq, fm.Layout, fm.X, fm.Y, fm.Angle))
+ return scanFurnitureMap(update.QueryRow(floorplan.Seq, fm.id.Seq, fm.FurnitureID.Seq, fm.Layout, fm.X, fm.Y, fm.Angle))
}
-func (f FurnitureMap) Delete(e *Env, tx *sql.Tx, user, floorplan string) (DBObject, error) {
+func (f FurnitureMap) Delete(e *Env, tx *sql.Tx, floorplan ObjectID) (DBObject, error) {
del, err := e.CacheTxStmt(tx, "dele_furnmap",
`DELETE FROM spaceplanner.furniture_maps
- WHERE floorplan = spaceplanner.floorplan_id($1, $2) AND id = $3
+ WHERE floorplan = $1 AND id = $2
RETURNING id, furniture_id, layout, x, y, angle`)
if err != nil {
return f, err
}
- return scanFurnitureMap(del.QueryRow(user, floorplan, f.id.Seq))
+ return scanFurnitureMap(del.QueryRow(floorplan.Seq, f.id.Seq))
}
func (f Furniture) ID() ObjectID {
diff --git a/internal/backend/id.go b/internal/backend/id.go
@@ -16,6 +16,7 @@ type ObjectID struct {
}
var (
+ IDTypeFloorplan ObjectType = "flp"
IDTypePoint ObjectType = "pnt"
IDTypePointMap ObjectType = "pntmap"
IDTypeFurniture ObjectType = "fur"
@@ -26,6 +27,7 @@ var objectTypes []ObjectType
func init() {
objectTypes = []ObjectType{
+ IDTypeFloorplan,
IDTypePoint,
IDTypePointMap,
IDTypeFurniture,
@@ -95,3 +97,7 @@ func (id *ObjectID) UnmarshalText(b []byte) error {
*id = d
return err
}
+
+func (id *ObjectID) UnmarshalParam(p string) error {
+ return id.UnmarshalText([]byte(p))
+}
diff --git a/internal/backend/permissions.go b/internal/backend/permissions.go
@@ -0,0 +1,41 @@
+package backend
+
+/*
+ * In the future this will hold something like this
+ *
+ * type Permission {
+ * Name string
+ * Allowed bool
+ * }
+ *
+ * for whatever permissions might need to be enforced, such as
+ * service levels.
+ */
+
+type unsignedNumeric interface {
+ uint8 | uint16 | uint32 | uint64 | uint
+}
+
+var (
+ NoAccess AccessPermission = 0
+ ReadAccess AccessPermission = 1
+ WriteAccess AccessPermission = 2
+)
+
+type AccessPermission uint8
+
+func (p AccessPermission) Read() bool {
+ return (p & ReadAccess) != 0
+}
+
+func (p AccessPermission) Write() bool {
+ return (p & WriteAccess) != 0
+}
+
+func setBit(mask uint8, bit uint8, set bool) uint8 {
+ if set {
+ return mask | bit
+ } else {
+ return mask & ^bit
+ }
+}