[v9.2.x] SSE: Make sure to forward headers, user and cookies/OAuth token (#58897) (#59430)

SSE: Make sure to forward headers, user and cookies/OAuth token (#58897)

Fixes #58793 and Fixes https://github.com/grafana/azure-data-explorer-datasource/issues/513

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
(cherry picked from commit 5623b5afaf)

Co-authored-by: Kyle Brandt <kyle@grafana.com>
This commit is contained in:
Marcus Efraimsson
2022-11-28 19:06:59 +01:00
committed by GitHub
co-authored by Kyle Brandt
parent 8bb5b17692
commit 9b7ad3d663
7 changed files with 429 additions and 66 deletions
+6 -5
View File
@@ -143,11 +143,12 @@ func (s *Service) buildGraph(req *Request) (*simple.DirectedGraph, error) {
}
rn := &rawNode{
Query: rawQueryProp,
RefID: query.RefID,
TimeRange: query.TimeRange,
QueryType: query.QueryType,
DataSource: query.DataSource,
Query: rawQueryProp,
RefID: query.RefID,
TimeRange: query.TimeRange,
QueryType: query.QueryType,
DataSource: query.DataSource,
QueryEnricher: query.QueryEnricher,
}
var node Node
+41 -33
View File
@@ -42,11 +42,12 @@ type baseNode struct {
}
type rawNode struct {
RefID string `json:"refId"`
Query map[string]interface{}
QueryType string
TimeRange TimeRange
DataSource *datasources.DataSource
RefID string `json:"refId"`
Query map[string]interface{}
QueryType string
TimeRange TimeRange
DataSource *datasources.DataSource
QueryEnricher QueryDataRequestEnricher
}
func (rn *rawNode) GetCommandType() (c CommandType, err error) {
@@ -137,8 +138,9 @@ const (
// DSNode is a DPNode that holds a datasource request.
type DSNode struct {
baseNode
query json.RawMessage
datasource *datasources.DataSource
query json.RawMessage
datasource *datasources.DataSource
queryEnricher QueryDataRequestEnricher
orgID int64
queryType string
@@ -164,14 +166,15 @@ func (s *Service) buildDSNode(dp *simple.DirectedGraph, rn *rawNode, req *Reques
id: dp.NewNode().ID(),
refID: rn.RefID,
},
orgID: req.OrgId,
query: json.RawMessage(encodedQuery),
queryType: rn.QueryType,
intervalMS: defaultIntervalMS,
maxDP: defaultMaxDP,
timeRange: rn.TimeRange,
request: *req,
datasource: rn.DataSource,
orgID: req.OrgId,
query: json.RawMessage(encodedQuery),
queryType: rn.QueryType,
intervalMS: defaultIntervalMS,
maxDP: defaultMaxDP,
timeRange: rn.TimeRange,
request: *req,
datasource: rn.DataSource,
queryEnricher: rn.QueryEnricher,
}
var floatIntervalMS float64
@@ -205,27 +208,32 @@ func (dn *DSNode) Execute(ctx context.Context, vars mathexp.Vars, s *Service) (m
OrgID: dn.orgID,
DataSourceInstanceSettings: dsInstanceSettings,
PluginID: dn.datasource.Type,
User: dn.request.User,
}
q := []backend.DataQuery{
{
RefID: dn.refID,
MaxDataPoints: dn.maxDP,
Interval: time.Duration(int64(time.Millisecond) * dn.intervalMS),
JSON: dn.query,
TimeRange: backend.TimeRange{
From: dn.timeRange.From,
To: dn.timeRange.To,
},
QueryType: dn.queryType,
},
}
resp, err := s.dataService.QueryData(ctx, &backend.QueryDataRequest{
req := &backend.QueryDataRequest{
PluginContext: pc,
Queries: q,
Headers: dn.request.Headers,
})
Queries: []backend.DataQuery{
{
RefID: dn.refID,
MaxDataPoints: dn.maxDP,
Interval: time.Duration(int64(time.Millisecond) * dn.intervalMS),
JSON: dn.query,
TimeRange: backend.TimeRange{
From: dn.timeRange.From,
To: dn.timeRange.To,
},
QueryType: dn.queryType,
},
},
Headers: dn.request.Headers,
}
if dn.queryEnricher != nil {
ctx = dn.queryEnricher(ctx, req)
}
resp, err := s.dataService.QueryData(ctx, req)
if err != nil {
return mathexp.Results{}, err
}
+5
View File
@@ -35,14 +35,19 @@ type Request struct {
Debug bool
OrgId int64
Queries []Query
User *backend.User
}
// QueryDataRequestEnricher function definition for enriching a backend.QueryDataRequest request.
type QueryDataRequestEnricher func(ctx context.Context, req *backend.QueryDataRequest) context.Context
// Query is like plugins.DataSubQuery, but with a a time range, and only the UID
// for the data source. Also interval is a time.Duration.
type Query struct {
RefID string
TimeRange TimeRange
DataSource *datasources.DataSource `json:"datasource"`
QueryEnricher QueryDataRequestEnricher
JSON json.RawMessage
Interval time.Duration
QueryType string
@@ -132,7 +132,7 @@ func buildQueryDataService(t *testing.T, cs datasources.CacheService, fpc *fakeP
}
return query.ProvideService(
nil,
setting.NewCfg(),
cs,
nil,
&fakePluginRequestValidator{},
+13 -15
View File
@@ -3,7 +3,6 @@ package query
import (
"context"
"fmt"
"net/http"
"time"
"github.com/grafana/grafana/pkg/api/dtos"
@@ -112,10 +111,17 @@ func (s *Service) QueryDataMultipleSources(ctx context.Context, user *user.Signe
// handleExpressions handles POST /api/ds/query when there is an expression.
func (s *Service) handleExpressions(ctx context.Context, user *user.SignedInUser, parsedReq *parsedRequest) (*backend.QueryDataResponse, error) {
exprReq := expr.Request{
OrgId: user.OrgID,
Queries: []expr.Query{},
}
if user != nil { // for passthrough authentication, SSE does not authenticate
exprReq.User = adapters.BackendUserFromSignedInUser(user)
exprReq.OrgId = user.OrgID
}
disallowedCookies := []string{s.cfg.LoginCookieName}
queryEnrichers := parsedReq.createDataSourceQueryEnrichers(ctx, user, s.oAuthTokenService, disallowedCookies)
for _, pq := range parsedReq.parsedQueries {
if pq.datasource == nil {
return nil, ErrMissingDataSourceInfo.Build(errutil.TemplateData{
@@ -136,6 +142,7 @@ func (s *Service) handleExpressions(ctx context.Context, user *user.SignedInUser
From: pq.query.TimeRange.From,
To: pq.query.TimeRange.To,
},
QueryEnricher: queryEnrichers[pq.datasource.Uid],
})
}
@@ -168,10 +175,11 @@ func (s *Service) handleQueryData(ctx context.Context, user *user.SignedInUser,
Queries: []backend.DataQuery{},
}
disallowedCookies := []string{s.cfg.LoginCookieName}
middlewares := []httpclient.Middleware{}
if parsedReq.httpRequest != nil {
middlewares = append(middlewares,
httpclientprovider.ForwardedCookiesMiddleware(parsedReq.httpRequest.Cookies(), ds.AllowedCookies(), []string{s.cfg.LoginCookieName}),
httpclientprovider.ForwardedCookiesMiddleware(parsedReq.httpRequest.Cookies(), ds.AllowedCookies(), disallowedCookies),
)
}
@@ -188,7 +196,7 @@ func (s *Service) handleQueryData(ctx context.Context, user *user.SignedInUser,
}
if parsedReq.httpRequest != nil {
proxyutil.ClearCookieHeader(parsedReq.httpRequest, ds.AllowedCookies(), []string{s.cfg.LoginCookieName})
proxyutil.ClearCookieHeader(parsedReq.httpRequest, ds.AllowedCookies(), disallowedCookies)
if cookieStr := parsedReq.httpRequest.Header.Get("Cookie"); cookieStr != "" {
req.Headers["Cookie"] = cookieStr
}
@@ -203,17 +211,7 @@ func (s *Service) handleQueryData(ctx context.Context, user *user.SignedInUser,
return s.pluginClient.QueryData(ctx, req)
}
type parsedQuery struct {
datasource *datasources.DataSource
query backend.DataQuery
}
type parsedRequest struct {
hasExpression bool
parsedQueries []parsedQuery
httpRequest *http.Request
}
// parseRequest parses a request into parsed queries grouped by datasource uid
func (s *Service) parseMetricRequest(ctx context.Context, user *user.SignedInUser, skipCache bool, reqDTO dtos.MetricRequest) (*parsedRequest, error) {
if len(reqDTO.Queries) == 0 {
return nil, ErrNoQueriesFound
+91
View File
@@ -0,0 +1,91 @@
package query
import (
"context"
"fmt"
"net/http"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/expr"
"github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/util/proxyutil"
"golang.org/x/oauth2"
)
type parsedQuery struct {
datasource *datasources.DataSource
query backend.DataQuery
}
type parsedRequest struct {
hasExpression bool
parsedQueries []parsedQuery
httpRequest *http.Request
}
func (pr parsedRequest) createDataSourceQueryEnrichers(ctx context.Context, signedInUser *user.SignedInUser, oAuthTokenService oauthtoken.OAuthTokenService, disallowedCookies []string) map[string]expr.QueryDataRequestEnricher {
datasourcesHeaderProvider := map[string]expr.QueryDataRequestEnricher{}
if pr.httpRequest == nil {
return datasourcesHeaderProvider
}
if len(pr.parsedQueries) == 0 || pr.parsedQueries[0].datasource == nil {
return datasourcesHeaderProvider
}
for _, q := range pr.parsedQueries {
ds := q.datasource
uid := ds.Uid
if expr.IsDataSource(uid) {
continue
}
if _, exists := datasourcesHeaderProvider[uid]; exists {
continue
}
allowedCookies := ds.AllowedCookies()
clonedReq := pr.httpRequest.Clone(pr.httpRequest.Context())
var token *oauth2.Token
if oAuthTokenService.IsOAuthPassThruEnabled(ds) {
token = oAuthTokenService.GetCurrentOAuthToken(ctx, signedInUser)
}
datasourcesHeaderProvider[uid] = func(ctx context.Context, req *backend.QueryDataRequest) context.Context {
if len(req.Headers) == 0 {
req.Headers = map[string]string{}
}
if len(allowedCookies) > 0 {
proxyutil.ClearCookieHeader(clonedReq, allowedCookies, disallowedCookies)
if cookieStr := clonedReq.Header.Get("Cookie"); cookieStr != "" {
req.Headers["Cookie"] = cookieStr
}
ctx = httpclient.WithContextualMiddleware(ctx, httpclientprovider.ForwardedCookiesMiddleware(clonedReq.Cookies(), allowedCookies, disallowedCookies))
}
if token != nil {
req.Headers["Authorization"] = fmt.Sprintf("%s %s", token.Type(), token.AccessToken)
idToken, ok := token.Extra("id_token").(string)
if ok && idToken != "" {
req.Headers["X-ID-Token"] = idToken
}
ctx = httpclient.WithContextualMiddleware(ctx, httpclientprovider.ForwardedOAuthIdentityMiddleware(token))
}
return ctx
}
}
return datasourcesHeaderProvider
}
+272 -12
View File
@@ -1,18 +1,18 @@
package query_test
package query
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/expr"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/expr"
"github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/plugins"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
@@ -20,15 +20,243 @@ import (
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
dsSvc "github.com/grafana/grafana/pkg/services/datasources/service"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/query"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
)
func TestParseMetricRequest(t *testing.T) {
t.Run("Test a simple single datasource query", func(t *testing.T) {
tc := setup(t)
json, err := simplejson.NewJson([]byte(`{
"keepCookies": [ "cookie1", "cookie3", "login" ]
}`))
require.NoError(t, err)
tc.dataSourceCache.dsByUid = func(ctx context.Context, datasourceUID string, user *user.SignedInUser, skipCache bool) (*datasources.DataSource, error) {
if datasourceUID == "gIEkMvIVz" {
return &datasources.DataSource{
Uid: "gIEkMvIVz",
JsonData: json,
}, nil
}
return nil, nil
}
token := &oauth2.Token{
TokenType: "bearer",
AccessToken: "access-token",
}
token = token.WithExtra(map[string]interface{}{"id_token": "id-token"})
tc.oauthTokenService.passThruEnabled = true
tc.oauthTokenService.token = token
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, 2)
assert.Equal(t, "gIEkMvIVz", parsedReq.parsedQueries[0].datasource.Uid)
assert.Equal(t, "gIEkMvIVz", parsedReq.parsedQueries[1].datasource.Uid)
t.Run("createDataSourceQueryEnrichers should return 0 enrichers when no HTTP request", func(t *testing.T) {
enrichers := parsedReq.createDataSourceQueryEnrichers(context.Background(), nil, tc.oauthTokenService, []string{})
require.Empty(t, enrichers)
})
t.Run("createDataSourceQueryEnrichers should return 1 enricher", func(t *testing.T) {
parsedReq.httpRequest = httptest.NewRequest(http.MethodGet, "/", nil)
parsedReq.httpRequest.AddCookie(&http.Cookie{Name: "cookie1"})
parsedReq.httpRequest.AddCookie(&http.Cookie{Name: "cookie2"})
parsedReq.httpRequest.AddCookie(&http.Cookie{Name: "cookie3"})
parsedReq.httpRequest.AddCookie(&http.Cookie{Name: "login"})
enrichers := parsedReq.createDataSourceQueryEnrichers(context.Background(), nil, tc.oauthTokenService, []string{"login"})
require.Len(t, enrichers, 1)
require.NotNil(t, enrichers["gIEkMvIVz"])
req := &backend.QueryDataRequest{}
ctx := enrichers["gIEkMvIVz"](context.Background(), req)
require.Len(t, req.Headers, 3)
require.Equal(t, "Bearer access-token", req.Headers["Authorization"])
require.Equal(t, "id-token", req.Headers["X-ID-Token"])
require.Equal(t, "cookie1=; cookie3=", req.Headers["Cookie"])
middlewares := httpclient.ContextualMiddlewareFromContext(ctx)
require.Len(t, middlewares, 2)
require.Equal(t, httpclientprovider.ForwardedCookiesMiddlewareName, middlewares[0].(httpclient.MiddlewareName).MiddlewareName())
require.Equal(t, httpclientprovider.ForwardedOAuthIdentityMiddlewareName, middlewares[1].(httpclient.MiddlewareName).MiddlewareName())
})
})
t.Run("Test a single datasource query with expressions", func(t *testing.T) {
tc := setup(t)
json, err := simplejson.NewJson([]byte(`{
"keepCookies": [ "cookie1", "cookie3", "login" ]
}`))
require.NoError(t, err)
tc.dataSourceCache.dsByUid = func(ctx context.Context, datasourceUID string, user *user.SignedInUser, skipCache bool) (*datasources.DataSource, error) {
if datasourceUID == "gIEkMvIVz" {
return &datasources.DataSource{
Uid: "gIEkMvIVz",
JsonData: json,
}, nil
}
return nil, nil
}
token := &oauth2.Token{
TokenType: "bearer",
AccessToken: "access-token",
}
token = token.WithExtra(map[string]interface{}{"id_token": "id-token"})
tc.oauthTokenService.passThruEnabled = true
tc.oauthTokenService.token = token
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)
assert.True(t, parsedReq.hasExpression)
assert.Len(t, parsedReq.parsedQueries, 2)
assert.Equal(t, "gIEkMvIVz", parsedReq.parsedQueries[0].datasource.Uid)
assert.Equal(t, expr.DatasourceUID, parsedReq.parsedQueries[1].datasource.Uid)
// Make sure we end up with something valid
_, err = tc.queryService.handleExpressions(context.Background(), tc.signedInUser, parsedReq)
assert.NoError(t, err)
t.Run("createDataSourceQueryEnrichers should return 1 enricher", func(t *testing.T) {
parsedReq.httpRequest = httptest.NewRequest(http.MethodGet, "/", nil)
parsedReq.httpRequest.AddCookie(&http.Cookie{Name: "cookie1"})
parsedReq.httpRequest.AddCookie(&http.Cookie{Name: "cookie2"})
parsedReq.httpRequest.AddCookie(&http.Cookie{Name: "cookie3"})
parsedReq.httpRequest.AddCookie(&http.Cookie{Name: "login"})
enrichers := parsedReq.createDataSourceQueryEnrichers(context.Background(), nil, tc.oauthTokenService, []string{"login"})
require.Len(t, enrichers, 1)
require.NotNil(t, enrichers["gIEkMvIVz"])
req := &backend.QueryDataRequest{}
ctx := enrichers["gIEkMvIVz"](context.Background(), req)
require.Len(t, req.Headers, 3)
require.Equal(t, "Bearer access-token", req.Headers["Authorization"])
require.Equal(t, "id-token", req.Headers["X-ID-Token"])
require.Equal(t, "cookie1=; cookie3=", req.Headers["Cookie"])
middlewares := httpclient.ContextualMiddlewareFromContext(ctx)
require.Len(t, middlewares, 2)
require.Equal(t, httpclientprovider.ForwardedCookiesMiddlewareName, middlewares[0].(httpclient.MiddlewareName).MiddlewareName())
require.Equal(t, httpclientprovider.ForwardedOAuthIdentityMiddlewareName, middlewares[1].(httpclient.MiddlewareName).MiddlewareName())
})
})
t.Run("Test a mixed datasource query with expressions", func(t *testing.T) {
tc := setup(t)
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, 5)
assert.Equal(t, "gIEkMvIVz", parsedReq.parsedQueries[0].datasource.Uid)
assert.Equal(t, "sEx6ZvSVk", parsedReq.parsedQueries[1].datasource.Uid)
assert.Equal(t, expr.DatasourceUID, parsedReq.parsedQueries[2].datasource.Uid)
assert.Equal(t, expr.DatasourceUID, parsedReq.parsedQueries[3].datasource.Uid)
assert.Equal(t, expr.DatasourceUID, parsedReq.parsedQueries[4].datasource.Uid)
// Make sure we end up with something valid
_, err = tc.queryService.handleExpressions(context.Background(), tc.signedInUser, parsedReq)
assert.NoError(t, err)
t.Run("createDataSourceQueryEnrichers should return 2 enrichers", func(t *testing.T) {
parsedReq.httpRequest = &http.Request{}
enrichers := parsedReq.createDataSourceQueryEnrichers(context.Background(), nil, tc.oauthTokenService, []string{})
require.Len(t, enrichers, 2)
require.NotNil(t, enrichers["gIEkMvIVz"])
require.NotNil(t, enrichers["sEx6ZvSVk"])
})
})
}
func TestQueryDataMultipleSources(t *testing.T) {
t.Run("can query multiple datasources", func(t *testing.T) {
tc := setup(t)
@@ -127,7 +355,12 @@ func TestQueryData(t *testing.T) {
tc.oauthTokenService.passThruEnabled = true
tc.oauthTokenService.token = token
_, err := tc.queryService.QueryData(context.Background(), nil, true, metricRequest(), false)
metricReq := metricRequest()
httpReq, err := http.NewRequest(http.MethodGet, "/", nil)
require.NoError(t, err)
metricReq.HTTPRequest = httpReq
_, err = tc.queryService.QueryData(context.Background(), nil, true, metricReq, false)
require.Nil(t, err)
expected := map[string]string{
@@ -190,7 +423,9 @@ func setup(t *testing.T) *testContext {
DataSources: nil,
SimulatePluginFailure: false,
}
exprService := expr.ProvideService(nil, pc, fakeDatasourceService)
cfg := setting.NewCfg()
cfg.ExpressionsEnabled = true
exprService := expr.ProvideService(cfg, pc, fakeDatasourceService)
return &testContext{
pluginContext: pc,
@@ -198,7 +433,8 @@ func setup(t *testing.T) *testContext {
dataSourceCache: dc,
oauthTokenService: tc,
pluginRequestValidator: rv,
queryService: query.ProvideService(setting.NewCfg(), dc, exprService, rv, ds, pc, tc),
queryService: ProvideService(setting.NewCfg(), dc, exprService, rv, ds, pc, tc),
signedInUser: &user.SignedInUser{OrgID: 1},
}
}
@@ -208,7 +444,8 @@ type testContext struct {
dataSourceCache *fakeDataSourceCache
oauthTokenService *fakeOAuthTokenService
pluginRequestValidator *fakePluginRequestValidator
queryService *query.Service
queryService *Service
signedInUser *user.SignedInUser
}
func metricRequest() dtos.MetricRequest {
@@ -221,6 +458,22 @@ func metricRequest() dtos.MetricRequest {
}
}
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,
}
}
type fakePluginRequestValidator struct {
err error
}
@@ -243,7 +496,8 @@ func (ts *fakeOAuthTokenService) IsOAuthPassThruEnabled(*datasources.DataSource)
}
type fakeDataSourceCache struct {
ds *datasources.DataSource
ds *datasources.DataSource
dsByUid func(ctx context.Context, datasourceUID string, user *user.SignedInUser, skipCache bool) (*datasources.DataSource, error)
}
func (c *fakeDataSourceCache) GetDatasource(ctx context.Context, datasourceID int64, user *user.SignedInUser, skipCache bool) (*datasources.DataSource, error) {
@@ -251,7 +505,13 @@ func (c *fakeDataSourceCache) GetDatasource(ctx context.Context, datasourceID in
}
func (c *fakeDataSourceCache) GetDatasourceByUID(ctx context.Context, datasourceUID string, user *user.SignedInUser, skipCache bool) (*datasources.DataSource, error) {
return c.ds, nil
if c.dsByUid != nil {
return c.dsByUid(ctx, datasourceUID, user, skipCache)
}
return &datasources.DataSource{
Uid: datasourceUID,
}, nil
}
type fakePluginClient struct {