ultisuite-backend/internal/api/apivalidate/apivalidate.go

75 lines
2.6 KiB
Go

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)
}