init push
This commit is contained in:
93
internal/transport/http/handlers/error_helpers.go
Normal file
93
internal/transport/http/handlers/error_helpers.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"wucher/internal/shared/pkg/apperrorsx"
|
||||
"wucher/internal/transport/http/jsonapi"
|
||||
)
|
||||
|
||||
// bodyParseError maps a request-body JSON parse error to a precise app error. It returns
|
||||
// a non-nil *AppError only for a data-type mismatch (INVALID_DATA_TYPE, pointing at the
|
||||
// offending field); for any other parse error it returns nil so the caller can fall back
|
||||
// to its module-specific invalid-json error.
|
||||
func bodyParseError(err error) *apperrorsx.AppError {
|
||||
var typeErr *json.UnmarshalTypeError
|
||||
if !errors.As(err, &typeErr) {
|
||||
return nil
|
||||
}
|
||||
appErr := apperrorsx.New(apperrorsx.ErrInvalidDataType).WithCause(err)
|
||||
if typeErr.Field != "" {
|
||||
appErr.Source = &jsonapi.ErrorSource{Pointer: "/" + strings.ReplaceAll(typeErr.Field, ".", "/")}
|
||||
appErr.Message = fmt.Sprintf("field %q expects type %s", typeErr.Field, typeErr.Type.String())
|
||||
} else {
|
||||
appErr.Message = fmt.Sprintf("expected type %s", typeErr.Type.String())
|
||||
}
|
||||
return appErr
|
||||
}
|
||||
|
||||
// writeBodyParseError writes a precise INVALID_DATA_TYPE error for a data-type mismatch,
|
||||
// or falls back to the given module invalid-json def for any other parse error.
|
||||
func writeBodyParseError(c *fiber.Ctx, err error, fallback apperrorsx.Def) error {
|
||||
if appErr := bodyParseError(err); appErr != nil {
|
||||
return apperrorsx.Write(c, appErr)
|
||||
}
|
||||
return apperrorsx.Write(c, apperrorsx.New(fallback).WithCause(err))
|
||||
}
|
||||
|
||||
func writeInternalHandlerError(c *fiber.Ctx, operation string, err error) error {
|
||||
return writeInternalHandlerErrorWithStatus(c, fiber.StatusInternalServerError, operation, err)
|
||||
}
|
||||
|
||||
func writeHandlerErrorDef(c *fiber.Ctx, operation string, def apperrorsx.Def, err error) error {
|
||||
logHandlerError(c, operation, def.Status, def.Code, def.ErrorCode, err)
|
||||
appErr := apperrorsx.New(def)
|
||||
if err != nil {
|
||||
appErr.Cause = err
|
||||
}
|
||||
return apperrorsx.Write(c, appErr)
|
||||
}
|
||||
|
||||
func writeHandlerErrorDefWithStatus(c *fiber.Ctx, operation string, status int, def apperrorsx.Def, err error) error {
|
||||
logHandlerError(c, operation, status, def.Code, def.ErrorCode, err)
|
||||
appErr := apperrorsx.New(def)
|
||||
appErr.Status = status
|
||||
if err != nil {
|
||||
appErr.Cause = err
|
||||
}
|
||||
return apperrorsx.Write(c, appErr)
|
||||
}
|
||||
|
||||
func writeInternalHandlerErrorWithStatus(c *fiber.Ctx, status int, operation string, err error) error {
|
||||
logHandlerError(c, operation, status, apperrorsx.CodeInternalServerError, apperrorsx.ErrInternal.ErrorCode, err)
|
||||
appErr := apperrorsx.New(apperrorsx.ErrInternal)
|
||||
appErr.Status = status
|
||||
return apperrorsx.Write(c, appErr)
|
||||
}
|
||||
|
||||
func safeInternalDetail() string {
|
||||
return apperrorsx.ErrInternal.Message
|
||||
}
|
||||
|
||||
func logHandlerError(c *fiber.Ctx, operation string, status int, code, errorCode string, err error) {
|
||||
args := []any{
|
||||
slog.String("request_id", requestIDFromCtx(c)),
|
||||
slog.String("operation", strings.TrimSpace(operation)),
|
||||
slog.Int("status", status),
|
||||
slog.String("code", strings.TrimSpace(code)),
|
||||
slog.String("error_code", strings.TrimSpace(errorCode)),
|
||||
slog.String("path", c.Path()),
|
||||
slog.Any("cause", err),
|
||||
}
|
||||
if status >= fiber.StatusInternalServerError {
|
||||
slog.ErrorContext(c.UserContext(), "handler error", args...)
|
||||
return
|
||||
}
|
||||
slog.WarnContext(c.UserContext(), "handler error", args...)
|
||||
}
|
||||
Reference in New Issue
Block a user