mirror of https://github.com/grafana/grafana.git
				
				
				
			Query Service: Fix Time Bug (#108337)
This commit is contained in:
		
							parent
							
								
									7626508842
								
							
						
					
					
						commit
						f34127c449
					
				|  | @ -408,7 +408,12 @@ func (dn *DSNode) Execute(ctx context.Context, now time.Time, _ mathexp.Vars, s | |||
| 		} | ||||
| 	} else { | ||||
| 		// transform request from backend.QueryDataRequest to k8s request
 | ||||
| 		k8sReq := &data.QueryDataRequest{} | ||||
| 		k8sReq := &data.QueryDataRequest{ | ||||
| 			TimeRange: data.TimeRange{ | ||||
| 				From: req.Queries[0].TimeRange.From.Format(time.RFC3339), | ||||
| 				To:   req.Queries[0].TimeRange.To.Format(time.RFC3339), | ||||
| 			}, | ||||
| 		} | ||||
| 		for _, q := range req.Queries { | ||||
| 			var dataQuery data.DataQuery | ||||
| 			err := json.Unmarshal(q.JSON, &dataQuery) | ||||
|  |  | |||
|  | @ -64,3 +64,23 @@ func NewMtDatasourceClientBuilderWithClientSupplier( | |||
| 		logger:         logger, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func NewTestMTDSClientBuilder(isMultiTenant bool, mockClient clientapi.QueryDataClient) MTDatasourceClientBuilder { | ||||
| 	return &testBuilder{ | ||||
| 		mockClient:    mockClient, | ||||
| 		isMultitenant: isMultiTenant, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type testBuilder struct { | ||||
| 	mockClient    clientapi.QueryDataClient | ||||
| 	isMultitenant bool | ||||
| } | ||||
| 
 | ||||
| func (b *testBuilder) BuildClient(pluginId string, uid string) (clientapi.QueryDataClient, bool) { | ||||
| 	if !b.isMultitenant { | ||||
| 		return nil, false | ||||
| 	} | ||||
| 
 | ||||
| 	return b.mockClient, true | ||||
| } | ||||
|  |  | |||
|  | @ -295,7 +295,12 @@ func (s *ServiceImpl) handleQuerySingleDatasource(ctx context.Context, user iden | |||
| 		return s.pluginClient.QueryData(ctx, req) | ||||
| 	} else { // multi tenant flow
 | ||||
| 		// transform request from backend.QueryDataRequest to k8s request
 | ||||
| 		k8sReq := &data.QueryDataRequest{} | ||||
| 		k8sReq := &data.QueryDataRequest{ | ||||
| 			TimeRange: data.TimeRange{ | ||||
| 				From: req.Queries[0].TimeRange.From.Format(time.RFC3339), | ||||
| 				To:   req.Queries[0].TimeRange.To.Format(time.RFC3339), | ||||
| 			}, | ||||
| 		} | ||||
| 		for _, q := range req.Queries { | ||||
| 			var dataQuery data.DataQuery | ||||
| 			err := json.Unmarshal(q.JSON, &dataQuery) | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ import ( | |||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/grafana/grafana-plugin-sdk-go/backend" | ||||
| 	data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 
 | ||||
|  | @ -24,6 +25,7 @@ import ( | |||
| 	"github.com/grafana/grafana/pkg/infra/log" | ||||
| 	"github.com/grafana/grafana/pkg/infra/tracing" | ||||
| 	"github.com/grafana/grafana/pkg/plugins" | ||||
| 	"github.com/grafana/grafana/pkg/registry/apis/query/clientapi" | ||||
| 	"github.com/grafana/grafana/pkg/services/contexthandler" | ||||
| 	"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey" | ||||
| 	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" | ||||
|  | @ -50,7 +52,7 @@ func TestMain(m *testing.M) { | |||
| 
 | ||||
| func TestIntegrationParseMetricRequest(t *testing.T) { | ||||
| 	t.Run("Test a simple single datasource query", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 		mr := metricRequestWithQueries(t, `{ | ||||
| 			"refId": "A", | ||||
| 			"datasource": { | ||||
|  | @ -74,7 +76,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { | |||
| 	}) | ||||
| 
 | ||||
| 	t.Run("Test a single datasource query with expressions", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 		mr := metricRequestWithQueries(t, `{ | ||||
| 			"refId": "A", | ||||
| 			"datasource": { | ||||
|  | @ -114,7 +116,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { | |||
| 	}) | ||||
| 
 | ||||
| 	t.Run("Test a simple mixed datasource query", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 		mr := metricRequestWithQueries(t, `{ | ||||
| 			"refId": "A", | ||||
| 			"datasource": { | ||||
|  | @ -147,7 +149,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { | |||
| 	}) | ||||
| 
 | ||||
| 	t.Run("Test a mixed datasource query with expressions", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 		mr := metricRequestWithQueries(t, `{ | ||||
| 			"refId": "A", | ||||
| 			"datasource": { | ||||
|  | @ -208,7 +210,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { | |||
| 	}) | ||||
| 
 | ||||
| 	t.Run("Header validation", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 		mr := metricRequestWithQueries(t, `{ | ||||
| 			"refId": "A", | ||||
| 			"datasource": { | ||||
|  | @ -250,7 +252,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { | |||
| 	}) | ||||
| 
 | ||||
| 	t.Run("Test a duplicated refId", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 		mr := metricRequestWithQueries(t, `{ | ||||
| 			"refId": "A", | ||||
| 			"datasource": { | ||||
|  | @ -271,7 +273,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { | |||
| 
 | ||||
| func TestIntegrationQueryDataMultipleSources(t *testing.T) { | ||||
| 	t.Run("can query multiple datasources", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 		query1, err := simplejson.NewJson([]byte(` | ||||
| 			{ | ||||
| 				"datasource": { | ||||
|  | @ -320,7 +322,7 @@ func TestIntegrationQueryDataMultipleSources(t *testing.T) { | |||
| 	}) | ||||
| 
 | ||||
| 	t.Run("can query multiple datasources with an expression present", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 		query1, err := simplejson.NewJson([]byte(` | ||||
| 			{ | ||||
| 				"datasource": { | ||||
|  | @ -386,7 +388,7 @@ func TestIntegrationQueryDataMultipleSources(t *testing.T) { | |||
| 	}) | ||||
| 
 | ||||
| 	t.Run("error is returned in query when one of the queries fails", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 
 | ||||
| 		query1, _ := simplejson.NewJson([]byte(` | ||||
| 			{ | ||||
|  | @ -426,7 +428,7 @@ func TestIntegrationQueryDataMultipleSources(t *testing.T) { | |||
| 	}) | ||||
| 
 | ||||
| 	t.Run("ignores a deprecated datasourceID", func(t *testing.T) { | ||||
| 		tc := setup(t) | ||||
| 		tc := setup(t, false, nil) | ||||
| 		query1, err := simplejson.NewJson([]byte(` | ||||
| 			{ | ||||
| 				"datasource": { | ||||
|  | @ -452,7 +454,63 @@ func TestIntegrationQueryDataMultipleSources(t *testing.T) { | |||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func setup(t *testing.T) *testContext { | ||||
| func TestIntegrationQueryDataWithMTDSClient(t *testing.T) { | ||||
| 	t.Run("can run a simple datasource query with a mt ds client", func(t *testing.T) { | ||||
| 		stubbedResponse := &backend.QueryDataResponse{Responses: make(backend.Responses)} | ||||
| 		testClient := &testClient{ | ||||
| 			queryDataStubbedResponse: stubbedResponse, | ||||
| 		} | ||||
| 		tc := setup(t, true, testClient) | ||||
| 		mr := metricRequestWithQueries(t, `{ | ||||
| 			"refId": "A", | ||||
| 			"datasource": { | ||||
| 				"uid": "gIEkMvIVz", | ||||
| 				"type": "postgres" | ||||
| 			} | ||||
| 		}`, `{ | ||||
| 			"refId": "B", | ||||
| 			"datasource": { | ||||
| 				"uid": "gIEkMvIVz", | ||||
| 				"type": "postgres" | ||||
| 			} | ||||
| 		}`) | ||||
| 		mr.From = "2022-01-01" | ||||
| 		mr.To = "2022-01-02" | ||||
| 		ctx := context.Background() | ||||
| 		_, err := tc.queryService.QueryData(ctx, tc.signedInUser, true, mr) | ||||
| 		require.NoError(t, err) | ||||
| 
 | ||||
| 		assert.Equal(t, data.QueryDataRequest{ | ||||
| 			TimeRange: data.TimeRange{ | ||||
| 				From: "2022-01-01T00:00:00Z", | ||||
| 				To:   "2022-01-02T00:00:00Z", | ||||
| 			}, | ||||
| 			Queries: []data.DataQuery{ | ||||
| 				{ | ||||
| 					CommonQueryProperties: data.CommonQueryProperties{ | ||||
| 						RefID: "A", | ||||
| 						Datasource: &data.DataSourceRef{ | ||||
| 							Type: "postgres", | ||||
| 							UID:  "gIEkMvIVz", | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					CommonQueryProperties: data.CommonQueryProperties{ | ||||
| 						RefID: "B", | ||||
| 						Datasource: &data.DataSourceRef{ | ||||
| 							Type: "postgres", | ||||
| 							UID:  "gIEkMvIVz", | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			Debug: false, | ||||
| 		}, testClient.queryDataLastCalledWith) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func setup(t *testing.T, isMultiTenant bool, mockClient clientapi.QueryDataClient) *testContext { | ||||
| 	dss := []*datasources.DataSource{ | ||||
| 		{UID: "gIEkMvIVz", Type: "postgres"}, | ||||
| 		{UID: "sEx6ZvSVk", Type: "testdata"}, | ||||
|  | @ -468,24 +526,55 @@ func setup(t *testing.T) *testContext { | |||
| 	sqlStore, cfg := db.InitTestDBWithCfg(t) | ||||
| 	secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) | ||||
| 	ss := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) | ||||
| 
 | ||||
| 	fakeDatasourceService := &fakeDatasources.FakeDataSourceService{ | ||||
| 		DataSources:           dss, | ||||
| 		SimulatePluginFailure: false, | ||||
| 	} | ||||
| 
 | ||||
| 	pCtxProvider := plugincontext.ProvideService(cfg, | ||||
| 		localcache.ProvideService(), &pluginstore.FakePluginStore{ | ||||
| 	pCtxProvider := plugincontext.ProvideService( | ||||
| 		cfg, | ||||
| 		localcache.ProvideService(), | ||||
| 		&pluginstore.FakePluginStore{ | ||||
| 			PluginList: []pluginstore.Plugin{ | ||||
| 				{JSONData: plugins.JSONData{ID: "postgres"}}, | ||||
| 				{JSONData: plugins.JSONData{ID: "testdata"}}, | ||||
| 				{JSONData: plugins.JSONData{ID: "mysql"}}, | ||||
| 			}, | ||||
| 		}, &fakeDatasources.FakeCacheService{}, fakeDatasourceService, | ||||
| 		pluginSettings.ProvideService(sqlStore, secretsService), pluginconfig.NewFakePluginRequestConfigProvider(), | ||||
| 		}, | ||||
| 		&fakeDatasources.FakeCacheService{}, | ||||
| 		fakeDatasourceService, | ||||
| 		pluginSettings.ProvideService(sqlStore, secretsService), | ||||
| 		pluginconfig.NewFakePluginRequestConfigProvider(), | ||||
| 	) | ||||
| 	exprService := expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, pc, pCtxProvider, | ||||
| 		featuremgmt.WithFeatures(), nil, tracing.InitializeTracerForTest(), mtdsclient.NewNullMTDatasourceClientBuilder()) | ||||
| 	queryService := ProvideService(setting.NewCfg(), dc, exprService, rv, pc, pCtxProvider, mtdsclient.NewNullMTDatasourceClientBuilder()) // provider belonging to this package
 | ||||
| 
 | ||||
| 	var mtdsClientBuilder mtdsclient.MTDatasourceClientBuilder | ||||
| 	if isMultiTenant { | ||||
| 		mtdsClientBuilder = mtdsclient.NewTestMTDSClientBuilder(isMultiTenant, mockClient) | ||||
| 	} else { | ||||
| 		mtdsClientBuilder = mtdsclient.NewTestMTDSClientBuilder(false, nil) | ||||
| 	} | ||||
| 
 | ||||
| 	exprService := expr.ProvideService( | ||||
| 		&setting.Cfg{ExpressionsEnabled: true}, | ||||
| 		pc, | ||||
| 		pCtxProvider, | ||||
| 		featuremgmt.WithFeatures(), | ||||
| 		nil, | ||||
| 		tracing.InitializeTracerForTest(), | ||||
| 		mtdsClientBuilder, | ||||
| 	) | ||||
| 
 | ||||
| 	queryService := ProvideService( | ||||
| 		setting.NewCfg(), | ||||
| 		dc, | ||||
| 		exprService, | ||||
| 		rv, | ||||
| 		pc, | ||||
| 		pCtxProvider, | ||||
| 		mtdsClientBuilder, | ||||
| 	) | ||||
| 
 | ||||
| 	return &testContext{ | ||||
| 		pluginContext:          pc, | ||||
| 		secretStore:            ss, | ||||
|  | @ -573,3 +662,20 @@ func (c *fakePluginClient) QueryData(ctx context.Context, req *backend.QueryData | |||
| 
 | ||||
| 	return &backend.QueryDataResponse{Responses: make(backend.Responses)}, nil | ||||
| } | ||||
| 
 | ||||
| type testClient struct { | ||||
| 	queryDataLastCalledWith  data.QueryDataRequest | ||||
| 	queryDataStubbedResponse *backend.QueryDataResponse | ||||
| 	queryDataStubbedError    error | ||||
| } | ||||
| 
 | ||||
| func (c *testClient) QueryData(ctx context.Context, req data.QueryDataRequest) (*backend.QueryDataResponse, error) { | ||||
| 	c.queryDataLastCalledWith = req | ||||
| 	if c.queryDataStubbedError != nil { | ||||
| 		return nil, c.queryDataStubbedError | ||||
| 	} | ||||
| 	if c.queryDataStubbedResponse != nil { | ||||
| 		return c.queryDataStubbedResponse, nil | ||||
| 	} | ||||
| 	return nil, errors.New("no response stubbed") | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue