package awards import ( "encoding/json" "errors" "net/http" "strconv" "strings" "github.com/go-chi/chi/v5" "github.com/missionworkforce/missionmatch/internal/platform/security" ) // Handler serves the cached awards read API. type Handler struct{ svc *Service } func NewHandler(svc *Service) *Handler { return &Handler{svc: svc} } // Routes mounts the awards endpoints under the /api group. // Routes mounts the cached federal award feed. Gated for the reason set out on the opportunity routes: // an endpoint should name its own authority rather than inherit it from where it is mounted. func (h *Handler) Routes(r chi.Router) { read := security.RequireCapability(security.CapCandidateRead) r.With(read).Get("/awards", h.incumbents) r.With(read).Get("/awards/galapagos", h.galapagos) } // incumbents returns the largest comparable awards for one or more NAICS codes. func (h *Handler) incumbents(w http.ResponseWriter, r *http.Request) { naics := multiVals(r, "naics") items, err := h.svc.Incumbents(r.Context(), naics, intParam(r, "limit", 0)) if err != nil { writeErr(w, err) return } writeJSON(w, http.StatusOK, items) } // galapagos returns Galapagos own past performance. func (h *Handler) galapagos(w http.ResponseWriter, r *http.Request) { items, err := h.svc.Galapagos(r.Context(), intParam(r, "limit", 0)) if err != nil { writeErr(w, err) return } writeJSON(w, http.StatusOK, items) } func multiVals(r *http.Request, key string) []string { var out []string for _, v := range r.URL.Query()[key] { for _, p := range strings.Split(v, ",") { if p = strings.TrimSpace(p); p != "" { out = append(out, p) } } } return out } func intParam(r *http.Request, key string, def int) int { if v := strings.TrimSpace(r.URL.Query().Get(key)); v != "" { if n, err := strconv.Atoi(v); err == nil { return n } } return def } func writeErr(w http.ResponseWriter, err error) { if errors.Is(err, ErrNoDatabase) { writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "database not configured"}) return } writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal error"}) } func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(v) }