diff --git a/pkg/restheadspec/handler.go b/pkg/restheadspec/handler.go index 620e546..e3349ef 100644 --- a/pkg/restheadspec/handler.go +++ b/pkg/restheadspec/handler.go @@ -233,8 +233,18 @@ func (h *Handler) Handle(w common.ResponseWriter, r common.Request, params map[s return } validId, _ := strconv.ParseInt(id, 10, 64) - if validId > 0 { - h.handleUpdate(ctx, w, id, nil, data, options) + updateID := id + isUpdate := 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. + if pkID, ok := h.extractPrimaryKeyFromBody(model, data); ok && pkID != "0" { + updateID = pkID + isUpdate = true + } + } + if isUpdate { + h.handleUpdate(ctx, w, updateID, nil, data, options) } else { h.handleCreate(ctx, w, data, options) } @@ -271,6 +281,49 @@ func (h *Handler) Handle(w common.ResponseWriter, r common.Request, params map[s } } +// extractPrimaryKeyFromBody looks for a valid primary key value inside a +// decoded (single-record) POST body, keyed by the model's primary key column +// or its JSON equivalent. It returns the string form of that value and true +// if one was found and is non-empty/non-zero; otherwise ("", false). +func (h *Handler) extractPrimaryKeyFromBody(model interface{}, data interface{}) (string, bool) { + dataMap, ok := data.(map[string]interface{}) + if !ok { + // Batch payloads (slices) aren't eligible for this implicit-update detection. + return "", false + } + + pkCol := reflection.GetPrimaryKeyName(model) + if pkCol == "" { + return "", false + } + + val, exists := dataMap[pkCol] + if !exists { + modelType := reflection.GetPointerElement(reflect.TypeOf(model)) + for jsonKey, col := range reflection.BuildJSONToDBColumnMap(modelType) { + if col == pkCol { + val, exists = dataMap[jsonKey] + break + } + } + } + if !exists || val == nil || reflection.IsEmptyValue(val) { + return "", false + } + + switch v := val.(type) { + case float64: + if v <= 0 { + return "", false + } + return strconv.FormatInt(int64(v), 10), true + case string: + return v, true + default: + return fmt.Sprintf("%v", v), true + } +} + // HandleGet processes GET requests for metadata func (h *Handler) HandleGet(w common.ResponseWriter, r common.Request, params map[string]string) { // Capture panics and return error response