| 
									
										
										
										
											2022-03-08 02:33:01 +08:00
										 |  |  | package api | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2022-03-08 02:33:01 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2022-03-08 02:33:01 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2022-03-08 02:33:01 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:52:22 +08:00
										 |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend" | 
					
						
							| 
									
										
										
										
											2022-06-28 00:23:15 +08:00
										 |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-13 12:11:35 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/apimachinery/errutil" | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/db/dbtest" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/infra/localcache" | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/plugins/backendplugin" | 
					
						
							|  |  |  | 	pluginClient "github.com/grafana/grafana/pkg/plugins/manager/client" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/plugins/manager/registry" | 
					
						
							| 
									
										
										
										
											2023-05-23 22:29:20 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/datasources" | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 	fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes" | 
					
						
							| 
									
										
										
										
											2024-02-27 19:38:02 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig" | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings" | 
					
						
							|  |  |  | 	pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service" | 
					
						
							| 
									
										
										
										
											2023-09-11 19:59:24 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/query" | 
					
						
							| 
									
										
										
										
											2022-07-16 00:06:44 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/quota/quotatest" | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	secretstest "github.com/grafana/grafana/pkg/services/secrets/fakes" | 
					
						
							| 
									
										
										
										
											2022-08-10 17:56:48 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/user" | 
					
						
							| 
									
										
										
										
											2022-10-21 19:54:55 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2022-06-14 07:23:56 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/web/webtest" | 
					
						
							| 
									
										
										
										
											2022-03-08 02:33:01 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-24 23:01:46 +08:00
										 |  |  | type fakeDataSourceRequestValidator struct { | 
					
						
							| 
									
										
										
										
											2022-03-08 02:33:01 +08:00
										 |  |  | 	err error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | type secretsErrorResponseBody struct { | 
					
						
							|  |  |  | 	Error   string `json:"error"` | 
					
						
							|  |  |  | 	Message string `json:"message"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-24 23:01:46 +08:00
										 |  |  | func (rv *fakeDataSourceRequestValidator) Validate(ds *datasources.DataSource, req *http.Request) error { | 
					
						
							| 
									
										
										
										
											2022-03-08 02:33:01 +08:00
										 |  |  | 	return rv.err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-04 00:02:20 +08:00
										 |  |  | // `/ds/query` endpoint test
 | 
					
						
							|  |  |  | func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2023-09-21 17:33:31 +08:00
										 |  |  | 	cfg := setting.NewCfg() | 
					
						
							| 
									
										
										
										
											2022-05-04 00:02:20 +08:00
										 |  |  | 	qds := query.ProvideService( | 
					
						
							| 
									
										
										
										
											2023-09-21 17:33:31 +08:00
										 |  |  | 		cfg, | 
					
						
							| 
									
										
										
										
											2022-05-04 00:02:20 +08:00
										 |  |  | 		nil, | 
					
						
							|  |  |  | 		nil, | 
					
						
							| 
									
										
										
										
											2025-01-24 23:01:46 +08:00
										 |  |  | 		&fakeDataSourceRequestValidator{}, | 
					
						
							| 
									
										
										
										
											2022-05-04 00:02:20 +08:00
										 |  |  | 		&fakePluginClient{ | 
					
						
							|  |  |  | 			QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { | 
					
						
							|  |  |  | 				resp := backend.Responses{ | 
					
						
							|  |  |  | 					"A": backend.DataResponse{ | 
					
						
							| 
									
										
										
										
											2022-12-02 03:51:12 +08:00
										 |  |  | 						Error: errors.New("query failed"), | 
					
						
							| 
									
										
										
										
											2022-05-04 00:02:20 +08:00
										 |  |  | 					}, | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return &backend.QueryDataResponse{Responses: resp}, nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2023-09-21 17:33:31 +08:00
										 |  |  | 		plugincontext.ProvideService(cfg, localcache.ProvideService(), &pluginstore.FakePluginStore{ | 
					
						
							| 
									
										
										
										
											2023-09-11 19:59:24 +08:00
										 |  |  | 			PluginList: []pluginstore.Plugin{ | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					JSONData: plugins.JSONData{ | 
					
						
							|  |  |  | 						ID: "grafana", | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2024-01-19 22:56:52 +08:00
										 |  |  | 		}, &fakeDatasources.FakeCacheService{}, &fakeDatasources.FakeDataSourceService{}, | 
					
						
							| 
									
										
										
										
											2024-02-27 19:38:02 +08:00
										 |  |  | 			pluginSettings.ProvideService(dbtest.NewFakeDB(), secretstest.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider()), | 
					
						
							| 
									
										
										
										
											2022-05-04 00:02:20 +08:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2024-07-10 17:15:10 +08:00
										 |  |  | 	server := SetupAPITestServer(t, func(hs *HTTPServer) { | 
					
						
							| 
									
										
										
										
											2022-05-04 00:02:20 +08:00
										 |  |  | 		hs.queryDataService = qds | 
					
						
							| 
									
										
										
										
											2022-11-15 03:08:10 +08:00
										 |  |  | 		hs.QuotaService = quotatest.New(false, nil) | 
					
						
							| 
									
										
										
										
											2022-05-04 00:02:20 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-10 17:15:10 +08:00
										 |  |  | 	t.Run("Status code is 400 when data source response has an error", func(t *testing.T) { | 
					
						
							|  |  |  | 		req := server.NewPostRequest("/api/ds/query", strings.NewReader(reqValid)) | 
					
						
							| 
									
										
										
										
											2023-05-23 22:29:20 +08:00
										 |  |  | 		webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {datasources.ActionQuery: []string{datasources.ScopeAll}}}}) | 
					
						
							| 
									
										
										
										
											2024-07-10 17:15:10 +08:00
										 |  |  | 		resp, err := server.SendJSON(req) | 
					
						
							| 
									
										
										
										
											2022-05-04 00:02:20 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.NoError(t, resp.Body.Close()) | 
					
						
							|  |  |  | 		require.Equal(t, http.StatusBadRequest, resp.StatusCode) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2023-09-21 17:33:31 +08:00
										 |  |  | 	cfg := setting.NewCfg() | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	ds := &fakeDatasources.FakeDataSourceService{SimulatePluginFailure: true} | 
					
						
							|  |  |  | 	db := &dbtest.FakeDB{ExpectedError: pluginsettings.ErrPluginSettingNotFound} | 
					
						
							| 
									
										
										
										
											2023-09-21 17:33:31 +08:00
										 |  |  | 	pcp := plugincontext.ProvideService(cfg, localcache.ProvideService(), | 
					
						
							| 
									
										
										
										
											2023-09-11 19:59:24 +08:00
										 |  |  | 		&pluginstore.FakePluginStore{ | 
					
						
							|  |  |  | 			PluginList: []pluginstore.Plugin{ | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					JSONData: plugins.JSONData{ | 
					
						
							|  |  |  | 						ID: "grafana", | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-01-19 22:56:52 +08:00
										 |  |  | 		&fakeDatasources.FakeCacheService{}, | 
					
						
							| 
									
										
										
										
											2024-02-27 19:38:02 +08:00
										 |  |  | 		ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider(), | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 	qds := query.ProvideService( | 
					
						
							| 
									
										
										
										
											2023-09-21 17:33:31 +08:00
										 |  |  | 		cfg, | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 		nil, | 
					
						
							|  |  |  | 		nil, | 
					
						
							| 
									
										
										
										
											2025-01-24 23:01:46 +08:00
										 |  |  | 		&fakeDataSourceRequestValidator{}, | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 		&fakePluginClient{ | 
					
						
							|  |  |  | 			QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { | 
					
						
							|  |  |  | 				resp := backend.Responses{ | 
					
						
							|  |  |  | 					"A": backend.DataResponse{ | 
					
						
							| 
									
										
										
										
											2022-12-02 03:51:12 +08:00
										 |  |  | 						Error: errors.New("query failed"), | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 					}, | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return &backend.QueryDataResponse{Responses: resp}, nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 		pcp, | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	httpServer := SetupAPITestServer(t, func(hs *HTTPServer) { | 
					
						
							|  |  |  | 		hs.queryDataService = qds | 
					
						
							| 
									
										
										
										
											2022-11-15 03:08:10 +08:00
										 |  |  | 		hs.QuotaService = quotatest.New(false, nil) | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 		hs.pluginContextProvider = pcp | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("Status code is 500 and a secrets plugin error is returned if there is a problem getting secrets from the remote plugin", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 		req := httpServer.NewPostRequest("/api/ds/query", strings.NewReader(reqValid)) | 
					
						
							| 
									
										
										
										
											2023-05-23 22:29:20 +08:00
										 |  |  | 		webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {datasources.ActionQuery: []string{datasources.ScopeAll}}}}) | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 		resp, err := httpServer.SendJSON(req) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Equal(t, http.StatusInternalServerError, resp.StatusCode) | 
					
						
							|  |  |  | 		buf := new(bytes.Buffer) | 
					
						
							|  |  |  | 		_, err = buf.ReadFrom(resp.Body) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.NoError(t, resp.Body.Close()) | 
					
						
							|  |  |  | 		var resObj secretsErrorResponseBody | 
					
						
							|  |  |  | 		err = json.Unmarshal(buf.Bytes(), &resObj) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-10-31 02:06:26 +08:00
										 |  |  | 		require.Equal(t, "", resObj.Error) | 
					
						
							| 
									
										
										
										
											2022-07-13 21:27:03 +08:00
										 |  |  | 		require.Contains(t, resObj.Message, "Secrets Plugin error:") | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | var reqValid = `{ | 
					
						
							|  |  |  | 	"from": "", | 
					
						
							|  |  |  | 	"to": "", | 
					
						
							|  |  |  | 	"queries": [ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"type": "datasource", | 
					
						
							|  |  |  | 				"uid": "grafana" | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			"queryType": "randomWalk", | 
					
						
							|  |  |  | 			"refId": "A" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	] | 
					
						
							|  |  |  | }` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var reqNoQueries = `{ | 
					
						
							|  |  |  | 	"from": "", | 
					
						
							|  |  |  | 	"to": "", | 
					
						
							|  |  |  | 	"queries": [] | 
					
						
							|  |  |  | }` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var reqQueryWithInvalidDatasourceID = `{ | 
					
						
							|  |  |  | 	"from": "", | 
					
						
							|  |  |  | 	"to": "", | 
					
						
							|  |  |  | 	"queries": [ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"queryType": "randomWalk", | 
					
						
							|  |  |  | 			"refId": "A" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	] | 
					
						
							|  |  |  | }` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var reqDatasourceByUidNotFound = `{ | 
					
						
							|  |  |  | 	"from": "", | 
					
						
							|  |  |  | 	"to": "", | 
					
						
							|  |  |  | 	"queries": [ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"type": "datasource", | 
					
						
							|  |  |  | 				"uid": "not-found" | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			"queryType": "randomWalk", | 
					
						
							|  |  |  | 			"refId": "A" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	] | 
					
						
							|  |  |  | }` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var reqDatasourceByIdNotFound = `{ | 
					
						
							|  |  |  | 	"from": "", | 
					
						
							|  |  |  | 	"to": "", | 
					
						
							|  |  |  | 	"queries": [ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"datasourceId": 1, | 
					
						
							|  |  |  | 			"queryType": "randomWalk", | 
					
						
							|  |  |  | 			"refId": "A" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	] | 
					
						
							|  |  |  | }` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestDataSourceQueryError(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 	type body struct { | 
					
						
							|  |  |  | 		Message    string `json:"message"` | 
					
						
							|  |  |  | 		MessageId  string `json:"messageId"` | 
					
						
							|  |  |  | 		StatusCode int    `json:"statusCode"` | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 	tcs := []struct { | 
					
						
							|  |  |  | 		request        string | 
					
						
							|  |  |  | 		clientErr      error | 
					
						
							|  |  |  | 		expectedStatus int | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 		expectedBody   body | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			request:        reqValid, | 
					
						
							| 
									
										
										
										
											2023-09-25 17:56:03 +08:00
										 |  |  | 			clientErr:      plugins.ErrPluginUnavailable, | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 			expectedStatus: http.StatusInternalServerError, | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 			expectedBody: body{ | 
					
						
							|  |  |  | 				Message:    "Plugin unavailable", | 
					
						
							|  |  |  | 				MessageId:  "plugin.unavailable", | 
					
						
							|  |  |  | 				StatusCode: 500, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			request:        reqValid, | 
					
						
							| 
									
										
										
										
											2023-09-25 17:56:03 +08:00
										 |  |  | 			clientErr:      plugins.ErrMethodNotImplemented, | 
					
						
							|  |  |  | 			expectedStatus: http.StatusNotFound, | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 			expectedBody: body{ | 
					
						
							|  |  |  | 				Message:    "Method not implemented", | 
					
						
							|  |  |  | 				MessageId:  "plugin.notImplemented", | 
					
						
							|  |  |  | 				StatusCode: 404, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			request:        reqValid, | 
					
						
							|  |  |  | 			clientErr:      errors.New("surprise surprise"), | 
					
						
							|  |  |  | 			expectedStatus: errutil.StatusInternal.HTTPStatus(), | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 			expectedBody: body{ | 
					
						
							|  |  |  | 				Message:    "An error occurred within the plugin", | 
					
						
							| 
									
										
										
										
											2025-02-20 17:23:53 +08:00
										 |  |  | 				MessageId:  "plugin.requestFailureError", | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 				StatusCode: 500, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			request:        reqNoQueries, | 
					
						
							|  |  |  | 			expectedStatus: http.StatusBadRequest, | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 			expectedBody: body{ | 
					
						
							|  |  |  | 				Message:    "No queries found", | 
					
						
							|  |  |  | 				MessageId:  "query.noQueries", | 
					
						
							|  |  |  | 				StatusCode: 400, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			request:        reqQueryWithInvalidDatasourceID, | 
					
						
							|  |  |  | 			expectedStatus: http.StatusBadRequest, | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 			expectedBody: body{ | 
					
						
							|  |  |  | 				Message:    "Query does not contain a valid data source identifier", | 
					
						
							|  |  |  | 				MessageId:  "query.invalidDatasourceId", | 
					
						
							|  |  |  | 				StatusCode: 400, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			request:        reqDatasourceByUidNotFound, | 
					
						
							|  |  |  | 			expectedStatus: http.StatusNotFound, | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 			expectedBody: body{ | 
					
						
							|  |  |  | 				Message: "Data source not found", | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			request:        reqDatasourceByIdNotFound, | 
					
						
							|  |  |  | 			expectedStatus: http.StatusNotFound, | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 			expectedBody: body{ | 
					
						
							|  |  |  | 				Message: "Data source not found", | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tc := range tcs { | 
					
						
							|  |  |  | 		t.Run(fmt.Sprintf("Plugin client error %q should propagate to API", tc.clientErr), func(t *testing.T) { | 
					
						
							|  |  |  | 			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.clientErr | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			srv := SetupAPITestServer(t, func(hs *HTTPServer) { | 
					
						
							| 
									
										
										
										
											2023-09-21 17:33:31 +08:00
										 |  |  | 				cfg := setting.NewCfg() | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 				r := registry.NewInMemory() | 
					
						
							|  |  |  | 				err := r.Add(context.Background(), p) | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 				ds := &fakeDatasources.FakeDataSourceService{} | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 				hs.queryDataService = query.ProvideService( | 
					
						
							| 
									
										
										
										
											2023-09-21 17:33:31 +08:00
										 |  |  | 					cfg, | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 					&fakeDatasources.FakeCacheService{}, | 
					
						
							|  |  |  | 					nil, | 
					
						
							| 
									
										
										
										
											2025-01-24 23:01:46 +08:00
										 |  |  | 					&fakeDataSourceRequestValidator{}, | 
					
						
							| 
									
										
										
										
											2024-03-11 23:28:46 +08:00
										 |  |  | 					pluginClient.ProvideService(r), | 
					
						
							| 
									
										
										
										
											2023-09-21 17:33:31 +08:00
										 |  |  | 					plugincontext.ProvideService(cfg, localcache.ProvideService(), &pluginstore.FakePluginStore{ | 
					
						
							| 
									
										
										
										
											2023-09-11 19:59:24 +08:00
										 |  |  | 						PluginList: []pluginstore.Plugin{pluginstore.ToGrafanaDTO(p)}, | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2024-01-19 22:56:52 +08:00
										 |  |  | 						&fakeDatasources.FakeCacheService{}, ds, | 
					
						
							|  |  |  | 						pluginSettings.ProvideService(dbtest.NewFakeDB(), | 
					
						
							| 
									
										
										
										
											2024-02-27 19:38:02 +08:00
										 |  |  | 							secretstest.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider()), | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 				) | 
					
						
							| 
									
										
										
										
											2022-11-15 03:08:10 +08:00
										 |  |  | 				hs.QuotaService = quotatest.New(false, nil) | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 			req := srv.NewPostRequest("/api/ds/query", strings.NewReader(tc.request)) | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 22:29:20 +08:00
										 |  |  | 			webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {datasources.ActionQuery: []string{datasources.ScopeAll}}}}) | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 			resp, err := srv.SendJSON(req) | 
					
						
							|  |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			require.Equal(t, tc.expectedStatus, resp.StatusCode) | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			bodyBytes, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 			require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2024-08-17 06:08:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			var responseBody body | 
					
						
							|  |  |  | 			err = json.Unmarshal(bodyBytes, &responseBody) | 
					
						
							|  |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			require.Equal(t, tc.expectedBody.Message, responseBody.Message) | 
					
						
							|  |  |  | 			require.Equal(t, tc.expectedBody.MessageId, responseBody.MessageId) | 
					
						
							|  |  |  | 			require.Equal(t, tc.expectedBody.StatusCode, responseBody.StatusCode) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-15 00:19:57 +08:00
										 |  |  | 			require.NoError(t, resp.Body.Close()) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | } |