package sourcing import ( "encoding/json" "errors" "net/http" "github.com/go-chi/chi/v5" "github.com/google/uuid" "github.com/missionworkforce/missionmatch/internal/platform/security" "github.com/missionworkforce/missionmatch/internal/platform/tenant" ) // Handler serves the external sourcing read API and the delete path. type Handler struct{ svc *Service } func NewHandler(svc *Service) *Handler { return &Handler{svc: svc} } // Routes mounts the sourcing endpoints under the tenant scoped /api group. The contact fields and the // compliance log require sourcing.read, and erasure of a sourced candidate requires the destructive // privacy.erase capability, so these privacy sensitive paths are held to a privacy capable role. func (h *Handler) Routes(r chi.Router) { r.With(security.RequireCapability(security.CapSourcingRead)).Get("/sourcing/candidates", h.list) r.With(security.RequireCapability(security.CapSourcingRead)).Get("/sourcing/log", h.log) r.With(security.RequireCapability(security.CapPrivacyErase)).Delete("/sourcing/candidates/{id}", h.del) } func (h *Handler) list(w http.ResponseWriter, r *http.Request) { items, err := h.svc.ListExternal(r.Context()) writeResult(w, items, err) } func (h *Handler) log(w http.ResponseWriter, r *http.Request) { items, err := h.svc.Log(r.Context()) writeResult(w, items, err) } func (h *Handler) del(w http.ResponseWriter, r *http.Request) { id, err := uuid.Parse(chi.URLParam(r, "id")) if err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid id"}) return } err = h.svc.Delete(r.Context(), id) switch { case err == nil: writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"}) case errors.Is(err, ErrNotFound): writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"}) case errors.Is(err, tenant.ErrNoTenant): writeJSON(w, http.StatusForbidden, map[string]string{"error": "no validated tenant"}) case errors.Is(err, ErrNoDatabase): writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "database not configured"}) default: writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal error"}) } } func writeResult(w http.ResponseWriter, items any, err error) { if err != nil { switch { case errors.Is(err, tenant.ErrNoTenant): writeJSON(w, http.StatusForbidden, map[string]string{"error": "no validated tenant"}) case errors.Is(err, ErrNoDatabase): writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "database not configured"}) default: writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal error"}) } return } writeJSON(w, http.StatusOK, items) } func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(v) }