🏡 index : quine-global/quintodrome.git

package apiv1

import (
	"encoding/json"
	"errors"
	"net/http"

	"github.com/navidrome/navidrome/log"
	"github.com/navidrome/navidrome/model"
)

const problemContentType = "application/problem+json"

func writeProblem(w http.ResponseWriter, r *http.Request, err error) {
	status, code := classifyError(err)
	detail := err.Error()
	if status == http.StatusInternalServerError {
		log.Error(r.Context(), "API v1: unexpected error", "path", r.URL.Path, err)
		detail = ""
	}
	writeProblemStatus(w, r, status, code, detail)
}

func classifyError(err error) (int, ProblemCode) {
	switch {
	case errors.Is(err, model.ErrNotFound):
		return http.StatusNotFound, ProblemCodeNotFound
	case errors.Is(err, model.ErrNotAuthorized):
		return http.StatusForbidden, ProblemCodeForbidden
	case errors.Is(err, model.ErrInvalidAuth), errors.Is(err, model.ErrExpired):
		return http.StatusUnauthorized, ProblemCodeUnauthorized
	case errors.Is(err, model.ErrValidation):
		return http.StatusBadRequest, ProblemCodeValidation
	case errors.Is(err, model.ErrNotAvailable):
		return http.StatusServiceUnavailable, ProblemCodeUnavailable
	}
	return http.StatusInternalServerError, ProblemCodeInternal
}

func writeProblemStatus(w http.ResponseWriter, r *http.Request, status int, code ProblemCode, detail string, fieldErrors ...ValidationError) {
	p := Problem{Title: http.StatusText(status), Status: status, Code: code}
	if detail != "" {
		p.Detail = &detail
	}
	if len(fieldErrors) > 0 {
		p.Errors = &fieldErrors
	}
	w.Header().Set("Content-Type", problemContentType)
	w.WriteHeader(status)
	if err := json.NewEncoder(w).Encode(p); err != nil {
		log.Warn(r.Context(), "API v1: could not write problem response", err)
	}
}

func bindingErrorHandler(w http.ResponseWriter, r *http.Request, err error) {
	var fieldErrors []ValidationError
	var required *RequiredParamError
	var invalid *InvalidParamFormatError
	var tooMany *TooManyValuesForParamError
	var unmarshal *UnmarshalingParamError
	switch {
	case errors.As(err, &required):
		fieldErrors = append(fieldErrors, ValidationError{Field: required.ParamName, Message: "is required"})
	case errors.As(err, &invalid):
		fieldErrors = append(fieldErrors, ValidationError{Field: invalid.ParamName, Message: invalid.Err.Error()})
	case errors.As(err, &tooMany):
		fieldErrors = append(fieldErrors, ValidationError{Field: tooMany.ParamName, Message: "expected a single value"})
	case errors.As(err, &unmarshal):
		fieldErrors = append(fieldErrors, ValidationError{Field: unmarshal.ParamName, Message: unmarshal.Err.Error()})
	}
	writeProblemStatus(w, r, http.StatusBadRequest, ProblemCodeValidation, err.Error(), fieldErrors...)
}