github.com/hellofresh/janus@v0.0.0-20230925145208-ce8de8183c67/pkg/errors/error.go (about)

     1  /*
     2  Package errors provides a nice way of handling http errors
     3  
     4  Examples:
     5  To create an error:
     6  	err := errors.New(http.StatusBadRequest, "Something went wrong")
     7  */
     8  package errors
     9  
    10  import (
    11  	"net/http"
    12  	"runtime/debug"
    13  
    14  	log "github.com/sirupsen/logrus"
    15  
    16  	"github.com/hellofresh/janus/pkg/observability"
    17  	"github.com/hellofresh/janus/pkg/render"
    18  )
    19  
    20  var (
    21  	// ErrRouteNotFound happens when no route was matched
    22  	ErrRouteNotFound = New(http.StatusNotFound, "no API found with those values")
    23  	// ErrInvalidID represents an invalid identifier
    24  	ErrInvalidID = New(http.StatusBadRequest, "please provide a valid ID")
    25  )
    26  
    27  // Error is a custom error that implements the `error` interface.
    28  // When creating errors you should provide a code (could be and http status code)
    29  // and a message, this way we can handle the errors in a centralized place.
    30  type Error struct {
    31  	Code    int    `json:"-"`
    32  	Message string `json:"error"`
    33  }
    34  
    35  // New creates a new instance of Error
    36  func New(code int, message string) *Error {
    37  	return &Error{code, message}
    38  }
    39  
    40  func (e *Error) Error() string {
    41  	return e.Message
    42  }
    43  
    44  // NotFound handler is called when no route is matched
    45  func NotFound(w http.ResponseWriter, r *http.Request) {
    46  	Handler(w, r, ErrRouteNotFound)
    47  }
    48  
    49  // RecoveryHandler handler is used when a panic happens
    50  func RecoveryHandler(w http.ResponseWriter, r *http.Request, err interface{}) {
    51  	Handler(w, r, err)
    52  }
    53  
    54  // Handler marshals an error to JSON, automatically escaping HTML and setting the
    55  // Content-Type as application/json.
    56  func Handler(w http.ResponseWriter, r *http.Request, err interface{}) {
    57  	entry := log.WithField("request-id", observability.RequestIDFromContext(r.Context()))
    58  
    59  	switch internalErr := err.(type) {
    60  	case *Error:
    61  		entry.WithFields(log.Fields{
    62  			"code":       internalErr.Code,
    63  			log.ErrorKey: internalErr.Error(),
    64  		}).Info("Internal error handled")
    65  		render.JSON(w, internalErr.Code, internalErr)
    66  	case error:
    67  		entry.WithError(internalErr).WithField("stack", string(debug.Stack())).Error("Internal server error handled")
    68  		render.JSON(w, http.StatusInternalServerError, internalErr.Error())
    69  	default:
    70  		entry.WithFields(log.Fields{
    71  			log.ErrorKey: err,
    72  			"stack":      string(debug.Stack()),
    73  		}).Error("Internal server error handled")
    74  		render.JSON(w, http.StatusInternalServerError, err)
    75  	}
    76  }