mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Refactor creation of plugin context to dedicated service (#66451)
* first pass * fix tests * return errs * change signature * tidy * delete unnecessary fields from test * tidy * fix tests * simplify * separate error check in API * apply nits
This commit is contained in:
parent
49ad802ca6
commit
624777258b
@ -20,7 +20,7 @@ import (
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/adapters"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@ -811,23 +811,16 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *contextmodel.ReqContext) response
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) checkDatasourceHealth(c *contextmodel.ReqContext, ds *datasources.DataSource) response.Response {
|
||||
plugin, exists := hs.pluginStore.Plugin(c.Req.Context(), ds.Type)
|
||||
if !exists {
|
||||
return response.Error(http.StatusInternalServerError, "Unable to find datasource plugin", nil)
|
||||
}
|
||||
|
||||
dsInstanceSettings, err := adapters.ModelToInstanceSettings(ds, hs.decryptSecureJsonDataFn(c.Req.Context()))
|
||||
pCtx, err := hs.pluginContextProvider.GetWithDataSource(c.Req.Context(), ds.Type, c.SignedInUser, ds)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Unable to get datasource model", err)
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
return response.Error(http.StatusNotFound, "Unable to find datasource plugin", nil)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Unable to get plugin context", err)
|
||||
}
|
||||
req := &backend.CheckHealthRequest{
|
||||
PluginContext: backend.PluginContext{
|
||||
User: adapters.BackendUserFromSignedInUser(c.SignedInUser),
|
||||
OrgID: c.OrgID,
|
||||
PluginID: plugin.ID,
|
||||
DataSourceInstanceSettings: dsInstanceSettings,
|
||||
},
|
||||
Headers: map[string]string{},
|
||||
PluginContext: pCtx,
|
||||
Headers: map[string]string{},
|
||||
}
|
||||
|
||||
var dsURL string
|
||||
@ -868,12 +861,6 @@ func (hs *HTTPServer) checkDatasourceHealth(c *contextmodel.ReqContext, ds *data
|
||||
return response.JSON(http.StatusOK, payload)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) decryptSecureJsonDataFn(ctx context.Context) func(ds *datasources.DataSource) (map[string]string, error) {
|
||||
return func(ds *datasources.DataSource) (map[string]string, error) {
|
||||
return hs.DataSourcesService.DecryptedValues(ctx, ds)
|
||||
}
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) filterDatasourcesByQueryPermission(ctx context.Context, user *user.SignedInUser, ds []*datasources.DataSource) ([]*datasources.DataSource, error) {
|
||||
query := datasources.DatasourcesPermissionFilterQuery{
|
||||
User: user,
|
||||
|
@ -111,7 +111,7 @@ type HTTPServer struct {
|
||||
namedMiddlewares []routing.RegisterNamedMiddleware
|
||||
bus bus.Bus
|
||||
|
||||
PluginContextProvider *plugincontext.Provider
|
||||
pluginContextProvider *plugincontext.Provider
|
||||
RouteRegister routing.RouteRegister
|
||||
RenderService rendering.Service
|
||||
Cfg *setting.Cfg
|
||||
@ -291,7 +291,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
SearchService: searchService,
|
||||
Live: live,
|
||||
LivePushGateway: livePushGateway,
|
||||
PluginContextProvider: plugCtxProvider,
|
||||
pluginContextProvider: plugCtxProvider,
|
||||
ContextHandler: contextHandler,
|
||||
LoggerMiddleware: loggerMiddleware,
|
||||
AlertNG: alertNG,
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
@ -22,6 +23,9 @@ func (hs *HTTPServer) handleQueryMetricsError(err error) *response.NormalRespons
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(http.StatusNotFound, "Data source not found", err)
|
||||
}
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
return response.Error(http.StatusNotFound, "Plugin not found", err)
|
||||
}
|
||||
|
||||
var secretsPlugin datasources.ErrDatasourceSecretsPluginUserFriendly
|
||||
if errors.As(err, &secretsPlugin) {
|
||||
|
@ -14,6 +14,8 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
@ -22,8 +24,12 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
|
||||
"github.com/grafana/grafana/pkg/services/query"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
secretstest "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
@ -50,7 +56,6 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
&fakePluginRequestValidator{},
|
||||
&fakeDatasources.FakeDataSourceService{},
|
||||
&fakePluginClient{
|
||||
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
resp := backend.Responses{
|
||||
@ -61,6 +66,17 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
|
||||
return &backend.QueryDataResponse{Responses: resp}, nil
|
||||
},
|
||||
},
|
||||
plugincontext.ProvideService(localcache.ProvideService(), &plugins.FakePluginStore{
|
||||
PluginList: []plugins.PluginDTO{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "grafana",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &fakeDatasources.FakeDataSourceService{}, pluginSettings.ProvideService(dbtest.NewFakeDB(),
|
||||
secretstest.NewFakeSecretsService()),
|
||||
),
|
||||
)
|
||||
serverFeatureEnabled := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.queryDataService = qds
|
||||
@ -93,12 +109,25 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
|
||||
ds := &fakeDatasources.FakeDataSourceService{SimulatePluginFailure: true}
|
||||
db := &dbtest.FakeDB{ExpectedError: pluginsettings.ErrPluginSettingNotFound}
|
||||
pcp := plugincontext.ProvideService(localcache.ProvideService(),
|
||||
&plugins.FakePluginStore{
|
||||
PluginList: []plugins.PluginDTO{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "grafana",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()),
|
||||
)
|
||||
qds := query.ProvideService(
|
||||
setting.NewCfg(),
|
||||
nil,
|
||||
nil,
|
||||
&fakePluginRequestValidator{},
|
||||
&fakeDatasources.FakeDataSourceService{SimulatePluginFailure: true},
|
||||
&fakePluginClient{
|
||||
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
resp := backend.Responses{
|
||||
@ -109,10 +138,12 @@ func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
|
||||
return &backend.QueryDataResponse{Responses: resp}, nil
|
||||
},
|
||||
},
|
||||
pcp,
|
||||
)
|
||||
httpServer := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.queryDataService = qds
|
||||
hs.QuotaService = quotatest.New(false, nil)
|
||||
hs.pluginContextProvider = pcp
|
||||
})
|
||||
|
||||
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) {
|
||||
@ -255,13 +286,19 @@ func TestDataSourceQueryError(t *testing.T) {
|
||||
r := registry.NewInMemory()
|
||||
err := r.Add(context.Background(), p)
|
||||
require.NoError(t, err)
|
||||
ds := &fakeDatasources.FakeDataSourceService{}
|
||||
hs.queryDataService = query.ProvideService(
|
||||
setting.NewCfg(),
|
||||
&fakeDatasources.FakeCacheService{},
|
||||
nil,
|
||||
&fakePluginRequestValidator{},
|
||||
&fakeDatasources.FakeDataSourceService{},
|
||||
pluginClient.ProvideService(r, &config.Cfg{}),
|
||||
plugincontext.ProvideService(localcache.ProvideService(), &plugins.FakePluginStore{
|
||||
PluginList: []plugins.PluginDTO{p.ToDTO()},
|
||||
},
|
||||
ds, pluginSettings.ProvideService(dbtest.NewFakeDB(),
|
||||
secretstest.NewFakeSecretsService()),
|
||||
),
|
||||
)
|
||||
hs.QuotaService = quotatest.New(false, nil)
|
||||
})
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/httpresponsesender"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/util/proxyutil"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
@ -25,15 +26,15 @@ func (hs *HTTPServer) CallResource(c *contextmodel.ReqContext) {
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) callPluginResource(c *contextmodel.ReqContext, pluginID string) {
|
||||
pCtx, found, err := hs.PluginContextProvider.Get(c.Req.Context(), pluginID, c.SignedInUser)
|
||||
pCtx, err := hs.pluginContextProvider.Get(c.Req.Context(), pluginID, c.SignedInUser)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
c.JsonApiErr(404, "Plugin not found", nil)
|
||||
return
|
||||
}
|
||||
c.JsonApiErr(500, "Failed to get plugin settings", err)
|
||||
return
|
||||
}
|
||||
if !found {
|
||||
c.JsonApiErr(404, "Plugin not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
req, err := hs.pluginResourceRequest(c)
|
||||
if err != nil {
|
||||
@ -47,15 +48,15 @@ func (hs *HTTPServer) callPluginResource(c *contextmodel.ReqContext, pluginID st
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) callPluginResourceWithDataSource(c *contextmodel.ReqContext, pluginID string, ds *datasources.DataSource) {
|
||||
pCtx, found, err := hs.PluginContextProvider.GetWithDataSource(c.Req.Context(), pluginID, c.SignedInUser, ds)
|
||||
pCtx, err := hs.pluginContextProvider.GetWithDataSource(c.Req.Context(), pluginID, c.SignedInUser, ds)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
c.JsonApiErr(404, "Plugin not found", nil)
|
||||
return
|
||||
}
|
||||
c.JsonApiErr(500, "Failed to get plugin settings", err)
|
||||
return
|
||||
}
|
||||
if !found {
|
||||
c.JsonApiErr(404, "Plugin not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
var dsURL string
|
||||
if pCtx.DataSourceInstanceSettings != nil {
|
||||
|
@ -42,6 +42,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
fakeSecrets "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
||||
@ -74,11 +75,12 @@ func TestCallResource(t *testing.T) {
|
||||
ps, err := store.ProvideService(reg, srcs, l)
|
||||
require.NoError(t, err)
|
||||
|
||||
pcp := plugincontext.ProvideService(localcache.ProvideService(), ps, &datasources.FakeCacheService{}, &datasources.FakeDataSourceService{}, pluginSettings.ProvideService(db.InitTestDB(t), nil))
|
||||
pcp := plugincontext.ProvideService(localcache.ProvideService(), ps, &datasources.FakeDataSourceService{},
|
||||
pluginSettings.ProvideService(db.InitTestDB(t), fakeSecrets.NewFakeSecretsService()))
|
||||
|
||||
srv := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = cfg
|
||||
hs.PluginContextProvider = pcp
|
||||
hs.pluginContextProvider = pcp
|
||||
hs.QuotaService = quotatest.New(false, nil)
|
||||
hs.pluginStore = ps
|
||||
hs.pluginClient = pluginClient.ProvideService(reg, pCfg)
|
||||
@ -117,7 +119,7 @@ func TestCallResource(t *testing.T) {
|
||||
|
||||
srv = SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = cfg
|
||||
hs.PluginContextProvider = pcp
|
||||
hs.pluginContextProvider = pcp
|
||||
hs.QuotaService = quotatest.New(false, nil)
|
||||
hs.pluginStore = ps
|
||||
hs.pluginClient = pc
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@ -259,7 +260,7 @@ func (hs *HTTPServer) UpdatePluginSetting(c *contextmodel.ReqContext) response.R
|
||||
return response.Error(500, "Failed to update plugin setting", err)
|
||||
}
|
||||
|
||||
hs.PluginContextProvider.InvalidateSettingsCache(c.Req.Context(), pluginID)
|
||||
hs.pluginContextProvider.InvalidateSettingsCache(c.Req.Context(), pluginID)
|
||||
|
||||
return response.Success("Plugin settings updated")
|
||||
}
|
||||
@ -392,15 +393,13 @@ func (hs *HTTPServer) redirectCDNPluginAsset(c *contextmodel.ReqContext, plugin
|
||||
// /api/plugins/:pluginId/health
|
||||
func (hs *HTTPServer) CheckHealth(c *contextmodel.ReqContext) response.Response {
|
||||
pluginID := web.Params(c.Req)[":pluginId"]
|
||||
|
||||
pCtx, found, err := hs.PluginContextProvider.Get(c.Req.Context(), pluginID, c.SignedInUser)
|
||||
pCtx, err := hs.pluginContextProvider.Get(c.Req.Context(), pluginID, c.SignedInUser)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
return response.Error(404, "Plugin not found", nil)
|
||||
}
|
||||
return response.Error(500, "Failed to get plugin settings", err)
|
||||
}
|
||||
if !found {
|
||||
return response.Error(404, "Plugin not found", nil)
|
||||
}
|
||||
|
||||
resp, err := hs.pluginClient.CheckHealth(c.Req.Context(), &backend.CheckHealthRequest{
|
||||
PluginContext: pCtx,
|
||||
Headers: map[string]string{},
|
||||
|
@ -10,9 +10,12 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
datafakes "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -46,12 +49,16 @@ func framesPassThroughService(t *testing.T, frames data.Frames) (data.Frames, er
|
||||
cfg := setting.NewCfg()
|
||||
|
||||
s := Service{
|
||||
cfg: cfg,
|
||||
dataService: me,
|
||||
dataSourceService: &datafakes.FakeDataSourceService{},
|
||||
features: &featuremgmt.FeatureManager{},
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
metrics: newMetrics(nil),
|
||||
cfg: cfg,
|
||||
dataService: me,
|
||||
features: &featuremgmt.FeatureManager{},
|
||||
pCtxProvider: plugincontext.ProvideService(nil, &plugins.FakePluginStore{
|
||||
PluginList: []plugins.PluginDTO{
|
||||
{JSONData: plugins.JSONData{ID: "test"}},
|
||||
}},
|
||||
&datafakes.FakeDataSourceService{}, nil),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
metrics: newMetrics(nil),
|
||||
}
|
||||
queries := []Query{{
|
||||
RefID: "A",
|
||||
@ -67,7 +74,10 @@ func framesPassThroughService(t *testing.T, frames data.Frames) (data.Frames, er
|
||||
},
|
||||
}}
|
||||
|
||||
req := &Request{Queries: queries}
|
||||
req := &Request{
|
||||
Queries: queries,
|
||||
User: &user.SignedInUser{},
|
||||
}
|
||||
|
||||
pl, err := s.BuildPipeline(req)
|
||||
require.NoError(t, err)
|
||||
|
@ -16,7 +16,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/adapters"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -207,20 +206,14 @@ func (dn *DSNode) Execute(ctx context.Context, now time.Time, _ mathexp.Vars, s
|
||||
ctx, span := s.tracer.Start(ctx, "SSE.ExecuteDatasourceQuery")
|
||||
defer span.End()
|
||||
|
||||
dsInstanceSettings, err := adapters.ModelToInstanceSettings(dn.datasource, s.decryptSecureJsonDataFn(ctx))
|
||||
pCtx, err := s.pCtxProvider.GetWithDataSource(ctx, dn.datasource.Type, dn.request.User, dn.datasource)
|
||||
if err != nil {
|
||||
return mathexp.Results{}, fmt.Errorf("%v: %w", "failed to convert datasource instance settings", err)
|
||||
}
|
||||
pc := backend.PluginContext{
|
||||
OrgID: dn.orgID,
|
||||
DataSourceInstanceSettings: dsInstanceSettings,
|
||||
PluginID: dn.datasource.Type,
|
||||
User: dn.request.User,
|
||||
return mathexp.Results{}, err
|
||||
}
|
||||
span.SetAttributes("datasource.type", dn.datasource.Type, attribute.Key("datasource.type").String(dn.datasource.Type))
|
||||
|
||||
req := &backend.QueryDataRequest{
|
||||
PluginContext: pc,
|
||||
PluginContext: pCtx,
|
||||
Queries: []backend.DataQuery{
|
||||
{
|
||||
RefID: dn.refID,
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@ -40,23 +41,24 @@ func IsDataSource(uid string) bool {
|
||||
|
||||
// Service is service representation for expression handling.
|
||||
type Service struct {
|
||||
cfg *setting.Cfg
|
||||
dataService backend.QueryDataHandler
|
||||
dataSourceService datasources.DataSourceService
|
||||
features featuremgmt.FeatureToggles
|
||||
cfg *setting.Cfg
|
||||
dataService backend.QueryDataHandler
|
||||
pCtxProvider *plugincontext.Provider
|
||||
features featuremgmt.FeatureToggles
|
||||
|
||||
tracer tracing.Tracer
|
||||
metrics *metrics
|
||||
}
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, pluginClient plugins.Client, dataSourceService datasources.DataSourceService, features featuremgmt.FeatureToggles, registerer prometheus.Registerer, tracer tracing.Tracer) *Service {
|
||||
func ProvideService(cfg *setting.Cfg, pluginClient plugins.Client, pCtxProvider *plugincontext.Provider,
|
||||
features featuremgmt.FeatureToggles, registerer prometheus.Registerer, tracer tracing.Tracer) *Service {
|
||||
return &Service{
|
||||
cfg: cfg,
|
||||
dataService: pluginClient,
|
||||
dataSourceService: dataSourceService,
|
||||
features: features,
|
||||
tracer: tracer,
|
||||
metrics: newMetrics(registerer),
|
||||
cfg: cfg,
|
||||
dataService: pluginClient,
|
||||
pCtxProvider: pCtxProvider,
|
||||
features: features,
|
||||
tracer: tracer,
|
||||
metrics: newMetrics(registerer),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,9 +13,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
datafakes "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@ -28,15 +31,19 @@ func TestService(t *testing.T) {
|
||||
Frames: []*data.Frame{dsDF},
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
pCtxProvider := plugincontext.ProvideService(nil, &plugins.FakePluginStore{
|
||||
PluginList: []plugins.PluginDTO{
|
||||
{JSONData: plugins.JSONData{ID: "test"}},
|
||||
},
|
||||
}, &datafakes.FakeDataSourceService{}, nil)
|
||||
|
||||
s := Service{
|
||||
cfg: cfg,
|
||||
dataService: me,
|
||||
dataSourceService: &datafakes.FakeDataSourceService{},
|
||||
features: &featuremgmt.FeatureManager{},
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
metrics: newMetrics(nil),
|
||||
cfg: setting.NewCfg(),
|
||||
dataService: me,
|
||||
pCtxProvider: pCtxProvider,
|
||||
features: &featuremgmt.FeatureManager{},
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
metrics: newMetrics(nil),
|
||||
}
|
||||
|
||||
queries := []Query{
|
||||
@ -60,7 +67,7 @@ func TestService(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
req := &Request{Queries: queries}
|
||||
req := &Request{Queries: queries, User: &user.SignedInUser{}}
|
||||
|
||||
pl, err := s.BuildPipeline(req)
|
||||
require.NoError(t, err)
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
// Request is similar to plugins.DataQuery but with the Time Ranges is per Query.
|
||||
@ -17,7 +18,7 @@ type Request struct {
|
||||
Debug bool
|
||||
OrgId int64
|
||||
Queries []Query
|
||||
User *backend.User
|
||||
User *user.SignedInUser
|
||||
}
|
||||
|
||||
// Query is like plugins.DataSubQuery, but with a a time range, and only the UID
|
||||
@ -136,9 +137,3 @@ func hiddenRefIDs(queries []Query) (map[string]struct{}, error) {
|
||||
}
|
||||
return hidden, nil
|
||||
}
|
||||
|
||||
func (s *Service) 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)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package features
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/centrifugal/centrifuge"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
@ -9,13 +10,14 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/live/model"
|
||||
"github.com/grafana/grafana/pkg/services/live/orgchannel"
|
||||
"github.com/grafana/grafana/pkg/services/live/runstream"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
//go:generate mockgen -destination=plugin_mock.go -package=features github.com/grafana/grafana/pkg/services/live/features PluginContextGetter
|
||||
|
||||
type PluginContextGetter interface {
|
||||
GetPluginContext(ctx context.Context, user *user.SignedInUser, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, bool, error)
|
||||
GetPluginContext(ctx context.Context, user *user.SignedInUser, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, error)
|
||||
}
|
||||
|
||||
// PluginRunner can handle streaming operations for channels belonging to plugins.
|
||||
@ -62,15 +64,15 @@ type PluginPathRunner struct {
|
||||
|
||||
// OnSubscribe passes control to a plugin.
|
||||
func (r *PluginPathRunner) OnSubscribe(ctx context.Context, user *user.SignedInUser, e model.SubscribeEvent) (model.SubscribeReply, backend.SubscribeStreamStatus, error) {
|
||||
pCtx, found, err := r.pluginContextGetter.GetPluginContext(ctx, user, r.pluginID, r.datasourceUID, false)
|
||||
pCtx, err := r.pluginContextGetter.GetPluginContext(ctx, user, r.pluginID, r.datasourceUID, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
logger.Error("Plugin context not found", "path", r.path)
|
||||
return model.SubscribeReply{}, 0, centrifuge.ErrorInternal
|
||||
}
|
||||
logger.Error("Get plugin context error", "error", err, "path", r.path)
|
||||
return model.SubscribeReply{}, 0, err
|
||||
}
|
||||
if !found {
|
||||
logger.Error("Plugin context not found", "path", r.path)
|
||||
return model.SubscribeReply{}, 0, centrifuge.ErrorInternal
|
||||
}
|
||||
resp, err := r.handler.SubscribeStream(ctx, &backend.SubscribeStreamRequest{
|
||||
PluginContext: pCtx,
|
||||
Path: r.path,
|
||||
@ -106,15 +108,15 @@ func (r *PluginPathRunner) OnSubscribe(ctx context.Context, user *user.SignedInU
|
||||
|
||||
// OnPublish passes control to a plugin.
|
||||
func (r *PluginPathRunner) OnPublish(ctx context.Context, user *user.SignedInUser, e model.PublishEvent) (model.PublishReply, backend.PublishStreamStatus, error) {
|
||||
pCtx, found, err := r.pluginContextGetter.GetPluginContext(ctx, user, r.pluginID, r.datasourceUID, false)
|
||||
pCtx, err := r.pluginContextGetter.GetPluginContext(ctx, user, r.pluginID, r.datasourceUID, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
logger.Error("Plugin context not found", "path", r.path)
|
||||
return model.PublishReply{}, 0, centrifuge.ErrorInternal
|
||||
}
|
||||
logger.Error("Get plugin context error", "error", err, "path", r.path)
|
||||
return model.PublishReply{}, 0, err
|
||||
}
|
||||
if !found {
|
||||
logger.Error("Plugin context not found", "path", r.path)
|
||||
return model.PublishReply{}, 0, centrifuge.ErrorInternal
|
||||
}
|
||||
resp, err := r.handler.PublishStream(ctx, &backend.PublishStreamRequest{
|
||||
PluginContext: pCtx,
|
||||
Path: r.path,
|
||||
|
@ -5,13 +5,12 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"context"
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
backend "github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
user "github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
// MockPluginContextGetter is a mock of PluginContextGetter interface.
|
||||
@ -38,17 +37,16 @@ func (m *MockPluginContextGetter) EXPECT() *MockPluginContextGetterMockRecorder
|
||||
}
|
||||
|
||||
// GetPluginContext mocks base method.
|
||||
func (m *MockPluginContextGetter) GetPluginContext(ctx context.Context, arg0 *user.SignedInUser, arg1, arg2 string) (backend.PluginContext, bool, error) {
|
||||
func (m *MockPluginContextGetter) GetPluginContext(arg0 context.Context, arg1 *user.SignedInUser, arg2, arg3 string, arg4 bool) (backend.PluginContext, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetPluginContext", arg0, arg1, arg2)
|
||||
ret := m.ctrl.Call(m, "GetPluginContext", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(backend.PluginContext)
|
||||
ret1, _ := ret[1].(bool)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetPluginContext indicates an expected call of GetPluginContext.
|
||||
func (mr *MockPluginContextGetterMockRecorder) GetPluginContext(ctx context.Context, arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
func (mr *MockPluginContextGetterMockRecorder) GetPluginContext(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPluginContext", reflect.TypeOf((*MockPluginContextGetter)(nil).GetPluginContext), arg0, arg1, arg2)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPluginContext", reflect.TypeOf((*MockPluginContextGetter)(nil).GetPluginContext), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
@ -72,14 +72,14 @@ func NewContextGetter(pluginContextProvider *plugincontext.Provider, dataSourceC
|
||||
}
|
||||
}
|
||||
|
||||
func (g *ContextGetter) GetPluginContext(ctx context.Context, user *user.SignedInUser, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, bool, error) {
|
||||
func (g *ContextGetter) GetPluginContext(ctx context.Context, user *user.SignedInUser, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, error) {
|
||||
if datasourceUID == "" {
|
||||
return g.pluginContextProvider.Get(ctx, pluginID, user)
|
||||
}
|
||||
|
||||
ds, err := g.dataSourceCache.GetDatasourceByUID(ctx, datasourceUID, user, skipCache)
|
||||
if err != nil {
|
||||
return backend.PluginContext{}, false, fmt.Errorf("%v: %w", "Failed to get datasource", err)
|
||||
return backend.PluginContext{}, fmt.Errorf("%v: %w", "Failed to get datasource", err)
|
||||
}
|
||||
return g.pluginContextProvider.GetWithDataSource(ctx, pluginID, user, ds)
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
@ -25,7 +26,7 @@ type ChannelLocalPublisher interface {
|
||||
}
|
||||
|
||||
type PluginContextGetter interface {
|
||||
GetPluginContext(ctx context.Context, user *user.SignedInUser, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, bool, error)
|
||||
GetPluginContext(ctx context.Context, user *user.SignedInUser, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, error)
|
||||
}
|
||||
|
||||
type NumLocalSubscribersGetter interface {
|
||||
@ -182,15 +183,15 @@ func (s *Manager) watchStream(ctx context.Context, cancelFn func(), sr streamReq
|
||||
case <-datasourceTicker.C:
|
||||
if sr.PluginContext.DataSourceInstanceSettings != nil {
|
||||
dsUID := sr.PluginContext.DataSourceInstanceSettings.UID
|
||||
pCtx, ok, err := s.pluginContextGetter.GetPluginContext(ctx, sr.user, sr.PluginContext.PluginID, dsUID, false)
|
||||
pCtx, err := s.pluginContextGetter.GetPluginContext(ctx, sr.user, sr.PluginContext.PluginID, dsUID, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
logger.Debug("Datasource not found, stop stream", "channel", sr.Channel, "path", sr.Path)
|
||||
return
|
||||
}
|
||||
logger.Error("Error getting datasource context", "channel", sr.Channel, "path", sr.Path, "error", err)
|
||||
continue
|
||||
}
|
||||
if !ok {
|
||||
logger.Debug("Datasource not found, stop stream", "channel", sr.Channel, "path", sr.Path)
|
||||
return
|
||||
}
|
||||
if pCtx.DataSourceInstanceSettings.Updated != sr.PluginContext.DataSourceInstanceSettings.Updated {
|
||||
logger.Debug("Datasource changed, re-establish stream", "channel", sr.Channel, "path", sr.Path)
|
||||
err := s.HandleDatasourceUpdate(pCtx.OrgID, dsUID)
|
||||
@ -283,16 +284,16 @@ func (s *Manager) runStream(ctx context.Context, cancelFn func(), sr streamReque
|
||||
if pluginCtx.DataSourceInstanceSettings != nil {
|
||||
datasourceUID = pluginCtx.DataSourceInstanceSettings.UID
|
||||
}
|
||||
newPluginCtx, ok, err := s.pluginContextGetter.GetPluginContext(ctx, sr.user, pluginCtx.PluginID, datasourceUID, false)
|
||||
newPluginCtx, err := s.pluginContextGetter.GetPluginContext(ctx, sr.user, pluginCtx.PluginID, datasourceUID, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
logger.Info("No plugin context found, stopping stream", "path", sr.Path)
|
||||
return
|
||||
}
|
||||
logger.Error("Error getting plugin context", "path", sr.Path, "error", err)
|
||||
isReconnect = true
|
||||
continue
|
||||
}
|
||||
if !ok {
|
||||
logger.Info("No plugin context found, stopping stream", "path", sr.Path)
|
||||
return
|
||||
}
|
||||
pluginCtx = newPluginCtx
|
||||
}
|
||||
|
||||
@ -407,13 +408,13 @@ func (s *Manager) SubmitStream(ctx context.Context, user *user.SignedInUser, cha
|
||||
if pCtx.DataSourceInstanceSettings != nil {
|
||||
datasourceUID = pCtx.DataSourceInstanceSettings.UID
|
||||
}
|
||||
newPluginCtx, ok, err := s.pluginContextGetter.GetPluginContext(ctx, user, pCtx.PluginID, datasourceUID, false)
|
||||
newPluginCtx, err := s.pluginContextGetter.GetPluginContext(ctx, user, pCtx.PluginID, datasourceUID, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugincontext.ErrPluginNotFound) {
|
||||
return nil, errDatasourceNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, errDatasourceNotFound
|
||||
}
|
||||
pCtx = newPluginCtx
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ func TestStreamManager_SubmitStream_Send(t *testing.T) {
|
||||
|
||||
mockStreamRunner := NewMockStreamRunner(mockCtrl)
|
||||
mockStreamRunner.EXPECT().RunStream(
|
||||
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
).DoAndReturn(func(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
require.Equal(t, "test", req.Path)
|
||||
close(startedCh)
|
||||
@ -139,7 +139,7 @@ func TestStreamManager_SubmitStream_DifferentOrgID(t *testing.T) {
|
||||
|
||||
mockStreamRunner1 := NewMockStreamRunner(mockCtrl)
|
||||
mockStreamRunner1.EXPECT().RunStream(
|
||||
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
).DoAndReturn(func(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
require.Equal(t, "test", req.Path)
|
||||
close(startedCh1)
|
||||
@ -152,7 +152,7 @@ func TestStreamManager_SubmitStream_DifferentOrgID(t *testing.T) {
|
||||
|
||||
mockStreamRunner2 := NewMockStreamRunner(mockCtrl)
|
||||
mockStreamRunner2.EXPECT().RunStream(
|
||||
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
).DoAndReturn(func(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
require.Equal(t, "test", req.Path)
|
||||
close(startedCh2)
|
||||
@ -212,7 +212,7 @@ func TestStreamManager_SubmitStream_CloseNoSubscribers(t *testing.T) {
|
||||
mockNumSubscribersGetter.EXPECT().GetNumLocalSubscribers("1/test").Return(0, nil).Times(3)
|
||||
|
||||
mockStreamRunner := NewMockStreamRunner(mockCtrl)
|
||||
mockStreamRunner.EXPECT().RunStream(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
mockStreamRunner.EXPECT().RunStream(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
close(startedCh)
|
||||
<-ctx.Done()
|
||||
close(doneCh)
|
||||
@ -264,7 +264,7 @@ func TestStreamManager_SubmitStream_ErrorRestartsRunStream(t *testing.T) {
|
||||
|
||||
mockStreamRunner := NewMockStreamRunner(mockCtrl)
|
||||
mockStreamRunner.EXPECT().RunStream(
|
||||
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
).DoAndReturn(func(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
if currentErrors >= numErrors {
|
||||
return nil
|
||||
@ -302,7 +302,7 @@ func TestStreamManager_SubmitStream_NilErrorStopsRunStream(t *testing.T) {
|
||||
|
||||
mockStreamRunner := NewMockStreamRunner(mockCtrl)
|
||||
mockStreamRunner.EXPECT().RunStream(
|
||||
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
).DoAndReturn(func(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
return nil
|
||||
}).Times(1)
|
||||
@ -352,7 +352,7 @@ func TestStreamManager_HandleDatasourceUpdate(t *testing.T) {
|
||||
|
||||
mockStreamRunner := NewMockStreamRunner(mockCtrl)
|
||||
mockStreamRunner.EXPECT().RunStream(
|
||||
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
).DoAndReturn(func(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
if isFirstCall {
|
||||
// first RunStream will wait till context done.
|
||||
@ -415,7 +415,7 @@ func TestStreamManager_HandleDatasourceDelete(t *testing.T) {
|
||||
|
||||
mockStreamRunner := NewMockStreamRunner(mockCtrl)
|
||||
mockStreamRunner.EXPECT().RunStream(
|
||||
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
gomock.Any(), gomock.Any(), gomock.Any(),
|
||||
).DoAndReturn(func(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
close(doneCh)
|
||||
<-ctx.Done()
|
||||
|
@ -10,8 +10,7 @@ import (
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
backend "github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
user "github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
// MockChannelLocalPublisher is a mock of ChannelLocalPublisher interface.
|
||||
@ -121,7 +120,7 @@ func (m *MockStreamRunner) RunStream(arg0 context.Context, arg1 *backend.RunStre
|
||||
}
|
||||
|
||||
// RunStream indicates an expected call of RunStream.
|
||||
func (mr *MockStreamRunnerMockRecorder) RunStream(ctx, arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
func (mr *MockStreamRunnerMockRecorder) RunStream(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunStream", reflect.TypeOf((*MockStreamRunner)(nil).RunStream), arg0, arg1, arg2)
|
||||
}
|
||||
@ -150,17 +149,16 @@ func (m *MockPluginContextGetter) EXPECT() *MockPluginContextGetterMockRecorder
|
||||
}
|
||||
|
||||
// GetPluginContext mocks base method.
|
||||
func (m *MockPluginContextGetter) GetPluginContext(ctx context.Context, arg0 *user.SignedInUser, arg1, arg2 string, arg3 bool) (backend.PluginContext, bool, error) {
|
||||
func (m *MockPluginContextGetter) GetPluginContext(arg0 context.Context, arg1 *user.SignedInUser, arg2, arg3 string, arg4 bool) (backend.PluginContext, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetPluginContext", ctx, arg0, arg1, arg2, arg3)
|
||||
ret := m.ctrl.Call(m, "GetPluginContext", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(backend.PluginContext)
|
||||
ret1, _ := ret[1].(bool)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetPluginContext indicates an expected call of GetPluginContext.
|
||||
func (mr *MockPluginContextGetterMockRecorder) GetPluginContext(ctx, arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
func (mr *MockPluginContextGetterMockRecorder) GetPluginContext(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPluginContext", reflect.TypeOf((*MockPluginContextGetter)(nil).GetPluginContext), ctx, arg0, arg1, arg2, arg3)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPluginContext", reflect.TypeOf((*MockPluginContextGetter)(nil).GetPluginContext), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
@ -247,6 +247,7 @@ func getExprRequest(ctx EvaluationContext, data []models.AlertQuery, dsCacheServ
|
||||
req := &expr.Request{
|
||||
OrgId: ctx.User.OrgID,
|
||||
Headers: buildDatasourceHeaders(ctx.Ctx),
|
||||
User: ctx.User,
|
||||
}
|
||||
|
||||
datasources := make(map[string]*datasources.DataSource, len(data))
|
||||
|
@ -11,68 +11,78 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/adapters"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
var ErrPluginNotFound = errors.New("plugin not found")
|
||||
|
||||
func ProvideService(cacheService *localcache.CacheService, pluginStore plugins.Store,
|
||||
dataSourceCache datasources.CacheService, dataSourceService datasources.DataSourceService,
|
||||
pluginSettingsService pluginsettings.Service) *Provider {
|
||||
dataSourceService datasources.DataSourceService, pluginSettingsService pluginsettings.Service) *Provider {
|
||||
return &Provider{
|
||||
cacheService: cacheService,
|
||||
pluginStore: pluginStore,
|
||||
dataSourceCache: dataSourceCache,
|
||||
dataSourceService: dataSourceService,
|
||||
pluginSettingsService: pluginSettingsService,
|
||||
logger: log.New("plugincontext"),
|
||||
}
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
cacheService *localcache.CacheService
|
||||
pluginStore plugins.Store
|
||||
dataSourceCache datasources.CacheService
|
||||
dataSourceService datasources.DataSourceService
|
||||
pluginSettingsService pluginsettings.Service
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// Get allows getting plugin context by its ID. If datasourceUID is not empty string
|
||||
// then PluginContext.DataSourceInstanceSettings will be resolved and appended to
|
||||
// returned context.
|
||||
func (p *Provider) Get(ctx context.Context, pluginID string, user *user.SignedInUser) (backend.PluginContext, bool, error) {
|
||||
return p.pluginContext(ctx, pluginID, user)
|
||||
func (p *Provider) Get(ctx context.Context, pluginID string, user *user.SignedInUser) (backend.PluginContext, error) {
|
||||
plugin, exists := p.pluginStore.Plugin(ctx, pluginID)
|
||||
if !exists {
|
||||
return backend.PluginContext{}, ErrPluginNotFound
|
||||
}
|
||||
|
||||
pCtx := backend.PluginContext{
|
||||
OrgID: user.OrgID,
|
||||
PluginID: pluginID,
|
||||
User: adapters.BackendUserFromSignedInUser(user),
|
||||
}
|
||||
|
||||
if plugin.IsApp() {
|
||||
appSettings, err := p.appInstanceSettings(ctx, pluginID, user)
|
||||
if err != nil {
|
||||
return backend.PluginContext{}, err
|
||||
}
|
||||
pCtx.AppInstanceSettings = appSettings
|
||||
}
|
||||
|
||||
return pCtx, nil
|
||||
}
|
||||
|
||||
// GetWithDataSource allows getting plugin context by its ID and PluginContext.DataSourceInstanceSettings will be
|
||||
// resolved and appended to the returned context.
|
||||
func (p *Provider) GetWithDataSource(ctx context.Context, pluginID string, user *user.SignedInUser, ds *datasources.DataSource) (backend.PluginContext, bool, error) {
|
||||
pCtx, exists, err := p.pluginContext(ctx, pluginID, user)
|
||||
func (p *Provider) GetWithDataSource(ctx context.Context, pluginID string, user *user.SignedInUser, ds *datasources.DataSource) (backend.PluginContext, error) {
|
||||
pCtx, err := p.Get(ctx, pluginID, user)
|
||||
if err != nil {
|
||||
return pCtx, exists, err
|
||||
return backend.PluginContext{}, err
|
||||
}
|
||||
|
||||
datasourceSettings, err := adapters.ModelToInstanceSettings(ds, p.decryptSecureJsonDataFn(ctx))
|
||||
if err != nil {
|
||||
return pCtx, exists, fmt.Errorf("%v: %w", "Failed to convert datasource", err)
|
||||
return pCtx, err
|
||||
}
|
||||
pCtx.DataSourceInstanceSettings = datasourceSettings
|
||||
|
||||
return pCtx, true, nil
|
||||
return pCtx, nil
|
||||
}
|
||||
|
||||
const pluginSettingsCacheTTL = 5 * time.Second
|
||||
const pluginSettingsCachePrefix = "plugin-setting-"
|
||||
|
||||
func (p *Provider) pluginContext(ctx context.Context, pluginID string, user *user.SignedInUser) (backend.PluginContext, bool, error) {
|
||||
plugin, exists := p.pluginStore.Plugin(ctx, pluginID)
|
||||
if !exists {
|
||||
return backend.PluginContext{}, false, nil
|
||||
}
|
||||
|
||||
func (p *Provider) appInstanceSettings(ctx context.Context, pluginID string, user *user.SignedInUser) (*backend.AppInstanceSettings, error) {
|
||||
jsonData := json.RawMessage{}
|
||||
decryptedSecureJSONData := map[string]string{}
|
||||
var updated time.Time
|
||||
@ -80,29 +90,28 @@ func (p *Provider) pluginContext(ctx context.Context, pluginID string, user *use
|
||||
ps, err := p.getCachedPluginSettings(ctx, pluginID, user)
|
||||
if err != nil {
|
||||
// pluginsettings.ErrPluginSettingNotFound is expected if there's no row found for plugin setting in database (if non-app plugin).
|
||||
// If it's not this expected error something is wrong with cache or database and we return the error to the client.
|
||||
// Otherwise, something is wrong with cache or database, and we return the error to the client.
|
||||
if !errors.Is(err, pluginsettings.ErrPluginSettingNotFound) {
|
||||
return backend.PluginContext{}, false, fmt.Errorf("%v: %w", "Failed to get plugin settings", err)
|
||||
return nil, fmt.Errorf("%v: %w", "Failed to get plugin settings", err)
|
||||
}
|
||||
} else {
|
||||
jsonData, err = json.Marshal(ps.JSONData)
|
||||
if err != nil {
|
||||
return backend.PluginContext{}, false, fmt.Errorf("%v: %w", "Failed to unmarshal plugin json data", err)
|
||||
return nil, fmt.Errorf("%v: %w", "Failed to unmarshal plugin json data", err)
|
||||
}
|
||||
decryptedSecureJSONData = p.pluginSettingsService.DecryptedValues(ps)
|
||||
updated = ps.Updated
|
||||
}
|
||||
|
||||
return backend.PluginContext{
|
||||
OrgID: user.OrgID,
|
||||
PluginID: plugin.ID,
|
||||
User: adapters.BackendUserFromSignedInUser(user),
|
||||
AppInstanceSettings: &backend.AppInstanceSettings{
|
||||
JSONData: jsonData,
|
||||
DecryptedSecureJSONData: decryptedSecureJSONData,
|
||||
Updated: updated,
|
||||
},
|
||||
}, true, nil
|
||||
return &backend.AppInstanceSettings{
|
||||
JSONData: jsonData,
|
||||
DecryptedSecureJSONData: decryptedSecureJSONData,
|
||||
Updated: updated,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) InvalidateSettingsCache(_ context.Context, pluginID string) {
|
||||
p.cacheService.Delete(getCacheKey(pluginID))
|
||||
}
|
||||
|
||||
func (p *Provider) getCachedPluginSettings(ctx context.Context, pluginID string, user *user.SignedInUser) (*pluginsettings.DTO, error) {
|
||||
@ -127,10 +136,6 @@ func (p *Provider) getCachedPluginSettings(ctx context.Context, pluginID string,
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func (p *Provider) InvalidateSettingsCache(_ context.Context, pluginID string) {
|
||||
p.cacheService.Delete(getCacheKey(pluginID))
|
||||
}
|
||||
|
||||
func (p *Provider) decryptSecureJsonDataFn(ctx context.Context) func(ds *datasources.DataSource) (map[string]string, error) {
|
||||
return func(ds *datasources.DataSource) (map[string]string, error) {
|
||||
return p.dataSourceService.DecryptedValues(ctx, ds)
|
||||
|
@ -25,8 +25,11 @@ import (
|
||||
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
datasourceService "github.com/grafana/grafana/pkg/services/datasources/service"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
|
||||
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
||||
"github.com/grafana/grafana/pkg/services/query"
|
||||
fakeSecrets "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
@ -129,13 +132,24 @@ func buildQueryDataService(t *testing.T, cs datasources.CacheService, fpc *fakeP
|
||||
}
|
||||
}
|
||||
|
||||
ds := &fakeDatasources.FakeDataSourceService{}
|
||||
pCtxProvider := plugincontext.ProvideService(localcache.ProvideService(), &plugins.FakePluginStore{
|
||||
PluginList: []plugins.PluginDTO{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "mysql",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, ds, pluginSettings.ProvideService(store, fakeSecrets.NewFakeSecretsService()))
|
||||
|
||||
return query.ProvideService(
|
||||
setting.NewCfg(),
|
||||
cs,
|
||||
nil,
|
||||
&fakePluginRequestValidator{},
|
||||
&fakeDatasources.FakeDataSourceService{},
|
||||
fpc,
|
||||
pCtxProvider,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/adapters"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/validations"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@ -37,16 +37,16 @@ func ProvideService(
|
||||
dataSourceCache datasources.CacheService,
|
||||
expressionService *expr.Service,
|
||||
pluginRequestValidator validations.PluginRequestValidator,
|
||||
dataSourceService datasources.DataSourceService,
|
||||
pluginClient plugins.Client,
|
||||
pCtxProvider *plugincontext.Provider,
|
||||
) *ServiceImpl {
|
||||
g := &ServiceImpl{
|
||||
cfg: cfg,
|
||||
dataSourceCache: dataSourceCache,
|
||||
expressionService: expressionService,
|
||||
pluginRequestValidator: pluginRequestValidator,
|
||||
dataSourceService: dataSourceService,
|
||||
pluginClient: pluginClient,
|
||||
pCtxProvider: pCtxProvider,
|
||||
log: log.New("query_data"),
|
||||
}
|
||||
g.log.Info("Query Service initialization")
|
||||
@ -67,8 +67,8 @@ type ServiceImpl struct {
|
||||
dataSourceCache datasources.CacheService
|
||||
expressionService *expr.Service
|
||||
pluginRequestValidator validations.PluginRequestValidator
|
||||
dataSourceService datasources.DataSourceService
|
||||
pluginClient plugins.Client
|
||||
pCtxProvider *plugincontext.Provider
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
@ -178,7 +178,7 @@ func (s *ServiceImpl) handleExpressions(ctx context.Context, user *user.SignedIn
|
||||
}
|
||||
|
||||
if user != nil { // for passthrough authentication, SSE does not authenticate
|
||||
exprReq.User = adapters.BackendUserFromSignedInUser(user)
|
||||
exprReq.User = user
|
||||
exprReq.OrgId = user.OrgID
|
||||
}
|
||||
|
||||
@ -227,20 +227,14 @@ func (s *ServiceImpl) handleQuerySingleDatasource(ctx context.Context, user *use
|
||||
}
|
||||
}
|
||||
|
||||
instanceSettings, err := adapters.ModelToInstanceSettings(ds, s.decryptSecureJsonDataFn(ctx))
|
||||
pCtx, err := s.pCtxProvider.GetWithDataSource(ctx, ds.Type, user, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &backend.QueryDataRequest{
|
||||
PluginContext: backend.PluginContext{
|
||||
OrgID: ds.OrgID,
|
||||
PluginID: ds.Type,
|
||||
User: adapters.BackendUserFromSignedInUser(user),
|
||||
DataSourceInstanceSettings: instanceSettings,
|
||||
},
|
||||
Headers: map[string]string{},
|
||||
Queries: []backend.DataQuery{},
|
||||
PluginContext: pCtx,
|
||||
Headers: map[string]string{},
|
||||
Queries: []backend.DataQuery{},
|
||||
}
|
||||
|
||||
for _, q := range queries {
|
||||
@ -355,9 +349,3 @@ func (s *ServiceImpl) getDataSourceFromQuery(ctx context.Context, user *user.Sig
|
||||
|
||||
return nil, ErrInvalidDatasourceID
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -16,18 +16,18 @@ import (
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/models/roletype"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
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/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
|
||||
"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"
|
||||
@ -431,28 +431,42 @@ func TestQueryDataMultipleSources(t *testing.T) {
|
||||
}
|
||||
|
||||
func setup(t *testing.T) *testContext {
|
||||
dss := []*datasources.DataSource{
|
||||
{UID: "gIEkMvIVz", Type: "postgres"},
|
||||
{UID: "sEx6ZvSVk", Type: "testdata"},
|
||||
{UID: "ds1", Type: "mysql"},
|
||||
{UID: "ds2", Type: "mysql"},
|
||||
}
|
||||
|
||||
t.Helper()
|
||||
pc := &fakePluginClient{}
|
||||
dc := &fakeDataSourceCache{ds: &datasources.DataSource{}}
|
||||
dc := &fakeDataSourceCache{cache: dss}
|
||||
rv := &fakePluginRequestValidator{}
|
||||
|
||||
sqlStore := db.InitTestDB(t)
|
||||
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
ss := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
|
||||
ssvc := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
quotaService := quotatest.New(false, nil)
|
||||
ds, err := dsSvc.ProvideService(nil, ssvc, ss, nil, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
|
||||
require.NoError(t, err)
|
||||
fakeDatasourceService := &fakeDatasources.FakeDataSourceService{
|
||||
DataSources: nil,
|
||||
DataSources: dss,
|
||||
SimulatePluginFailure: false,
|
||||
}
|
||||
exprService := expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, pc, fakeDatasourceService, &featuremgmt.FeatureManager{}, nil, tracing.InitializeTracerForTest())
|
||||
queryService := ProvideService(setting.NewCfg(), dc, exprService, rv, ds, pc) // provider belonging to this package
|
||||
|
||||
pCtxProvider := plugincontext.ProvideService(
|
||||
localcache.ProvideService(), &plugins.FakePluginStore{
|
||||
PluginList: []plugins.PluginDTO{
|
||||
{JSONData: plugins.JSONData{ID: "postgres"}},
|
||||
{JSONData: plugins.JSONData{ID: "testdata"}},
|
||||
{JSONData: plugins.JSONData{ID: "mysql"}},
|
||||
},
|
||||
}, fakeDatasourceService,
|
||||
pluginSettings.ProvideService(sqlStore, secretsService),
|
||||
)
|
||||
exprService := expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, pc, pCtxProvider,
|
||||
&featuremgmt.FeatureManager{}, nil, tracing.InitializeTracerForTest())
|
||||
queryService := ProvideService(setting.NewCfg(), dc, exprService, rv, pc, pCtxProvider) // provider belonging to this package
|
||||
return &testContext{
|
||||
pluginContext: pc,
|
||||
secretStore: ss,
|
||||
dataSourceCache: dc,
|
||||
pluginRequestValidator: rv,
|
||||
queryService: queryService,
|
||||
signedInUser: &user.SignedInUser{OrgID: 1, Login: "login", Name: "name", Email: "email", OrgRole: roletype.RoleAdmin},
|
||||
@ -462,7 +476,6 @@ func setup(t *testing.T) *testContext {
|
||||
type testContext struct {
|
||||
pluginContext *fakePluginClient
|
||||
secretStore secretskvs.SecretsKVStore
|
||||
dataSourceCache *fakeDataSourceCache
|
||||
pluginRequestValidator *fakePluginRequestValidator
|
||||
queryService *ServiceImpl // implementation belonging to this package
|
||||
signedInUser *user.SignedInUser
|
||||
@ -493,7 +506,7 @@ func (rv *fakePluginRequestValidator) Validate(dsURL string, req *http.Request)
|
||||
}
|
||||
|
||||
type fakeDataSourceCache struct {
|
||||
ds *datasources.DataSource
|
||||
cache []*datasources.DataSource
|
||||
}
|
||||
|
||||
func (c *fakeDataSourceCache) GetDatasource(ctx context.Context, datasourceID int64, user *user.SignedInUser, skipCache bool) (*datasources.DataSource, error) {
|
||||
@ -502,9 +515,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 &datasources.DataSource{
|
||||
UID: datasourceUID,
|
||||
}, nil
|
||||
for _, ds := range c.cache {
|
||||
if ds.UID == datasourceUID {
|
||||
return ds, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
type fakePluginClient struct {
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/adapters"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||
)
|
||||
|
||||
@ -17,25 +17,22 @@ type Service struct {
|
||||
pluginsClient plugins.Client
|
||||
oAuthTokenService oauthtoken.OAuthTokenService
|
||||
dataSourcesService datasources.DataSourceService
|
||||
pCtxProvider *plugincontext.Provider
|
||||
}
|
||||
|
||||
func ProvideService(pluginsClient plugins.Client, oAuthTokenService oauthtoken.OAuthTokenService,
|
||||
dataSourcesService datasources.DataSourceService) *Service {
|
||||
dataSourcesService datasources.DataSourceService, pCtxProvider *plugincontext.Provider) *Service {
|
||||
return &Service{
|
||||
pluginsClient: pluginsClient,
|
||||
oAuthTokenService: oAuthTokenService,
|
||||
dataSourcesService: dataSourcesService,
|
||||
pCtxProvider: pCtxProvider,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:staticcheck // legacydata.DataResponse deprecated
|
||||
func (h *Service) HandleRequest(ctx context.Context, ds *datasources.DataSource, query legacydata.DataQuery) (legacydata.DataResponse, error) {
|
||||
decryptedJsonData, err := h.dataSourcesService.DecryptedValues(ctx, ds)
|
||||
if err != nil {
|
||||
return legacydata.DataResponse{}, err
|
||||
}
|
||||
|
||||
req, err := generateRequest(ctx, ds, decryptedJsonData, query)
|
||||
req, err := h.generateRequest(ctx, ds, query)
|
||||
if err != nil {
|
||||
return legacydata.DataResponse{}, err
|
||||
}
|
||||
@ -72,39 +69,20 @@ func (h *Service) HandleRequest(ctx context.Context, ds *datasources.DataSource,
|
||||
return tR, nil
|
||||
}
|
||||
|
||||
func generateRequest(ctx context.Context, ds *datasources.DataSource, decryptedJsonData map[string]string, query legacydata.DataQuery) (*backend.QueryDataRequest, error) {
|
||||
jsonDataBytes, err := ds.JsonData.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instanceSettings := &backend.DataSourceInstanceSettings{
|
||||
ID: ds.ID,
|
||||
Name: ds.Name,
|
||||
URL: ds.URL,
|
||||
Database: ds.Database,
|
||||
User: ds.User,
|
||||
BasicAuthEnabled: ds.BasicAuth,
|
||||
BasicAuthUser: ds.BasicAuthUser,
|
||||
JSONData: jsonDataBytes,
|
||||
DecryptedSecureJSONData: decryptedJsonData,
|
||||
Updated: ds.Updated,
|
||||
UID: ds.UID,
|
||||
}
|
||||
|
||||
func (h *Service) generateRequest(ctx context.Context, ds *datasources.DataSource, query legacydata.DataQuery) (*backend.QueryDataRequest, error) {
|
||||
if query.Headers == nil {
|
||||
query.Headers = make(map[string]string)
|
||||
}
|
||||
|
||||
pCtx, err := h.pCtxProvider.GetWithDataSource(ctx, ds.Type, query.User, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &backend.QueryDataRequest{
|
||||
PluginContext: backend.PluginContext{
|
||||
OrgID: ds.OrgID,
|
||||
PluginID: ds.Type,
|
||||
User: adapters.BackendUserFromSignedInUser(query.User),
|
||||
DataSourceInstanceSettings: instanceSettings,
|
||||
},
|
||||
Queries: []backend.DataQuery{},
|
||||
Headers: query.Headers,
|
||||
PluginContext: pCtx,
|
||||
Queries: []backend.DataQuery{},
|
||||
Headers: query.Headers,
|
||||
}
|
||||
|
||||
for _, q := range query.Queries {
|
||||
|
@ -9,16 +9,20 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
datasourceservice "github.com/grafana/grafana/pkg/services/datasources/service"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"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/user"
|
||||
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||
)
|
||||
|
||||
@ -38,15 +42,19 @@ func TestHandleRequest(t *testing.T) {
|
||||
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, sqlStore.Cfg, featuremgmt.WithFeatures(), acmock.New(), datasourcePermissions, quotaService)
|
||||
require.NoError(t, err)
|
||||
|
||||
s := ProvideService(client, nil, dsService)
|
||||
pCtxProvider := plugincontext.ProvideService(localcache.ProvideService(), &plugins.FakePluginStore{
|
||||
PluginList: []plugins.PluginDTO{{JSONData: plugins.JSONData{ID: "test"}}},
|
||||
}, dsService, pluginSettings.ProvideService(sqlStore, secretsService))
|
||||
s := ProvideService(client, nil, dsService, pCtxProvider)
|
||||
|
||||
ds := &datasources.DataSource{ID: 12, Type: "unregisteredType", JsonData: simplejson.New()}
|
||||
ds := &datasources.DataSource{ID: 12, Type: "test", JsonData: simplejson.New()}
|
||||
req := legacydata.DataQuery{
|
||||
TimeRange: &legacydata.DataTimeRange{},
|
||||
Queries: []legacydata.DataSubQuery{
|
||||
{RefID: "A", DataSource: &datasources.DataSource{ID: 1, Type: "test"}, Model: simplejson.New()},
|
||||
{RefID: "B", DataSource: &datasources.DataSource{ID: 1, Type: "test"}, Model: simplejson.New()},
|
||||
{RefID: "A", Model: simplejson.New()},
|
||||
{RefID: "B", Model: simplejson.New()},
|
||||
},
|
||||
User: &user.SignedInUser{},
|
||||
}
|
||||
res, err := s.HandleRequest(context.Background(), ds, req)
|
||||
require.NoError(t, err)
|
||||
|
Loading…
Reference in New Issue
Block a user