package apivalidate import ( "encoding/json" "errors" "net/http" "github.com/ultisuite/ulti-backend/internal/api/apiresponse" "github.com/ultisuite/ulti-backend/internal/api/query" ) // FieldDetail identifies a single invalid request field. type FieldDetail struct { Field string `json:"field"` Message string `json:"message"` } // ValidationError reports invalid request bodies using field-level details. type ValidationError struct { Details []FieldDetail } func (e *ValidationError) Error() string { return "invalid request body" } // NewValidationError builds a validation error from field details. func NewValidationError(details ...FieldDetail) *ValidationError { return &ValidationError{Details: details} } // DecodeJSON reads and decodes a JSON body with a size cap. func DecodeJSON(w http.ResponseWriter, r *http.Request, limit int64, dest any) error { r.Body = http.MaxBytesReader(w, r.Body, limit) if err := json.NewDecoder(r.Body).Decode(dest); err != nil { var maxBytesErr *http.MaxBytesError if errors.As(err, &maxBytesErr) { apiresponse.WriteError(w, r, http.StatusRequestEntityTooLarge, apiresponse.CodePayloadTooLarge, "request body too large", nil) return err } apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, "invalid request body", nil) return err } return nil } // WriteValidationError writes a standardized validation error response. func WriteValidationError(w http.ResponseWriter, r *http.Request, err *ValidationError) { apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, "invalid request body", err.Details) } // WriteQueryError maps query validation errors to API responses. func WriteQueryError(w http.ResponseWriter, r *http.Request, err error) bool { var qerr *query.ValidationError if errors.As(err, &qerr) { apiresponse.WriteError(w, r, http.StatusBadRequest, qerr.Code, qerr.Message, qerr.Details) return true } apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidQueryParam, "invalid query parameters", nil) return true } // WriteNotFound writes a standardized not-found response. func WriteNotFound(w http.ResponseWriter, r *http.Request, message string) { if message == "" { message = "not found" } apiresponse.WriteError(w, r, http.StatusNotFound, apiresponse.CodeNotFound, message, nil) } // WriteInternal writes a standardized internal error response. func WriteInternal(w http.ResponseWriter, r *http.Request) { apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "internal error", nil) }