diff --git a/pkg/restheadspec/handler.go b/pkg/restheadspec/handler.go index 9beb02c..3600e3d 100644 --- a/pkg/restheadspec/handler.go +++ b/pkg/restheadspec/handler.go @@ -241,9 +241,11 @@ func (h *Handler) Handle(w common.ResponseWriter, r common.Request, params map[s h.sendError(w, http.StatusBadRequest, "invalid_request", "Invalid request body", err) return } - validId, _ := strconv.ParseInt(id, 10, 64) + // A URL id is valid when it is a positive integer or any non-numeric + // string (string primary keys); "", "0" and negatives mean no id. + validId, parseErr := strconv.ParseInt(id, 10, 64) updateID := id - isUpdate := validId > 0 + isUpdate := id != "" && (parseErr != nil || validId > 0) if !isUpdate { // No valid /:id in the URL - check whether the body itself carries // a valid primary key value and treat this as an update if so. diff --git a/pkg/restheadspec/pk_update_integration_test.go b/pkg/restheadspec/pk_update_integration_test.go index cba6e69..aed4eb6 100644 --- a/pkg/restheadspec/pk_update_integration_test.go +++ b/pkg/restheadspec/pk_update_integration_test.go @@ -8,6 +8,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "strings" "testing" "time" @@ -123,3 +124,30 @@ func TestUpdateChangesPrimaryKeyWhenURLIDGiven(t *testing.T) { require.NoError(t, db.QueryRow(`SELECT category FROM public.t_pkasset WHERE description = 'new-pk'`).Scan(&cat)) require.Equal(t, "cat5", cat) } + +// Exercises the real dispatch: a POST with a string id in the URL and a different +// PK in the body must update the row identified by the URL id. +func TestHandlePostWithStringURLIDChangesPrimaryKey(t *testing.T) { + db := setupPKTestDB(t) + reg := modelregistry.NewModelRegistry() + require.NoError(t, reg.RegisterModel("public.t_pkasset", pkAsset{})) + h := NewHandler(database.NewBunAdapter(bun.NewDB(db, pgdialect.New())), reg) + + for _, method := range []string{http.MethodPost, http.MethodPut} { + t.Run(method, func(t *testing.T) { + from, to := "old-pk", "new-pk-"+method + if method == http.MethodPut { + from = "new-pk-" + http.MethodPost + } + req := httptest.NewRequest(method, "/public/t_pkasset/"+from, + strings.NewReader(`{"description":"`+to+`","category":"c"}`)) + rec := httptest.NewRecorder() + w, r := common.WrapHTTPRequest(rec, req) + h.Handle(w, r, map[string]string{"schema": "public", "entity": "t_pkasset", "id": from}) + + require.Equal(t, http.StatusOK, rec.Code, rec.Body.String()) + require.Equal(t, 0, countPK(t, db, from)) + require.Equal(t, 1, countPK(t, db, to)) + }) + } +}