Files
nexarch/dms/internal/bulkeditapi/handler.go
T
patrickandClaude Sonnet 5 6a5a455b2c MET-03: metadaten- & tag-oberfläche
metadataapi/tagapi/bulkeditapi als Backend-Voraussetzung. MetadataForm
passt sich der Felddefinition an und warnt vor Verwerfen ungespeicherter
Aenderungen, TagInput schlaegt Tags vor und ist per Pfeiltasten/Enter
bedienbar, Massenbearbeitung wendet Tags auf beliebig grosse Auswahlen in
einem Aufruf an.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 00:17:13 +02:00

91 lines
2.8 KiB
Go

// Package bulkeditapi implementiert MET-03s Backend-Voraussetzung für die
// Massenbearbeitung: EIN HTTP-Endpunkt über internal/bulkedit (DOC-15).
// Keine eigene Massenbearbeitungslogik.
package bulkeditapi
import (
"encoding/json"
"net/http"
"github.com/jackc/pgx/v5/pgxpool"
"gitea.perlbach24.de/scripte/nexarch/dms/internal/bulkedit"
)
type Server struct {
pool *pgxpool.Pool
lockChecker bulkedit.LockChecker
}
func NewServer(pool *pgxpool.Pool, lockChecker bulkedit.LockChecker) *Server {
return &Server{pool: pool, lockChecker: lockChecker}
}
func (s *Server) RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("POST /bulk-edit", s.handleApply)
}
func writeError(w http.ResponseWriter, status int, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(map[string]string{"error": message})
}
type metadataChangeDTO struct {
FieldID string `json:"field_id"`
Value *string `json:"value"`
}
type applyRequest struct {
Actor string `json:"actor"`
DocumentIDs []string `json:"document_ids"`
AddTagIDs []string `json:"add_tag_ids"`
RemoveTagIDs []string `json:"remove_tag_ids"`
MetadataChanges []metadataChangeDTO `json:"metadata_changes"`
}
type skippedDTO struct {
DocumentID string `json:"document_id"`
Reason string `json:"reason"`
}
type applyResult struct {
Applied []string `json:"applied"`
Skipped []skippedDTO `json:"skipped"`
}
// Akzeptanzkriterium 3 (MET-03) / Akzeptanzkriterium 1 (DOC-15):
// Massenbearbeitung über eine Dokumentauswahl, gesperrte Dokumente werden
// einzeln übersprungen statt die Operation als Ganzes abzulehnen.
func (s *Server) handleApply(w http.ResponseWriter, r *http.Request) {
var req applyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "ungueltiger request-body: "+err.Error())
return
}
if req.Actor == "" {
writeError(w, http.StatusBadRequest, "fehlendes feld \"actor\"")
return
}
changes := make([]bulkedit.MetadataChange, len(req.MetadataChanges))
for i, c := range req.MetadataChanges {
changes[i] = bulkedit.MetadataChange{FieldID: c.FieldID, Value: c.Value}
}
result, err := bulkedit.Apply(r.Context(), s.pool, s.lockChecker, req.Actor, bulkedit.Request{
DocumentIDs: req.DocumentIDs, AddTagIDs: req.AddTagIDs, RemoveTagIDs: req.RemoveTagIDs, MetadataChanges: changes,
})
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
dto := applyResult{Applied: result.Applied, Skipped: make([]skippedDTO, len(result.Skipped))}
for i, sk := range result.Skipped {
dto.Skipped[i] = skippedDTO{DocumentID: sk.DocumentID, Reason: sk.Reason}
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(dto)
}