sp3-test-1770368381/pkg/app/handler.go
jordan d7096586aa
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/manual/woodpecker Pipeline was successful
Initialize project from skeleton template
2026-02-06 08:59:42 +00:00

128 lines
4.1 KiB
Go

package app
import (
"errors"
"net/http"
"git.threesix.ai/jordan/sp3-test-1770368381/pkg/httperror"
"git.threesix.ai/jordan/sp3-test-1770368381/pkg/httpresponse"
"git.threesix.ai/jordan/sp3-test-1770368381/pkg/logging"
)
// HandlerFunc is a handler function that returns an error.
// Use with Wrap() to automatically handle error responses.
//
// Example:
//
// func GetUser(w http.ResponseWriter, r *http.Request) error {
// user, err := svc.Get(ctx, id)
// if err != nil {
// return httperror.NotFoundf("user %s not found", id)
// }
// httpresponse.OK(w, r, user)
// return nil
// }
type HandlerFunc func(w http.ResponseWriter, r *http.Request) error
// Wrap converts a HandlerFunc to http.HandlerFunc, automatically handling
// error responses. HTTPErrors are written with their status code and details;
// other errors are logged and returned as 500 Internal Server Error.
//
// Example:
//
// r.Get("/users/{id}", app.Wrap(handlers.GetUser))
//
// func (h *Handlers) GetUser(w http.ResponseWriter, r *http.Request) error {
// id := chi.URLParam(r, "id")
// user, err := h.userSvc.Get(r.Context(), id)
// if err != nil {
// if errors.Is(err, ErrUserNotFound) {
// return httperror.NotFoundf("user %s not found", id)
// }
// return err // Will be logged and returned as 500
// }
// httpresponse.OK(w, r, user)
// return nil
// }
func Wrap(h HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := h(w, r); err != nil {
writeErrorFromErr(w, r, err, nil)
}
}
}
// WrapWithLogger is like Wrap but accepts a logger for internal error logging.
// Use this when you want to log internal errors that aren't HTTPErrors.
//
// Example:
//
// r.Get("/users/{id}", app.WrapWithLogger(handlers.GetUser, logger))
func WrapWithLogger(h HandlerFunc, logger *logging.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := h(w, r); err != nil {
writeErrorFromErr(w, r, err, logger)
}
}
}
// writeErrorFromErr writes an error response based on the error type.
// HTTPErrors are written with their status code; other errors become 500.
func writeErrorFromErr(w http.ResponseWriter, r *http.Request, err error, logger *logging.Logger) {
var httpErr *httperror.HTTPError
if errors.As(err, &httpErr) {
// Write the HTTPError directly
httpresponse.WriteError(w, r, httpErr.Status, httpErr.Code, httpErr.Message, httpErr.Details)
return
}
// For non-HTTP errors, log and return generic 500
if logger != nil {
logger.Error("internal error",
"error", err,
"method", r.Method,
"path", r.URL.Path,
)
}
httpresponse.InternalError(w, r, "internal error")
}
// -----------------------------------------------------------------------------
// Middleware Helpers
// -----------------------------------------------------------------------------
// MiddlewareFunc is a middleware that returns an error.
// Use with WrapMiddleware() to automatically handle error responses.
type MiddlewareFunc func(next http.Handler) func(w http.ResponseWriter, r *http.Request) error
// WrapMiddleware converts a MiddlewareFunc to a standard http middleware.
//
// Example:
//
// func AuthMiddleware(next http.Handler) func(w http.ResponseWriter, r *http.Request) error {
// return func(w http.ResponseWriter, r *http.Request) error {
// token := r.Header.Get("Authorization")
// if token == "" {
// return httperror.Unauthorized("missing authorization header")
// }
// user, err := validateToken(token)
// if err != nil {
// return httperror.Unauthorized("invalid token")
// }
// ctx := context.WithValue(r.Context(), userKey, user)
// next.ServeHTTP(w, r.WithContext(ctx))
// return nil
// }
// }
//
// r.Use(app.WrapMiddleware(AuthMiddleware))
func WrapMiddleware(m MiddlewareFunc) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := m(next)(w, r); err != nil {
writeErrorFromErr(w, r, err, nil)
}
})
}
}