PublicDashboards: Allow hidden queries execution (#64858)

This commit is contained in:
Ezequiel Victorero
2023-03-16 15:39:17 -03:00
committed by GitHub
parent 54dd8943ca
commit 8f0a9729f0
10 changed files with 125 additions and 91 deletions
+21 -12
View File
@@ -37,8 +37,8 @@ func ProvideService(
pluginRequestValidator validations.PluginRequestValidator,
dataSourceService datasources.DataSourceService,
pluginClient plugins.Client,
) *Service {
g := &Service{
) *ServiceImpl {
g := &ServiceImpl{
cfg: cfg,
dataSourceCache: dataSourceCache,
expressionService: expressionService,
@@ -51,7 +51,16 @@ func ProvideService(
return g
}
type Service struct {
//go:generate mockery --name Service --structname FakeQueryService --inpackage --filename query_service_mock.go
type Service interface {
Run(ctx context.Context) error
QueryData(ctx context.Context, user *user.SignedInUser, skipCache bool, reqDTO dtos.MetricRequest) (*backend.QueryDataResponse, error)
}
// Gives us compile time error if the service does not adhere to the contract of the interface
var _ Service = (*ServiceImpl)(nil)
type ServiceImpl struct {
cfg *setting.Cfg
dataSourceCache datasources.CacheService
expressionService *expr.Service
@@ -61,14 +70,14 @@ type Service struct {
log log.Logger
}
// Run Service.
func (s *Service) Run(ctx context.Context) error {
// Run ServiceImpl.
func (s *ServiceImpl) Run(ctx context.Context) error {
<-ctx.Done()
return ctx.Err()
}
// QueryData processes queries and returns query responses. It handles queries to single or mixed datasources, as well as expressions.
func (s *Service) QueryData(ctx context.Context, user *user.SignedInUser, skipCache bool, reqDTO dtos.MetricRequest) (*backend.QueryDataResponse, error) {
func (s *ServiceImpl) QueryData(ctx context.Context, user *user.SignedInUser, skipCache bool, reqDTO dtos.MetricRequest) (*backend.QueryDataResponse, error) {
// Parse the request into parsed queries grouped by datasource uid
parsedReq, err := s.parseMetricRequest(ctx, user, skipCache, reqDTO)
if err != nil {
@@ -88,7 +97,7 @@ func (s *Service) QueryData(ctx context.Context, user *user.SignedInUser, skipCa
}
// executeConcurrentQueries executes queries to multiple datasources concurrently and returns the aggregate result.
func (s *Service) executeConcurrentQueries(ctx context.Context, user *user.SignedInUser, skipCache bool, reqDTO dtos.MetricRequest, queriesbyDs map[string][]parsedQuery) (*backend.QueryDataResponse, error) {
func (s *ServiceImpl) executeConcurrentQueries(ctx context.Context, user *user.SignedInUser, skipCache bool, reqDTO dtos.MetricRequest, queriesbyDs map[string][]parsedQuery) (*backend.QueryDataResponse, error) {
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(8) // arbitrary limit to prevent too many concurrent requests
rchan := make(chan backend.Responses, len(queriesbyDs))
@@ -158,7 +167,7 @@ func buildErrorResponses(err error, queries []*simplejson.Json) backend.Response
}
// 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) {
func (s *ServiceImpl) handleExpressions(ctx context.Context, user *user.SignedInUser, parsedReq *parsedRequest) (*backend.QueryDataResponse, error) {
exprReq := expr.Request{
Queries: []expr.Query{},
}
@@ -199,7 +208,7 @@ func (s *Service) handleExpressions(ctx context.Context, user *user.SignedInUser
}
// handleQuerySingleDatasource handles one or more queries to a single datasource
func (s *Service) handleQuerySingleDatasource(ctx context.Context, user *user.SignedInUser, parsedReq *parsedRequest) (*backend.QueryDataResponse, error) {
func (s *ServiceImpl) handleQuerySingleDatasource(ctx context.Context, user *user.SignedInUser, parsedReq *parsedRequest) (*backend.QueryDataResponse, error) {
queries := parsedReq.getFlattenedQueries()
ds := queries[0].datasource
if err := s.pluginRequestValidator.Validate(ds.URL, nil); err != nil {
@@ -237,7 +246,7 @@ func (s *Service) handleQuerySingleDatasource(ctx context.Context, user *user.Si
}
// 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) {
func (s *ServiceImpl) parseMetricRequest(ctx context.Context, user *user.SignedInUser, skipCache bool, reqDTO dtos.MetricRequest) (*parsedRequest, error) {
if len(reqDTO.Queries) == 0 {
return nil, ErrNoQueriesFound
}
@@ -298,7 +307,7 @@ func (s *Service) parseMetricRequest(ctx context.Context, user *user.SignedInUse
return req, req.validateRequest(ctx)
}
func (s *Service) getDataSourceFromQuery(ctx context.Context, user *user.SignedInUser, skipCache bool, query *simplejson.Json, history map[string]*datasources.DataSource) (*datasources.DataSource, error) {
func (s *ServiceImpl) getDataSourceFromQuery(ctx context.Context, user *user.SignedInUser, skipCache bool, query *simplejson.Json, history map[string]*datasources.DataSource) (*datasources.DataSource, error) {
var err error
uid := query.Get("datasource").Get("uid").MustString()
@@ -342,7 +351,7 @@ func (s *Service) getDataSourceFromQuery(ctx context.Context, user *user.SignedI
return nil, ErrInvalidDatasourceID
}
func (s *Service) decryptSecureJsonDataFn(ctx context.Context) func(ds *datasources.DataSource) (map[string]string, error) {
func (s *ServiceImpl) decryptSecureJsonDataFn(ctx context.Context) func(ds *datasources.DataSource) (map[string]string, error) {
return func(ds *datasources.DataSource) (map[string]string, error) {
return s.dataSourceService.DecryptedValues(ctx, ds)
}
+72
View File
@@ -0,0 +1,72 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
package query
import (
context "context"
backend "github.com/grafana/grafana-plugin-sdk-go/backend"
dtos "github.com/grafana/grafana/pkg/api/dtos"
mock "github.com/stretchr/testify/mock"
user "github.com/grafana/grafana/pkg/services/user"
)
// FakeQueryService is an autogenerated mock type for the Service type
type FakeQueryService struct {
mock.Mock
}
// QueryData provides a mock function with given fields: ctx, _a1, skipCache, reqDTO
func (_m *FakeQueryService) QueryData(ctx context.Context, _a1 *user.SignedInUser, skipCache bool, reqDTO dtos.MetricRequest) (*backend.QueryDataResponse, error) {
ret := _m.Called(ctx, _a1, skipCache, reqDTO)
var r0 *backend.QueryDataResponse
if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, bool, dtos.MetricRequest) *backend.QueryDataResponse); ok {
r0 = rf(ctx, _a1, skipCache, reqDTO)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*backend.QueryDataResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, bool, dtos.MetricRequest) error); ok {
r1 = rf(ctx, _a1, skipCache, reqDTO)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Run provides a mock function with given fields: ctx
func (_m *FakeQueryService) Run(ctx context.Context) error {
ret := _m.Called(ctx)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
r0 = rf(ctx)
} else {
r0 = ret.Error(0)
}
return r0
}
type mockConstructorTestingTNewFakeQueryService interface {
mock.TestingT
Cleanup(func())
}
// NewFakeQueryService creates a new instance of FakeQueryService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewFakeQueryService(t mockConstructorTestingTNewFakeQueryService) *FakeQueryService {
mock := &FakeQueryService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+1 -1
View File
@@ -463,7 +463,7 @@ type testContext struct {
secretStore secretskvs.SecretsKVStore
dataSourceCache *fakeDataSourceCache
pluginRequestValidator *fakePluginRequestValidator
queryService *Service // implementation belonging to this package
queryService *ServiceImpl // implementation belonging to this package
signedInUser *user.SignedInUser
}