mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 14:32:15 +08:00
DataSources: Add datasource fetching + querying interface (#80749)
* first pass * separate oss + enterprise * tidy things up * add ctx * fix tests * use standalone svcs * mv plugin context provide * fix wire * fix import
This commit is contained in:
@ -77,8 +77,8 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &fakeDatasources.FakeDataSourceService{}, pluginSettings.ProvideService(dbtest.NewFakeDB(),
|
||||
secretstest.NewFakeSecretsService()), pluginFakes.NewFakeLicensingService(), &config.Cfg{}),
|
||||
}, &fakeDatasources.FakeCacheService{}, &fakeDatasources.FakeDataSourceService{},
|
||||
pluginSettings.ProvideService(dbtest.NewFakeDB(), secretstest.NewFakeSecretsService()), pluginFakes.NewFakeLicensingService(), &config.Cfg{}),
|
||||
)
|
||||
serverFeatureEnabled := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.queryDataService = qds
|
||||
@ -124,6 +124,7 @@ func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&fakeDatasources.FakeCacheService{},
|
||||
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()), pluginFakes.NewFakeLicensingService(), &config.Cfg{},
|
||||
)
|
||||
qds := query.ProvideService(
|
||||
@ -300,7 +301,8 @@ func TestDataSourceQueryError(t *testing.T) {
|
||||
plugincontext.ProvideService(cfg, localcache.ProvideService(), &pluginstore.FakePluginStore{
|
||||
PluginList: []pluginstore.Plugin{pluginstore.ToGrafanaDTO(p)},
|
||||
},
|
||||
ds, pluginSettings.ProvideService(dbtest.NewFakeDB(),
|
||||
&fakeDatasources.FakeCacheService{}, ds,
|
||||
pluginSettings.ProvideService(dbtest.NewFakeDB(),
|
||||
secretstest.NewFakeSecretsService()), pluginFakes.NewFakeLicensingService(), &config.Cfg{}),
|
||||
)
|
||||
hs.QuotaService = quotatest.New(false, nil)
|
||||
|
@ -55,8 +55,8 @@ func TestCallResource(t *testing.T) {
|
||||
|
||||
textCtx := pluginsintegration.CreateIntegrationTestCtx(t, cfg, coreRegistry)
|
||||
|
||||
pcp := plugincontext.ProvideService(cfg, localcache.ProvideService(), textCtx.PluginStore, &datasources.FakeDataSourceService{},
|
||||
pluginSettings.ProvideService(db.InitTestDB(t), fakeSecrets.NewFakeSecretsService()), nil, &pCfg)
|
||||
pcp := plugincontext.ProvideService(cfg, localcache.ProvideService(), textCtx.PluginStore, &datasources.FakeCacheService{},
|
||||
&datasources.FakeDataSourceService{}, pluginSettings.ProvideService(db.InitTestDB(t), fakeSecrets.NewFakeSecretsService()), nil, &pCfg)
|
||||
|
||||
srv := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = cfg
|
||||
|
@ -61,7 +61,8 @@ func framesPassThroughService(t *testing.T, frames data.Frames) (data.Frames, er
|
||||
PluginList: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "test"}},
|
||||
}},
|
||||
&datafakes.FakeDataSourceService{}, nil, pluginFakes.NewFakeLicensingService(), &config.Cfg{}),
|
||||
&datafakes.FakeCacheService{}, &datafakes.FakeDataSourceService{},
|
||||
nil, pluginFakes.NewFakeLicensingService(), &config.Cfg{}),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
metrics: newMetrics(nil),
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func TestService(t *testing.T) {
|
||||
PluginList: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "test"}},
|
||||
},
|
||||
}, &datafakes.FakeDataSourceService{}, nil, fakes.NewFakeLicensingService(), &config.Cfg{})
|
||||
}, &datafakes.FakeCacheService{}, &datafakes.FakeDataSourceService{}, nil, fakes.NewFakeLicensingService(), &config.Cfg{})
|
||||
|
||||
s := Service{
|
||||
cfg: setting.NewCfg(),
|
||||
@ -128,7 +128,7 @@ func TestDSQueryError(t *testing.T) {
|
||||
PluginList: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "test"}},
|
||||
},
|
||||
}, &datafakes.FakeDataSourceService{}, nil, nil, &config.Cfg{})
|
||||
}, &datafakes.FakeCacheService{}, &datafakes.FakeDataSourceService{}, nil, nil, &config.Cfg{})
|
||||
|
||||
s := Service{
|
||||
cfg: setting.NewCfg(),
|
||||
|
@ -2,18 +2,13 @@ package datasource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/grafana-apiserver/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -27,7 +22,7 @@ var (
|
||||
type connectionAccess struct {
|
||||
resourceInfo common.ResourceInfo
|
||||
tableConverter rest.TableConvertor
|
||||
builder *DataSourceAPIBuilder
|
||||
builder Querier
|
||||
}
|
||||
|
||||
func (s *connectionAccess) New() runtime.Object {
|
||||
@ -57,48 +52,9 @@ func (s *connectionAccess) ConvertToTable(ctx context.Context, object runtime.Ob
|
||||
}
|
||||
|
||||
func (s *connectionAccess) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ns := request.NamespaceValue(ctx)
|
||||
ds, err := s.builder.getDataSource(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.asConnection(ds, ns)
|
||||
return s.builder.Datasource(ctx, name)
|
||||
}
|
||||
|
||||
func (s *connectionAccess) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
ns := request.NamespaceValue(ctx)
|
||||
if ns == "" {
|
||||
// require a namespace so we do not need to support reverse mappings (yet)
|
||||
return nil, fmt.Errorf("missing namespace in request URL")
|
||||
}
|
||||
result := &v0alpha1.DataSourceConnectionList{
|
||||
Items: []v0alpha1.DataSourceConnection{},
|
||||
}
|
||||
vals, err := s.builder.getDataSources(ctx)
|
||||
if err == nil {
|
||||
for _, ds := range vals {
|
||||
v, _ := s.asConnection(ds, ns)
|
||||
result.Items = append(result.Items, *v)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *connectionAccess) asConnection(ds *datasources.DataSource, ns string) (*v0alpha1.DataSourceConnection, error) {
|
||||
v := &v0alpha1.DataSourceConnection{
|
||||
TypeMeta: s.resourceInfo.TypeMeta(),
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ds.UID,
|
||||
Namespace: ns,
|
||||
CreationTimestamp: metav1.NewTime(ds.Created),
|
||||
ResourceVersion: fmt.Sprintf("%d", ds.Updated.UnixMilli()),
|
||||
},
|
||||
Title: ds.Name,
|
||||
}
|
||||
v.UID = utils.CalculateClusterWideUID(v) // indicates if the value changed on the server
|
||||
meta, err := utils.MetaAccessor(v)
|
||||
if err != nil {
|
||||
meta.SetUpdatedTimestamp(&ds.Updated)
|
||||
}
|
||||
return v, err
|
||||
return s.builder.Datasources(ctx)
|
||||
}
|
||||
|
11
pkg/registry/apis/datasource/plugincontext.go
Normal file
11
pkg/registry/apis/datasource/plugincontext.go
Normal file
@ -0,0 +1,11 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
)
|
||||
|
||||
type PluginContextProvider interface {
|
||||
PluginContextForDataSource(ctx context.Context, pluginID, name string) (backend.PluginContext, error)
|
||||
}
|
169
pkg/registry/apis/datasource/querier.go
Normal file
169
pkg/registry/apis/datasource/querier.go
Normal file
@ -0,0 +1,169 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/grafana-apiserver/utils"
|
||||
)
|
||||
|
||||
type QuerierFactoryFunc func(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error)
|
||||
|
||||
type QuerierProvider interface {
|
||||
Querier(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error)
|
||||
}
|
||||
|
||||
type DefaultQuerierProvider struct {
|
||||
factory QuerierFactoryFunc
|
||||
}
|
||||
|
||||
func ProvideDefaultQuerierProvider(pluginClient plugins.Client, dsService datasources.DataSourceService,
|
||||
dsCache datasources.CacheService) *DefaultQuerierProvider {
|
||||
return NewQuerierProvider(func(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error) {
|
||||
return NewDefaultQuerier(ri, pj, pluginClient, dsService, dsCache), nil
|
||||
})
|
||||
}
|
||||
|
||||
func NewQuerierProvider(factory QuerierFactoryFunc) *DefaultQuerierProvider {
|
||||
return &DefaultQuerierProvider{
|
||||
factory: factory,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *DefaultQuerierProvider) Querier(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error) {
|
||||
return p.factory(ctx, ri, pj)
|
||||
}
|
||||
|
||||
// Querier is the interface that wraps the Query method.
|
||||
type Querier interface {
|
||||
// Query runs the query on behalf of the user in context.
|
||||
Query(ctx context.Context, query *backend.QueryDataRequest) (*backend.QueryDataResponse, error)
|
||||
// Health checks the health of the plugin.
|
||||
Health(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error)
|
||||
// Resource gets a resource plugin.
|
||||
Resource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error
|
||||
// Datasource gets all data source plugins (with elevated permissions).
|
||||
Datasource(ctx context.Context, name string) (*v0alpha1.DataSourceConnection, error)
|
||||
// Datasources lists all data sources (with elevated permissions).
|
||||
Datasources(ctx context.Context) (*v0alpha1.DataSourceConnectionList, error)
|
||||
}
|
||||
|
||||
type DefaultQuerier struct {
|
||||
connectionResourceInfo common.ResourceInfo
|
||||
pluginJSON plugins.JSONData
|
||||
pluginClient plugins.Client
|
||||
dsService datasources.DataSourceService
|
||||
dsCache datasources.CacheService
|
||||
}
|
||||
|
||||
func NewDefaultQuerier(
|
||||
connectionResourceInfo common.ResourceInfo,
|
||||
pluginJSON plugins.JSONData,
|
||||
pluginClient plugins.Client,
|
||||
dsService datasources.DataSourceService,
|
||||
dsCache datasources.CacheService,
|
||||
) *DefaultQuerier {
|
||||
return &DefaultQuerier{
|
||||
connectionResourceInfo: connectionResourceInfo,
|
||||
pluginJSON: pluginJSON,
|
||||
pluginClient: pluginClient,
|
||||
dsService: dsService,
|
||||
dsCache: dsCache,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *DefaultQuerier) Query(ctx context.Context, query *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
_, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.pluginClient.QueryData(ctx, query)
|
||||
}
|
||||
|
||||
func (q *DefaultQuerier) Resource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
_, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return q.pluginClient.CallResource(ctx, req, sender)
|
||||
}
|
||||
|
||||
func (q *DefaultQuerier) Health(ctx context.Context, query *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
_, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.pluginClient.CheckHealth(ctx, query)
|
||||
}
|
||||
|
||||
func (q *DefaultQuerier) Datasource(ctx context.Context, name string) (*v0alpha1.DataSourceConnection, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := appcontext.User(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds, err := q.dsCache.GetDatasourceByUID(ctx, name, user, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return asConnection(q.connectionResourceInfo.TypeMeta(), ds, info.Value)
|
||||
}
|
||||
|
||||
func (q *DefaultQuerier) Datasources(ctx context.Context) (*v0alpha1.DataSourceConnectionList, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ds, err := q.dsService.GetDataSourcesByType(ctx, &datasources.GetDataSourcesByTypeQuery{
|
||||
OrgID: info.OrgID,
|
||||
Type: q.pluginJSON.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return asConnectionList(q.connectionResourceInfo.TypeMeta(), ds, info.Value)
|
||||
}
|
||||
|
||||
func asConnection(typeMeta metav1.TypeMeta, ds *datasources.DataSource, ns string) (*v0alpha1.DataSourceConnection, error) {
|
||||
v := &v0alpha1.DataSourceConnection{
|
||||
TypeMeta: typeMeta,
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ds.UID,
|
||||
Namespace: ns,
|
||||
CreationTimestamp: metav1.NewTime(ds.Created),
|
||||
ResourceVersion: fmt.Sprintf("%d", ds.Updated.UnixMilli()),
|
||||
},
|
||||
Title: ds.Name,
|
||||
}
|
||||
v.UID = utils.CalculateClusterWideUID(v) // indicates if the value changed on the server
|
||||
meta, err := utils.MetaAccessor(v)
|
||||
if err != nil {
|
||||
meta.SetUpdatedTimestamp(&ds.Updated)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
func asConnectionList(typeMeta metav1.TypeMeta, dss []*datasources.DataSource, ns string) (*v0alpha1.DataSourceConnectionList, error) {
|
||||
result := &v0alpha1.DataSourceConnectionList{
|
||||
Items: []v0alpha1.DataSourceConnection{},
|
||||
}
|
||||
for _, ds := range dss {
|
||||
v, _ := asConnection(typeMeta, ds, ns)
|
||||
result.Items = append(result.Items, *v)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@ -19,39 +18,32 @@ import (
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/grafana-apiserver/utils"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var _ grafanaapiserver.APIGroupBuilder = (*DataSourceAPIBuilder)(nil)
|
||||
|
||||
// This is used just so wire has something unique to return
|
||||
// DataSourceAPIBuilder is used just so wire has something unique to return
|
||||
type DataSourceAPIBuilder struct {
|
||||
connectionResourceInfo common.ResourceInfo
|
||||
|
||||
plugin plugins.JSONData
|
||||
client plugins.Client
|
||||
dsService datasources.DataSourceService
|
||||
dsCache datasources.CacheService
|
||||
pluginJSON plugins.JSONData
|
||||
querier Querier
|
||||
pluginContext PluginContextProvider
|
||||
accessControl accesscontrol.AccessControl
|
||||
}
|
||||
|
||||
func RegisterAPIService(
|
||||
cfg *setting.Cfg,
|
||||
querierProvider QuerierProvider,
|
||||
features featuremgmt.FeatureToggles,
|
||||
apiregistration grafanaapiserver.APIRegistrar,
|
||||
pluginClient plugins.Client,
|
||||
apiRegistrar grafanaapiserver.APIRegistrar,
|
||||
pluginContext PluginContextProvider,
|
||||
pluginStore pluginstore.Store,
|
||||
dsService datasources.DataSourceService,
|
||||
dsCache datasources.CacheService,
|
||||
accessControl accesscontrol.AccessControl,
|
||||
) (*DataSourceAPIBuilder, error) {
|
||||
// This requires devmode!
|
||||
@ -71,31 +63,35 @@ func RegisterAPIService(
|
||||
continue // skip this one
|
||||
}
|
||||
|
||||
builder, err = NewDataSourceAPIBuilder(ds.JSONData, pluginClient, dsService, dsCache, accessControl)
|
||||
builder, err = NewDataSourceAPIBuilder(ds.JSONData, querierProvider, pluginContext, accessControl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiregistration.RegisterAPI(builder)
|
||||
apiRegistrar.RegisterAPI(builder)
|
||||
}
|
||||
return builder, nil // only used for wire
|
||||
}
|
||||
|
||||
func NewDataSourceAPIBuilder(
|
||||
plugin plugins.JSONData,
|
||||
client plugins.Client,
|
||||
dsService datasources.DataSourceService,
|
||||
dsCache datasources.CacheService,
|
||||
querierProvider QuerierProvider,
|
||||
pluginContext PluginContextProvider,
|
||||
accessControl accesscontrol.AccessControl) (*DataSourceAPIBuilder, error) {
|
||||
group, err := getDatasourceGroupNameFromPluginID(plugin.ID)
|
||||
ri, err := resourceFromPluginID(plugin.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
querier, err := querierProvider.Querier(context.Background(), ri, plugin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DataSourceAPIBuilder{
|
||||
connectionResourceInfo: v0alpha1.GenericConnectionResourceInfo.WithGroupAndShortName(group, plugin.ID+"-connection"),
|
||||
plugin: plugin,
|
||||
client: client,
|
||||
dsService: dsService,
|
||||
dsCache: dsCache,
|
||||
connectionResourceInfo: ri,
|
||||
pluginJSON: plugin,
|
||||
querier: querier,
|
||||
pluginContext: pluginContext,
|
||||
accessControl: accessControl,
|
||||
}, nil
|
||||
}
|
||||
@ -135,16 +131,24 @@ func (b *DataSourceAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
return scheme.SetVersionPriority(gv)
|
||||
}
|
||||
|
||||
func resourceFromPluginID(pluginID string) (common.ResourceInfo, error) {
|
||||
group, err := getDatasourceGroupNameFromPluginID(pluginID)
|
||||
if err != nil {
|
||||
return common.ResourceInfo{}, err
|
||||
}
|
||||
return v0alpha1.GenericConnectionResourceInfo.WithGroupAndShortName(group, pluginID+"-connection"), nil
|
||||
}
|
||||
|
||||
func (b *DataSourceAPIBuilder) GetAPIGroupInfo(
|
||||
scheme *runtime.Scheme,
|
||||
codecs serializer.CodecFactory, // pointer?
|
||||
optsGetter generic.RESTOptionsGetter,
|
||||
_ generic.RESTOptionsGetter,
|
||||
) (*genericapiserver.APIGroupInfo, error) {
|
||||
storage := map[string]rest.Storage{}
|
||||
|
||||
conn := b.connectionResourceInfo
|
||||
storage[conn.StoragePath()] = &connectionAccess{
|
||||
builder: b,
|
||||
builder: b.querier,
|
||||
resourceInfo: conn,
|
||||
tableConverter: utils.NewTableConverter(
|
||||
conn.GroupResource(),
|
||||
@ -175,8 +179,8 @@ func (b *DataSourceAPIBuilder) GetAPIGroupInfo(
|
||||
storage[conn.StoragePath("resource")] = &subResourceREST{builder: b}
|
||||
|
||||
// Frontend proxy
|
||||
if len(b.plugin.Routes) > 0 {
|
||||
storage[conn.StoragePath("proxy")] = &subProxyREST{builder: b}
|
||||
if len(b.pluginJSON.Routes) > 0 {
|
||||
storage[conn.StoragePath("proxy")] = &subProxyREST{pluginJSON: b.pluginJSON}
|
||||
}
|
||||
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(
|
||||
@ -195,64 +199,3 @@ func (b *DataSourceAPIBuilder) GetOpenAPIDefinitions() openapi.GetOpenAPIDefinit
|
||||
func (b *DataSourceAPIBuilder) GetAPIRoutes() *grafanaapiserver.APIRoutes {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *DataSourceAPIBuilder) getDataSourcePluginContext(ctx context.Context, name string) (*backend.PluginContext, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := appcontext.User(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds, err := b.dsCache.GetDatasourceByUID(ctx, name, user, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settings := backend.DataSourceInstanceSettings{}
|
||||
settings.ID = ds.ID
|
||||
settings.UID = ds.UID
|
||||
settings.Name = ds.Name
|
||||
settings.URL = ds.URL
|
||||
settings.Updated = ds.Updated
|
||||
settings.User = ds.User
|
||||
settings.JSONData, err = ds.JsonData.ToDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settings.DecryptedSecureJSONData, err = b.dsService.DecryptedValues(ctx, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &backend.PluginContext{
|
||||
OrgID: info.OrgID,
|
||||
PluginID: b.plugin.ID,
|
||||
PluginVersion: b.plugin.Info.Version,
|
||||
User: &backend.User{},
|
||||
AppInstanceSettings: &backend.AppInstanceSettings{},
|
||||
DataSourceInstanceSettings: &settings,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *DataSourceAPIBuilder) getDataSource(ctx context.Context, name string) (*datasources.DataSource, error) {
|
||||
user, err := appcontext.User(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b.dsCache.GetDatasourceByUID(ctx, name, user, false)
|
||||
}
|
||||
|
||||
func (b *DataSourceAPIBuilder) getDataSources(ctx context.Context) ([]*datasources.DataSource, error) {
|
||||
orgId, err := request.OrgIDForList(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.dsService.GetDataSourcesByType(ctx, &datasources.GetDataSourcesByTypeQuery{
|
||||
OrgID: orgId,
|
||||
Type: b.plugin.ID,
|
||||
})
|
||||
}
|
||||
|
@ -4,19 +4,25 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
testdatasource "github.com/grafana/grafana/pkg/tsdb/grafana-testdata-datasource"
|
||||
)
|
||||
|
||||
// This is a helper function to create a new datasource API server for a group
|
||||
// NewStandaloneDatasource is a helper function to create a new datasource API server for a group.
|
||||
// This currently has no dependencies and only works for testdata. In future iterations
|
||||
// this will include here (or elsewhere) versions that can load config from HG api or
|
||||
// the remote SQL directly
|
||||
// the remote SQL directly.
|
||||
func NewStandaloneDatasource(group string) (*DataSourceAPIBuilder, error) {
|
||||
pluginID := "grafana-testdata-datasource"
|
||||
|
||||
if group != "testdata.datasource.grafana.app" {
|
||||
return nil, fmt.Errorf("only testadata is currently supported")
|
||||
return nil, fmt.Errorf("only %s is currently supported", pluginID)
|
||||
}
|
||||
pluginId := "grafana-testdata-datasource"
|
||||
|
||||
cfg, err := setting.NewCfgFromArgs(setting.CommandLineArgs{
|
||||
// TODO: Add support for args?
|
||||
@ -25,21 +31,30 @@ func NewStandaloneDatasource(group string) (*DataSourceAPIBuilder, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessControl, pluginstoreService, dsService, cacheServiceImpl, err := apiBuilderServices(cfg, pluginId)
|
||||
_, pluginStore, dsService, dsCache, err := apiBuilderServices(cfg, pluginID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
testdataPlugin, found := pluginstoreService.Plugin(context.Background(), pluginId)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("plugin %s not found", pluginId)
|
||||
td, exists := pluginStore.Plugin(context.Background(), pluginID)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("plugin %s not found", pluginID)
|
||||
}
|
||||
|
||||
var testsDataQuerierFactory QuerierFactoryFunc = func(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error) {
|
||||
return NewDefaultQuerier(ri, td.JSONData, testdatasource.ProvideService(), dsService, dsCache), nil
|
||||
}
|
||||
|
||||
return NewDataSourceAPIBuilder(
|
||||
testdataPlugin.JSONData,
|
||||
testdatasource.ProvideService(),
|
||||
dsService,
|
||||
cacheServiceImpl,
|
||||
accessControl,
|
||||
td.JSONData,
|
||||
NewQuerierProvider(testsDataQuerierFactory),
|
||||
&TestDataPluginContextProvider{},
|
||||
acimpl.ProvideAccessControl(cfg),
|
||||
)
|
||||
}
|
||||
|
||||
type TestDataPluginContextProvider struct{}
|
||||
|
||||
func (p *TestDataPluginContextProvider) PluginContextForDataSource(_ context.Context, _, _ string) (backend.PluginContext, error) {
|
||||
return backend.PluginContext{}, nil
|
||||
}
|
||||
|
@ -77,8 +77,8 @@ func apiBuilderServices(cfg *setting.Cfg, pluginID string) (
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
bundleregistryService := bundleregistry.ProvideService()
|
||||
usageStats, err := service.ProvideService(cfg, kvStore, routeRegisterImpl, tracingService, accessControl, acimplService, bundleregistryService)
|
||||
bundleRegistry := bundleregistry.ProvideService()
|
||||
usageStats, err := service.ProvideService(cfg, kvStore, routeRegisterImpl, tracingService, accessControl, acimplService, bundleRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
@ -88,32 +88,32 @@ func apiBuilderServices(cfg *setting.Cfg, pluginID string) (
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
osskmsprovidersService := osskmsproviders.ProvideService(serviceService, cfg, featureToggles)
|
||||
secretsService, err := manager.ProvideSecretsService(secretsStoreImpl, osskmsprovidersService, serviceService, cfg, featureToggles, usageStats)
|
||||
kmsProviders := osskmsproviders.ProvideService(serviceService, cfg, featureToggles)
|
||||
secretsService, err := manager.ProvideSecretsService(secretsStoreImpl, kmsProviders, serviceService, cfg, featureToggles, usageStats)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
ossImpl := setting.ProvideProvider(cfg)
|
||||
configCfg, err := config.ProvideConfig(ossImpl, cfg, featureToggles)
|
||||
pluginCfg, err := config.ProvideConfig(ossImpl, cfg, featureToggles)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
inMemory := registry.ProvideService()
|
||||
pluginRegistry := registry.ProvideService()
|
||||
quotaService := quotaimpl.ProvideService(sqlStore, cfg)
|
||||
loaderLoader, err := createLoader(configCfg, inMemory)
|
||||
pluginLoader, err := createLoader(pluginCfg, pluginRegistry)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
pluginstoreService, err := pluginstore.ProvideService(inMemory, newPluginSource(cfg, pluginID), loaderLoader)
|
||||
pluginStore, err := pluginstore.ProvideService(pluginRegistry, newPluginSource(cfg, pluginID), pluginLoader)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
secretsKVStore, err := kvstoreService.ProvideService(sqlStore, secretsService, pluginstoreService, kvStore, featureToggles, cfg)
|
||||
secretsKVStore, err := kvstoreService.ProvideService(sqlStore, secretsService, pluginStore, kvStore, featureToggles, cfg)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
datasourcePermissionsService := ossaccesscontrol.ProvideDatasourcePermissionsService()
|
||||
service13, err := datasourceService.ProvideService(sqlStore, secretsService, secretsKVStore, cfg, featureToggles, accessControl, datasourcePermissionsService, quotaService, pluginstoreService)
|
||||
dsPermissionsService := ossaccesscontrol.ProvideDatasourcePermissionsService()
|
||||
dsService, err := datasourceService.ProvideService(sqlStore, secretsService, secretsKVStore, cfg, featureToggles, accessControl, dsPermissionsService, quotaService, pluginStore)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
@ -121,7 +121,7 @@ func apiBuilderServices(cfg *setting.Cfg, pluginID string) (
|
||||
ossProvider := guardian.ProvideGuardian()
|
||||
cacheServiceImpl := datasourceService.ProvideCacheService(cacheService, sqlStore, ossProvider)
|
||||
|
||||
return accessControl, pluginstoreService, service13, cacheServiceImpl, nil
|
||||
return accessControl, pluginStore, dsService, cacheServiceImpl, nil
|
||||
}
|
||||
|
||||
var _ sources.Registry = (*pluginSource)(nil)
|
||||
|
@ -35,14 +35,14 @@ func (r *subHealthREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
|
||||
func (r *subHealthREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
pluginCtx, err := r.builder.getDataSourcePluginContext(ctx, name)
|
||||
pluginCtx, err := r.builder.pluginContext.PluginContextForDataSource(ctx, r.builder.pluginJSON.ID, name)
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
healthResponse, err := r.builder.client.CheckHealth(ctx, &backend.CheckHealthRequest{
|
||||
PluginContext: *pluginCtx,
|
||||
healthResponse, err := r.builder.querier.Health(ctx, &backend.CheckHealthRequest{
|
||||
PluginContext: pluginCtx,
|
||||
})
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
|
@ -8,10 +8,12 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
)
|
||||
|
||||
type subProxyREST struct {
|
||||
builder *DataSourceAPIBuilder
|
||||
pluginJSON plugins.JSONData
|
||||
}
|
||||
|
||||
var _ = rest.Connecter(&subProxyREST{})
|
||||
@ -25,7 +27,7 @@ func (r *subProxyREST) Destroy() {}
|
||||
func (r *subProxyREST) ConnectMethods() []string {
|
||||
unique := map[string]bool{}
|
||||
methods := []string{}
|
||||
for _, r := range r.builder.plugin.Routes {
|
||||
for _, r := range r.pluginJSON.Routes {
|
||||
if unique[r.Method] {
|
||||
continue
|
||||
}
|
||||
@ -40,12 +42,7 @@ func (r *subProxyREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
}
|
||||
|
||||
func (r *subProxyREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
pluginCtx, err := r.builder.getDataSourcePluginContext(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
responder.Error(fmt.Errorf("TODO, proxy: " + pluginCtx.PluginID))
|
||||
responder.Error(fmt.Errorf("TODO, proxy: " + r.pluginJSON.ID))
|
||||
}), nil
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ func (r *subQueryREST) readQueries(req *http.Request) ([]backend.DataQuery, erro
|
||||
}
|
||||
|
||||
func (r *subQueryREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
pluginCtx, err := r.builder.getDataSourcePluginContext(ctx, name)
|
||||
pluginCtx, err := r.builder.pluginContext.PluginContextForDataSource(ctx, r.builder.pluginJSON.ID, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -83,8 +83,8 @@ func (r *subQueryREST) Connect(ctx context.Context, name string, opts runtime.Ob
|
||||
return
|
||||
}
|
||||
|
||||
queryResponse, err := r.builder.client.QueryData(ctx, &backend.QueryDataRequest{
|
||||
PluginContext: *pluginCtx,
|
||||
queryResponse, err := r.builder.querier.Query(ctx, &backend.QueryDataRequest{
|
||||
PluginContext: pluginCtx,
|
||||
Queries: queries,
|
||||
// Headers: // from context
|
||||
})
|
||||
|
@ -46,7 +46,7 @@ func (r *subResourceREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
}
|
||||
|
||||
func (r *subResourceREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
pluginCtx, err := r.builder.getDataSourcePluginContext(ctx, name)
|
||||
pluginCtx, err := r.builder.pluginContext.PluginContextForDataSource(ctx, r.builder.pluginJSON.ID, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -65,8 +65,8 @@ func (r *subResourceREST) Connect(ctx context.Context, name string, opts runtime
|
||||
}
|
||||
|
||||
path := req.URL.Path[idx+len("/resource"):]
|
||||
err = r.builder.client.CallResource(ctx, &backend.CallResourceRequest{
|
||||
PluginContext: *pluginCtx,
|
||||
err = r.builder.querier.Resource(ctx, &backend.CallResourceRequest{
|
||||
PluginContext: pluginCtx,
|
||||
Path: path,
|
||||
Method: req.Method,
|
||||
Body: body,
|
||||
|
@ -9,11 +9,17 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/featuretoggle"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/folders"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
)
|
||||
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideRegistryServiceSink, // dummy background service that forces registration
|
||||
|
||||
wire.Bind(new(datasource.QuerierProvider), new(*datasource.DefaultQuerierProvider)),
|
||||
datasource.ProvideDefaultQuerierProvider,
|
||||
plugincontext.ProvideService,
|
||||
wire.Bind(new(datasource.PluginContextProvider), new(*plugincontext.Provider)),
|
||||
|
||||
// Each must be added here *and* in the ServiceSink above
|
||||
playlist.RegisterAPIService,
|
||||
dashboard.RegisterAPIService,
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/useragent"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
@ -30,12 +31,13 @@ const (
|
||||
)
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, cacheService *localcache.CacheService, pluginStore pluginstore.Store,
|
||||
dataSourceService datasources.DataSourceService, pluginSettingsService pluginsettings.Service,
|
||||
licensing plugins.Licensing, pCfg *config.Cfg) *Provider {
|
||||
dataSourceCache datasources.CacheService, dataSourceService datasources.DataSourceService,
|
||||
pluginSettingsService pluginsettings.Service, licensing plugins.Licensing, pCfg *config.Cfg) *Provider {
|
||||
return &Provider{
|
||||
cfg: cfg,
|
||||
cacheService: cacheService,
|
||||
pluginStore: pluginStore,
|
||||
dataSourceCache: dataSourceCache,
|
||||
dataSourceService: dataSourceService,
|
||||
pluginSettingsService: pluginSettingsService,
|
||||
pluginEnvVars: envvars.NewProvider(pCfg, licensing),
|
||||
@ -48,6 +50,7 @@ type Provider struct {
|
||||
pluginEnvVars *envvars.Service
|
||||
cacheService *localcache.CacheService
|
||||
pluginStore pluginstore.Store
|
||||
dataSourceCache datasources.CacheService
|
||||
dataSourceService datasources.DataSourceService
|
||||
pluginSettingsService pluginsettings.Service
|
||||
logger log.Logger
|
||||
@ -128,6 +131,48 @@ func (p *Provider) GetWithDataSource(ctx context.Context, pluginID string, user
|
||||
return pCtx, nil
|
||||
}
|
||||
|
||||
func (p *Provider) PluginContextForDataSource(ctx context.Context, pluginID, name string) (backend.PluginContext, error) {
|
||||
plugin, exists := p.pluginStore.Plugin(ctx, pluginID)
|
||||
if !exists {
|
||||
return backend.PluginContext{}, plugins.ErrPluginNotRegistered
|
||||
}
|
||||
|
||||
user, err := appcontext.User(ctx)
|
||||
if err != nil {
|
||||
return backend.PluginContext{}, err
|
||||
}
|
||||
ds, err := p.dataSourceCache.GetDatasourceByUID(ctx, name, user, false)
|
||||
if err != nil {
|
||||
return backend.PluginContext{}, err
|
||||
}
|
||||
|
||||
pCtx := backend.PluginContext{
|
||||
PluginID: plugin.ID,
|
||||
PluginVersion: plugin.Info.Version,
|
||||
}
|
||||
if user != nil && !user.IsNil() {
|
||||
pCtx.OrgID = user.GetOrgID()
|
||||
pCtx.User = adapters.BackendUserFromSignedInUser(user)
|
||||
}
|
||||
|
||||
datasourceSettings, err := adapters.ModelToInstanceSettings(ds, p.decryptSecureJsonDataFn(ctx))
|
||||
if err != nil {
|
||||
return pCtx, err
|
||||
}
|
||||
pCtx.DataSourceInstanceSettings = datasourceSettings
|
||||
|
||||
settings := p.pluginEnvVars.GetConfigMap(ctx, pluginID, plugin.ExternalService)
|
||||
pCtx.GrafanaConfig = backend.NewGrafanaCfg(settings)
|
||||
|
||||
ua, err := useragent.New(p.cfg.BuildVersion, runtime.GOOS, runtime.GOARCH)
|
||||
if err != nil {
|
||||
p.logger.Warn("Could not create user agent", "error", err)
|
||||
}
|
||||
pCtx.UserAgent = ua
|
||||
|
||||
return pCtx, nil
|
||||
}
|
||||
|
||||
func (p *Provider) appInstanceSettings(ctx context.Context, pluginID string, orgID int64) (*backend.AppInstanceSettings, error) {
|
||||
jsonData := json.RawMessage{}
|
||||
decryptedSecureJSONData := map[string]string{}
|
||||
|
@ -42,7 +42,7 @@ func TestGet(t *testing.T) {
|
||||
ds := &fakeDatasources.FakeDataSourceService{}
|
||||
db := &dbtest.FakeDB{ExpectedError: pluginsettings.ErrPluginSettingNotFound}
|
||||
pcp := plugincontext.ProvideService(cfg, localcache.ProvideService(),
|
||||
pluginstore.New(preg, &pluginFakes.FakeLoader{}),
|
||||
pluginstore.New(preg, &pluginFakes.FakeLoader{}), &fakeDatasources.FakeCacheService{},
|
||||
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()), pluginFakes.NewFakeLicensingService(), &config.Cfg{},
|
||||
)
|
||||
identity := &user.SignedInUser{OrgID: int64(1), Login: "admin"}
|
||||
|
@ -42,7 +42,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/loader"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
|
||||
@ -94,7 +93,6 @@ var WireSet = wire.NewSet(
|
||||
wire.Bind(new(registry.Service), new(*registry.InMemory)),
|
||||
repo.ProvideService,
|
||||
wire.Bind(new(repo.Service), new(*repo.Manager)),
|
||||
plugincontext.ProvideService,
|
||||
licensing.ProvideLicensing,
|
||||
wire.Bind(new(plugins.Licensing), new(*licensing.Service)),
|
||||
wire.Bind(new(sources.Registry), new(*sources.Service)),
|
||||
|
@ -140,7 +140,8 @@ func buildQueryDataService(t *testing.T, cs datasources.CacheService, fpc *fakeP
|
||||
},
|
||||
},
|
||||
},
|
||||
}, ds, pluginSettings.ProvideService(store, fakeSecrets.NewFakeSecretsService()), fakes.NewFakeLicensingService(),
|
||||
}, &fakeDatasources.FakeCacheService{}, ds,
|
||||
pluginSettings.ProvideService(store, fakeSecrets.NewFakeSecretsService()), fakes.NewFakeLicensingService(),
|
||||
&config.Cfg{})
|
||||
|
||||
return query.ProvideService(
|
||||
|
@ -476,7 +476,7 @@ func setup(t *testing.T) *testContext {
|
||||
{JSONData: plugins.JSONData{ID: "testdata"}},
|
||||
{JSONData: plugins.JSONData{ID: "mysql"}},
|
||||
},
|
||||
}, fakeDatasourceService,
|
||||
}, &fakeDatasources.FakeCacheService{}, fakeDatasourceService,
|
||||
pluginSettings.ProvideService(sqlStore, secretsService), pluginFakes.NewFakeLicensingService(), &config.Cfg{},
|
||||
)
|
||||
exprService := expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, pc, pCtxProvider,
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
pluginFakes "github.com/grafana/grafana/pkg/plugins/manager/fakes"
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/guardian"
|
||||
datasourceservice "github.com/grafana/grafana/pkg/services/datasources/service"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
@ -42,12 +43,13 @@ func TestHandleRequest(t *testing.T) {
|
||||
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
|
||||
datasourcePermissions := acmock.NewMockedPermissionsService()
|
||||
quotaService := quotatest.New(false, nil)
|
||||
dsCache := datasourceservice.ProvideCacheService(localcache.ProvideService(), sqlStore, guardian.ProvideGuardian())
|
||||
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, sqlStore.Cfg, featuremgmt.WithFeatures(), acmock.New(), datasourcePermissions, quotaService, &pluginstore.FakePluginStore{})
|
||||
require.NoError(t, err)
|
||||
|
||||
pCtxProvider := plugincontext.ProvideService(sqlStore.Cfg, localcache.ProvideService(), &pluginstore.FakePluginStore{
|
||||
PluginList: []pluginstore.Plugin{{JSONData: plugins.JSONData{ID: "test"}}},
|
||||
}, dsService, pluginSettings.ProvideService(sqlStore, secretsService), pluginFakes.NewFakeLicensingService(), &config.Cfg{})
|
||||
}, dsCache, dsService, pluginSettings.ProvideService(sqlStore, secretsService), pluginFakes.NewFakeLicensingService(), &config.Cfg{})
|
||||
s := ProvideService(client, nil, dsService, pCtxProvider)
|
||||
|
||||
ds := &datasources.DataSource{ID: 12, Type: "test", JsonData: simplejson.New()}
|
||||
|
Reference in New Issue
Block a user