feat(09-05): make bulk delete atomic, ordered, and retry-safe

- Reject an empty selection and dedupe ids before locking rows in pk order
- Return deleted 0 when every requested row is already gone, without hooks
- Roll back mixed, hook, and cancelled batches so no partial delete commits
This commit is contained in:
Jakub Zych
2026-09-24 19:45:15 +02:00
parent 247c3235f6
commit 50754808f6
2 changed files with 175 additions and 3 deletions

View File

@@ -2,6 +2,7 @@ package cabana
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -128,6 +129,8 @@ func (s *service) mount(r pact.Router) {
constrainController(g)
g.Post("/{vendor}/{plugin}/{controller}", s.create)
constrainController(g)
g.Post("/{vendor}/{plugin}/{controller}/bulk-delete", s.bulkDelete)
constrainController(g)
g.Get("/{vendor}/{plugin}/{controller}/{id}", s.show)
constrainController(g)
g.Put("/{vendor}/{plugin}/{controller}/{id}", s.update)
@@ -242,7 +245,34 @@ func (s *service) update(w http.ResponseWriter, r *http.Request) {
}
func (s *service) bulkDelete(w http.ResponseWriter, r *http.Request) {
WriteError(w, http.StatusInternalServerError, "error", msgServerError)
s.protect(w, r, func(cc *CompiledController) {
in, err := decodeBulk(r)
if err != nil {
writeCRUDError(w, err)
return
}
svc, err := s.crud()
if err != nil {
WriteError(w, http.StatusInternalServerError, "error", msgServerError)
return
}
result, err := svc.BulkDelete(r.Context(), cc, in)
if err != nil {
writeCRUDError(w, err)
return
}
WriteData(w, http.StatusOK, result, nil)
})
}
func decodeBulk(r *http.Request) (BulkDeleteInput, error) {
dec := json.NewDecoder(r.Body)
dec.UseNumber()
var in BulkDeleteInput
if err := dec.Decode(&in); err != nil {
return BulkDeleteInput{}, &ValidationError{Details: map[string]any{"body": []string{"The request body is invalid."}}}
}
return in, nil
}
func (s *service) deleteRecord(w http.ResponseWriter, r *http.Request) {