grafana/pkg/tests/api/plugins/backendplugin/backendplugin_test.go
suntala 49b3027049
Chore: Remove Result field from datasources (#63048)
* Remove Result field from AddDataSourceCommand
* Remove DatasourcesPermissionFilterQuery Result
* Remove GetDataSourceQuery Result
* Remove GetDataSourcesByTypeQuery Result
* Remove GetDataSourcesQuery Result
* Remove GetDefaultDataSourceQuery Result
* Remove UpdateDataSourceCommand Result
2023-02-09 15:49:44 +01:00

706 lines
22 KiB
Go

package backendplugin
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/server"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/tests/testinfra"
)
const loginCookieName = "grafana_session"
func TestIntegrationBackendPlugins(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
oauthToken := &oauth2.Token{
TokenType: "bearer",
AccessToken: "access-token",
RefreshToken: "refresh-token",
Expiry: time.Now().UTC().Add(24 * time.Hour),
}
oauthToken = oauthToken.WithExtra(map[string]interface{}{"id_token": "id-token"})
newTestScenario(t, "Datasource with no custom HTTP settings",
options(
withIncomingRequest(func(req *http.Request) {
req.Header.Set("X-Custom", "custom")
req.AddCookie(&http.Cookie{Name: "cookie1"})
req.AddCookie(&http.Cookie{Name: "cookie2"})
req.AddCookie(&http.Cookie{Name: "cookie3"})
req.AddCookie(&http.Cookie{Name: loginCookieName})
}),
),
func(t *testing.T, tsCtx *testScenarioContext) {
verify := func(h backend.ForwardHTTPHeaders) {
require.NotNil(t, h)
require.Empty(t, h.GetHTTPHeader(backend.CookiesHeaderName))
require.Empty(t, h.GetHTTPHeader("Authorization"))
require.NotNil(t, tsCtx.outgoingRequest)
require.NotEmpty(t, tsCtx.outgoingRequest.Header)
}
tsCtx.runCheckHealthTest(t, func(pReq *backend.CheckHealthRequest) {
verify(pReq)
})
tsCtx.runCallResourceTest(t, func(pReq *backend.CallResourceRequest) {
verify(pReq)
require.Equal(t, "custom", pReq.GetHTTPHeader("X-Custom"))
require.Equal(t, "custom", tsCtx.outgoingRequest.Header.Get("X-Custom"))
})
verifyQueryData := func(pReq *backend.QueryDataRequest) {
verify(pReq)
}
t.Run("regular query", func(t *testing.T) {
tsCtx.runQueryDataTest(t, createRegularQuery(t, tsCtx), verifyQueryData)
t.Run("expression query", func(t *testing.T) {
tsCtx.runQueryDataTest(t, createExpressionQuery(t, tsCtx), verifyQueryData)
})
})
})
newTestScenario(t, "Datasource with most HTTP settings set except oauthPassThru and oauth token available",
options(
withIncomingRequest(func(req *http.Request) {
req.AddCookie(&http.Cookie{Name: "cookie1"})
req.AddCookie(&http.Cookie{Name: "cookie2"})
req.AddCookie(&http.Cookie{Name: "cookie3"})
req.AddCookie(&http.Cookie{Name: loginCookieName})
}),
withOAuthToken(oauthToken),
withDsBasicAuth("basicAuthUser", "basicAuthPassword"),
withDsCustomHeader(map[string]string{"X-CUSTOM-HEADER": "custom-header-value"}),
withDsCookieForwarding([]string{"cookie1", "cookie3", loginCookieName}),
),
func(t *testing.T, tsCtx *testScenarioContext) {
verify := func(h backend.ForwardHTTPHeaders) {
require.NotNil(t, h)
require.Equal(t, "cookie1=; cookie3=", h.GetHTTPHeader(backend.CookiesHeaderName))
require.Empty(t, h.GetHTTPHeader(backend.OAuthIdentityTokenHeaderName))
require.Empty(t, h.GetHTTPHeader(backend.OAuthIdentityIDTokenHeaderName))
require.NotNil(t, tsCtx.outgoingRequest)
require.Equal(t, "cookie1=; cookie3=", tsCtx.outgoingRequest.Header.Get(backend.CookiesHeaderName))
require.Equal(t, "custom-header-value", tsCtx.outgoingRequest.Header.Get("X-CUSTOM-HEADER"))
username, pwd, ok := tsCtx.outgoingRequest.BasicAuth()
require.True(t, ok)
require.Equal(t, "basicAuthUser", username)
require.Equal(t, "basicAuthPassword", pwd)
}
tsCtx.runCheckHealthTest(t, func(pReq *backend.CheckHealthRequest) {
verify(pReq)
})
tsCtx.runCallResourceTest(t, func(pReq *backend.CallResourceRequest) {
verify(pReq)
})
verifyQueryData := func(pReq *backend.QueryDataRequest) {
verify(pReq)
}
t.Run("regular query", func(t *testing.T) {
tsCtx.runQueryDataTest(t, createRegularQuery(t, tsCtx), verifyQueryData)
t.Run("expression query", func(t *testing.T) {
tsCtx.runQueryDataTest(t, createExpressionQuery(t, tsCtx), verifyQueryData)
})
})
})
newTestScenario(t, "Datasource with oauthPassThru and basic auth configured and oauth token available",
options(
withOAuthToken(oauthToken),
withDsOAuthForwarding(),
withDsBasicAuth("basicAuthUser", "basicAuthPassword"),
),
func(t *testing.T, tsCtx *testScenarioContext) {
verify := func(h backend.ForwardHTTPHeaders) {
require.NotNil(t, h)
expectedAuthHeader := fmt.Sprintf("Bearer %s", oauthToken.AccessToken)
expectedTokenHeader := oauthToken.Extra("id_token").(string)
require.Equal(t, expectedAuthHeader, h.GetHTTPHeader(backend.OAuthIdentityTokenHeaderName))
require.Equal(t, expectedTokenHeader, h.GetHTTPHeader(backend.OAuthIdentityIDTokenHeaderName))
require.NotNil(t, tsCtx.outgoingRequest)
require.Equal(t, expectedAuthHeader, tsCtx.outgoingRequest.Header.Get(backend.OAuthIdentityTokenHeaderName))
require.Equal(t, expectedTokenHeader, tsCtx.outgoingRequest.Header.Get(backend.OAuthIdentityIDTokenHeaderName))
}
tsCtx.runCheckHealthTest(t, func(pReq *backend.CheckHealthRequest) {
verify(pReq)
})
tsCtx.runCallResourceTest(t, func(pReq *backend.CallResourceRequest) {
verify(pReq)
})
verifyQueryData := func(pReq *backend.QueryDataRequest) {
verify(pReq)
}
t.Run("regular query", func(t *testing.T) {
tsCtx.runQueryDataTest(t, createRegularQuery(t, tsCtx), verifyQueryData)
t.Run("expression query", func(t *testing.T) {
tsCtx.runQueryDataTest(t, createExpressionQuery(t, tsCtx), verifyQueryData)
})
})
})
}
type testScenarioContext struct {
testPluginID string
uid string
grafanaListeningAddr string
testEnv *server.TestEnv
outgoingServer *httptest.Server
outgoingRequest *http.Request
backendTestPlugin *testPlugin
rt http.RoundTripper
modifyIncomingRequest func(req *http.Request)
}
type testScenarioInput struct {
ds *datasources.AddDataSourceCommand
token *oauth2.Token
modifyIncomingRequest func(req *http.Request)
}
type testScenarioOption func(*testScenarioInput)
func options(opts ...testScenarioOption) []testScenarioOption {
return opts
}
func withIncomingRequest(cb func(req *http.Request)) testScenarioOption {
return func(in *testScenarioInput) {
in.modifyIncomingRequest = cb
}
}
func withOAuthToken(token *oauth2.Token) testScenarioOption {
return func(in *testScenarioInput) {
in.token = token
}
}
func withDsBasicAuth(username, password string) testScenarioOption {
return func(in *testScenarioInput) {
in.ds.BasicAuth = true
in.ds.BasicAuthUser = username
in.ds.SecureJsonData["basicAuthPassword"] = password
}
}
func withDsCustomHeader(headers map[string]string) testScenarioOption {
return func(in *testScenarioInput) {
index := 1
for k, v := range headers {
in.ds.JsonData.Set(fmt.Sprintf("httpHeaderName%d", index), k)
in.ds.SecureJsonData[fmt.Sprintf("httpHeaderValue%d", index)] = v
index++
}
}
}
func withDsOAuthForwarding() testScenarioOption {
return func(in *testScenarioInput) {
in.ds.JsonData.Set("oauthPassThru", true)
}
}
func withDsCookieForwarding(names []string) testScenarioOption {
return func(in *testScenarioInput) {
in.ds.JsonData.Set("keepCookies", names)
}
}
func newTestScenario(t *testing.T, name string, opts []testScenarioOption, callback func(t *testing.T, ctx *testScenarioContext)) {
tsCtx := testScenarioContext{
testPluginID: "test-plugin",
}
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableAnonymous: true,
// EnableLog: true,
})
grafanaListeningAddr, testEnv := testinfra.StartGrafanaEnv(t, dir, path)
tsCtx.grafanaListeningAddr = grafanaListeningAddr
testEnv.SQLStore.Cfg.LoginCookieName = loginCookieName
tsCtx.testEnv = testEnv
ctx := context.Background()
testinfra.CreateUser(t, testEnv.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleAdmin),
Password: "admin",
Login: "admin",
})
tsCtx.outgoingServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tsCtx.outgoingRequest = r
w.WriteHeader(http.StatusUnauthorized)
}))
t.Cleanup(tsCtx.outgoingServer.Close)
testPlugin, backendTestPlugin := createTestPlugin(tsCtx.testPluginID)
tsCtx.backendTestPlugin = backendTestPlugin
err := testEnv.PluginRegistry.Add(ctx, testPlugin)
require.NoError(t, err)
jsonData := simplejson.New()
secureJSONData := map[string]string{}
tsCtx.uid = "test-plugin"
cmd := &datasources.AddDataSourceCommand{
OrgID: 1,
Access: datasources.DS_ACCESS_PROXY,
Name: "TestPlugin",
Type: tsCtx.testPluginID,
UID: tsCtx.uid,
URL: tsCtx.outgoingServer.URL,
JsonData: jsonData,
SecureJsonData: secureJSONData,
}
in := &testScenarioInput{ds: cmd}
for _, opt := range opts {
opt(in)
}
tsCtx.modifyIncomingRequest = in.modifyIncomingRequest
tsCtx.testEnv.OAuthTokenService.Token = in.token
_, err = testEnv.Server.HTTPServer.DataSourcesService.AddDataSource(ctx, cmd)
require.NoError(t, err)
getDataSourceQuery := &datasources.GetDataSourceQuery{
OrgID: 1,
UID: tsCtx.uid,
}
dataSource, err := testEnv.Server.HTTPServer.DataSourcesService.GetDataSource(ctx, getDataSourceQuery)
require.NoError(t, err)
rt, err := testEnv.Server.HTTPServer.DataSourcesService.GetHTTPTransport(ctx, dataSource, testEnv.HTTPClientProvider)
require.NoError(t, err)
tsCtx.rt = rt
t.Run(name, func(t *testing.T) {
callback(t, &tsCtx)
})
}
func createRegularQuery(t *testing.T, tsCtx *testScenarioContext) dtos.MetricRequest {
t.Helper()
return metricRequestWithQueries(t, fmt.Sprintf(`{
"datasource": {
"uid": "%s"
}
}`, tsCtx.uid))
}
func createExpressionQuery(t *testing.T, tsCtx *testScenarioContext) dtos.MetricRequest {
t.Helper()
return metricRequestWithQueries(t, fmt.Sprintf(`{
"refId": "A",
"datasource": {
"uid": "%s",
"type": "%s"
}
}`, tsCtx.uid, tsCtx.testPluginID), `{
"refId": "B",
"datasource": {
"type": "__expr__",
"uid": "__expr__",
"name": "Expression"
},
"type": "math",
"expression": "$A - 50"
}`)
}
func (tsCtx *testScenarioContext) runQueryDataTest(t *testing.T, mr dtos.MetricRequest, callback func(req *backend.QueryDataRequest)) {
t.Run("When calling /api/ds/query should set expected headers on outgoing QueryData and HTTP request", func(t *testing.T) {
var received *backend.QueryDataRequest
tsCtx.backendTestPlugin.QueryDataHandler = backend.QueryDataHandlerFunc(func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
received = req
c := http.Client{
Transport: tsCtx.rt,
}
outReq, err := http.NewRequestWithContext(ctx, http.MethodGet, tsCtx.outgoingServer.URL, nil)
require.NoError(t, err)
resp, err := c.Do(outReq)
if err != nil {
return nil, err
}
defer func() {
if err := resp.Body.Close(); err != nil {
tsCtx.testEnv.Server.HTTPServer.Cfg.Logger.Error("Failed to close body", "error", err)
}
}()
_, err = io.Copy(io.Discard, resp.Body)
if err != nil {
tsCtx.testEnv.Server.HTTPServer.Cfg.Logger.Error("Failed to discard body", "error", err)
}
return &backend.QueryDataResponse{}, nil
})
buf1 := &bytes.Buffer{}
err := json.NewEncoder(buf1).Encode(mr)
require.NoError(t, err)
u := fmt.Sprintf("http://admin:admin@%s/api/ds/query", tsCtx.grafanaListeningAddr)
req, err := http.NewRequest(http.MethodPost, u, buf1)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
if tsCtx.modifyIncomingRequest != nil {
tsCtx.modifyIncomingRequest(req)
}
require.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode, string(b))
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
_, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.NotEmpty(t, tsCtx.outgoingRequest.Header.Get("Accept-Encoding"))
require.Equal(t, fmt.Sprintf("Grafana/%s", tsCtx.testEnv.SQLStore.Cfg.BuildVersion), tsCtx.outgoingRequest.Header.Get("User-Agent"))
callback(received)
})
}
func (tsCtx *testScenarioContext) runCheckHealthTest(t *testing.T, callback func(req *backend.CheckHealthRequest)) {
t.Run("When calling /api/datasources/uid/:uid/health should set expected headers on outgoing CheckHealth and HTTP request", func(t *testing.T) {
var received *backend.CheckHealthRequest
tsCtx.backendTestPlugin.CheckHealthHandler = backend.CheckHealthHandlerFunc(func(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
received = req
c := http.Client{
Transport: tsCtx.rt,
}
outReq, err := http.NewRequestWithContext(ctx, http.MethodGet, tsCtx.outgoingServer.URL, nil)
require.NoError(t, err)
resp, err := c.Do(outReq)
if err != nil {
return nil, err
}
defer func() {
if err := resp.Body.Close(); err != nil {
tsCtx.testEnv.Server.HTTPServer.Cfg.Logger.Error("Failed to close body", "error", err)
}
}()
_, err = io.Copy(io.Discard, resp.Body)
if err != nil {
tsCtx.testEnv.Server.HTTPServer.Cfg.Logger.Error("Failed to discard body", "error", err)
}
return &backend.CheckHealthResult{
Status: backend.HealthStatusOk,
}, nil
})
u := fmt.Sprintf("http://admin:admin@%s/api/datasources/uid/%s/health", tsCtx.grafanaListeningAddr, tsCtx.uid)
req, err := http.NewRequest(http.MethodGet, u, nil)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
if tsCtx.modifyIncomingRequest != nil {
tsCtx.modifyIncomingRequest(req)
}
require.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode, string(b))
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
_, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.NotEmpty(t, tsCtx.outgoingRequest.Header.Get("Accept-Encoding"))
require.Equal(t, fmt.Sprintf("Grafana/%s", tsCtx.testEnv.SQLStore.Cfg.BuildVersion), tsCtx.outgoingRequest.Header.Get("User-Agent"))
callback(received)
})
}
func (tsCtx *testScenarioContext) runCallResourceTest(t *testing.T, callback func(req *backend.CallResourceRequest)) {
t.Run("When calling /api/datasources/uid/:uid/resources should set expected headers on outgoing CallResource and HTTP request", func(t *testing.T) {
var received *backend.CallResourceRequest
tsCtx.backendTestPlugin.CallResourceHandler = backend.CallResourceHandlerFunc(func(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
received = req
c := http.Client{
Transport: tsCtx.rt,
}
outReq, err := http.NewRequestWithContext(ctx, http.MethodGet, tsCtx.outgoingServer.URL, nil)
require.NoError(t, err)
for k, vals := range req.Headers {
for _, v := range vals {
outReq.Header.Add(k, v)
}
}
resp, err := c.Do(outReq)
if err != nil {
return err
}
defer func() {
if err := resp.Body.Close(); err != nil {
tsCtx.testEnv.Server.HTTPServer.Cfg.Logger.Error("Failed to close body", "error", err)
}
}()
_, err = io.Copy(io.Discard, resp.Body)
if err != nil {
tsCtx.testEnv.Server.HTTPServer.Cfg.Logger.Error("Failed to discard body", "error", err)
}
responseHeaders := map[string][]string{
"Connection": {"close, TE"},
"Te": {"foo", "bar, trailers"},
"Proxy-Connection": {"should be deleted"},
"Upgrade": {"foo"},
"Set-Cookie": {"should be deleted"},
"X-Custom": {"should not be deleted"},
}
err = sender.Send(&backend.CallResourceResponse{
Status: http.StatusOK,
Headers: responseHeaders,
})
return err
})
u := fmt.Sprintf("http://admin:admin@%s/api/datasources/uid/%s/resources", tsCtx.grafanaListeningAddr, tsCtx.uid)
req, err := http.NewRequest(http.MethodGet, u, nil)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Connection", "X-Some-Conn-Header")
req.Header.Set("X-Some-Conn-Header", "should be deleted")
req.Header.Set("Proxy-Connection", "should be deleted")
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
if tsCtx.modifyIncomingRequest != nil {
tsCtx.modifyIncomingRequest(req)
}
require.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode, string(b))
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
_, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Empty(t, resp.Header.Get("Connection"))
require.Empty(t, resp.Header.Get("Te"))
require.Empty(t, resp.Header.Get("Proxy-Connection"))
require.Empty(t, resp.Header.Get("Upgrade"))
require.Empty(t, resp.Header.Get("Set-Cookie"))
require.Equal(t, "should not be deleted", resp.Header.Get("X-Custom"))
require.NotNil(t, received)
require.Empty(t, received.Headers["Connection"])
require.Empty(t, received.Headers["X-Some-Conn-Header"])
require.Empty(t, received.Headers["Proxy-Connection"])
require.Empty(t, tsCtx.outgoingRequest.Header.Get("Connection"))
require.Empty(t, tsCtx.outgoingRequest.Header.Get("X-Some-Conn-Header"))
require.Empty(t, tsCtx.outgoingRequest.Header.Get("Proxy-Connection"))
require.NotEmpty(t, tsCtx.outgoingRequest.Header.Get("Accept-Encoding"))
require.Equal(t, fmt.Sprintf("Grafana/%s", tsCtx.testEnv.SQLStore.Cfg.BuildVersion), tsCtx.outgoingRequest.Header.Get("User-Agent"))
callback(received)
})
}
func createTestPlugin(id string) (*plugins.Plugin, *testPlugin) {
p := &plugins.Plugin{
JSONData: plugins.JSONData{
ID: id,
},
Class: plugins.Core,
}
p.SetLogger(log.New("test-plugin"))
tp := &testPlugin{
pluginID: id,
logger: p.Logger(),
QueryDataHandler: backend.QueryDataHandlerFunc(func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
return &backend.QueryDataResponse{}, nil
}),
}
p.RegisterClient(tp)
return p, tp
}
type testPlugin struct {
pluginID string
logger log.Logger
backend.CheckHealthHandler
backend.CallResourceHandler
backend.QueryDataHandler
backend.StreamHandler
}
func (tp *testPlugin) PluginID() string {
return tp.pluginID
}
func (tp *testPlugin) Logger() log.Logger {
return tp.logger
}
func (tp *testPlugin) Start(ctx context.Context) error {
return nil
}
func (tp *testPlugin) Stop(ctx context.Context) error {
return nil
}
func (tp *testPlugin) IsManaged() bool {
return true
}
func (tp *testPlugin) Exited() bool {
return false
}
func (tp *testPlugin) Decommission() error {
return nil
}
func (tp *testPlugin) IsDecommissioned() bool {
return false
}
func (tp *testPlugin) Target() backendplugin.Target {
return backendplugin.TargetNone
}
func (tp *testPlugin) CollectMetrics(_ context.Context, _ *backend.CollectMetricsRequest) (*backend.CollectMetricsResult, error) {
return nil, backendplugin.ErrMethodNotImplemented
}
func (tp *testPlugin) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
if tp.CheckHealthHandler != nil {
return tp.CheckHealthHandler.CheckHealth(ctx, req)
}
return nil, backendplugin.ErrMethodNotImplemented
}
func (tp *testPlugin) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
if tp.QueryDataHandler != nil {
return tp.QueryDataHandler.QueryData(ctx, req)
}
return nil, backendplugin.ErrMethodNotImplemented
}
func (tp *testPlugin) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
if tp.CallResourceHandler != nil {
return tp.CallResourceHandler.CallResource(ctx, req, sender)
}
return backendplugin.ErrMethodNotImplemented
}
func (tp *testPlugin) SubscribeStream(ctx context.Context, req *backend.SubscribeStreamRequest) (*backend.SubscribeStreamResponse, error) {
if tp.StreamHandler != nil {
return tp.StreamHandler.SubscribeStream(ctx, req)
}
return nil, backendplugin.ErrMethodNotImplemented
}
func (tp *testPlugin) PublishStream(ctx context.Context, req *backend.PublishStreamRequest) (*backend.PublishStreamResponse, error) {
if tp.StreamHandler != nil {
return tp.StreamHandler.PublishStream(ctx, req)
}
return nil, backendplugin.ErrMethodNotImplemented
}
func (tp *testPlugin) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
if tp.StreamHandler != nil {
return tp.StreamHandler.RunStream(ctx, req, sender)
}
return backendplugin.ErrMethodNotImplemented
}
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,
}
}