Files

116 lines
3.5 KiB
Go

// Package caseoverviewapi ist die HTTP-Bindung der workflow-eigenen
// Akten-Uebersicht (VOR-05). Nutzeridentitaet kommt uebergangsweise aus
// X-User-Id, wie bereits bei caseapi (VOR-01) und backchannelapi (VOR-04)
// etabliert.
package caseoverviewapi
import (
"encoding/json"
"errors"
"net/http"
"time"
"gitea.perlbach24.de/scripte/nexarch/workflow/internal/caseobj"
"gitea.perlbach24.de/scripte/nexarch/workflow/internal/caseoverview"
"gitea.perlbach24.de/scripte/nexarch/workflow/internal/reminder"
)
type Server struct {
svc *caseoverview.Service
now func() time.Time
}
func NewServer(svc *caseoverview.Service) *Server {
return &Server{svc: svc, now: time.Now}
}
// RegisterRoutes bindet die Uebersichts-Routen. Alle Routen verlangen
// X-User-Id, wie die uebrige workflow-eigene API.
func (s *Server) RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /cases", s.requireUser(s.handleList))
mux.HandleFunc("GET /cases/{id}/overview", s.requireUser(s.handleDetail))
mux.HandleFunc("GET /reminders/kanban", s.requireUser(s.handleKanban))
mux.HandleFunc("POST /reminders/{id}/move", s.requireUser(s.handleMove))
}
func (s *Server) requireUser(next func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-User-Id") == "" {
writeError(w, http.StatusUnauthorized, "X-User-Id fehlt")
return
}
next(w, r)
}
}
func (s *Server) handleList(w http.ResponseWriter, r *http.Request) {
status := r.URL.Query().Get("status")
responsible := r.URL.Query().Get("responsible")
cases, err := s.svc.List(r.Context(), status, responsible)
if err != nil {
writeError(w, http.StatusInternalServerError, "akten konnten nicht gelesen werden")
return
}
if cases == nil {
cases = []caseobj.Case{}
}
writeJSON(w, http.StatusOK, cases)
}
func (s *Server) handleDetail(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
detail, err := s.svc.Detail(r.Context(), id)
switch {
case err == nil:
writeJSON(w, http.StatusOK, detail)
case errors.Is(err, caseobj.ErrNotFound):
writeError(w, http.StatusNotFound, "akte nicht gefunden")
default:
writeError(w, http.StatusInternalServerError, "akte konnte nicht gelesen werden")
}
}
func (s *Server) handleKanban(w http.ResponseWriter, r *http.Request) {
items, err := s.svc.Kanban(r.Context(), s.now())
if err != nil {
writeError(w, http.StatusInternalServerError, "wiedervorlagen konnten nicht gelesen werden")
return
}
if items == nil {
items = []caseoverview.KanbanItem{}
}
writeJSON(w, http.StatusOK, items)
}
type moveRequest struct {
Target string `json:"target"`
}
func (s *Server) handleMove(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
var req moveRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "ungueltiger anfrage-koerper")
return
}
err := s.svc.Move(r.Context(), id, caseoverview.KanbanColumn(req.Target))
switch {
case err == nil:
w.WriteHeader(http.StatusNoContent)
case errors.Is(err, reminder.ErrNotFound):
writeError(w, http.StatusNotFound, "wiedervorlage nicht gefunden")
default:
writeError(w, http.StatusInternalServerError, "wiedervorlage konnte nicht verschoben werden")
}
}
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(v)
}
func writeError(w http.ResponseWriter, status int, message string) {
writeJSON(w, status, map[string]string{"error": message})
}