HTTP: Add function for using new style errors with fallback (#51627)

This commit is contained in:
Emil Tullstedt 2022-07-13 13:14:28 +02:00 committed by GitHub
parent 4ff0f006dd
commit dd6d71ee4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 147 additions and 0 deletions

View File

@ -239,6 +239,22 @@ func Err(err error) *NormalResponse {
return resp
}
// ErrOrFallback uses the information in an errutil.Error if available
// and otherwise falls back to the status and message provided as
// arguments.
//
// The signature is equivalent to that of Error which allows us to
// rename this to Error when we're confident that that would be safe to
// do.
func ErrOrFallback(status int, message string, err error) *NormalResponse {
grafanaErr := &errutil.Error{}
if errors.As(err, grafanaErr) {
return Err(err)
}
return Error(status, message, err)
}
// Empty creates an empty NormalResponse.
func Empty(status int) *NormalResponse {
return Respond(status, nil)

View File

@ -0,0 +1,127 @@
package response
import (
"errors"
"net/http"
"testing"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestErrors(t *testing.T) {
const fakeNotFoundMessage = "I looked, but did not find the thing"
const genericErrorMessage = "Something went wrong in parsing the request"
cases := []struct {
name string
// inputs
err error
statusCode int
message string
// responses
legacyResponse *NormalResponse
newResponse *NormalResponse
fallbackUseNew bool
compareErr bool
}{
{
name: "base case",
legacyResponse: &NormalResponse{},
newResponse: &NormalResponse{
status: http.StatusInternalServerError,
},
},
{
name: "not found error",
err: errors.New("not found"),
statusCode: http.StatusNotFound,
message: fakeNotFoundMessage,
legacyResponse: &NormalResponse{
status: http.StatusNotFound,
errMessage: fakeNotFoundMessage,
},
newResponse: &NormalResponse{
status: http.StatusInternalServerError,
},
},
{
name: "grafana error with fallback to other error",
err: errutil.NewBase(errutil.StatusTimeout, "thing.timeout").Errorf("whoops"),
statusCode: http.StatusBadRequest,
message: genericErrorMessage,
legacyResponse: &NormalResponse{
status: http.StatusBadRequest,
errMessage: genericErrorMessage,
},
newResponse: &NormalResponse{
status: http.StatusGatewayTimeout,
errMessage: errutil.StatusTimeout.String(),
},
fallbackUseNew: true,
},
}
compareResponses := func(expected *NormalResponse, actual *NormalResponse, compareErr bool) func(t *testing.T) {
return func(t *testing.T) {
if expected == nil {
require.Nil(t, actual)
return
}
require.NotNil(t, actual)
assert.Equal(t, expected.status, actual.status)
if expected.body != nil {
assert.Equal(t, expected.body.Bytes(), actual.body.Bytes())
}
if expected.header != nil {
assert.EqualValues(t, expected.header, actual.header)
}
assert.Equal(t, expected.errMessage, actual.errMessage)
if compareErr {
assert.ErrorIs(t, expected.err, actual.err)
}
}
}
for _, tc := range cases {
tc := tc
t.Run(
tc.name+" Error",
compareResponses(tc.legacyResponse, Error(
tc.statusCode,
tc.message,
tc.err,
), tc.compareErr),
)
t.Run(
tc.name+" Err",
compareResponses(tc.newResponse, Err(
tc.err,
), tc.compareErr),
)
fallbackResponse := tc.legacyResponse
if tc.fallbackUseNew {
fallbackResponse = tc.newResponse
}
t.Run(
tc.name+" ErrOrFallback",
compareResponses(fallbackResponse, ErrOrFallback(
tc.statusCode,
tc.message,
tc.err,
), tc.compareErr),
)
}
}

View File

@ -110,6 +110,10 @@ func (s CoreStatus) LogLevel() LogLevel {
}
}
func (s CoreStatus) String() string {
return string(s)
}
// ProxyStatus implies that an error originated from the data source
// proxy.
type ProxyStatus CoreStatus