Skip to content

Commit

Permalink
HTTP: Add function for using new style errors with fallback (grafana#…
Browse files Browse the repository at this point in the history
  • Loading branch information
sakjur authored Jul 13, 2022
1 parent 4ff0f00 commit dd6d71e
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 0 deletions.
16 changes: 16 additions & 0 deletions pkg/api/response/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
127 changes: 127 additions & 0 deletions pkg/api/response/response_test.go
Original file line number Diff line number Diff line change
@@ -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),
)
}
}
4 changes: 4 additions & 0 deletions pkg/util/errutil/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit dd6d71e

Please sign in to comment.