Skip to content

Commit 2df0291

Browse files
committed
Delete v1
1 parent aa4bd4f commit 2df0291

15 files changed

+259
-1097
lines changed

.golangci.yaml

Lines changed: 0 additions & 96 deletions
This file was deleted.

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
![GitHub](https://img.shields.io/github/license/ing-bank/ginerr)
55
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/ing-bank/ginerr)
66

7-
**[❗ 🚨 Click here for version 2 🚨 ❗](./v2)**
8-
97
Sending any error back to the user can pose a [big security risk](https://owasp.org/www-community/Improper_Error_Handling).
108
For this reason we developed an error registry that allows you to register specific error handlers
119
for your application. This way you can control what information is sent back to the user.
@@ -15,9 +13,20 @@ You can register errors in 3 ways:
1513
- By value of string errors
1614
- By defining the error name yourself
1715

16+
## 👷 V2 migration guide
17+
18+
V2 of this library changes the interface of all the methods to allow contexts to be passed to handlers. This
19+
allows you to add additional data to the final response.
20+
21+
The interface changes are as follows.
22+
23+
- `RegisterErrorHandler` and all its variants take a context as a first parameter in the handler, allowing you to pass more data to the response
24+
- `RegisterErrorHandler` and all its variants require the callback function to return `(int, any)` instead of `(int, R)`, removing the unnecessary generic
25+
- Both `NewErrorResponse` and `NewErrorResponseFrom` take a context as a first parameter, this could be the request context but that's up to you
26+
1827
## ⬇️ Installation
1928

20-
`go get github.com/ing-bank/ginerr`
29+
`go get github.com/ing-bank/ginerr/v2`
2130

2231
## 📋 Usage
2332

@@ -26,7 +35,7 @@ package main
2635

2736
import (
2837
"github.com/gin-gonic/gin"
29-
"github.com/ing-bank/ginerr"
38+
"github.com/ing-bank/ginerr/v2"
3039
"net/http"
3140
)
3241

@@ -43,7 +52,7 @@ type Response struct {
4352
}
4453

4554
func main() {
46-
handler := func(myError *MyError) (int, Response) {
55+
handler := func(ctx context.Context, myError *MyError) (int, any) {
4756
return http.StatusInternalServerError, Response{
4857
Errors: map[string]any{
4958
"error": myError.Error(),
@@ -58,7 +67,7 @@ func main() {
5867

5968
func handleGet(c *gin.Context) {
6069
err := &MyError{}
61-
c.JSON(ginerr.NewErrorResponse(err))
70+
c.JSON(ginerr.NewErrorResponse(c.Request.Context(), err))
6271
}
6372
```
6473

errors.go

Lines changed: 66 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,20 @@
11
package ginerr
22

33
import (
4-
"fmt"
4+
"context"
55
"net/http"
6+
"reflect"
67
)
78

89
const defaultCode = http.StatusInternalServerError
910

10-
// Deprecated: Please use v2 of this library
1111
var DefaultErrorRegistry = NewErrorRegistry()
1212

1313
type (
14-
internalHandler func(err error) (int, any)
15-
internalStringHandler func(err string) (int, any)
14+
internalHandler func(ctx context.Context, err error) (int, any)
15+
internalStringHandler func(ctx context.Context, err string) (int, any)
1616
)
1717

18-
// CustomErrorHandler is the template for unexported errors. For example binding.SliceValidationError
19-
// or uuid.invalidLengthError
20-
// Deprecated: Please use v2 of this library
21-
type CustomErrorHandler[R any] func(err error) (int, R)
22-
23-
// ErrorStringHandler is the template for string errors that don't have their own object available. For example
24-
// "record not found" or "invalid input"
25-
// Deprecated: Please use v2 of this library
26-
type ErrorStringHandler[R any] func(err string) (int, R)
27-
28-
// ErrorHandler is the template of an error handler in the ErrorRegistry. The E type is the error type that
29-
// the handler is registered for. The R type is the type of the response body.
30-
// Deprecated: Please use v2 of this library
31-
type ErrorHandler[E error, R any] func(E) (int, R)
32-
33-
// Deprecated: Please use v2 of this library
3418
func NewErrorRegistry() *ErrorRegistry {
3519
registry := &ErrorRegistry{
3620
handlers: make(map[string]internalHandler),
@@ -39,122 +23,137 @@ func NewErrorRegistry() *ErrorRegistry {
3923
}
4024

4125
// Make sure the stringHandlers are available in the handlers
42-
registry.handlers["*errors.errorString"] = func(err error) (int, any) {
26+
registry.handlers["errors.errorString"] = func(ctx context.Context, err error) (int, any) {
4327
// Check if the error string exists
4428
if handler, ok := registry.stringHandlers[err.Error()]; ok {
45-
return handler(err.Error())
29+
return handler(ctx, err.Error())
4630
}
4731

48-
return registry.DefaultCode, registry.DefaultResponse
32+
return registry.defaultResponse(ctx, err)
4933
}
5034

5135
return registry
5236
}
5337

54-
// Deprecated: Please use v2 of this library
5538
type ErrorRegistry struct {
5639
// handlers are used when we know the type of the error
5740
handlers map[string]internalHandler
5841

5942
// stringHandlers are used when the error is only a string
6043
stringHandlers map[string]internalStringHandler
6144

62-
// DefaultCode to return when no handler is found
45+
// DefaultHandler takes precedent over DefaultCode and DefaultResponse
46+
DefaultHandler func(ctx context.Context, err error) (int, any)
47+
48+
// DefaultCode to return when no handler is found. Deprecated: Prefer DefaultHandler
6349
DefaultCode int
6450

65-
// DefaultResponse to return when no handler is found
51+
// DefaultResponse to return when no handler is found. Deprecated: Prefer DefaultHandler
6652
DefaultResponse any
6753
}
6854

69-
// Deprecated: Please use v2 of this library
55+
// SetDefaultResponse is deprecated, prefer RegisterDefaultHandler
7056
func (e *ErrorRegistry) SetDefaultResponse(code int, response any) {
7157
e.DefaultCode = code
7258
e.DefaultResponse = response
7359
}
7460

61+
func (e *ErrorRegistry) RegisterDefaultHandler(callback func(ctx context.Context, err error) (int, any)) {
62+
e.DefaultHandler = callback
63+
}
64+
65+
func (e *ErrorRegistry) defaultResponse(ctx context.Context, err error) (int, any) {
66+
// In production, we should return a generic error message. If you want to know why, read this:
67+
// https://owasp.org/www-community/Improper_Error_Handling
68+
if e.DefaultHandler != nil {
69+
return e.DefaultHandler(ctx, err)
70+
}
71+
72+
return e.DefaultCode, e.DefaultResponse
73+
}
74+
7575
// NewErrorResponse Returns an error response using the DefaultErrorRegistry. If no specific handler could be found,
76-
// it will return the defaults. It returns an HTTP status code and a response object.
77-
//
78-
// Deprecated: Please use v2 of this library
79-
//
80-
//nolint:gocritic // Unnamed return arguments are described
81-
func NewErrorResponse(err error) (int, any) {
82-
return NewErrorResponseFrom(DefaultErrorRegistry, err)
76+
// it will return the defaults.
77+
func NewErrorResponse(ctx context.Context, err error) (int, any) {
78+
return NewErrorResponseFrom(DefaultErrorRegistry, ctx, err)
8379
}
8480

8581
// NewErrorResponseFrom Returns an error response using the given registry. If no specific handler could be found,
86-
// it will return the defaults. It returns an HTTP status code and a response object.
87-
//
88-
// Deprecated: Please use v2 of this library
89-
//
90-
//nolint:gocritic // Unnamed return arguments are described
91-
func NewErrorResponseFrom(registry *ErrorRegistry, err error) (int, any) {
92-
errorType := fmt.Sprintf("%T", err)
82+
// it will return the defaults.
83+
func NewErrorResponseFrom[E error](registry *ErrorRegistry, ctx context.Context, err E) (int, any) {
84+
errorType := getErrorType[E](err)
9385

9486
// If a handler is registered for the error type, use it.
9587
if entry, ok := registry.handlers[errorType]; ok {
96-
return entry(err)
88+
return entry(ctx, err)
9789
}
9890

99-
// In production, we should return a generic error message. If you want to know why, read this:
100-
// https://owasp.org/www-community/Improper_Error_Handling
101-
return registry.DefaultCode, registry.DefaultResponse
91+
return registry.defaultResponse(ctx, err)
10292
}
10393

10494
// RegisterErrorHandler registers an error handler in DefaultErrorRegistry. The R type is the type of the response body.
105-
// Deprecated: Please use v2 of this library
106-
func RegisterErrorHandler[E error, R any](handler ErrorHandler[E, R]) {
95+
func RegisterErrorHandler[E error](handler func(context.Context, E) (int, any)) {
10796
RegisterErrorHandlerOn(DefaultErrorRegistry, handler)
10897
}
10998

11099
// RegisterErrorHandlerOn registers an error handler in the given registry. The R type is the type of the response body.
111-
// Deprecated: Please use v2 of this library
112-
func RegisterErrorHandlerOn[E error, R any](registry *ErrorRegistry, handler ErrorHandler[E, R]) {
100+
func RegisterErrorHandlerOn[E error](registry *ErrorRegistry, handler func(context.Context, E) (int, any)) {
113101
// Name of the type
114-
errorType := fmt.Sprintf("%T", *new(E))
102+
errorType := getErrorType[E](new(E))
115103

116104
// Wrap it in a closure, we can't save it directly because err E is not available in NewErrorResponseFrom. It will
117105
// be available in the closure when it is called. Check out TestErrorResponseFrom_ReturnsErrorBInInterface for an example.
118-
registry.handlers[errorType] = func(err error) (int, any) {
119-
// We can safely cast it here, because we know it's the right type.
120-
//nolint:errorlint // Not relevant, we're casting anyway
121-
return handler(err.(E))
106+
registry.handlers[errorType] = func(ctx context.Context, err error) (int, any) {
107+
return handler(ctx, err.(E))
122108
}
123109
}
124110

125111
// RegisterCustomErrorTypeHandler registers an error handler in DefaultErrorRegistry. Same as RegisterErrorHandler,
126112
// but you can set the fmt.Sprint("%T", err) error yourself. Allows you to register error types that aren't exported
127113
// from their respective packages such as the uuid error or *errors.errorString. The R type is the type of the response body.
128-
// Deprecated: Please use v2 of this library
129-
func RegisterCustomErrorTypeHandler[R any](errorType string, handler CustomErrorHandler[R]) {
114+
func RegisterCustomErrorTypeHandler(errorType string, handler func(ctx context.Context, err error) (int, any)) {
130115
RegisterCustomErrorTypeHandlerOn(DefaultErrorRegistry, errorType, handler)
131116
}
132117

133118
// RegisterCustomErrorTypeHandlerOn registers an error handler in the given registry. Same as RegisterErrorHandlerOn,
134119
// but you can set the fmt.Sprint("%T", err) error yourself. Allows you to register error types that aren't exported
135120
// from their respective packages such as the uuid error or *errors.errorString. The R type is the type of the response body.
136-
// Deprecated: Please use v2 of this library
137-
func RegisterCustomErrorTypeHandlerOn[R any](registry *ErrorRegistry, errorType string, handler CustomErrorHandler[R]) {
121+
func RegisterCustomErrorTypeHandlerOn(registry *ErrorRegistry, errorType string, handler func(ctx context.Context, err error) (int, any)) {
138122
// Wrap it in a closure, we can't save it directly
139-
registry.handlers[errorType] = func(err error) (int, any) {
140-
return handler(err)
141-
}
123+
registry.handlers[errorType] = handler
142124
}
143125

144126
// RegisterStringErrorHandler allows you to register an error handler for a simple errorString created with
145127
// errors.New() or fmt.Errorf(). Can be used in case you are dealing with libraries that don't have exported
146128
// error objects. Uses the DefaultErrorRegistry. The R type is the type of the response body.
147-
// Deprecated: Please use v2 of this library
148-
func RegisterStringErrorHandler[R any](errorString string, handler ErrorStringHandler[R]) {
129+
func RegisterStringErrorHandler(errorString string, handler func(ctx context.Context, err string) (int, any)) {
149130
RegisterStringErrorHandlerOn(DefaultErrorRegistry, errorString, handler)
150131
}
151132

152133
// RegisterStringErrorHandlerOn allows you to register an error handler for a simple errorString created with
153134
// errors.New() or fmt.Errorf(). Can be used in case you are dealing with libraries that don't have exported
154135
// error objects. The R type is the type of the response body.
155-
// Deprecated: Please use v2 of this library
156-
func RegisterStringErrorHandlerOn[R any](registry *ErrorRegistry, errorString string, handler ErrorStringHandler[R]) {
157-
registry.stringHandlers[errorString] = func(err string) (int, any) {
158-
return handler(err)
136+
func RegisterStringErrorHandlerOn(registry *ErrorRegistry, errorString string, handler func(ctx context.Context, err string) (int, any)) {
137+
registry.stringHandlers[errorString] = handler
138+
}
139+
140+
// getErrorType returns the errorType from the generic type. If the generic type returns the typealias "error",
141+
// e.g. due to `type SomeError error`, retry with the concrete `err` value.
142+
func getErrorType[E error](err any) string {
143+
typeOf := reflect.ValueOf(new(E)).Type()
144+
for typeOf.Kind() == reflect.Pointer {
145+
typeOf = typeOf.Elem()
159146
}
147+
errorType := typeOf.String()
148+
149+
if errorType == "error" {
150+
// try once more but with err instead of new(E)
151+
typeOf = reflect.ValueOf(err).Type()
152+
for typeOf.Kind() == reflect.Pointer {
153+
typeOf = typeOf.Elem()
154+
}
155+
errorType = typeOf.String()
156+
}
157+
158+
return errorType
160159
}

0 commit comments

Comments
 (0)