Add plant catalog backend: CRUD + seeded built-ins (#12)
Extends the plants store (which had only the /full read side) with full catalog CRUD, adds a service layer with visibility/immutability rules, REST endpoints, and a seed migration of ~32 built-ins. - 0003_seed_plants.sql: 32 built-in plants (owner_id NULL, read-only), seeded once by the version-tracked migration runner. - store: ListPlantsForActor (built-ins + own), Get/Create/Update/Delete + CountPlantingsForPlant; isForeignKeyViolation backstop for the RESTRICT FK. - service: built-ins are read-only (ErrForbidden); another user's plants are invisible (ErrNotFound); delete of a referenced plant → ErrPlantInUse (409); validation mirrors the schema (category enum, spacing>0, hex color, icon). - api: GET,POST /plants and PATCH,DELETE /plants/:id with the version guard. - Made the migration-count store tests derive their expectation from the files so future migrations don't break them. Service + API tests cover visibility, built-in immutability, validation, version conflict, and delete-in-use. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
This commit is contained in:
@@ -28,6 +28,8 @@ func writeServiceError(c *gin.Context, err error) {
|
||||
writeAPIError(c, http.StatusForbidden, "FORBIDDEN", "you don't have access")
|
||||
case errors.Is(err, domain.ErrVersionConflict):
|
||||
writeAPIError(c, http.StatusConflict, "VERSION_CONFLICT", "the resource was modified; refetch and retry")
|
||||
case errors.Is(err, domain.ErrPlantInUse):
|
||||
writeAPIError(c, http.StatusConflict, "PLANT_IN_USE", "this plant is used by plantings and can't be deleted")
|
||||
case errors.Is(err, domain.ErrInvalidCredentials):
|
||||
writeAPIError(c, http.StatusUnauthorized, "INVALID_CREDENTIALS", "invalid email or password")
|
||||
case errors.Is(err, domain.ErrEmailTaken):
|
||||
|
||||
Reference in New Issue
Block a user