Add garden objects backend: polymorphic CRUD + /full (#10)
Garden objects (beds, bags, containers, in-ground, trees, paths, structures) in one polymorphic table, following the #7 service conventions. - store/objects.go: scanObject + Create (RETURNING), Get, ListForGarden (z_index order), version-guarded Update (RETURNING, returns current row on conflict), Delete (plantings cascade via FK). - store/plantings.go + plants.go: the read side /full needs now — ListActivePlantingsForGarden (removed_at IS NULL) and ListReferencedPlants (distinct plants used by active plantings). Both return empty until #14 adds plantings; #12/#14 extend these files. - service/objects.go: Create/Update(partial patch)/Delete/GardenFull, all (ctx, actorID, ...) through requireGardenRole(editor for mutations, viewer for /full). finalizeObject validates kind + shape (rect/circle; polygon reserved), finite dims in [1cm,100m], NaN/Inf rejection, loose bbox-overlaps-garden placement (partial overhang OK), hex color, valid JSON props, name/notes caps; normalizes rotation to [0,360). Plantable defaults by kind, overridable. - api/objects.go: POST /gardens/:id/objects, PATCH/DELETE /objects/:id, GET /gardens/:id/full ({garden,objects,plantings,plants}). props is any JSON value stored as text; PATCH is partial + required version, 409 returns the current row. Tests: service (defaults, plantable-by-kind, rotation normalize, validation rejects incl. off-field/polygon/bad-color/bad-props, partial patch + version conflict, cross-user ErrNotFound, delete, /full shape) and api (CRUD + /full, 409 envelope, cross-user 404, polygon/no-kind 400, props round-trip). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
This commit is contained in:
@@ -81,6 +81,14 @@ func New(cfg *config.Config, svc *service.Service) *gin.Engine {
|
||||
gardens.GET("/:id", h.getGarden)
|
||||
gardens.PATCH("/:id", h.updateGarden)
|
||||
gardens.DELETE("/:id", h.deleteGarden)
|
||||
gardens.GET("/:id/full", h.gardenFull) // one-shot editor load
|
||||
gardens.POST("/:id/objects", h.createObject)
|
||||
|
||||
// Objects are addressed by their own id; the service resolves the owning
|
||||
// garden for the permission check.
|
||||
objects := v1.Group("/objects", h.requireAuth())
|
||||
objects.PATCH("/:id", h.updateObject)
|
||||
objects.DELETE("/:id", h.deleteObject)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user