mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			159 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
package api
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"context"
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/grafana/grafana-plugin-sdk-go/backend"
 | 
						|
	"github.com/stretchr/testify/require"
 | 
						|
 | 
						|
	"github.com/grafana/grafana/pkg/services/datasources"
 | 
						|
	"github.com/grafana/grafana/pkg/services/featuremgmt"
 | 
						|
	"github.com/grafana/grafana/pkg/services/quota/quotatest"
 | 
						|
	"github.com/grafana/grafana/pkg/web/webtest"
 | 
						|
 | 
						|
	"golang.org/x/oauth2"
 | 
						|
 | 
						|
	"github.com/grafana/grafana/pkg/models"
 | 
						|
	fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
 | 
						|
	"github.com/grafana/grafana/pkg/services/query"
 | 
						|
)
 | 
						|
 | 
						|
var queryDatasourceInput = `{
 | 
						|
"from": "",
 | 
						|
		"to": "",
 | 
						|
		"queries": [
 | 
						|
			{
 | 
						|
				"datasource": {
 | 
						|
					"type": "datasource",
 | 
						|
					"uid": "grafana"
 | 
						|
				},
 | 
						|
				"queryType": "randomWalk",
 | 
						|
				"refId": "A"
 | 
						|
			}
 | 
						|
		]
 | 
						|
	}`
 | 
						|
 | 
						|
type fakePluginRequestValidator struct {
 | 
						|
	err error
 | 
						|
}
 | 
						|
 | 
						|
type secretsErrorResponseBody struct {
 | 
						|
	Error   string `json:"error"`
 | 
						|
	Message string `json:"message"`
 | 
						|
}
 | 
						|
 | 
						|
func (rv *fakePluginRequestValidator) Validate(dsURL string, req *http.Request) error {
 | 
						|
	return rv.err
 | 
						|
}
 | 
						|
 | 
						|
type fakeOAuthTokenService struct {
 | 
						|
	passThruEnabled bool
 | 
						|
	token           *oauth2.Token
 | 
						|
}
 | 
						|
 | 
						|
func (ts *fakeOAuthTokenService) GetCurrentOAuthToken(context.Context, *models.SignedInUser) *oauth2.Token {
 | 
						|
	return ts.token
 | 
						|
}
 | 
						|
 | 
						|
func (ts *fakeOAuthTokenService) IsOAuthPassThruEnabled(*datasources.DataSource) bool {
 | 
						|
	return ts.passThruEnabled
 | 
						|
}
 | 
						|
 | 
						|
// `/ds/query` endpoint test
 | 
						|
func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
 | 
						|
	qds := query.ProvideService(
 | 
						|
		nil,
 | 
						|
		nil,
 | 
						|
		nil,
 | 
						|
		&fakePluginRequestValidator{},
 | 
						|
		&fakeDatasources.FakeDataSourceService{},
 | 
						|
		&fakePluginClient{
 | 
						|
			QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
 | 
						|
				resp := backend.Responses{
 | 
						|
					"A": backend.DataResponse{
 | 
						|
						Error: fmt.Errorf("query failed"),
 | 
						|
					},
 | 
						|
				}
 | 
						|
				return &backend.QueryDataResponse{Responses: resp}, nil
 | 
						|
			},
 | 
						|
		},
 | 
						|
		&fakeOAuthTokenService{},
 | 
						|
	)
 | 
						|
	serverFeatureEnabled := SetupAPITestServer(t, func(hs *HTTPServer) {
 | 
						|
		hs.queryDataService = qds
 | 
						|
		hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagDatasourceQueryMultiStatus, true)
 | 
						|
		hs.QuotaService = quotatest.NewQuotaServiceFake()
 | 
						|
	})
 | 
						|
	serverFeatureDisabled := SetupAPITestServer(t, func(hs *HTTPServer) {
 | 
						|
		hs.queryDataService = qds
 | 
						|
		hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagDatasourceQueryMultiStatus, false)
 | 
						|
		hs.QuotaService = quotatest.NewQuotaServiceFake()
 | 
						|
	})
 | 
						|
 | 
						|
	t.Run("Status code is 400 when data source response has an error and feature toggle is disabled", func(t *testing.T) {
 | 
						|
		req := serverFeatureDisabled.NewPostRequest("/api/ds/query", strings.NewReader(queryDatasourceInput))
 | 
						|
		webtest.RequestWithSignedInUser(req, &models.SignedInUser{UserId: 1, OrgId: 1, OrgRole: models.ROLE_VIEWER})
 | 
						|
		resp, err := serverFeatureDisabled.SendJSON(req)
 | 
						|
		require.NoError(t, err)
 | 
						|
		require.NoError(t, resp.Body.Close())
 | 
						|
		require.Equal(t, http.StatusBadRequest, resp.StatusCode)
 | 
						|
	})
 | 
						|
 | 
						|
	t.Run("Status code is 207 when data source response has an error and feature toggle is enabled", func(t *testing.T) {
 | 
						|
		req := serverFeatureEnabled.NewPostRequest("/api/ds/query", strings.NewReader(queryDatasourceInput))
 | 
						|
		webtest.RequestWithSignedInUser(req, &models.SignedInUser{UserId: 1, OrgId: 1, OrgRole: models.ROLE_VIEWER})
 | 
						|
		resp, err := serverFeatureEnabled.SendJSON(req)
 | 
						|
		require.NoError(t, err)
 | 
						|
		require.NoError(t, resp.Body.Close())
 | 
						|
		require.Equal(t, http.StatusMultiStatus, resp.StatusCode)
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
 | 
						|
	qds := query.ProvideService(
 | 
						|
		nil,
 | 
						|
		nil,
 | 
						|
		nil,
 | 
						|
		&fakePluginRequestValidator{},
 | 
						|
		&fakeDatasources.FakeDataSourceService{SimulatePluginFailure: true},
 | 
						|
		&fakePluginClient{
 | 
						|
			QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
 | 
						|
				resp := backend.Responses{
 | 
						|
					"A": backend.DataResponse{
 | 
						|
						Error: fmt.Errorf("query failed"),
 | 
						|
					},
 | 
						|
				}
 | 
						|
				return &backend.QueryDataResponse{Responses: resp}, nil
 | 
						|
			},
 | 
						|
		},
 | 
						|
		&fakeOAuthTokenService{},
 | 
						|
	)
 | 
						|
	httpServer := SetupAPITestServer(t, func(hs *HTTPServer) {
 | 
						|
		hs.queryDataService = qds
 | 
						|
		hs.QuotaService = quotatest.NewQuotaServiceFake()
 | 
						|
	})
 | 
						|
 | 
						|
	t.Run("Status code is 500 and a secrets plugin error is returned if there is a problem getting secrets from the remote plugin", func(t *testing.T) {
 | 
						|
		req := httpServer.NewPostRequest("/api/ds/query", strings.NewReader(queryDatasourceInput))
 | 
						|
		webtest.RequestWithSignedInUser(req, &models.SignedInUser{UserId: 1, OrgId: 1, OrgRole: models.ROLE_VIEWER})
 | 
						|
		resp, err := httpServer.SendJSON(req)
 | 
						|
		require.NoError(t, err)
 | 
						|
		require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
 | 
						|
		buf := new(bytes.Buffer)
 | 
						|
		_, err = buf.ReadFrom(resp.Body)
 | 
						|
		require.NoError(t, err)
 | 
						|
		require.NoError(t, resp.Body.Close())
 | 
						|
		var resObj secretsErrorResponseBody
 | 
						|
		err = json.Unmarshal(buf.Bytes(), &resObj)
 | 
						|
		require.NoError(t, err)
 | 
						|
		require.Equal(t, "unknown error", resObj.Error)
 | 
						|
		require.Contains(t, resObj.Message, "Secrets Plugin error:")
 | 
						|
	})
 | 
						|
}
 |