* api: detect an absent copy body by io.EOF from the decoder rather than
Content-Length. A chunked request reports -1, not 0, so the length test
read an empty chunked body as malformed and 400'd instead of taking the
default-name path. Covered by a new unknown-length test.
* store: hoist the garden_objects/plantings INSERTs into shared
objectInsert/plantingInsert statements with matching *InsertArgs
helpers. CopyGarden had its own copies of both column lists, so a
column added to the table could be wired into Create* and silently
dropped from a copy.
* store: move the queryer interface to sqlite.go, next to the other
shared query plumbing, instead of users.go.
* web: derive a copy's prefilled name via defaultCopyName, mirroring the
server's copyName including its 200-BYTE cap. The inline
`${name} (copy)` both duplicated the suffix and, for a garden whose
name was already at the cap, prefilled an over-long name that the
server rejected with a 400. Unit-tested, including multi-byte
truncation on a code-point boundary.
* test: drop a throwaway `_ = kept`.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -115,13 +116,13 @@ func (h *handlers) copyGarden(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
// The body is optional — POST with no body means "copy under the default
|
||||
// name" — so only decode when one was actually sent.
|
||||
// name". An absent body surfaces as io.EOF from the decoder, which is the
|
||||
// reliable signal: Content-Length is -1 (not 0) for a chunked request, so
|
||||
// testing the length would misread an empty chunked body as malformed.
|
||||
var req gardenCopyRequest
|
||||
if c.Request.ContentLength != 0 {
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
writeAPIError(c, http.StatusBadRequest, "INVALID_INPUT", "name must be a string")
|
||||
return
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil && !errors.Is(err, io.EOF) {
|
||||
writeAPIError(c, http.StatusBadRequest, "INVALID_INPUT", "name must be a string")
|
||||
return
|
||||
}
|
||||
g, err := h.svc.CopyGarden(c.Request.Context(), mustActor(c).ID, id, req.Name)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user