mirror of https://github.com/grafana/grafana
Plugins: Use error plane for api/ds/query (#54750)
* plugin client returns error base * fix api test * add plugin client test * add fallback err * fix linting * wip * replace bad query * template is an error * failing test of templated error * add one test passing * fix failing test * move test * rename ErrBadQuery to ErrQueryValidationFailure * tidy diff * Change to one error per specific error kind * last err + fix test * fix imports * more tests * keep req vars together Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>pull/55169/head^2
parent
deb86e3250
commit
29327cbba2
@ -0,0 +1,16 @@ |
||||
package plugins |
||||
|
||||
import "github.com/grafana/grafana/pkg/util/errutil" |
||||
|
||||
var ( |
||||
// ErrPluginNotRegistered error returned when a plugin is not registered.
|
||||
ErrPluginNotRegistered = errutil.NewBase(errutil.StatusNotFound, "plugin.notRegistered") |
||||
// ErrHealthCheckFailed error returned when a plugin health check failed.
|
||||
ErrHealthCheckFailed = errutil.NewBase(errutil.StatusInternal, "plugin.failedHealthCheck") |
||||
// ErrPluginUnavailable error returned when a plugin is unavailable.
|
||||
ErrPluginUnavailable = errutil.NewBase(errutil.StatusInternal, "plugin.unavailable") |
||||
// ErrMethodNotImplemented error returned when a plugin method is not implemented.
|
||||
ErrMethodNotImplemented = errutil.NewBase(errutil.StatusNotImplemented, "plugin.notImplemented") |
||||
// ErrPluginDownstreamError error returned when a plugin method is not implemented.
|
||||
ErrPluginDownstreamError = errutil.NewBase(errutil.StatusInternal, "plugin.downstreamError", errutil.WithPublicMessage("An error occurred within the plugin")) |
||||
) |
@ -0,0 +1,88 @@ |
||||
package client |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
"fmt" |
||||
"testing" |
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend" |
||||
"github.com/grafana/grafana/pkg/plugins" |
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/fakes" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestQueryData(t *testing.T) { |
||||
t.Run("Empty registry should return not registered error", func(t *testing.T) { |
||||
registry := fakes.NewFakePluginRegistry() |
||||
client := ProvideService(registry) |
||||
_, err := client.QueryData(context.Background(), &backend.QueryDataRequest{}) |
||||
require.Error(t, err) |
||||
require.ErrorIs(t, err, plugins.ErrPluginNotRegistered) |
||||
}) |
||||
|
||||
t.Run("Non-empty registry", func(t *testing.T) { |
||||
tcs := []struct { |
||||
err error |
||||
expectedError error |
||||
}{ |
||||
{ |
||||
err: backendplugin.ErrPluginUnavailable, |
||||
expectedError: plugins.ErrPluginUnavailable, |
||||
}, |
||||
{ |
||||
err: backendplugin.ErrMethodNotImplemented, |
||||
expectedError: plugins.ErrMethodNotImplemented, |
||||
}, |
||||
{ |
||||
err: errors.New("surprise surprise"), |
||||
expectedError: plugins.ErrPluginDownstreamError, |
||||
}, |
||||
} |
||||
|
||||
for _, tc := range tcs { |
||||
t.Run(fmt.Sprintf("Plugin client error %q should return expected error", tc.err), func(t *testing.T) { |
||||
registry := fakes.NewFakePluginRegistry() |
||||
p := &plugins.Plugin{ |
||||
JSONData: plugins.JSONData{ |
||||
ID: "grafana", |
||||
}, |
||||
} |
||||
p.RegisterClient(&fakePluginBackend{ |
||||
qdr: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { |
||||
return nil, tc.err |
||||
}, |
||||
}) |
||||
err := registry.Add(context.Background(), p) |
||||
require.NoError(t, err) |
||||
|
||||
client := ProvideService(registry) |
||||
_, err = client.QueryData(context.Background(), &backend.QueryDataRequest{ |
||||
PluginContext: backend.PluginContext{ |
||||
PluginID: "grafana", |
||||
}, |
||||
}) |
||||
require.Error(t, err) |
||||
require.ErrorIs(t, err, tc.expectedError) |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
type fakePluginBackend struct { |
||||
qdr backend.QueryDataHandlerFunc |
||||
|
||||
backendplugin.Plugin |
||||
} |
||||
|
||||
func (f *fakePluginBackend) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { |
||||
if f.qdr != nil { |
||||
return f.qdr(ctx, req) |
||||
} |
||||
return backend.NewQueryDataResponse(), nil |
||||
} |
||||
|
||||
func (f *fakePluginBackend) IsDecommissioned() bool { |
||||
return false |
||||
} |
@ -1,17 +1,12 @@ |
||||
package query |
||||
|
||||
import "fmt" |
||||
|
||||
// ErrBadQuery returned whenever request is malformed and must contain a message
|
||||
// suitable to return in API response.
|
||||
type ErrBadQuery struct { |
||||
Message string |
||||
} |
||||
|
||||
func NewErrBadQuery(msg string) *ErrBadQuery { |
||||
return &ErrBadQuery{Message: msg} |
||||
} |
||||
|
||||
func (e ErrBadQuery) Error() string { |
||||
return fmt.Sprintf("bad query: %s", e.Message) |
||||
} |
||||
import ( |
||||
"github.com/grafana/grafana/pkg/util/errutil" |
||||
) |
||||
|
||||
var ( |
||||
ErrNoQueriesFound = errutil.NewBase(errutil.StatusBadRequest, "query.noQueries", errutil.WithPublicMessage("No queries found")).Errorf("no queries found") |
||||
ErrInvalidDatasourceID = errutil.NewBase(errutil.StatusBadRequest, "query.invalidDatasourceId", errutil.WithPublicMessage("Query does not contain a valid data source identifier")).Errorf("invalid data source identifier") |
||||
ErrMultipleDatasources = errutil.NewBase(errutil.StatusBadRequest, "query.differentDatasources", errutil.WithPublicMessage("All queries must use the same datasource")).Errorf("all queries must use the same datasource") |
||||
ErrMissingDataSourceInfo = errutil.NewBase(errutil.StatusBadRequest, "query.missingDataSourceInfo").MustTemplate("query missing datasource info: {{ .Public.RefId }}", errutil.WithPublic("Query {{ .Public.RefId }} is missing datasource information")) |
||||
) |
||||
|
Loading…
Reference in new issue