diff --git a/exerr/builder.go b/exerr/builder.go index 6404599..7e2d28e 100644 --- a/exerr/builder.go +++ b/exerr/builder.go @@ -72,7 +72,7 @@ type Builder struct { } func Get(err error) *Builder { - return &Builder{errorData: fromError(err)} + return &Builder{errorData: FromError(err)} } func New(t ErrorType, msg string) *Builder { @@ -80,7 +80,12 @@ func New(t ErrorType, msg string) *Builder { } func Wrap(err error, msg string) *Builder { - return &Builder{errorData: wrapExErr(fromError(err), msg, CatWrap, 1)} + if !pkgconfig.RecursiveErrors { + v := FromError(err) + v.Message = msg + return &Builder{errorData: v} + } + return &Builder{errorData: wrapExErr(FromError(err), msg, CatWrap, 1)} } // ---------------------------------------------------------------------------- @@ -372,7 +377,7 @@ func (b *Builder) Output(ctx context.Context, g *gin.Context) { b.GinReq(ctx, g, g.Request) } - b.errorData.Output(ctx, g) + b.errorData.Output(g) if b.errorData.Severity == SevErr || b.errorData.Severity == SevFatal { b.errorData.Log(stackSkipLogger.Error()) diff --git a/exerr/constructor.go b/exerr/constructor.go index 4d5c35c..2b66dab 100644 --- a/exerr/constructor.go +++ b/exerr/constructor.go @@ -11,7 +11,7 @@ import ( var reflectTypeStr = reflect.TypeOf("") -func fromError(err error) *ExErr { +func FromError(err error) *ExErr { if verr, ok := err.(*ExErr); ok { // A simple ExErr return verr diff --git a/exerr/data.go b/exerr/data.go index 8ad8b59..1761a27 100644 --- a/exerr/data.go +++ b/exerr/data.go @@ -2,7 +2,6 @@ package exerr import ( "gogs.mikescher.com/BlackForestBytes/goext/langext" - "net/http" ) type ErrorCategory struct{ Category string } @@ -35,9 +34,20 @@ type ErrorType struct { } var ( - TypeInternal = ErrorType{"Internal", langext.Ptr(http.StatusInternalServerError)} - TypePanic = ErrorType{"Panic", langext.Ptr(http.StatusInternalServerError)} - TypeWrap = ErrorType{"Wrap", nil} + TypeInternal = ErrorType{"INTERNAL_ERROR", langext.Ptr(500)} + TypePanic = ErrorType{"PANIC", langext.Ptr(500)} + TypeNotImplemented = ErrorType{"NOT_IMPLEMENTED", langext.Ptr(500)} + + TypeWrap = ErrorType{"Wrap", nil} + + TypeBindFailURI = ErrorType{"BINDFAIL_URI", langext.Ptr(400)} + TypeBindFailQuery = ErrorType{"BINDFAIL_QUERY", langext.Ptr(400)} + TypeBindFailJSON = ErrorType{"BINDFAIL_JSON", langext.Ptr(400)} + TypeBindFailFormData = ErrorType{"BINDFAIL_FORMDATA", langext.Ptr(400)} + + TypeUnauthorized = ErrorType{"UNAUTHORIZED", langext.Ptr(401)} + TypeAuthFailed = ErrorType{"AUTH_FAILED", langext.Ptr(401)} + // other values come from pkgconfig ) diff --git a/exerr/errinit.go b/exerr/errinit.go index e55696f..382dd81 100644 --- a/exerr/errinit.go +++ b/exerr/errinit.go @@ -6,29 +6,32 @@ import ( ) type ErrorPackageConfig struct { - ZeroLogErrTraces bool // autom print zerolog logs on .Build() (for SevErr and SevFatal) - ZeroLogAllTraces bool // autom print zerolog logs on .Build() (for all Severities) - RecursiveErrors bool // errors contains their Origin-Error - ExtendedGinOutput bool // Log extended data (trace, meta, ...) to gin in err.Output() - Types []ErrorType // all available error-types + ZeroLogErrTraces bool // autom print zerolog logs on .Build() (for SevErr and SevFatal) + ZeroLogAllTraces bool // autom print zerolog logs on .Build() (for all Severities) + RecursiveErrors bool // errors contains their Origin-Error + ExtendedGinOutput bool // Log extended data (trace, meta, ...) to gin in err.Output() + ExtendGinOutput func(json map[string]any) // (Optionally) extend the gin output with more fields + ExtendGinDataOutput func(json map[string]any) // (Optionally) extend the gin `__data` output with more fields } type ErrorPackageConfigInit struct { - ZeroLogErrTraces bool - ZeroLogAllTraces bool - RecursiveErrors bool - ExtendedGinOutput bool - InitTypes func(_ func(key string, defaultStatusCode *int) ErrorType) + ZeroLogErrTraces bool + ZeroLogAllTraces bool + RecursiveErrors bool + ExtendedGinOutput bool + ExtendGinOutput *func(json map[string]any) + ExtendGinDataOutput *func(json map[string]any) } var initialized = false var pkgconfig = ErrorPackageConfig{ - ZeroLogErrTraces: true, - ZeroLogAllTraces: false, - RecursiveErrors: true, - ExtendedGinOutput: false, - Types: []ErrorType{TypeInternal, TypePanic, TypeWrap}, + ZeroLogErrTraces: true, + ZeroLogAllTraces: false, + RecursiveErrors: true, + ExtendedGinOutput: false, + ExtendGinOutput: func(json map[string]any) {}, + ExtendGinDataOutput: func(json map[string]any) {}, } // Init initializes the exerr packages @@ -39,24 +42,13 @@ func Init(cfg ErrorPackageConfigInit) { panic("Cannot re-init error package") } - types := pkgconfig.Types - - fnAddType := func(key string, defaultStatusCode *int) ErrorType { - et := ErrorType{key, defaultStatusCode} - types = append(types, et) - return et - } - - if cfg.InitTypes != nil { - cfg.InitTypes(fnAddType) - } - pkgconfig = ErrorPackageConfig{ - ZeroLogErrTraces: cfg.ZeroLogErrTraces, - ZeroLogAllTraces: cfg.ZeroLogAllTraces, - RecursiveErrors: cfg.RecursiveErrors, - ExtendedGinOutput: cfg.ExtendedGinOutput, - Types: types, + ZeroLogErrTraces: cfg.ZeroLogErrTraces, + ZeroLogAllTraces: cfg.ZeroLogAllTraces, + RecursiveErrors: cfg.RecursiveErrors, + ExtendedGinOutput: cfg.ExtendedGinOutput, + ExtendGinOutput: langext.Coalesce(cfg.ExtendGinOutput, func(json map[string]any) {}), + ExtendGinDataOutput: langext.Coalesce(cfg.ExtendGinDataOutput, func(json map[string]any) {}), } initialized = true diff --git a/exerr/gin.go b/exerr/gin.go index b78882e..a054838 100644 --- a/exerr/gin.go +++ b/exerr/gin.go @@ -1,8 +1,8 @@ package exerr import ( - "context" "github.com/gin-gonic/gin" + json "gogs.mikescher.com/BlackForestBytes/goext/gojson" "net/http" "time" ) @@ -34,14 +34,19 @@ func (ee *ExErr) toJson() gin.H { if ee.Timestamp != (time.Time{}) { json["time"] = ee.Timestamp.Format(time.RFC3339) } + if ee.WrappedErrType != "" { + json["wrappedErrType"] = ee.WrappedErrType + } if ee.OriginalError != nil { json["original"] = ee.OriginalError.toJson() } + pkgconfig.ExtendGinDataOutput(json) + return json } -func (ee *ExErr) Output(ctx context.Context, g *gin.Context) { +func (ee *ExErr) Output(g *gin.Context) { var statuscode = http.StatusInternalServerError var baseCat = ee.RecursiveCategory() @@ -62,20 +67,18 @@ func (ee *ExErr) Output(ctx context.Context, g *gin.Context) { warnOnPkgConfigNotInitialized() - if pkgconfig.ExtendedGinOutput { - g.JSON(statuscode, gin.H{ - "errorid": ee.UniqueID, - "error": ee.RecursiveMessage(), - "errorcategory": ee.RecursiveCategory(), - "errortype": ee.RecursiveType(), - "errodata": ee.toJson(), - }) - } else { - g.JSON(statuscode, gin.H{ - "errorid": ee.UniqueID, - "error": ee.RecursiveMessage(), - "errorcategory": ee.RecursiveCategory(), - "errortype": ee.RecursiveType(), - }) + ginOutput := gin.H{ + "errorid": ee.UniqueID, + "message": ee.RecursiveMessage(), + "errorcode": ee.RecursiveType(), + "category": ee.RecursiveCategory(), } + + if pkgconfig.ExtendedGinOutput { + ginOutput["__data"] = ee.toJson() + } + + pkgconfig.ExtendGinOutput(ginOutput) + + g.Render(statuscode, json.GoJsonRender{Data: ginOutput, NilSafeSlices: true, NilSafeMaps: true}) } diff --git a/exerr/helper.go b/exerr/helper.go index 4291159..8392ceb 100644 --- a/exerr/helper.go +++ b/exerr/helper.go @@ -8,7 +8,7 @@ func IsType(err error, errType ErrorType) bool { return false } - bmerr := fromError(err) + bmerr := FromError(err) for bmerr != nil { if bmerr.Type == errType { return true @@ -28,7 +28,7 @@ func IsFrom(e error, original error) bool { return true } - bmerr := fromError(e) + bmerr := FromError(e) for bmerr == nil { return false } @@ -48,7 +48,7 @@ func HasSourceMessage(e error, msg string) bool { return false } - bmerr := fromError(e) + bmerr := FromError(e) for bmerr == nil { return false } @@ -71,7 +71,7 @@ func MessageMatch(e error, matcher func(string) bool) bool { return true } - bmerr := fromError(e) + bmerr := FromError(e) for bmerr == nil { return false } diff --git a/ginext/apiError.go b/ginext/apiError.go deleted file mode 100644 index f1fb4cd..0000000 --- a/ginext/apiError.go +++ /dev/null @@ -1,16 +0,0 @@ -package ginext - -type apiError struct { - ErrorCode string `json:"errorcode"` - Message string `json:"message"` - FAPIErrorMessage *string `json:"fapiMessage,omitempty"` -} - -type extAPIError struct { - ErrorCode string `json:"errorcode"` - Message string `json:"message"` - FAPIErrorMessage *string `json:"fapiMessage,omitempty"` - - RawError *string `json:"__error"` - Trace []string `json:"__trace"` -} diff --git a/ginext/commonApiErr/enums.go b/ginext/commonApiErr/enums.go deleted file mode 100644 index 3dd3a42..0000000 --- a/ginext/commonApiErr/enums.go +++ /dev/null @@ -1,21 +0,0 @@ -package commonApiErr - -type APIErrorCode struct { - HTTPStatusCode int - Key string -} - -//goland:noinspection GoSnakeCaseUsage -var ( - NotImplemented = APIErrorCode{500, "NOT_IMPLEMENTED"} - InternalError = APIErrorCode{500, "INTERNAL_ERROR"} - Panic = APIErrorCode{500, "PANIC"} - - BindFailURI = APIErrorCode{400, "BINDFAIL_URI"} - BindFailQuery = APIErrorCode{400, "BINDFAIL_QUERY"} - BindFailJSON = APIErrorCode{400, "BINDFAIL_JSON"} - BindFailFormData = APIErrorCode{400, "BINDFAIL_FORMDATA"} - - Unauthorized = APIErrorCode{401, "UNAUTHORIZED"} - AuthFailed = APIErrorCode{401, "AUTH_FAILED"} -) diff --git a/ginext/engine.go b/ginext/engine.go index 5104a8e..771905f 100644 --- a/ginext/engine.go +++ b/ginext/engine.go @@ -10,13 +10,12 @@ type GinWrapper struct { engine *gin.Engine SuppressGinLogs bool - allowCors bool - ginDebug bool - returnRawErrors bool - requestTimeout time.Duration + allowCors bool + ginDebug bool + requestTimeout time.Duration } -func NewEngine(allowCors bool, ginDebug bool, returnRawErrors bool, timeout time.Duration) *GinWrapper { +func NewEngine(allowCors bool, ginDebug bool, timeout time.Duration) *GinWrapper { engine := gin.New() wrapper := &GinWrapper{ @@ -24,7 +23,6 @@ func NewEngine(allowCors bool, ginDebug bool, returnRawErrors bool, timeout time SuppressGinLogs: false, allowCors: allowCors, ginDebug: ginDebug, - returnRawErrors: returnRawErrors, requestTimeout: timeout, } diff --git a/ginext/funcWrapper.go b/ginext/funcWrapper.go index b1d9ac0..516cc5a 100644 --- a/ginext/funcWrapper.go +++ b/ginext/funcWrapper.go @@ -1,10 +1,9 @@ package ginext import ( - "errors" "fmt" "github.com/gin-gonic/gin" - "github.com/rs/zerolog/log" + "gogs.mikescher.com/BlackForestBytes/goext/exerr" ) type WHandlerFunc func(PreContext) HTTPResponse @@ -13,18 +12,20 @@ func Wrap(w *GinWrapper, fn WHandlerFunc) gin.HandlerFunc { return func(g *gin.Context) { - g.Set("__returnRawErrors", w.returnRawErrors) - reqctx := g.Request.Context() wrap, stackTrace, panicObj := callPanicSafe(fn, PreContext{wrapper: w, ginCtx: g}) if panicObj != nil { + fmt.Printf("\n======== ======== STACKTRACE ======== ========\n%s\n======== ======== ======== ========\n\n", stackTrace) - log.Error(). - Interface("panicObj", panicObj). + + err := exerr. + New(exerr.TypePanic, "Panic occured (in gin handler)"). + Any("panicObj", panicObj). Str("trace", stackTrace). - Msg("Panic occured (in gin handler)") - wrap = APIError(g, commonApiErr.Panic, "A panic occured in the HTTP handler", errors.New(fmt.Sprintf("%+v", panicObj))) + Build() + + wrap = APIError(g, err) } if g.Writer.Written() { diff --git a/ginext/response.go b/ginext/response.go index e153b3a..9c318c3 100644 --- a/ginext/response.go +++ b/ginext/response.go @@ -3,11 +3,8 @@ package ginext import ( "fmt" "github.com/gin-gonic/gin" - "github.com/rs/zerolog/log" + "gogs.mikescher.com/BlackForestBytes/goext/exerr" json "gogs.mikescher.com/BlackForestBytes/goext/gojson" - "gogs.mikescher.com/BlackForestBytes/goext/langext" - "runtime/debug" - "strings" ) type HTTPResponse interface { @@ -74,6 +71,14 @@ func (j redirectHTTPResponse) Write(g *gin.Context) { g.Redirect(j.statusCode, j.url) } +type jsonAPIErrResponse struct { + err *exerr.ExErr +} + +func (j jsonAPIErrResponse) Write(g *gin.Context) { + j.err.Output(g) +} + func Status(sc int) HTTPResponse { return &emptyHTTPResponse{statusCode: sc} } @@ -102,52 +107,12 @@ func Redirect(sc int, newURL string) HTTPResponse { return &redirectHTTPResponse{statusCode: sc, url: newURL} } -func APIError(g *gin.Context, errcode commonApiErr.APIErrorCode, msg string, e error) HTTPResponse { - return createApiError(g, errcode, msg, e) +func APIError(g *gin.Context, e error) HTTPResponse { + return &jsonAPIErrResponse{ + err: exerr.FromError(e), + } } func NotImplemented(g *gin.Context) HTTPResponse { - return createApiError(g, commonApiErr.NotImplemented, "", nil) -} - -func createApiError(g *gin.Context, errcode commonApiErr.APIErrorCode, msg string, e error) HTTPResponse { - reqUri := "" - if g != nil && g.Request != nil { - reqUri = g.Request.Method + " :: " + g.Request.RequestURI - } - - log.Error(). - Str("errorcode.key", errcode.Key). - Int("errcode.status", errcode.HTTPStatusCode). - Str("uri", reqUri). - AnErr("err", e). - Stack(). - Msg(msg) - - var fapiMessage *string = nil - if v, ok := e.(interface{ FAPIMessage() string }); ok { - fapiMessage = langext.Ptr(v.FAPIMessage()) - } - - if g.GetBool("__returnRawErrors") { - return &jsonHTTPResponse{ - statusCode: errcode.HTTPStatusCode, - data: extAPIError{ - ErrorCode: errcode.Key, - Message: msg, - RawError: langext.Ptr(langext.Conditional(e == nil, "", fmt.Sprintf("%+v", e))), - FAPIErrorMessage: fapiMessage, - Trace: strings.Split(string(debug.Stack()), "\n"), - }, - } - } else { - return &jsonHTTPResponse{ - statusCode: errcode.HTTPStatusCode, - data: apiError{ - ErrorCode: errcode.Key, - Message: msg, - FAPIErrorMessage: fapiMessage, - }, - } - } + return APIError(g, exerr.New(exerr.TypeNotImplemented, "").Build()) } diff --git a/goextVersion.go b/goextVersion.go index 088f863..fd83175 100644 --- a/goextVersion.go +++ b/goextVersion.go @@ -1,5 +1,5 @@ package goext -const GoextVersion = "0.0.188" +const GoextVersion = "0.0.189" -const GoextVersionTimestamp = "2023-07-24T10:42:39+0200" +const GoextVersionTimestamp = "2023-07-24T11:11:15+0200"