2024-01-19 22:56:52 +08:00
|
|
|
package datasource
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-01-23 03:32:25 +08:00
|
|
|
"fmt"
|
2024-01-19 22:56:52 +08:00
|
|
|
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
2024-06-20 22:53:07 +08:00
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
2025-08-29 22:46:39 +08:00
|
|
|
datasourceV0 "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
|
2024-03-20 20:49:19 +08:00
|
|
|
"github.com/grafana/grafana/pkg/plugins"
|
2024-02-02 06:27:30 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
2024-01-23 03:32:25 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/datasources"
|
|
|
|
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
2025-08-29 22:46:39 +08:00
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2024-01-23 03:32:25 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// This provides access to settings saved in the database.
|
|
|
|
// Authorization checks will happen within each function, and the user in ctx will
|
|
|
|
// limit which namespace/tenant/org we are talking to
|
2024-01-24 23:44:40 +08:00
|
|
|
type PluginDatasourceProvider interface {
|
2025-08-29 22:46:39 +08:00
|
|
|
// Get a single data source (any type)
|
|
|
|
GetDataSource(ctx context.Context, uid string) (*datasourceV0.DataSource, error)
|
|
|
|
|
|
|
|
// List all datasources (any type)
|
|
|
|
ListDataSources(ctx context.Context) (*datasourceV0.DataSourceList, error)
|
|
|
|
|
|
|
|
// Create a data source
|
|
|
|
CreateDataSource(ctx context.Context, ds *datasourceV0.DataSource) (*datasourceV0.DataSource, error)
|
|
|
|
|
|
|
|
// Update a data source
|
|
|
|
UpdateDataSource(ctx context.Context, ds *datasourceV0.DataSource) (*datasourceV0.DataSource, error)
|
2024-01-23 03:32:25 +08:00
|
|
|
|
2025-08-29 22:46:39 +08:00
|
|
|
// Delete a data source (any type)
|
|
|
|
DeleteDataSource(ctx context.Context, uid string) error
|
2024-01-23 03:32:25 +08:00
|
|
|
|
|
|
|
// Return settings (decrypted!) for a specific plugin
|
|
|
|
// This will require "query" permission for the user in context
|
2024-03-20 20:49:19 +08:00
|
|
|
GetInstanceSettings(ctx context.Context, uid string) (*backend.DataSourceInstanceSettings, error)
|
2024-01-23 03:32:25 +08:00
|
|
|
}
|
|
|
|
|
2024-03-25 22:22:34 +08:00
|
|
|
type ScopedPluginDatasourceProvider interface {
|
|
|
|
GetDatasourceProvider(pluginJson plugins.JSONData) PluginDatasourceProvider
|
|
|
|
}
|
|
|
|
|
2024-01-23 03:32:25 +08:00
|
|
|
// PluginContext requires adding system settings (feature flags, etc) to the datasource config
|
|
|
|
type PluginContextWrapper interface {
|
|
|
|
PluginContextForDataSource(ctx context.Context, datasourceSettings *backend.DataSourceInstanceSettings) (backend.PluginContext, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ProvideDefaultPluginConfigs(
|
|
|
|
dsService datasources.DataSourceService,
|
|
|
|
dsCache datasources.CacheService,
|
2025-08-29 22:46:39 +08:00
|
|
|
contextProvider *plugincontext.Provider,
|
|
|
|
cfg *setting.Cfg,
|
|
|
|
) ScopedPluginDatasourceProvider {
|
2024-03-25 22:22:34 +08:00
|
|
|
return &cachingDatasourceProvider{
|
2024-01-23 03:32:25 +08:00
|
|
|
dsService: dsService,
|
|
|
|
dsCache: dsCache,
|
|
|
|
contextProvider: contextProvider,
|
2025-08-29 22:46:39 +08:00
|
|
|
converter: &converter{
|
|
|
|
mapper: request.GetNamespaceMapper(cfg),
|
|
|
|
},
|
2024-01-23 03:32:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-25 22:22:34 +08:00
|
|
|
type cachingDatasourceProvider struct {
|
|
|
|
dsService datasources.DataSourceService
|
|
|
|
dsCache datasources.CacheService
|
|
|
|
contextProvider *plugincontext.Provider
|
2025-08-29 22:46:39 +08:00
|
|
|
converter *converter
|
2024-03-25 22:22:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (q *cachingDatasourceProvider) GetDatasourceProvider(pluginJson plugins.JSONData) PluginDatasourceProvider {
|
2025-08-29 22:46:39 +08:00
|
|
|
group, _ := plugins.GetDatasourceGroupNameFromPluginID(pluginJson.ID)
|
2024-03-25 22:22:34 +08:00
|
|
|
return &scopedDatasourceProvider{
|
|
|
|
plugin: pluginJson,
|
|
|
|
dsService: q.dsService,
|
|
|
|
dsCache: q.dsCache,
|
|
|
|
contextProvider: q.contextProvider,
|
2025-08-29 22:46:39 +08:00
|
|
|
converter: &converter{
|
|
|
|
mapper: q.converter.mapper,
|
|
|
|
plugin: pluginJson.ID,
|
|
|
|
alias: pluginJson.AliasIDs,
|
|
|
|
group: group,
|
|
|
|
},
|
2024-03-25 22:22:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type scopedDatasourceProvider struct {
|
2024-03-20 20:49:19 +08:00
|
|
|
plugin plugins.JSONData
|
2024-01-23 03:32:25 +08:00
|
|
|
dsService datasources.DataSourceService
|
|
|
|
dsCache datasources.CacheService
|
|
|
|
contextProvider *plugincontext.Provider
|
2025-08-29 22:46:39 +08:00
|
|
|
converter *converter
|
2024-01-23 03:32:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2024-03-25 22:22:34 +08:00
|
|
|
_ PluginDatasourceProvider = (*scopedDatasourceProvider)(nil)
|
|
|
|
_ ScopedPluginDatasourceProvider = (*cachingDatasourceProvider)(nil)
|
2024-01-19 22:56:52 +08:00
|
|
|
)
|
|
|
|
|
2025-08-29 22:46:39 +08:00
|
|
|
func (q *scopedDatasourceProvider) GetInstanceSettings(ctx context.Context, uid string) (*backend.DataSourceInstanceSettings, error) {
|
|
|
|
if q.contextProvider == nil {
|
|
|
|
return nil, fmt.Errorf("missing contextProvider")
|
|
|
|
}
|
|
|
|
return q.contextProvider.GetDataSourceInstanceSettings(ctx, uid)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateDataSource implements PluginDatasourceProvider.
|
|
|
|
func (q *scopedDatasourceProvider) CreateDataSource(ctx context.Context, ds *datasourceV0.DataSource) (*datasourceV0.DataSource, error) {
|
|
|
|
cmd, err := q.converter.toAddCommand(ds)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
out, err := q.dsService.AddDataSource(ctx, cmd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return q.converter.asDataSource(out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateDataSource implements PluginDatasourceProvider.
|
|
|
|
func (q *scopedDatasourceProvider) UpdateDataSource(ctx context.Context, ds *datasourceV0.DataSource) (*datasourceV0.DataSource, error) {
|
|
|
|
cmd, err := q.converter.toUpdateCommand(ds)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
out, err := q.dsService.UpdateDataSource(ctx, cmd)
|
2024-01-23 03:32:25 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2025-08-29 22:46:39 +08:00
|
|
|
return q.converter.asDataSource(out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete implements PluginDatasourceProvider.
|
|
|
|
func (q *scopedDatasourceProvider) DeleteDataSource(ctx context.Context, uid string) error {
|
|
|
|
user, err := identity.GetRequester(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ds, err := q.dsCache.GetDatasourceByUID(ctx, uid, user, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ds == nil {
|
|
|
|
return fmt.Errorf("not found")
|
|
|
|
}
|
|
|
|
return q.dsService.DeleteDataSource(ctx, &datasources.DeleteDataSourceCommand{
|
|
|
|
ID: ds.ID,
|
|
|
|
UID: ds.UID,
|
|
|
|
OrgID: ds.OrgID,
|
|
|
|
Name: ds.Name,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetDataSource implements PluginDatasourceProvider.
|
|
|
|
func (q *scopedDatasourceProvider) GetDataSource(ctx context.Context, uid string) (*datasourceV0.DataSource, error) {
|
2024-06-20 22:53:07 +08:00
|
|
|
user, err := identity.GetRequester(ctx)
|
2024-01-23 03:32:25 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ds, err := q.dsCache.GetDatasourceByUID(ctx, uid, user, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2025-08-29 22:46:39 +08:00
|
|
|
return q.converter.asDataSource(ds)
|
2024-01-23 03:32:25 +08:00
|
|
|
}
|
|
|
|
|
2025-08-29 22:46:39 +08:00
|
|
|
// ListDataSource implements PluginDatasourceProvider.
|
|
|
|
func (q *scopedDatasourceProvider) ListDataSources(ctx context.Context) (*datasourceV0.DataSourceList, error) {
|
2024-01-23 03:32:25 +08:00
|
|
|
info, err := request.NamespaceInfoFrom(ctx, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dss, err := q.dsService.GetDataSourcesByType(ctx, &datasources.GetDataSourcesByTypeQuery{
|
2024-03-20 20:49:19 +08:00
|
|
|
OrgID: info.OrgID,
|
|
|
|
Type: q.plugin.ID,
|
|
|
|
AliasIDs: q.plugin.AliasIDs,
|
2024-01-23 03:32:25 +08:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2025-08-29 22:46:39 +08:00
|
|
|
result := &datasourceV0.DataSourceList{
|
|
|
|
Items: []datasourceV0.DataSource{},
|
2024-01-23 03:32:25 +08:00
|
|
|
}
|
|
|
|
for _, ds := range dss {
|
2025-08-29 22:46:39 +08:00
|
|
|
v, _ := q.converter.asDataSource(ds)
|
2024-01-23 03:32:25 +08:00
|
|
|
result.Items = append(result.Items, *v)
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|