Compare commits
	
		
			8 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 99cd92729e | |||
| ac416f7b69 | |||
| e10140e143 | |||
| e165f0f62f | |||
| 655d4daad9 | |||
| 87a004e577 | |||
| 376c6cab50 | |||
| 4a3f25baa0 | 
| @@ -7,6 +7,9 @@ type SyncSet[TData comparable] struct { | |||||||
| 	lock sync.Mutex | 	lock sync.Mutex | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Add adds `value` to the set | ||||||
|  | // returns true  if the value was actually inserted | ||||||
|  | // returns false if the value already existed | ||||||
| func (s *SyncSet[TData]) Add(value TData) bool { | func (s *SyncSet[TData]) Add(value TData) bool { | ||||||
| 	s.lock.Lock() | 	s.lock.Lock() | ||||||
| 	defer s.lock.Unlock() | 	defer s.lock.Unlock() | ||||||
| @@ -15,10 +18,10 @@ func (s *SyncSet[TData]) Add(value TData) bool { | |||||||
| 		s.data = make(map[TData]bool) | 		s.data = make(map[TData]bool) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_, ok := s.data[value] | 	_, existsInPreState := s.data[value] | ||||||
| 	s.data[value] = true | 	s.data[value] = true | ||||||
|  |  | ||||||
| 	return !ok | 	return !existsInPreState | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *SyncSet[TData]) AddAll(values []TData) { | func (s *SyncSet[TData]) AddAll(values []TData) { | ||||||
|   | |||||||
| @@ -80,6 +80,10 @@ func New(t ErrorType, msg string) *Builder { | |||||||
| } | } | ||||||
|  |  | ||||||
| func Wrap(err error, msg string) *Builder { | func Wrap(err error, msg string) *Builder { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return &Builder{errorData: newExErr(CatSystem, TypeInternal, msg)} // prevent NPE if we call Wrap with err==nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if !pkgconfig.RecursiveErrors { | 	if !pkgconfig.RecursiveErrors { | ||||||
| 		v := FromError(err) | 		v := FromError(err) | ||||||
| 		v.Message = msg | 		v.Message = msg | ||||||
| @@ -270,8 +274,12 @@ func (b *Builder) Any(key string, val any) *Builder { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Builder) Stringer(key string, val fmt.Stringer) *Builder { | func (b *Builder) Stringer(key string, val fmt.Stringer) *Builder { | ||||||
|  | 	if val == nil { | ||||||
|  | 		return b.addMeta(key, MDTString, "(!nil)") | ||||||
|  | 	} else { | ||||||
| 		return b.addMeta(key, MDTString, val.String()) | 		return b.addMeta(key, MDTString, val.String()) | ||||||
| 	} | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (b *Builder) Stack() *Builder { | func (b *Builder) Stack() *Builder { | ||||||
| 	return b.addMeta("@Stack", MDTString, string(debug.Stack())) | 	return b.addMeta("@Stack", MDTString, string(debug.Stack())) | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package exerr | package exerr | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"gogs.mikescher.com/BlackForestBytes/goext/dataext" | ||||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -37,25 +38,32 @@ type ErrorType struct { | |||||||
|  |  | ||||||
| //goland:noinspection GoUnusedGlobalVariable | //goland:noinspection GoUnusedGlobalVariable | ||||||
| var ( | var ( | ||||||
| 	TypeInternal       = ErrorType{"INTERNAL_ERROR", langext.Ptr(500)} | 	TypeInternal       = NewType("INTERNAL_ERROR", langext.Ptr(500)) | ||||||
| 	TypePanic          = ErrorType{"PANIC", langext.Ptr(500)} | 	TypePanic          = NewType("PANIC", langext.Ptr(500)) | ||||||
| 	TypeNotImplemented = ErrorType{"NOT_IMPLEMENTED", langext.Ptr(500)} | 	TypeNotImplemented = NewType("NOT_IMPLEMENTED", langext.Ptr(500)) | ||||||
|  |  | ||||||
| 	TypeWrap = ErrorType{"Wrap", nil} | 	TypeWrap = NewType("Wrap", nil) | ||||||
|  |  | ||||||
| 	TypeBindFailURI      = ErrorType{"BINDFAIL_URI", langext.Ptr(400)} | 	TypeBindFailURI      = NewType("BINDFAIL_URI", langext.Ptr(400)) | ||||||
| 	TypeBindFailQuery    = ErrorType{"BINDFAIL_QUERY", langext.Ptr(400)} | 	TypeBindFailQuery    = NewType("BINDFAIL_QUERY", langext.Ptr(400)) | ||||||
| 	TypeBindFailJSON     = ErrorType{"BINDFAIL_JSON", langext.Ptr(400)} | 	TypeBindFailJSON     = NewType("BINDFAIL_JSON", langext.Ptr(400)) | ||||||
| 	TypeBindFailFormData = ErrorType{"BINDFAIL_FORMDATA", langext.Ptr(400)} | 	TypeBindFailFormData = NewType("BINDFAIL_FORMDATA", langext.Ptr(400)) | ||||||
| 	TypeBindFailHeader   = ErrorType{"BINDFAIL_HEADER", langext.Ptr(400)} | 	TypeBindFailHeader   = NewType("BINDFAIL_HEADER", langext.Ptr(400)) | ||||||
|  |  | ||||||
| 	TypeUnauthorized = ErrorType{"UNAUTHORIZED", langext.Ptr(401)} | 	TypeUnauthorized = NewType("UNAUTHORIZED", langext.Ptr(401)) | ||||||
| 	TypeAuthFailed   = ErrorType{"AUTH_FAILED", langext.Ptr(401)} | 	TypeAuthFailed   = NewType("AUTH_FAILED", langext.Ptr(401)) | ||||||
|  |  | ||||||
| 	// other values come from pkgconfig | 	// other values come from pkgconfig | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var registeredTypes = dataext.SyncSet[string]{} | ||||||
|  |  | ||||||
| func NewType(key string, defStatusCode *int) ErrorType { | func NewType(key string, defStatusCode *int) ErrorType { | ||||||
|  | 	insertOkay := registeredTypes.Add(key) | ||||||
|  | 	if !insertOkay { | ||||||
|  | 		panic("Cannot register same ErrType ('" + key + "') more than once") | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return ErrorType{key, defStatusCode} | 	return ErrorType{key, defStatusCode} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ type ErrorPackageConfig struct { | |||||||
| 	ZeroLogAllTraces       bool                                             // autom print zerolog logs on .Build()  (for all Severities) | 	ZeroLogAllTraces       bool                                             // autom print zerolog logs on .Build()  (for all Severities) | ||||||
| 	RecursiveErrors        bool                                             // errors contains their Origin-Error | 	RecursiveErrors        bool                                             // errors contains their Origin-Error | ||||||
| 	ExtendedGinOutput      bool                                             // Log extended data (trace, meta, ...) to gin in err.Output() | 	ExtendedGinOutput      bool                                             // Log extended data (trace, meta, ...) to gin in err.Output() | ||||||
|  | 	IncludeMetaInGinOutput bool                                             // Log meta fields ( from e.g. `.Str(key, val).Build()` ) to gin in err.Output() | ||||||
| 	ExtendGinOutput        func(err *ExErr, json map[string]any)            // (Optionally) extend the gin output with more fields | 	ExtendGinOutput        func(err *ExErr, json map[string]any)            // (Optionally) extend the gin output with more fields | ||||||
| 	ExtendGinDataOutput    func(err *ExErr, depth int, json map[string]any) // (Optionally) extend the gin `__data` output with more fields | 	ExtendGinDataOutput    func(err *ExErr, depth int, json map[string]any) // (Optionally) extend the gin `__data` output with more fields | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								exerr/gin.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								exerr/gin.go
									
									
									
									
									
								
							| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func (ee *ExErr) toJson(depth int, applyExtendListener bool) langext.H { | func (ee *ExErr) toJson(depth int, applyExtendListener bool, outputMeta bool) langext.H { | ||||||
| 	ginJson := langext.H{} | 	ginJson := langext.H{} | ||||||
|  |  | ||||||
| 	if ee.UniqueID != "" { | 	if ee.UniqueID != "" { | ||||||
| @@ -39,7 +39,15 @@ func (ee *ExErr) toJson(depth int, applyExtendListener bool) langext.H { | |||||||
| 		ginJson["wrappedErrType"] = ee.WrappedErrType | 		ginJson["wrappedErrType"] = ee.WrappedErrType | ||||||
| 	} | 	} | ||||||
| 	if ee.OriginalError != nil { | 	if ee.OriginalError != nil { | ||||||
| 		ginJson["original"] = ee.OriginalError.toJson(depth+1, applyExtendListener) | 		ginJson["original"] = ee.OriginalError.toJson(depth+1, applyExtendListener, outputMeta) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if outputMeta { | ||||||
|  | 		metaJson := langext.H{} | ||||||
|  | 		for metaKey, metaVal := range ee.Meta { | ||||||
|  | 			metaJson[metaKey] = metaVal.rawValueForJson() | ||||||
|  | 		} | ||||||
|  | 		ginJson["meta"] = metaJson | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if applyExtendListener { | 	if applyExtendListener { | ||||||
| @@ -55,7 +63,8 @@ func (ee *ExErr) toJson(depth int, applyExtendListener bool) langext.H { | |||||||
| // Parameters: | // Parameters: | ||||||
| //   - [applyExtendListener]:  if false the pkgconfig.ExtendGinOutput / pkgconfig.ExtendGinDataOutput will not be applied | //   - [applyExtendListener]:  if false the pkgconfig.ExtendGinOutput / pkgconfig.ExtendGinDataOutput will not be applied | ||||||
| //   - [includeWrappedErrors]: if false we do not include the recursive/wrapped errors in `__data` | //   - [includeWrappedErrors]: if false we do not include the recursive/wrapped errors in `__data` | ||||||
| func (ee *ExErr) ToAPIJson(applyExtendListener bool, includeWrappedErrors bool) langext.H { | //   - [includeMetaFields]:    if true  we also include meta-values (aka from `.Str(key, value).Build()`), needs includeWrappedErrors=true | ||||||
|  | func (ee *ExErr) ToAPIJson(applyExtendListener bool, includeWrappedErrors bool, includeMetaFields bool) langext.H { | ||||||
|  |  | ||||||
| 	apiOutput := langext.H{ | 	apiOutput := langext.H{ | ||||||
| 		"errorid":   ee.UniqueID, | 		"errorid":   ee.UniqueID, | ||||||
| @@ -65,7 +74,7 @@ func (ee *ExErr) ToAPIJson(applyExtendListener bool, includeWrappedErrors bool) | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if includeWrappedErrors { | 	if includeWrappedErrors { | ||||||
| 		apiOutput["__data"] = ee.toJson(0, applyExtendListener) | 		apiOutput["__data"] = ee.toJson(0, applyExtendListener, includeMetaFields) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if applyExtendListener { | 	if applyExtendListener { | ||||||
| @@ -97,7 +106,7 @@ func (ee *ExErr) Output(g *gin.Context) { | |||||||
| 		statuscode = *baseType.DefaultStatusCode | 		statuscode = *baseType.DefaultStatusCode | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ginOutput := ee.ToAPIJson(true, pkgconfig.ExtendedGinOutput) | 	ginOutput := ee.ToAPIJson(true, pkgconfig.ExtendedGinOutput, pkgconfig.IncludeMetaInGinOutput) | ||||||
|  |  | ||||||
| 	g.Render(statuscode, json.GoJsonRender{Data: ginOutput, NilSafeSlices: true, NilSafeMaps: true}) | 	g.Render(statuscode, json.GoJsonRender{Data: ginOutput, NilSafeSlices: true, NilSafeMaps: true}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -585,6 +585,40 @@ func (v MetaValue) ValueString() string { | |||||||
| 	return "(err)" | 	return "(err)" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // rawValueForJson returns most-of-the-time the `Value` field | ||||||
|  | // but for some datatyes we do special processing | ||||||
|  | // all, so we can pluck the output value in json.Marshal without any suprises | ||||||
|  | func (v MetaValue) rawValueForJson() any { | ||||||
|  | 	if v.DataType == MDTAny { | ||||||
|  | 		if v.Value.(AnyWrap).IsNil { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 		return v.Value.(AnyWrap).Serialize() | ||||||
|  | 	} | ||||||
|  | 	if v.DataType == MDTID { | ||||||
|  | 		if v.Value.(IDWrap).IsNil { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 		return v.Value.(IDWrap).Value | ||||||
|  | 	} | ||||||
|  | 	if v.DataType == MDTBytes { | ||||||
|  | 		return hex.EncodeToString(v.Value.([]byte)) | ||||||
|  | 	} | ||||||
|  | 	if v.DataType == MDTDuration { | ||||||
|  | 		return v.Value.(time.Duration).String() | ||||||
|  | 	} | ||||||
|  | 	if v.DataType == MDTTime { | ||||||
|  | 		return v.Value.(time.Time).Format(time.RFC3339Nano) | ||||||
|  | 	} | ||||||
|  | 	if v.DataType == MDTObjectID { | ||||||
|  | 		return v.Value.(primitive.ObjectID).Hex() | ||||||
|  | 	} | ||||||
|  | 	if v.DataType == MDTNil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return v.Value | ||||||
|  | } | ||||||
|  |  | ||||||
| func (mm MetaMap) FormatOneLine(singleMaxLen int) string { | func (mm MetaMap) FormatOneLine(singleMaxLen int) string { | ||||||
| 	r := "" | 	r := "" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,10 +5,8 @@ import ( | |||||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/dataext" | 	"gogs.mikescher.com/BlackForestBytes/goext/dataext" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func BodyBuffer() gin.HandlerFunc { | func BodyBuffer(g *gin.Context) { | ||||||
| 	return func(g *gin.Context) { |  | ||||||
| 	if g.Request.Body != nil { | 	if g.Request.Body != nil { | ||||||
| 		g.Request.Body = dataext.NewBufferedReadCloser(g.Request.Body) | 		g.Request.Body = dataext.NewBufferedReadCloser(g.Request.Body) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| } |  | ||||||
|   | |||||||
| @@ -112,8 +112,8 @@ func (w *GinRouteBuilder) Use(middleware ...gin.HandlerFunc) *GinRouteBuilder { | |||||||
| func (w *GinRouteBuilder) Handle(handler WHandlerFunc) { | func (w *GinRouteBuilder) Handle(handler WHandlerFunc) { | ||||||
|  |  | ||||||
| 	if w.routes.wrapper.bufferBody { | 	if w.routes.wrapper.bufferBody { | ||||||
| 		arr := make([]gin.HandlerFunc, len(w.handlers)+1) | 		arr := make([]gin.HandlerFunc, 0, len(w.handlers)+1) | ||||||
| 		arr = append(arr, BodyBuffer()) | 		arr = append(arr, BodyBuffer) | ||||||
| 		arr = append(arr, w.handlers...) | 		arr = append(arr, w.handlers...) | ||||||
| 		w.handlers = arr | 		w.handlers = arr | ||||||
| 	} | 	} | ||||||
| @@ -143,13 +143,25 @@ func (w *GinRouteBuilder) Handle(handler WHandlerFunc) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (w *GinWrapper) NoRoute(handler WHandlerFunc) { | func (w *GinWrapper) NoRoute(handler WHandlerFunc) { | ||||||
| 	w.engine.NoRoute(Wrap(w, handler)) |  | ||||||
|  | 	handlers := make([]gin.HandlerFunc, 0) | ||||||
|  |  | ||||||
|  | 	if w.bufferBody { | ||||||
|  | 		handlers = append(handlers, BodyBuffer) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	middlewareNames := langext.ArrMap(handlers, func(v gin.HandlerFunc) string { return nameOfFunction(v) }) | ||||||
|  | 	handlerName := nameOfFunction(handler) | ||||||
|  |  | ||||||
|  | 	handlers = append(handlers, Wrap(w, handler)) | ||||||
|  |  | ||||||
|  | 	w.engine.NoRoute(handlers...) | ||||||
|  |  | ||||||
| 	w.routeSpecs = append(w.routeSpecs, ginRouteSpec{ | 	w.routeSpecs = append(w.routeSpecs, ginRouteSpec{ | ||||||
| 		Method:      "ANY", | 		Method:      "ANY", | ||||||
| 		URL:         "[NO_ROUTE]", | 		URL:         "[NO_ROUTE]", | ||||||
| 		Middlewares: nil, | 		Middlewares: middlewareNames, | ||||||
| 		Handler:     nameOfFunction(handler), | 		Handler:     handlerName, | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| package goext | package goext | ||||||
|  |  | ||||||
| const GoextVersion = "0.0.225" | const GoextVersion = "0.0.233" | ||||||
|  |  | ||||||
| const GoextVersionTimestamp = "2023-08-08T13:09:15+0200" | const GoextVersionTimestamp = "2023-08-09T10:37:59+0200" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user