mirror of
https://github.com/grafana/grafana.git
synced 2025-09-28 05:34:07 +08:00
Errors: Allow using Base as an error type (#53824)
This commit is contained in:
@ -80,16 +80,40 @@ func (b Base) Errorf(format string, args ...interface{}) Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error makes Base implement the error type. Relying on this is
|
||||||
|
// discouraged, as the Error type can carry additional information
|
||||||
|
// that's valuable when debugging.
|
||||||
|
func (b Base) Error() string {
|
||||||
|
return b.Errorf("").Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b Base) Status() StatusReason {
|
||||||
|
if b.reason == nil {
|
||||||
|
return StatusUnknown
|
||||||
|
}
|
||||||
|
return b.reason.Status()
|
||||||
|
}
|
||||||
|
|
||||||
// Is validates that an Error has the same reason and messageID as the
|
// Is validates that an Error has the same reason and messageID as the
|
||||||
// Base.
|
// Base.
|
||||||
func (b Base) Is(err error) bool {
|
func (b Base) Is(err error) bool {
|
||||||
gfErr := Error{}
|
// The linter complains that it wants to use errors.As because it
|
||||||
ok := errors.As(err, &gfErr)
|
// handles unwrapping, we don't want to do that here since we want
|
||||||
if !ok {
|
// to validate the equality between the two objects.
|
||||||
|
// errors.Is handles the unwrapping, should you want it.
|
||||||
|
//nolint:errorlint
|
||||||
|
base, isBase := err.(Base)
|
||||||
|
//nolint:errorlint
|
||||||
|
gfErr, isGrafanaError := err.(Error)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case isGrafanaError:
|
||||||
|
return b.reason == gfErr.Reason && b.messageID == gfErr.MessageID
|
||||||
|
case isBase:
|
||||||
|
return b.reason == base.reason && b.messageID == base.messageID
|
||||||
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.reason.Status() == gfErr.Reason.Status() && b.messageID == gfErr.MessageID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error is the error type for errors within Grafana, extending
|
// Error is the error type for errors within Grafana, extending
|
||||||
@ -138,12 +162,18 @@ func (e Error) Is(other error) bool {
|
|||||||
// to validate the equality between the two objects.
|
// to validate the equality between the two objects.
|
||||||
// errors.Is handles the unwrapping, should you want it.
|
// errors.Is handles the unwrapping, should you want it.
|
||||||
//nolint:errorlint
|
//nolint:errorlint
|
||||||
o, ok := other.(Error)
|
o, isGrafanaError := other.(Error)
|
||||||
if !ok {
|
//nolint:errorlint
|
||||||
|
base, isBase := other.(Base)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case isGrafanaError:
|
||||||
|
return o.Reason == e.Reason && o.MessageID == e.MessageID && o.Error() == e.Error()
|
||||||
|
case isBase:
|
||||||
|
return base.Is(e)
|
||||||
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return o.Reason == e.Reason && o.MessageID == e.MessageID && o.Error() == e.Error()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicError is derived from Error and only contains information
|
// PublicError is derived from Error and only contains information
|
||||||
|
74
pkg/util/errutil/errors_test.go
Normal file
74
pkg/util/errutil/errors_test.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package errutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBase_Is(t *testing.T) {
|
||||||
|
baseNotFound := NewBase(StatusNotFound, "test:not-found")
|
||||||
|
baseInternal := NewBase(StatusInternal, "test:internal")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
Base Base
|
||||||
|
Other error
|
||||||
|
Expect bool
|
||||||
|
ExpectUnwrapped bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Base: Base{},
|
||||||
|
Other: errors.New(""),
|
||||||
|
Expect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: Base{},
|
||||||
|
Other: Base{},
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: Base{},
|
||||||
|
Other: Error{},
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: baseNotFound,
|
||||||
|
Other: baseNotFound,
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: baseNotFound,
|
||||||
|
Other: baseNotFound.Errorf("this is an error derived from baseNotFound, it is considered to be equal to baseNotFound"),
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: baseNotFound,
|
||||||
|
Other: baseInternal,
|
||||||
|
Expect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Base: baseInternal,
|
||||||
|
Other: fmt.Errorf("wrapped, like a burrito: %w", baseInternal.Errorf("oh noes")),
|
||||||
|
Expect: false,
|
||||||
|
ExpectUnwrapped: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(fmt.Sprintf(
|
||||||
|
"Base '%s' == '%s' of type %s = %v (%v unwrapped)",
|
||||||
|
tc.Base.Error(),
|
||||||
|
tc.Other.Error(),
|
||||||
|
reflect.TypeOf(tc.Other),
|
||||||
|
tc.Expect,
|
||||||
|
tc.Expect || tc.ExpectUnwrapped,
|
||||||
|
), func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.Expect, tc.Base.Is(tc.Other), "direct comparison")
|
||||||
|
assert.Equal(t, tc.Expect, errors.Is(tc.Base, tc.Other), "comparison using errors.Is with other as target")
|
||||||
|
assert.Equal(t, tc.Expect || tc.ExpectUnwrapped, errors.Is(tc.Other, tc.Base), "comparison using errors.Is with base as target, should unwrap other")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user