diff --git a/pkg/services/datasourceproxy/datasourceproxy.go b/pkg/services/datasourceproxy/datasourceproxy.go index c03f63c303e..f186f94abaa 100644 --- a/pkg/services/datasourceproxy/datasourceproxy.go +++ b/pkg/services/datasourceproxy/datasourceproxy.go @@ -122,7 +122,8 @@ func (p *DataSourceProxyService) proxyDatasourceRequest(c *contextmodel.ReqConte proxy, err := pluginproxy.NewDataSourceProxy(ds, plugin.Routes, c, proxyPath, p.Cfg, p.HTTPClientProvider, p.OAuthTokenService, p.DataSourcesService, p.tracer) if err != nil { - if errors.Is(err, datasource.URLValidationError{}) { + var urlValidationError datasource.URLValidationError + if errors.As(err, &urlValidationError) { c.JsonApiErr(http.StatusBadRequest, fmt.Sprintf("Invalid data source URL: %q", ds.URL), err) } else { c.JsonApiErr(http.StatusInternalServerError, "Failed creating data source proxy", err) diff --git a/pkg/services/datasourceproxy/datasourceproxy_test.go b/pkg/services/datasourceproxy/datasourceproxy_test.go index 57da4aa33c0..235fc1b3fce 100644 --- a/pkg/services/datasourceproxy/datasourceproxy_test.go +++ b/pkg/services/datasourceproxy/datasourceproxy_test.go @@ -1,9 +1,22 @@ package datasourceproxy import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" "testing" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/manager/fakes" + contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" + "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/web" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestDataProxy(t *testing.T) { @@ -52,3 +65,72 @@ func TestDataProxy(t *testing.T) { } }) } + +// Tests request to datasource proxy service +func TestDatasourceProxy_proxyDatasourceRequest(t *testing.T) { + tcs := []struct { + name string + dsURL string + expectedErrorMsg string + }{ + { + name: "Empty datasource URL will return a 400 HTTP status code", + dsURL: "", + expectedErrorMsg: "validation of data source URL \"\" failed: empty URL string", + }, + { + name: "Invalid datasource URL will return a 400 HTTP status code", + dsURL: "://host/path", + expectedErrorMsg: "validation of data source URL \"://host/path\" failed: parse \"://host/path\": missing protocol scheme", + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + pluginID := datasources.DS_PROMETHEUS + + pluginStore := &fakes.FakePluginStore{PluginList: []plugins.PluginDTO{ + {JSONData: plugins.JSONData{ID: pluginID}}, + }} + + p := DataSourceProxyService{ + PluginRequestValidator: &fakePluginRequestValidator{}, + pluginStore: pluginStore, + } + + responseRecorder := httptest.NewRecorder() + c := &contextmodel.ReqContext{ + Context: &web.Context{ + Req: &http.Request{URL: &url.URL{}}, + Resp: web.NewResponseWriter("GET", responseRecorder), + }, + Logger: log.NewNopLogger(), + } + + p.proxyDatasourceRequest(c, &datasources.DataSource{ + Type: pluginID, + URL: tc.dsURL, + }) + + resp := responseRecorder.Result() + body := resp.Body + + b, err := io.ReadAll(body) + require.NoError(t, err) + require.NoError(t, body.Close()) + + jsonBody := make(map[string]string) + err = json.Unmarshal(b, &jsonBody) + require.NoError(t, err) + + require.Equal(t, http.StatusBadRequest, resp.StatusCode) + require.Equal(t, fmt.Sprintf("Invalid data source URL: %q", tc.dsURL), jsonBody["message"]) + require.Equal(t, tc.expectedErrorMsg, jsonBody["error"]) + }) + } +} + +type fakePluginRequestValidator struct{} + +func (rv *fakePluginRequestValidator) Validate(_ string, _ *http.Request) error { + return nil +}