| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | package query | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2022-11-15 08:35:50 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2023-02-02 22:43:07 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2023-07-06 22:15:43 +08:00
										 |  |  | 	"net/http/httptest" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	"testing" | 
					
						
							| 
									
										
										
										
											2023-07-06 22:15:43 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-28 00:23:15 +08:00
										 |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend" | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 	"github.com/stretchr/testify/assert" | 
					
						
							| 
									
										
										
										
											2022-06-28 00:23:15 +08:00
										 |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/api/dtos" | 
					
						
							| 
									
										
										
										
											2024-06-13 12:11:35 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/apimachinery/identity" | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/components/simplejson" | 
					
						
							| 
									
										
										
										
											2022-10-19 21:02:15 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/expr" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/infra/db" | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/localcache" | 
					
						
							| 
									
										
										
										
											2022-08-26 05:04:44 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/log" | 
					
						
							| 
									
										
										
										
											2023-04-18 20:04:51 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/tracing" | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins" | 
					
						
							| 
									
										
										
										
											2023-07-06 22:15:43 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/contexthandler" | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey" | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | 	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" | 
					
						
							| 
									
										
										
										
											2022-06-28 00:23:15 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/datasources" | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 	fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes" | 
					
						
							| 
									
										
										
										
											2022-04-26 00:57:45 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/featuremgmt" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							|  |  |  | 	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-04-26 00:57:45 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/secrets/fakes" | 
					
						
							| 
									
										
										
										
											2022-08-26 05:04:44 +08:00
										 |  |  | 	secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore" | 
					
						
							|  |  |  | 	secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager" | 
					
						
							| 
									
										
										
										
											2022-08-10 17:56:48 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/user" | 
					
						
							| 
									
										
										
										
											2022-10-19 21:02:15 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2024-02-09 22:35:39 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/tests/testsuite" | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/web" | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-09 22:35:39 +08:00
										 |  |  | func TestMain(m *testing.M) { | 
					
						
							|  |  |  | 	testsuite.Run(m) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | func TestParseMetricRequest(t *testing.T) { | 
					
						
							|  |  |  | 	t.Run("Test a simple single datasource query", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 		tc := setup(t) | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 		mr := metricRequestWithQueries(t, `{ | 
					
						
							|  |  |  | 			"refId": "A", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "gIEkMvIVz", | 
					
						
							|  |  |  | 				"type": "postgres" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "B", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "gIEkMvIVz", | 
					
						
							|  |  |  | 				"type": "postgres" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`) | 
					
						
							|  |  |  | 		parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.NotNil(t, parsedReq) | 
					
						
							|  |  |  | 		assert.False(t, parsedReq.hasExpression) | 
					
						
							|  |  |  | 		assert.Len(t, parsedReq.parsedQueries, 1) | 
					
						
							|  |  |  | 		assert.Contains(t, parsedReq.parsedQueries, "gIEkMvIVz") | 
					
						
							|  |  |  | 		assert.Len(t, parsedReq.getFlattenedQueries(), 2) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("Test a single datasource query with expressions", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 		tc := setup(t) | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 		mr := metricRequestWithQueries(t, `{ | 
					
						
							|  |  |  | 			"refId": "A", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "gIEkMvIVz", | 
					
						
							|  |  |  | 				"type": "postgres" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "B", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"type": "__expr__", | 
					
						
							|  |  |  | 				"uid": "__expr__", | 
					
						
							|  |  |  | 				"name": "Expression" | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			"type": "math", | 
					
						
							|  |  |  | 			"expression": "$A - 50" | 
					
						
							|  |  |  | 		}`) | 
					
						
							|  |  |  | 		parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.NotNil(t, parsedReq) | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 		require.True(t, parsedReq.hasExpression) | 
					
						
							|  |  |  | 		require.Len(t, parsedReq.parsedQueries, 2) | 
					
						
							|  |  |  | 		require.Contains(t, parsedReq.parsedQueries, "gIEkMvIVz") | 
					
						
							|  |  |  | 		require.Len(t, parsedReq.getFlattenedQueries(), 2) | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 		// Make sure we end up with something valid
 | 
					
						
							|  |  |  | 		_, err = tc.queryService.handleExpressions(context.Background(), tc.signedInUser, parsedReq) | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 		t.Run("Should forward user and org ID to QueryData from expression request", func(t *testing.T) { | 
					
						
							|  |  |  | 			require.NotNil(t, tc.pluginContext.req) | 
					
						
							|  |  |  | 			require.NotNil(t, tc.pluginContext.req.PluginContext.User) | 
					
						
							|  |  |  | 			require.Equal(t, tc.signedInUser.Login, tc.pluginContext.req.PluginContext.User.Login) | 
					
						
							|  |  |  | 			require.Equal(t, tc.signedInUser.Name, tc.pluginContext.req.PluginContext.User.Name) | 
					
						
							|  |  |  | 			require.Equal(t, tc.signedInUser.Email, tc.pluginContext.req.PluginContext.User.Email) | 
					
						
							|  |  |  | 			require.Equal(t, string(tc.signedInUser.OrgRole), tc.pluginContext.req.PluginContext.User.Role) | 
					
						
							|  |  |  | 			require.Equal(t, tc.signedInUser.OrgID, tc.pluginContext.req.PluginContext.OrgID) | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("Test a simple mixed datasource query", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 		tc := setup(t) | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 		mr := metricRequestWithQueries(t, `{ | 
					
						
							|  |  |  | 			"refId": "A", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "gIEkMvIVz", | 
					
						
							|  |  |  | 				"type": "postgres" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "B", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "sEx6ZvSVk", | 
					
						
							|  |  |  | 				"type": "testdata" | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "C", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "sEx6ZvSVk", | 
					
						
							|  |  |  | 				"type": "testdata" | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 		}`) | 
					
						
							|  |  |  | 		parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.NotNil(t, parsedReq) | 
					
						
							|  |  |  | 		assert.False(t, parsedReq.hasExpression) | 
					
						
							|  |  |  | 		assert.Len(t, parsedReq.parsedQueries, 2) | 
					
						
							|  |  |  | 		assert.Contains(t, parsedReq.parsedQueries, "gIEkMvIVz") | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 		assert.Len(t, parsedReq.parsedQueries["gIEkMvIVz"], 1) | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 		assert.Contains(t, parsedReq.parsedQueries, "sEx6ZvSVk") | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 		assert.Len(t, parsedReq.parsedQueries["sEx6ZvSVk"], 2) | 
					
						
							|  |  |  | 		assert.Len(t, parsedReq.getFlattenedQueries(), 3) | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("Test a mixed datasource query with expressions", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 		tc := setup(t) | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 		mr := metricRequestWithQueries(t, `{ | 
					
						
							|  |  |  | 			"refId": "A", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "gIEkMvIVz", | 
					
						
							|  |  |  | 				"type": "postgres" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "B", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "sEx6ZvSVk", | 
					
						
							|  |  |  | 				"type": "testdata" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "A_resample", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"type": "__expr__", | 
					
						
							|  |  |  | 				"uid": "__expr__", | 
					
						
							|  |  |  | 				"name": "Expression" | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			"expression": "A", | 
					
						
							|  |  |  | 			"type": "resample", | 
					
						
							|  |  |  | 			"downsampler": "mean", | 
					
						
							|  |  |  | 			"upsampler": "fillna", | 
					
						
							|  |  |  | 			"window": "10s" | 
					
						
							|  |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "B_resample", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"type": "__expr__", | 
					
						
							|  |  |  | 				"uid": "__expr__", | 
					
						
							|  |  |  | 				"name": "Expression" | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			"expression": "B", | 
					
						
							|  |  |  | 			"type": "resample", | 
					
						
							|  |  |  | 			"downsampler": "mean", | 
					
						
							|  |  |  | 			"upsampler": "fillna", | 
					
						
							|  |  |  | 			"window": "10s" | 
					
						
							|  |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "C", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"type": "__expr__", | 
					
						
							|  |  |  | 				"uid": "__expr__", | 
					
						
							|  |  |  | 				"name": "Expression" | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			"type": "math", | 
					
						
							|  |  |  | 			"expression": "$A_resample + $B_resample" | 
					
						
							|  |  |  | 		}`) | 
					
						
							|  |  |  | 		parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.NotNil(t, parsedReq) | 
					
						
							|  |  |  | 		assert.True(t, parsedReq.hasExpression) | 
					
						
							|  |  |  | 		assert.Len(t, parsedReq.parsedQueries, 3) | 
					
						
							|  |  |  | 		assert.Contains(t, parsedReq.parsedQueries, "gIEkMvIVz") | 
					
						
							|  |  |  | 		assert.Contains(t, parsedReq.parsedQueries, "sEx6ZvSVk") | 
					
						
							|  |  |  | 		assert.Len(t, parsedReq.getFlattenedQueries(), 5) | 
					
						
							|  |  |  | 		// Make sure we end up with something valid
 | 
					
						
							|  |  |  | 		_, err = tc.queryService.handleExpressions(context.Background(), tc.signedInUser, parsedReq) | 
					
						
							|  |  |  | 		assert.NoError(t, err) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-11-15 08:35:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("Header validation", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-11-28 20:40:06 +08:00
										 |  |  | 		tc := setup(t) | 
					
						
							| 
									
										
										
										
											2022-11-15 08:35:50 +08:00
										 |  |  | 		mr := metricRequestWithQueries(t, `{ | 
					
						
							|  |  |  | 			"refId": "A", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "gIEkMvIVz", | 
					
						
							|  |  |  | 				"type": "postgres" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "B", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "sEx6ZvSVk", | 
					
						
							|  |  |  | 				"type": "testdata" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`) | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 		httpreq, err := http.NewRequest(http.MethodPost, "http://localhost/", bytes.NewReader([]byte{})) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | 		reqCtx := &contextmodel.ReqContext{ | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 			Context: &web.Context{}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ctx := ctxkey.Set(context.Background(), reqCtx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		*httpreq = *httpreq.WithContext(ctx) | 
					
						
							|  |  |  | 		reqCtx.Req = httpreq | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-15 08:35:50 +08:00
										 |  |  | 		httpreq.Header.Add("X-Datasource-Uid", "gIEkMvIVz") | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 		_, err = tc.queryService.parseMetricRequest(httpreq.Context(), tc.signedInUser, true, mr) | 
					
						
							| 
									
										
										
										
											2022-11-18 18:46:50 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2022-11-15 08:35:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// With the second value it is OK
 | 
					
						
							|  |  |  | 		httpreq.Header.Add("X-Datasource-Uid", "sEx6ZvSVk") | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 		_, err = tc.queryService.parseMetricRequest(httpreq.Context(), tc.signedInUser, true, mr) | 
					
						
							| 
									
										
										
										
											2022-11-15 08:35:50 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Single header with comma syntax
 | 
					
						
							|  |  |  | 		httpreq, _ = http.NewRequest(http.MethodPost, "http://localhost/", bytes.NewReader([]byte{})) | 
					
						
							|  |  |  | 		httpreq.Header.Set("X-Datasource-Uid", "gIEkMvIVz, sEx6ZvSVk") | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 		_, err = tc.queryService.parseMetricRequest(httpreq.Context(), tc.signedInUser, true, mr) | 
					
						
							| 
									
										
										
										
											2022-11-15 08:35:50 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-02-09 17:11:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("Test a duplicated refId", func(t *testing.T) { | 
					
						
							|  |  |  | 		tc := setup(t) | 
					
						
							|  |  |  | 		mr := metricRequestWithQueries(t, `{ | 
					
						
							|  |  |  | 			"refId": "A", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "gIEkMvIVz", | 
					
						
							|  |  |  | 				"type": "postgres" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`, `{ | 
					
						
							|  |  |  | 			"refId": "A", | 
					
						
							|  |  |  | 			"datasource": { | 
					
						
							|  |  |  | 				"uid": "gIEkMvIVz", | 
					
						
							|  |  |  | 				"type": "postgres" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}`) | 
					
						
							|  |  |  | 		_, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) | 
					
						
							|  |  |  | 		require.Error(t, err) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | func TestQueryDataMultipleSources(t *testing.T) { | 
					
						
							|  |  |  | 	t.Run("can query multiple datasources", func(t *testing.T) { | 
					
						
							|  |  |  | 		tc := setup(t) | 
					
						
							|  |  |  | 		query1, err := simplejson.NewJson([]byte(` | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				"datasource": { | 
					
						
							|  |  |  | 					"type": "mysql", | 
					
						
							|  |  |  | 					"uid": "ds1" | 
					
						
							| 
									
										
										
										
											2022-11-28 23:21:54 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				"refId": "A" | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		`)) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		query2, err := simplejson.NewJson([]byte(` | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				"datasource": { | 
					
						
							|  |  |  | 					"type": "mysql", | 
					
						
							|  |  |  | 					"uid": "ds2" | 
					
						
							| 
									
										
										
										
											2022-11-28 23:21:54 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				"refId": "B" | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		`)) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		queries := []*simplejson.Json{query1, query2} | 
					
						
							|  |  |  | 		reqDTO := dtos.MetricRequest{ | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  | 			From:    "2022-01-01", | 
					
						
							|  |  |  | 			To:      "2022-01-02", | 
					
						
							|  |  |  | 			Queries: queries, | 
					
						
							|  |  |  | 			Debug:   false, | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-06 22:15:43 +08:00
										 |  |  | 		req, err := http.NewRequest("POST", "http://localhost:3000", nil) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		reqCtx := &contextmodel.ReqContext{ | 
					
						
							|  |  |  | 			SkipQueryCache: false, | 
					
						
							|  |  |  | 			Context: &web.Context{ | 
					
						
							|  |  |  | 				Resp: web.NewResponseWriter(http.MethodGet, httptest.NewRecorder()), | 
					
						
							|  |  |  | 				Req:  req, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ctx := ctxkey.Set(context.Background(), reqCtx) | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-06 22:15:43 +08:00
										 |  |  | 		_, err = tc.queryService.QueryData(ctx, tc.signedInUser, true, reqDTO) | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-07-06 22:15:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// response headers should be merged
 | 
					
						
							|  |  |  | 		header := contexthandler.FromContext(ctx).Resp.Header() | 
					
						
							|  |  |  | 		assert.Len(t, header.Values("test"), 2) | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("can query multiple datasources with an expression present", func(t *testing.T) { | 
					
						
							|  |  |  | 		tc := setup(t) | 
					
						
							|  |  |  | 		query1, err := simplejson.NewJson([]byte(` | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				"datasource": { | 
					
						
							|  |  |  | 					"type": "mysql", | 
					
						
							|  |  |  | 					"uid": "ds1" | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				"refId": "A" | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		`)) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		query2, err := simplejson.NewJson([]byte(` | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				"datasource": { | 
					
						
							|  |  |  | 					"type": "mysql", | 
					
						
							|  |  |  | 					"uid": "ds2" | 
					
						
							| 
									
										
										
										
											2022-11-28 23:21:54 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				"refId": "B" | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		`)) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		query3, err := simplejson.NewJson([]byte(` | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				"datasource": { | 
					
						
							|  |  |  | 					"name": "Expression", | 
					
						
							|  |  |  | 					"type": "__expr__", | 
					
						
							|  |  |  | 					"uid": "__expr__" | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				"expression": "$A + 1", | 
					
						
							|  |  |  | 				"hide": false, | 
					
						
							|  |  |  | 				"refId": "EXPRESSION", | 
					
						
							|  |  |  | 				"type": "math" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		`)) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		queries := []*simplejson.Json{query1, query2, query3} | 
					
						
							|  |  |  | 		reqDTO := dtos.MetricRequest{ | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  | 			From:    "2022-01-01", | 
					
						
							|  |  |  | 			To:      "2022-01-02", | 
					
						
							|  |  |  | 			Queries: queries, | 
					
						
							|  |  |  | 			Debug:   false, | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-16 23:17:24 +08:00
										 |  |  | 		// without query parameter
 | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 		_, err = tc.queryService.QueryData(context.Background(), tc.signedInUser, true, reqDTO) | 
					
						
							| 
									
										
										
										
											2022-11-16 23:17:24 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 		httpreq, err := http.NewRequest(http.MethodPost, "http://localhost/ds/query?expression=true", bytes.NewReader([]byte{})) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | 		reqCtx := &contextmodel.ReqContext{ | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 			Context: &web.Context{}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ctx := ctxkey.Set(context.Background(), reqCtx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		*httpreq = *httpreq.WithContext(ctx) | 
					
						
							|  |  |  | 		reqCtx.Req = httpreq | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-16 23:17:24 +08:00
										 |  |  | 		httpreq.Header.Add("X-Datasource-Uid", "gIEkMvIVz") | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-16 23:17:24 +08:00
										 |  |  | 		// with query parameter
 | 
					
						
							| 
									
										
										
										
											2022-12-02 02:08:36 +08:00
										 |  |  | 		_, err = tc.queryService.QueryData(httpreq.Context(), tc.signedInUser, true, reqDTO) | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-10-06 05:45:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 23:21:54 +08:00
										 |  |  | 	t.Run("error is returned in query when one of the queries fails", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-10-06 05:45:17 +08:00
										 |  |  | 		tc := setup(t) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		query1, _ := simplejson.NewJson([]byte(` | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				"datasource": { | 
					
						
							|  |  |  | 					"type": "mysql", | 
					
						
							|  |  |  | 					"uid": "ds1" | 
					
						
							| 
									
										
										
										
											2022-11-28 23:21:54 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				"refId": "A" | 
					
						
							| 
									
										
										
										
											2022-10-06 05:45:17 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		`)) | 
					
						
							|  |  |  | 		query2, _ := simplejson.NewJson([]byte(` | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				"datasource": { | 
					
						
							|  |  |  | 					"type": "prometheus", | 
					
						
							|  |  |  | 					"uid": "ds2" | 
					
						
							|  |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2022-11-28 23:21:54 +08:00
										 |  |  | 				"refId": "B", | 
					
						
							| 
									
										
										
										
											2022-10-06 05:45:17 +08:00
										 |  |  | 				"queryType": "FAIL" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		`)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		queries := []*simplejson.Json{query1, query2} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		reqDTO := dtos.MetricRequest{ | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  | 			From:    "2022-01-01", | 
					
						
							|  |  |  | 			To:      "2022-01-02", | 
					
						
							|  |  |  | 			Queries: queries, | 
					
						
							|  |  |  | 			Debug:   false, | 
					
						
							| 
									
										
										
										
											2022-10-06 05:45:17 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 23:21:54 +08:00
										 |  |  | 		res, err := tc.queryService.QueryData(context.Background(), tc.signedInUser, true, reqDTO) | 
					
						
							| 
									
										
										
										
											2022-10-06 05:45:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 23:21:54 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Error(t, res.Responses["B"].Error) | 
					
						
							|  |  |  | 		// Responses aren't mocked, so a "healthy" query will just return an empty response
 | 
					
						
							|  |  |  | 		require.NotContains(t, res.Responses, "A") | 
					
						
							| 
									
										
										
										
											2022-10-06 05:45:17 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-02-02 22:43:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("ignores a deprecated datasourceID", func(t *testing.T) { | 
					
						
							|  |  |  | 		tc := setup(t) | 
					
						
							|  |  |  | 		query1, err := simplejson.NewJson([]byte(` | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				"datasource": { | 
					
						
							|  |  |  | 					"type": "mysql", | 
					
						
							|  |  |  | 					"uid": "ds1" | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				"datasourceId": 1, | 
					
						
							|  |  |  | 				"refId": "A" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		`)) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		queries := []*simplejson.Json{query1} | 
					
						
							|  |  |  | 		reqDTO := dtos.MetricRequest{ | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  | 			From:    "2022-01-01", | 
					
						
							|  |  |  | 			To:      "2022-01-02", | 
					
						
							|  |  |  | 			Queries: queries, | 
					
						
							|  |  |  | 			Debug:   false, | 
					
						
							| 
									
										
										
										
											2023-02-02 22:43:07 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err = tc.queryService.QueryData(context.Background(), tc.signedInUser, true, reqDTO) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 00:57:45 +08:00
										 |  |  | func setup(t *testing.T) *testContext { | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	dss := []*datasources.DataSource{ | 
					
						
							|  |  |  | 		{UID: "gIEkMvIVz", Type: "postgres"}, | 
					
						
							|  |  |  | 		{UID: "sEx6ZvSVk", Type: "testdata"}, | 
					
						
							|  |  |  | 		{UID: "ds1", Type: "mysql"}, | 
					
						
							|  |  |  | 		{UID: "ds2", Type: "mysql"}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 	t.Helper() | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	pc := &fakePluginClient{} | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	dc := &fakeDataSourceCache{cache: dss} | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	rv := &fakePluginRequestValidator{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-24 16:38:40 +08:00
										 |  |  | 	sqlStore, cfg := db.InitTestDBWithCfg(t) | 
					
						
							| 
									
										
										
										
											2022-08-26 05:04:44 +08:00
										 |  |  | 	secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) | 
					
						
							|  |  |  | 	ss := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 	fakeDatasourceService := &fakeDatasources.FakeDataSourceService{ | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 		DataSources:           dss, | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 		SimulatePluginFailure: false, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-24 16:38:40 +08:00
										 |  |  | 	pCtxProvider := plugincontext.ProvideService(cfg, | 
					
						
							| 
									
										
										
										
											2023-09-11 19:59:24 +08:00
										 |  |  | 		localcache.ProvideService(), &pluginstore.FakePluginStore{ | 
					
						
							|  |  |  | 			PluginList: []pluginstore.Plugin{ | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 				{JSONData: plugins.JSONData{ID: "postgres"}}, | 
					
						
							|  |  |  | 				{JSONData: plugins.JSONData{ID: "testdata"}}, | 
					
						
							|  |  |  | 				{JSONData: plugins.JSONData{ID: "mysql"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2024-01-19 22:56:52 +08:00
										 |  |  | 		}, &fakeDatasources.FakeCacheService{}, fakeDatasourceService, | 
					
						
							| 
									
										
										
										
											2024-02-27 19:38:02 +08:00
										 |  |  | 		pluginSettings.ProvideService(sqlStore, secretsService), pluginconfig.NewFakePluginRequestConfigProvider(), | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	exprService := expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, pc, pCtxProvider, | 
					
						
							| 
									
										
										
										
											2024-04-05 00:22:31 +08:00
										 |  |  | 		featuremgmt.WithFeatures(), nil, tracing.InitializeTracerForTest()) | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	queryService := ProvideService(setting.NewCfg(), dc, exprService, rv, pc, pCtxProvider) // provider belonging to this package
 | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	return &testContext{ | 
					
						
							|  |  |  | 		pluginContext:          pc, | 
					
						
							| 
									
										
										
										
											2022-04-26 00:57:45 +08:00
										 |  |  | 		secretStore:            ss, | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 		pluginRequestValidator: rv, | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 		queryService:           queryService, | 
					
						
							| 
									
										
										
										
											2024-06-13 12:11:35 +08:00
										 |  |  | 		signedInUser:           &user.SignedInUser{OrgID: 1, Login: "login", Name: "name", Email: "email", OrgRole: identity.RoleAdmin}, | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type testContext struct { | 
					
						
							|  |  |  | 	pluginContext          *fakePluginClient | 
					
						
							| 
									
										
										
										
											2022-08-26 05:04:44 +08:00
										 |  |  | 	secretStore            secretskvs.SecretsKVStore | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	pluginRequestValidator *fakePluginRequestValidator | 
					
						
							| 
									
										
										
										
											2023-03-17 02:39:17 +08:00
										 |  |  | 	queryService           *ServiceImpl // implementation belonging to this package
 | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | 	signedInUser           *user.SignedInUser | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-14 22:27:06 +08:00
										 |  |  | func metricRequestWithQueries(t *testing.T, rawQueries ...string) dtos.MetricRequest { | 
					
						
							|  |  |  | 	t.Helper() | 
					
						
							|  |  |  | 	queries := make([]*simplejson.Json, 0) | 
					
						
							|  |  |  | 	for _, q := range rawQueries { | 
					
						
							|  |  |  | 		json, err := simplejson.NewJson([]byte(q)) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		queries = append(queries, json) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return dtos.MetricRequest{ | 
					
						
							|  |  |  | 		From:    "now-1h", | 
					
						
							|  |  |  | 		To:      "now", | 
					
						
							|  |  |  | 		Queries: queries, | 
					
						
							|  |  |  | 		Debug:   false, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | type fakePluginRequestValidator struct { | 
					
						
							|  |  |  | 	err error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (rv *fakePluginRequestValidator) Validate(dsURL string, req *http.Request) error { | 
					
						
							|  |  |  | 	return rv.err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type fakeDataSourceCache struct { | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	cache []*datasources.DataSource | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-30 22:51:18 +08:00
										 |  |  | func (c *fakeDataSourceCache) GetDatasource(ctx context.Context, datasourceID int64, user identity.Requester, skipCache bool) (*datasources.DataSource, error) { | 
					
						
							| 
									
										
										
										
											2023-02-02 22:43:07 +08:00
										 |  |  | 	// deprecated: fake an error to ensure we are using GetDatasourceByUID
 | 
					
						
							|  |  |  | 	return nil, fmt.Errorf("not found") | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-30 22:51:18 +08:00
										 |  |  | func (c *fakeDataSourceCache) GetDatasourceByUID(ctx context.Context, datasourceUID string, user identity.Requester, skipCache bool) (*datasources.DataSource, error) { | 
					
						
							| 
									
										
										
										
											2023-06-08 19:59:51 +08:00
										 |  |  | 	for _, ds := range c.cache { | 
					
						
							|  |  |  | 		if ds.UID == datasourceUID { | 
					
						
							|  |  |  | 			return ds, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil, fmt.Errorf("not found") | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type fakePluginClient struct { | 
					
						
							|  |  |  | 	plugins.Client | 
					
						
							|  |  |  | 	req *backend.QueryDataRequest | 
					
						
							| 
									
										
										
										
											2023-07-06 22:15:43 +08:00
										 |  |  | 	mu  sync.Mutex | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *fakePluginClient) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { | 
					
						
							| 
									
										
										
										
											2023-07-06 22:15:43 +08:00
										 |  |  | 	c.mu.Lock() | 
					
						
							|  |  |  | 	defer c.mu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | 	c.req = req | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// If an expression query ends up getting directly queried, we want it to return an error in our test.
 | 
					
						
							| 
									
										
										
										
											2023-02-01 01:50:10 +08:00
										 |  |  | 	if req.PluginContext.PluginID == expr.DatasourceUID { | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 		return nil, errors.New("cant query an expression datasource") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-06 05:45:17 +08:00
										 |  |  | 	if req.Queries[0].QueryType == "FAIL" { | 
					
						
							|  |  |  | 		return nil, errors.New("plugin client failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-06 22:15:43 +08:00
										 |  |  | 	if reqCtx := contexthandler.FromContext(ctx); reqCtx != nil && reqCtx.Resp != nil { | 
					
						
							|  |  |  | 		reqCtx.Resp.Header().Add("test", fmt.Sprintf("header-%d", time.Now().Nanosecond())) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 23:11:10 +08:00
										 |  |  | 	return &backend.QueryDataResponse{Responses: make(backend.Responses)}, nil | 
					
						
							| 
									
										
										
										
											2022-01-08 03:45:26 +08:00
										 |  |  | } |