package server import ( "encoding/json" "errors" "net/http" "mgit.msbls.de/m/mcables/internal/db" ) type cableEndpointBody struct { PortID *int64 `json:"port_id,omitempty"` DeviceID *int64 `json:"device_id,omitempty"` IOID *int64 `json:"io_id,omitempty"` } type cableCreate struct { TypeID int64 `json:"type_id"` Label string `json:"label,omitempty"` From cableEndpointBody `json:"from"` To cableEndpointBody `json:"to"` Auto bool `json:"auto,omitempty"` } type cablePatch struct { TypeID *int64 `json:"type_id,omitempty"` Label *string `json:"label,omitempty"` From *cableEndpointBody `json:"from,omitempty"` To *cableEndpointBody `json:"to,omitempty"` Auto *bool `json:"auto,omitempty"` // Promote=true asks the server to set auto=false when an auto cable // is being PATCHed (slice 6 §5b.3 — explicit promote-to-manual). Promote bool `json:"promote,omitempty"` } func toCableEndpoint(b cableEndpointBody) db.CableEndpoint { return db.CableEndpoint{PortID: b.PortID, DeviceID: b.DeviceID, IOID: b.IOID} } func (h *handlers) listCables(w http.ResponseWriter, r *http.Request) { pid, ok := parseInt64Path(r, "pid") if !ok { writeError(w, db.ErrInvalidInput, "pid must be a positive integer") return } cs, err := h.store.ListCables(pid) if err != nil { writeError(w, err, nil) return } writeJSON(w, http.StatusOK, cs) } func (h *handlers) createCable(w http.ResponseWriter, r *http.Request) { pid, ok := parseInt64Path(r, "pid") if !ok { writeError(w, db.ErrInvalidInput, "pid must be a positive integer") return } var body cableCreate if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, errors.Join(db.ErrInvalidInput, err), nil) return } c, err := h.store.CreateCable(pid, db.CableCreate{ TypeID: body.TypeID, Label: body.Label, From: toCableEndpoint(body.From), To: toCableEndpoint(body.To), Auto: body.Auto, }) if err != nil { writeError(w, err, nil) return } writeJSON(w, http.StatusCreated, c) } func (h *handlers) patchCable(w http.ResponseWriter, r *http.Request) { pid, ok := parseInt64Path(r, "pid") if !ok { writeError(w, db.ErrInvalidInput, "pid must be a positive integer") return } id, ok := parseInt64Path(r, "id") if !ok { writeError(w, db.ErrInvalidInput, "id must be a positive integer") return } var body cablePatch if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, errors.Join(db.ErrInvalidInput, err), nil) return } u := db.CableUpdate{ TypeID: body.TypeID, Label: body.Label, Auto: body.Auto, } if body.From != nil { ep := toCableEndpoint(*body.From) u.From = &ep } if body.To != nil { ep := toCableEndpoint(*body.To) u.To = &ep } // Promote semantics: explicit promote=true OR (PATCH touched // type/from/to AND the current cable is auto) → set auto=false. if body.Promote { f := false u.Auto = &f } c, err := h.store.UpdateCable(pid, id, u) if err != nil { writeError(w, err, nil) return } writeJSON(w, http.StatusOK, c) } func (h *handlers) deleteCable(w http.ResponseWriter, r *http.Request) { pid, ok := parseInt64Path(r, "pid") if !ok { writeError(w, db.ErrInvalidInput, "pid must be a positive integer") return } id, ok := parseInt64Path(r, "id") if !ok { writeError(w, db.ErrInvalidInput, "id must be a positive integer") return } if err := h.store.DeleteCable(pid, id); err != nil { writeError(w, err, nil) return } w.WriteHeader(http.StatusNoContent) } // ----------------------------------------------------------------- bundles type bundleCreate struct { Name string `json:"name"` CableIDs []int64 `json:"cable_ids"` } type bundlePatch struct { Name *string `json:"name,omitempty"` CableIDs *[]int64 `json:"cable_ids,omitempty"` } func (h *handlers) listBundles(w http.ResponseWriter, r *http.Request) { pid, ok := parseInt64Path(r, "pid") if !ok { writeError(w, db.ErrInvalidInput, "pid must be a positive integer") return } bs, err := h.store.ListBundles(pid) if err != nil { writeError(w, err, nil) return } writeJSON(w, http.StatusOK, bs) } func (h *handlers) createBundle(w http.ResponseWriter, r *http.Request) { pid, ok := parseInt64Path(r, "pid") if !ok { writeError(w, db.ErrInvalidInput, "pid must be a positive integer") return } var body bundleCreate if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, errors.Join(db.ErrInvalidInput, err), nil) return } b, err := h.store.CreateBundle(pid, db.BundleCreate{ Name: body.Name, CableIDs: body.CableIDs, Auto: false, }) if err != nil { writeError(w, err, nil) return } writeJSON(w, http.StatusCreated, b) } func (h *handlers) patchBundle(w http.ResponseWriter, r *http.Request) { pid, ok := parseInt64Path(r, "pid") if !ok { writeError(w, db.ErrInvalidInput, "pid must be a positive integer") return } id, ok := parseInt64Path(r, "id") if !ok { writeError(w, db.ErrInvalidInput, "id must be a positive integer") return } var body bundlePatch if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, errors.Join(db.ErrInvalidInput, err), nil) return } b, err := h.store.UpdateBundle(pid, id, db.BundleUpdate{ Name: body.Name, CableIDs: body.CableIDs, }) if err != nil { writeError(w, err, nil) return } writeJSON(w, http.StatusOK, b) } func (h *handlers) deleteBundle(w http.ResponseWriter, r *http.Request) { pid, ok := parseInt64Path(r, "pid") if !ok { writeError(w, db.ErrInvalidInput, "pid must be a positive integer") return } id, ok := parseInt64Path(r, "id") if !ok { writeError(w, db.ErrInvalidInput, "id must be a positive integer") return } if err := h.store.DeleteBundle(pid, id); err != nil { writeError(w, err, nil) return } w.WriteHeader(http.StatusNoContent) }