mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Add plugin settings DTO (#46283)
* add clearer service layer * re-order frontend settings for clarity * fix fetch fail * fix API response * fix mockstore * in -> where
This commit is contained in:
parent
1afd278bd0
commit
bda3f860a8
@ -8,129 +8,20 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins EnabledPlugins) (map[string]plugins.DataSourceDTO, error) {
|
||||
orgDataSources := make([]*models.DataSource, 0)
|
||||
|
||||
if c.OrgId != 0 {
|
||||
query := models.GetDataSourcesQuery{OrgId: c.OrgId, DataSourceLimit: hs.Cfg.DataSourceLimit}
|
||||
err := hs.SQLStore.GetDataSources(c.Req.Context(), &query)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filtered, err := hs.filterDatasourcesByQueryPermission(c.Req.Context(), c.SignedInUser, query.Result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgDataSources = filtered
|
||||
func (hs *HTTPServer) GetFrontendSettings(c *models.ReqContext) {
|
||||
settings, err := hs.getFrontendSettingsMap(c)
|
||||
if err != nil {
|
||||
c.JsonApiErr(400, "Failed to get frontend settings", err)
|
||||
return
|
||||
}
|
||||
|
||||
dataSources := make(map[string]plugins.DataSourceDTO)
|
||||
|
||||
for _, ds := range orgDataSources {
|
||||
url := ds.Url
|
||||
|
||||
if ds.Access == models.DS_ACCESS_PROXY {
|
||||
url = "/api/datasources/proxy/" + strconv.FormatInt(ds.Id, 10)
|
||||
}
|
||||
|
||||
dsDTO := plugins.DataSourceDTO{
|
||||
ID: ds.Id,
|
||||
UID: ds.Uid,
|
||||
Type: ds.Type,
|
||||
Name: ds.Name,
|
||||
URL: url,
|
||||
IsDefault: ds.IsDefault,
|
||||
Access: string(ds.Access),
|
||||
}
|
||||
|
||||
plugin, exists := enabledPlugins.Get(plugins.DataSource, ds.Type)
|
||||
if !exists {
|
||||
c.Logger.Error("Could not find plugin definition for data source", "datasource_type", ds.Type)
|
||||
continue
|
||||
}
|
||||
dsDTO.Preload = plugin.Preload
|
||||
dsDTO.Module = plugin.Module
|
||||
dsDTO.PluginMeta = &plugins.PluginMetaDTO{
|
||||
JSONData: plugin.JSONData,
|
||||
Signature: plugin.Signature,
|
||||
Module: plugin.Module,
|
||||
BaseURL: plugin.BaseURL,
|
||||
}
|
||||
|
||||
if ds.JsonData == nil {
|
||||
dsDTO.JSONData = make(map[string]interface{})
|
||||
} else {
|
||||
dsDTO.JSONData = ds.JsonData.MustMap()
|
||||
}
|
||||
|
||||
if ds.Access == models.DS_ACCESS_DIRECT {
|
||||
if ds.BasicAuth {
|
||||
dsDTO.BasicAuth = util.GetBasicAuthHeader(
|
||||
ds.BasicAuthUser,
|
||||
hs.DataSourcesService.DecryptedBasicAuthPassword(ds),
|
||||
)
|
||||
}
|
||||
if ds.WithCredentials {
|
||||
dsDTO.WithCredentials = ds.WithCredentials
|
||||
}
|
||||
|
||||
if ds.Type == models.DS_INFLUXDB_08 {
|
||||
dsDTO.Username = ds.User
|
||||
dsDTO.Password = hs.DataSourcesService.DecryptedPassword(ds)
|
||||
dsDTO.URL = url + "/db/" + ds.Database
|
||||
}
|
||||
|
||||
if ds.Type == models.DS_INFLUXDB {
|
||||
dsDTO.Username = ds.User
|
||||
dsDTO.Password = hs.DataSourcesService.DecryptedPassword(ds)
|
||||
dsDTO.URL = url
|
||||
}
|
||||
}
|
||||
|
||||
if (ds.Type == models.DS_INFLUXDB) || (ds.Type == models.DS_ES) {
|
||||
dsDTO.Database = ds.Database
|
||||
}
|
||||
|
||||
if ds.Type == models.DS_PROMETHEUS {
|
||||
// add unproxied server URL for link to Prometheus web UI
|
||||
ds.JsonData.Set("directUrl", ds.Url)
|
||||
}
|
||||
|
||||
dataSources[ds.Name] = dsDTO
|
||||
}
|
||||
|
||||
// add data sources that are built in (meaning they are not added via data sources page, nor have any entry in
|
||||
// the datasource table)
|
||||
for _, ds := range hs.pluginStore.Plugins(c.Req.Context(), plugins.DataSource) {
|
||||
if ds.BuiltIn {
|
||||
dto := plugins.DataSourceDTO{
|
||||
Type: string(ds.Type),
|
||||
Name: ds.Name,
|
||||
JSONData: make(map[string]interface{}),
|
||||
PluginMeta: &plugins.PluginMetaDTO{
|
||||
JSONData: ds.JSONData,
|
||||
Signature: ds.Signature,
|
||||
Module: ds.Module,
|
||||
BaseURL: ds.BaseURL,
|
||||
},
|
||||
}
|
||||
if ds.Name == grafanads.DatasourceName {
|
||||
dto.ID = grafanads.DatasourceID
|
||||
dto.UID = grafanads.DatasourceUID
|
||||
}
|
||||
dataSources[ds.Name] = dto
|
||||
}
|
||||
}
|
||||
|
||||
return dataSources, nil
|
||||
c.JSON(200, settings)
|
||||
}
|
||||
|
||||
// getFrontendSettingsMap returns a json object with all the settings needed for front end initialisation.
|
||||
@ -290,6 +181,126 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
||||
return jsonObj, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins EnabledPlugins) (map[string]plugins.DataSourceDTO, error) {
|
||||
orgDataSources := make([]*models.DataSource, 0)
|
||||
|
||||
if c.OrgId != 0 {
|
||||
query := models.GetDataSourcesQuery{OrgId: c.OrgId, DataSourceLimit: hs.Cfg.DataSourceLimit}
|
||||
err := hs.SQLStore.GetDataSources(c.Req.Context(), &query)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filtered, err := hs.filterDatasourcesByQueryPermission(c.Req.Context(), c.SignedInUser, query.Result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgDataSources = filtered
|
||||
}
|
||||
|
||||
dataSources := make(map[string]plugins.DataSourceDTO)
|
||||
|
||||
for _, ds := range orgDataSources {
|
||||
url := ds.Url
|
||||
|
||||
if ds.Access == models.DS_ACCESS_PROXY {
|
||||
url = "/api/datasources/proxy/" + strconv.FormatInt(ds.Id, 10)
|
||||
}
|
||||
|
||||
dsDTO := plugins.DataSourceDTO{
|
||||
ID: ds.Id,
|
||||
UID: ds.Uid,
|
||||
Type: ds.Type,
|
||||
Name: ds.Name,
|
||||
URL: url,
|
||||
IsDefault: ds.IsDefault,
|
||||
Access: string(ds.Access),
|
||||
}
|
||||
|
||||
plugin, exists := enabledPlugins.Get(plugins.DataSource, ds.Type)
|
||||
if !exists {
|
||||
c.Logger.Error("Could not find plugin definition for data source", "datasource_type", ds.Type)
|
||||
continue
|
||||
}
|
||||
dsDTO.Preload = plugin.Preload
|
||||
dsDTO.Module = plugin.Module
|
||||
dsDTO.PluginMeta = &plugins.PluginMetaDTO{
|
||||
JSONData: plugin.JSONData,
|
||||
Signature: plugin.Signature,
|
||||
Module: plugin.Module,
|
||||
BaseURL: plugin.BaseURL,
|
||||
}
|
||||
|
||||
if ds.JsonData == nil {
|
||||
dsDTO.JSONData = make(map[string]interface{})
|
||||
} else {
|
||||
dsDTO.JSONData = ds.JsonData.MustMap()
|
||||
}
|
||||
|
||||
if ds.Access == models.DS_ACCESS_DIRECT {
|
||||
if ds.BasicAuth {
|
||||
dsDTO.BasicAuth = util.GetBasicAuthHeader(
|
||||
ds.BasicAuthUser,
|
||||
hs.DataSourcesService.DecryptedBasicAuthPassword(ds),
|
||||
)
|
||||
}
|
||||
if ds.WithCredentials {
|
||||
dsDTO.WithCredentials = ds.WithCredentials
|
||||
}
|
||||
|
||||
if ds.Type == models.DS_INFLUXDB_08 {
|
||||
dsDTO.Username = ds.User
|
||||
dsDTO.Password = hs.DataSourcesService.DecryptedPassword(ds)
|
||||
dsDTO.URL = url + "/db/" + ds.Database
|
||||
}
|
||||
|
||||
if ds.Type == models.DS_INFLUXDB {
|
||||
dsDTO.Username = ds.User
|
||||
dsDTO.Password = hs.DataSourcesService.DecryptedPassword(ds)
|
||||
dsDTO.URL = url
|
||||
}
|
||||
}
|
||||
|
||||
if (ds.Type == models.DS_INFLUXDB) || (ds.Type == models.DS_ES) {
|
||||
dsDTO.Database = ds.Database
|
||||
}
|
||||
|
||||
if ds.Type == models.DS_PROMETHEUS {
|
||||
// add unproxied server URL for link to Prometheus web UI
|
||||
ds.JsonData.Set("directUrl", ds.Url)
|
||||
}
|
||||
|
||||
dataSources[ds.Name] = dsDTO
|
||||
}
|
||||
|
||||
// add data sources that are built in (meaning they are not added via data sources page, nor have any entry in
|
||||
// the datasource table)
|
||||
for _, ds := range hs.pluginStore.Plugins(c.Req.Context(), plugins.DataSource) {
|
||||
if ds.BuiltIn {
|
||||
dto := plugins.DataSourceDTO{
|
||||
Type: string(ds.Type),
|
||||
Name: ds.Name,
|
||||
JSONData: make(map[string]interface{}),
|
||||
PluginMeta: &plugins.PluginMetaDTO{
|
||||
JSONData: ds.JSONData,
|
||||
Signature: ds.Signature,
|
||||
Module: ds.Module,
|
||||
BaseURL: ds.BaseURL,
|
||||
},
|
||||
}
|
||||
if ds.Name == grafanads.DatasourceName {
|
||||
dto.ID = grafanads.DatasourceID
|
||||
dto.UID = grafanads.DatasourceUID
|
||||
}
|
||||
dataSources[ds.Name] = dto
|
||||
}
|
||||
}
|
||||
|
||||
return dataSources, nil
|
||||
}
|
||||
|
||||
func getPanelSort(id string) int {
|
||||
sort := 100
|
||||
switch id {
|
||||
@ -331,16 +342,6 @@ func getPanelSort(id string) int {
|
||||
return sort
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) GetFrontendSettings(c *models.ReqContext) {
|
||||
settings, err := hs.getFrontendSettingsMap(c)
|
||||
if err != nil {
|
||||
c.JsonApiErr(400, "Failed to get frontend settings", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, settings)
|
||||
}
|
||||
|
||||
// EnabledPlugins represents a mapping from plugin types (panel, data source, etc.) to plugin IDs to plugins
|
||||
// For example ["panel"] -> ["piechart"] -> {pie chart plugin DTO}
|
||||
type EnabledPlugins map[plugins.Type]map[string]plugins.PluginDTO
|
||||
@ -389,15 +390,15 @@ func (hs *HTTPServer) enabledPlugins(ctx context.Context, orgID int64) (EnabledP
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[string]*models.PluginSettingInfoDTO, error) {
|
||||
pluginSettings := make(map[string]*models.PluginSettingInfoDTO)
|
||||
func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[string]*pluginsettings.DTO, error) {
|
||||
pluginSettings := make(map[string]*pluginsettings.DTO)
|
||||
|
||||
// fill settings from database
|
||||
if pss, err := hs.PluginSettings.GetPluginSettings(ctx, orgID); err != nil {
|
||||
if pss, err := hs.PluginSettings.GetPluginSettings(ctx, &pluginsettings.GetArgs{OrgID: orgID}); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, ps := range pss {
|
||||
pluginSettings[ps.PluginId] = ps
|
||||
pluginSettings[ps.PluginID] = ps
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,9 +410,9 @@ func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[stri
|
||||
}
|
||||
|
||||
// add new setting which is enabled depending on if AutoEnabled: true
|
||||
pluginSetting := &models.PluginSettingInfoDTO{
|
||||
PluginId: plugin.ID,
|
||||
OrgId: orgID,
|
||||
pluginSetting := &pluginsettings.DTO{
|
||||
PluginID: plugin.ID,
|
||||
OrgID: orgID,
|
||||
Enabled: plugin.AutoEnabled,
|
||||
Pinned: plugin.AutoEnabled,
|
||||
}
|
||||
@ -427,9 +428,9 @@ func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[stri
|
||||
}
|
||||
|
||||
// add new setting which is enabled by default
|
||||
pluginSetting := &models.PluginSettingInfoDTO{
|
||||
PluginId: plugin.ID,
|
||||
OrgId: orgID,
|
||||
pluginSetting := &pluginsettings.DTO{
|
||||
PluginID: plugin.ID,
|
||||
OrgID: orgID,
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
|
@ -24,20 +24,21 @@ type templateData struct {
|
||||
func NewApiPluginProxy(ctx *models.ReqContext, proxyPath string, route *plugins.Route,
|
||||
appID string, cfg *setting.Cfg, pluginSettingsService pluginsettings.Service, secretsService secrets.Service) *httputil.ReverseProxy {
|
||||
director := func(req *http.Request) {
|
||||
query := models.GetPluginSettingByIdQuery{OrgId: ctx.OrgId, PluginId: appID}
|
||||
if err := pluginSettingsService.GetPluginSettingById(ctx.Req.Context(), &query); err != nil {
|
||||
query := pluginsettings.GetByPluginIDArgs{OrgID: ctx.OrgId, PluginID: appID}
|
||||
ps, err := pluginSettingsService.GetPluginSettingByPluginID(ctx.Req.Context(), &query)
|
||||
if err != nil {
|
||||
ctx.JsonApiErr(500, "Failed to fetch plugin settings", err)
|
||||
return
|
||||
}
|
||||
|
||||
secureJsonData, err := secretsService.DecryptJsonData(ctx.Req.Context(), query.Result.SecureJsonData)
|
||||
secureJsonData, err := secretsService.DecryptJsonData(ctx.Req.Context(), ps.SecureJSONData)
|
||||
if err != nil {
|
||||
ctx.JsonApiErr(500, "Failed to decrypt plugin settings", err)
|
||||
return
|
||||
}
|
||||
|
||||
data := templateData{
|
||||
JsonData: query.Result.JsonData,
|
||||
JsonData: ps.JSONData,
|
||||
SecureJsonData: secureJsonData,
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,8 @@ func TestPluginProxy(t *testing.T) {
|
||||
|
||||
store := &mockPluginsSettingsService{}
|
||||
key, _ := secretsService.Encrypt(context.Background(), []byte("123"), secrets.WithoutScope())
|
||||
store.pluginSetting = &models.PluginSetting{
|
||||
SecureJsonData: map[string][]byte{
|
||||
store.pluginSetting = &pluginsettings.DTO{
|
||||
SecureJSONData: map[string][]byte{
|
||||
"key": key,
|
||||
},
|
||||
}
|
||||
@ -65,7 +65,7 @@ func TestPluginProxy(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
store := &mockPluginsSettingsService{}
|
||||
store.pluginSetting = &models.PluginSetting{}
|
||||
store.pluginSetting = &pluginsettings.DTO{}
|
||||
|
||||
req := getPluginProxiedRequest(
|
||||
t,
|
||||
@ -92,7 +92,7 @@ func TestPluginProxy(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
store := &mockPluginsSettingsService{}
|
||||
store.pluginSetting = &models.PluginSetting{}
|
||||
store.pluginSetting = &pluginsettings.DTO{}
|
||||
|
||||
req := getPluginProxiedRequest(
|
||||
t,
|
||||
@ -118,7 +118,7 @@ func TestPluginProxy(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
store := &mockPluginsSettingsService{}
|
||||
store.pluginSetting = &models.PluginSetting{}
|
||||
store.pluginSetting = &pluginsettings.DTO{}
|
||||
|
||||
req := getPluginProxiedRequest(
|
||||
t,
|
||||
@ -145,8 +145,8 @@ func TestPluginProxy(t *testing.T) {
|
||||
}
|
||||
|
||||
store := &mockPluginsSettingsService{}
|
||||
store.pluginSetting = &models.PluginSetting{
|
||||
JsonData: map[string]interface{}{
|
||||
store.pluginSetting = &pluginsettings.DTO{
|
||||
JSONData: map[string]interface{}{
|
||||
"dynamicUrl": "https://dynamic.grafana.com",
|
||||
},
|
||||
}
|
||||
@ -180,7 +180,7 @@ func TestPluginProxy(t *testing.T) {
|
||||
}
|
||||
|
||||
store := &mockPluginsSettingsService{}
|
||||
store.pluginSetting = &models.PluginSetting{}
|
||||
store.pluginSetting = &pluginsettings.DTO{}
|
||||
|
||||
httpReq, err := http.NewRequest(http.MethodGet, "", nil)
|
||||
require.NoError(t, err)
|
||||
@ -216,9 +216,9 @@ func TestPluginProxy(t *testing.T) {
|
||||
map[string]string{"key": "123"},
|
||||
secrets.WithoutScope(),
|
||||
)
|
||||
store.pluginSetting = &models.PluginSetting{
|
||||
JsonData: map[string]interface{}{"dynamicUrl": "https://dynamic.grafana.com"},
|
||||
SecureJsonData: encryptedJsonData,
|
||||
store.pluginSetting = &pluginsettings.DTO{
|
||||
JSONData: map[string]interface{}{"dynamicUrl": "https://dynamic.grafana.com"},
|
||||
SecureJSONData: encryptedJsonData,
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequest(http.MethodGet, "", nil)
|
||||
@ -268,8 +268,8 @@ func TestPluginProxy(t *testing.T) {
|
||||
},
|
||||
}
|
||||
pluginSettingsService := &mockPluginsSettingsService{
|
||||
pluginSetting: &models.PluginSetting{
|
||||
SecureJsonData: map[string][]byte{},
|
||||
pluginSetting: &pluginsettings.DTO{
|
||||
SecureJSONData: map[string][]byte{},
|
||||
},
|
||||
}
|
||||
proxy := NewApiPluginProxy(ctx, "", route, "", &setting.Cfg{}, pluginSettingsService, secretsService)
|
||||
@ -304,27 +304,26 @@ func getPluginProxiedRequest(t *testing.T, secretsService secrets.Service, ctx *
|
||||
}
|
||||
|
||||
type mockPluginsSettingsService struct {
|
||||
pluginSetting *models.PluginSetting
|
||||
pluginSetting *pluginsettings.DTO
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *mockPluginsSettingsService) GetPluginSettings(_ context.Context, _ int64) ([]*models.PluginSettingInfoDTO, error) {
|
||||
func (s *mockPluginsSettingsService) GetPluginSettings(_ context.Context, _ *pluginsettings.GetArgs) ([]*pluginsettings.DTO, error) {
|
||||
return nil, s.err
|
||||
}
|
||||
|
||||
func (s *mockPluginsSettingsService) GetPluginSettingById(_ context.Context, query *models.GetPluginSettingByIdQuery) error {
|
||||
query.Result = s.pluginSetting
|
||||
func (s *mockPluginsSettingsService) GetPluginSettingByPluginID(_ context.Context, _ *pluginsettings.GetByPluginIDArgs) (*pluginsettings.DTO, error) {
|
||||
return s.pluginSetting, s.err
|
||||
}
|
||||
|
||||
func (s *mockPluginsSettingsService) UpdatePluginSettingPluginVersion(_ context.Context, _ *pluginsettings.UpdatePluginVersionArgs) error {
|
||||
return s.err
|
||||
}
|
||||
|
||||
func (s *mockPluginsSettingsService) UpdatePluginSettingVersion(_ context.Context, _ *models.UpdatePluginSettingVersionCmd) error {
|
||||
func (s *mockPluginsSettingsService) UpdatePluginSetting(_ context.Context, _ *pluginsettings.UpdateArgs) error {
|
||||
return s.err
|
||||
}
|
||||
|
||||
func (s *mockPluginsSettingsService) UpdatePluginSetting(_ context.Context, _ *models.UpdatePluginSettingCmd) error {
|
||||
return s.err
|
||||
}
|
||||
|
||||
func (s *mockPluginsSettingsService) DecryptedValues(_ *models.PluginSetting) map[string]string {
|
||||
func (s *mockPluginsSettingsService) DecryptedValues(_ *pluginsettings.DTO) map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/installer"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
"github.com/grafana/grafana/pkg/util/proxyutil"
|
||||
@ -142,15 +143,18 @@ func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Respon
|
||||
dto.Pinned = plugin.AutoEnabled
|
||||
}
|
||||
|
||||
query := models.GetPluginSettingByIdQuery{PluginId: pluginID, OrgId: c.OrgId}
|
||||
if err := hs.PluginSettings.GetPluginSettingById(c.Req.Context(), &query); err != nil {
|
||||
ps, err := hs.PluginSettings.GetPluginSettingByPluginID(c.Req.Context(), &pluginsettings.GetByPluginIDArgs{
|
||||
PluginID: pluginID,
|
||||
OrgID: c.OrgId,
|
||||
})
|
||||
if err != nil {
|
||||
if !errors.Is(err, models.ErrPluginSettingNotFound) {
|
||||
return response.Error(500, "Failed to get login settings", nil)
|
||||
return response.Error(http.StatusInternalServerError, "Failed to get plugin settings", nil)
|
||||
}
|
||||
} else {
|
||||
dto.Enabled = query.Result.Enabled
|
||||
dto.Pinned = query.Result.Pinned
|
||||
dto.JsonData = query.Result.JsonData
|
||||
dto.Enabled = ps.Enabled
|
||||
dto.Pinned = ps.Pinned
|
||||
dto.JsonData = ps.JSONData
|
||||
}
|
||||
|
||||
update, exists := hs.pluginsUpdateChecker.HasUpdate(c.Req.Context(), plugin.ID)
|
||||
@ -175,7 +179,16 @@ func (hs *HTTPServer) UpdatePluginSetting(c *models.ReqContext) response.Respons
|
||||
|
||||
cmd.OrgId = c.OrgId
|
||||
cmd.PluginId = pluginID
|
||||
if err := hs.PluginSettings.UpdatePluginSetting(c.Req.Context(), &cmd); err != nil {
|
||||
if err := hs.PluginSettings.UpdatePluginSetting(c.Req.Context(), &pluginsettings.UpdateArgs{
|
||||
Enabled: cmd.Enabled,
|
||||
Pinned: cmd.Pinned,
|
||||
JSONData: cmd.JsonData,
|
||||
SecureJSONData: cmd.SecureJsonData,
|
||||
PluginVersion: cmd.PluginVersion,
|
||||
PluginID: cmd.PluginId,
|
||||
OrgID: cmd.OrgId,
|
||||
EncryptedSecureJSONData: cmd.EncryptedSecureJsonData,
|
||||
}); err != nil {
|
||||
return response.Error(500, "Failed to update plugin setting", err)
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ func (p *Provider) Get(ctx context.Context, pluginID string, datasourceUID strin
|
||||
return pc, false, errutil.Wrap("Failed to get plugin settings", err)
|
||||
}
|
||||
} else {
|
||||
jsonData, err = json.Marshal(ps.JsonData)
|
||||
jsonData, err = json.Marshal(ps.JSONData)
|
||||
if err != nil {
|
||||
return pc, false, errutil.Wrap("Failed to unmarshal plugin json data", err)
|
||||
}
|
||||
@ -103,23 +103,26 @@ func (p *Provider) Get(ctx context.Context, pluginID string, datasourceUID strin
|
||||
const pluginSettingsCacheTTL = 5 * time.Second
|
||||
const pluginSettingsCachePrefix = "plugin-setting-"
|
||||
|
||||
func (p *Provider) getCachedPluginSettings(ctx context.Context, pluginID string, user *models.SignedInUser) (*models.PluginSetting, error) {
|
||||
func (p *Provider) getCachedPluginSettings(ctx context.Context, pluginID string, user *models.SignedInUser) (*pluginsettings.DTO, error) {
|
||||
cacheKey := pluginSettingsCachePrefix + pluginID
|
||||
|
||||
if cached, found := p.cacheService.Get(cacheKey); found {
|
||||
ps := cached.(*models.PluginSetting)
|
||||
if ps.OrgId == user.OrgId {
|
||||
ps := cached.(*pluginsettings.DTO)
|
||||
if ps.OrgID == user.OrgId {
|
||||
return ps, nil
|
||||
}
|
||||
}
|
||||
|
||||
query := models.GetPluginSettingByIdQuery{PluginId: pluginID, OrgId: user.OrgId}
|
||||
if err := p.pluginSettingsService.GetPluginSettingById(ctx, &query); err != nil {
|
||||
ps, err := p.pluginSettingsService.GetPluginSettingByPluginID(ctx, &pluginsettings.GetByPluginIDArgs{
|
||||
PluginID: pluginID,
|
||||
OrgID: user.OrgId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.cacheService.Set(cacheKey, query.Result, pluginSettingsCacheTTL)
|
||||
return query.Result, nil
|
||||
p.cacheService.Set(cacheKey, ps, pluginSettingsCacheTTL)
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func (p *Provider) decryptSecureJsonDataFn() func(map[string][]byte) map[string]string {
|
||||
|
@ -54,7 +54,7 @@ type DashboardUpdater struct {
|
||||
func (du *DashboardUpdater) updateAppDashboards() {
|
||||
du.logger.Debug("Looking for app dashboard updates")
|
||||
|
||||
pluginSettings, err := du.pluginSettingsService.GetPluginSettings(context.Background(), 0)
|
||||
pluginSettings, err := du.pluginSettingsService.GetPluginSettings(context.Background(), &pluginsettings.GetArgs{OrgID: 0})
|
||||
if err != nil {
|
||||
du.logger.Error("Failed to get all plugin settings", "error", err)
|
||||
return
|
||||
@ -66,9 +66,9 @@ func (du *DashboardUpdater) updateAppDashboards() {
|
||||
continue
|
||||
}
|
||||
|
||||
if pluginDef, exists := du.pluginStore.Plugin(context.Background(), pluginSetting.PluginId); exists {
|
||||
if pluginDef, exists := du.pluginStore.Plugin(context.Background(), pluginSetting.PluginID); exists {
|
||||
if pluginDef.Info.Version != pluginSetting.PluginVersion {
|
||||
du.syncPluginDashboards(context.Background(), pluginDef, pluginSetting.OrgId)
|
||||
du.syncPluginDashboards(context.Background(), pluginDef, pluginSetting.OrgID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,20 +112,20 @@ func (du *DashboardUpdater) syncPluginDashboards(ctx context.Context, plugin plu
|
||||
}
|
||||
|
||||
// update version in plugin_setting table to mark that we have processed the update
|
||||
query := models.GetPluginSettingByIdQuery{PluginId: plugin.ID, OrgId: orgID}
|
||||
if err := du.pluginSettingsService.GetPluginSettingById(ctx, &query); err != nil {
|
||||
query := pluginsettings.GetByPluginIDArgs{PluginID: plugin.ID, OrgID: orgID}
|
||||
ps, err := du.pluginSettingsService.GetPluginSettingByPluginID(ctx, &query)
|
||||
if err != nil {
|
||||
du.logger.Error("Failed to read plugin setting by ID", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
appSetting := query.Result
|
||||
cmd := models.UpdatePluginSettingVersionCmd{
|
||||
OrgId: appSetting.OrgId,
|
||||
PluginId: appSetting.PluginId,
|
||||
cmd := pluginsettings.UpdatePluginVersionArgs{
|
||||
OrgID: ps.OrgID,
|
||||
PluginID: ps.PluginID,
|
||||
PluginVersion: plugin.Info.Version,
|
||||
}
|
||||
|
||||
if err := du.pluginSettingsService.UpdatePluginSettingVersion(ctx, &cmd); err != nil {
|
||||
if err := du.pluginSettingsService.UpdatePluginSettingPluginVersion(ctx, &cmd); err != nil {
|
||||
du.logger.Error("Failed to update plugin setting version", "error", err)
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings/service"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -29,9 +30,9 @@ func TestDashboardUpdater(t *testing.T) {
|
||||
|
||||
scenario(t, "Without any stored enabled plugin shouldn't delete/import any dashboards",
|
||||
scenarioInput{
|
||||
storedPluginSettings: []*models.PluginSettingInfoDTO{
|
||||
storedPluginSettings: []*pluginsettings.DTO{
|
||||
{
|
||||
PluginId: "test",
|
||||
PluginID: "test",
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
@ -51,9 +52,9 @@ func TestDashboardUpdater(t *testing.T) {
|
||||
|
||||
scenario(t, "With stored enabled plugin, but not installed shouldn't delete/import any dashboards",
|
||||
scenarioInput{
|
||||
storedPluginSettings: []*models.PluginSettingInfoDTO{
|
||||
storedPluginSettings: []*pluginsettings.DTO{
|
||||
{
|
||||
PluginId: "test",
|
||||
PluginID: "test",
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
@ -73,9 +74,9 @@ func TestDashboardUpdater(t *testing.T) {
|
||||
|
||||
scenario(t, "With stored enabled plugin and installed with same version shouldn't delete/import any dashboards",
|
||||
scenarioInput{
|
||||
storedPluginSettings: []*models.PluginSettingInfoDTO{
|
||||
storedPluginSettings: []*pluginsettings.DTO{
|
||||
{
|
||||
PluginId: "test",
|
||||
PluginID: "test",
|
||||
Enabled: true,
|
||||
PluginVersion: "1.0.0",
|
||||
},
|
||||
@ -105,9 +106,9 @@ func TestDashboardUpdater(t *testing.T) {
|
||||
|
||||
scenario(t, "With stored enabled plugin and installed with different versions, but no dashboard updates shouldn't delete/import dashboards",
|
||||
scenarioInput{
|
||||
storedPluginSettings: []*models.PluginSettingInfoDTO{
|
||||
storedPluginSettings: []*pluginsettings.DTO{
|
||||
{
|
||||
PluginId: "test",
|
||||
PluginID: "test",
|
||||
Enabled: true,
|
||||
PluginVersion: "1.0.0",
|
||||
},
|
||||
@ -140,12 +141,12 @@ func TestDashboardUpdater(t *testing.T) {
|
||||
|
||||
scenario(t, "With stored enabled plugin and installed with different versions and with dashboard updates should delete/import dashboards",
|
||||
scenarioInput{
|
||||
storedPluginSettings: []*models.PluginSettingInfoDTO{
|
||||
storedPluginSettings: []*pluginsettings.DTO{
|
||||
{
|
||||
PluginId: "test",
|
||||
PluginID: "test",
|
||||
Enabled: true,
|
||||
PluginVersion: "1.0.0",
|
||||
OrgId: 2,
|
||||
OrgID: 2,
|
||||
},
|
||||
},
|
||||
installedPlugins: []plugins.PluginDTO{
|
||||
@ -215,11 +216,11 @@ func TestDashboardUpdater(t *testing.T) {
|
||||
|
||||
scenario(t, "When app plugin is disabled that have imported dashboards should delete them",
|
||||
scenarioInput{
|
||||
storedPluginSettings: []*models.PluginSettingInfoDTO{
|
||||
storedPluginSettings: []*pluginsettings.DTO{
|
||||
{
|
||||
PluginId: "test",
|
||||
PluginID: "test",
|
||||
Enabled: true,
|
||||
OrgId: 2,
|
||||
OrgID: 2,
|
||||
},
|
||||
},
|
||||
installedPlugins: []plugins.PluginDTO{
|
||||
@ -262,11 +263,11 @@ func TestDashboardUpdater(t *testing.T) {
|
||||
|
||||
scenario(t, "When app plugin is enabled, stored disabled plugin and with dashboard updates should import dashboards",
|
||||
scenarioInput{
|
||||
storedPluginSettings: []*models.PluginSettingInfoDTO{
|
||||
storedPluginSettings: []*pluginsettings.DTO{
|
||||
{
|
||||
PluginId: "test",
|
||||
PluginID: "test",
|
||||
Enabled: false,
|
||||
OrgId: 2,
|
||||
OrgID: 2,
|
||||
PluginVersion: "1.0.0",
|
||||
},
|
||||
},
|
||||
@ -389,36 +390,39 @@ func (m *importDashboardServiceMock) ImportDashboard(ctx context.Context, req *d
|
||||
type pluginsSettingsServiceMock struct {
|
||||
service.Service
|
||||
|
||||
storedPluginSettings []*models.PluginSettingInfoDTO
|
||||
storedPluginSettings []*pluginsettings.DTO
|
||||
getPluginSettingsArgs []int64
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *pluginsSettingsServiceMock) GetPluginSettings(_ context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error) {
|
||||
s.getPluginSettingsArgs = append(s.getPluginSettingsArgs, orgID)
|
||||
func (s *pluginsSettingsServiceMock) GetPluginSettings(_ context.Context, args *pluginsettings.GetArgs) ([]*pluginsettings.DTO, error) {
|
||||
s.getPluginSettingsArgs = append(s.getPluginSettingsArgs, args.OrgID)
|
||||
return s.storedPluginSettings, s.err
|
||||
}
|
||||
|
||||
func (s *pluginsSettingsServiceMock) GetPluginSettingById(_ context.Context, query *models.GetPluginSettingByIdQuery) error {
|
||||
func (s *pluginsSettingsServiceMock) GetPluginSettingByPluginID(_ context.Context, args *pluginsettings.GetByPluginIDArgs) (*pluginsettings.DTO, error) {
|
||||
for _, setting := range s.storedPluginSettings {
|
||||
if setting.PluginId == query.PluginId {
|
||||
query.Result = &models.PluginSetting{
|
||||
PluginId: query.PluginId,
|
||||
OrgId: query.OrgId,
|
||||
}
|
||||
break
|
||||
if setting.PluginID == args.PluginID {
|
||||
return &pluginsettings.DTO{
|
||||
PluginID: args.PluginID,
|
||||
OrgID: args.OrgID,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, s.err
|
||||
}
|
||||
|
||||
func (s *pluginsSettingsServiceMock) UpdatePluginSettingPluginVersion(_ context.Context, _ *pluginsettings.UpdatePluginVersionArgs) error {
|
||||
return s.err
|
||||
}
|
||||
|
||||
func (s *pluginsSettingsServiceMock) UpdatePluginSettingVersion(_ context.Context, _ *models.UpdatePluginSettingVersionCmd) error {
|
||||
func (s *pluginsSettingsServiceMock) UpdatePluginSetting(_ context.Context, _ *pluginsettings.UpdateArgs) error {
|
||||
return s.err
|
||||
}
|
||||
|
||||
func (s *pluginsSettingsServiceMock) UpdatePluginSetting(_ context.Context, _ *models.UpdatePluginSettingCmd) error {
|
||||
return s.err
|
||||
func (s *pluginsSettingsServiceMock) DecryptedValues(_ *pluginsettings.DTO) map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
type dashboardServiceMock struct {
|
||||
@ -441,7 +445,7 @@ func (s *dashboardServiceMock) DeleteDashboard(_ context.Context, dashboardId in
|
||||
}
|
||||
|
||||
type scenarioInput struct {
|
||||
storedPluginSettings []*models.PluginSettingInfoDTO
|
||||
storedPluginSettings []*pluginsettings.DTO
|
||||
installedPlugins []plugins.PluginDTO
|
||||
pluginDashboards []*plugindashboards.PluginDashboard
|
||||
}
|
||||
|
43
pkg/services/pluginsettings/models.go
Normal file
43
pkg/services/pluginsettings/models.go
Normal file
@ -0,0 +1,43 @@
|
||||
package pluginsettings
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type DTO struct {
|
||||
ID int64
|
||||
OrgID int64
|
||||
PluginID string
|
||||
PluginVersion string
|
||||
JSONData map[string]interface{}
|
||||
SecureJSONData map[string][]byte
|
||||
Enabled bool
|
||||
Pinned bool
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
type UpdateArgs struct {
|
||||
Enabled bool
|
||||
Pinned bool
|
||||
JSONData map[string]interface{}
|
||||
SecureJSONData map[string]string
|
||||
PluginVersion string
|
||||
PluginID string
|
||||
OrgID int64
|
||||
EncryptedSecureJSONData map[string][]byte
|
||||
}
|
||||
|
||||
type UpdatePluginVersionArgs struct {
|
||||
PluginVersion string
|
||||
PluginID string
|
||||
OrgID int64
|
||||
}
|
||||
|
||||
type GetArgs struct {
|
||||
OrgID int64
|
||||
}
|
||||
|
||||
type GetByPluginIDArgs struct {
|
||||
PluginID string
|
||||
OrgID int64
|
||||
}
|
@ -2,20 +2,18 @@ package pluginsettings
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
// GetPluginSettings returns all Plugin Settings for the provided Org
|
||||
GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error)
|
||||
// GetPluginSettingById returns a Plugin Settings by Plugin ID
|
||||
GetPluginSettingById(ctx context.Context, query *models.GetPluginSettingByIdQuery) error
|
||||
GetPluginSettings(ctx context.Context, args *GetArgs) ([]*DTO, error)
|
||||
// GetPluginSettingByPluginID returns a Plugin Settings by Plugin ID
|
||||
GetPluginSettingByPluginID(ctx context.Context, args *GetByPluginIDArgs) (*DTO, error)
|
||||
// UpdatePluginSetting updates a Plugin Setting
|
||||
UpdatePluginSetting(ctx context.Context, cmd *models.UpdatePluginSettingCmd) error
|
||||
// UpdatePluginSettingVersion updates a Plugin Setting's plugin version
|
||||
UpdatePluginSettingVersion(ctx context.Context, cmd *models.UpdatePluginSettingVersionCmd) error
|
||||
UpdatePluginSetting(ctx context.Context, args *UpdateArgs) error
|
||||
// UpdatePluginSettingPluginVersion updates a Plugin Setting's plugin version
|
||||
UpdatePluginSettingPluginVersion(ctx context.Context, args *UpdatePluginVersionArgs) error
|
||||
// DecryptedValues decrypts the encrypted secureJSONData of the provided plugin setting and
|
||||
// returns the decrypted values.
|
||||
DecryptedValues(ps *models.PluginSetting) map[string]string
|
||||
DecryptedValues(ps *DTO) map[string]string
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
)
|
||||
@ -42,43 +43,95 @@ type secureJSONDecryptionCache struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (s *Service) GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error) {
|
||||
return s.sqlStore.GetPluginSettings(ctx, orgID)
|
||||
func (s *Service) GetPluginSettings(ctx context.Context, args *pluginsettings.GetArgs) ([]*pluginsettings.DTO, error) {
|
||||
ps, err := s.sqlStore.GetPluginSettings(ctx, args.OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []*pluginsettings.DTO
|
||||
for _, p := range ps {
|
||||
result = append(result, &pluginsettings.DTO{
|
||||
ID: p.Id,
|
||||
OrgID: p.OrgId,
|
||||
PluginID: p.PluginId,
|
||||
PluginVersion: p.PluginVersion,
|
||||
JSONData: p.JsonData,
|
||||
SecureJSONData: p.SecureJsonData,
|
||||
Enabled: p.Enabled,
|
||||
Pinned: p.Pinned,
|
||||
Updated: p.Updated,
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetPluginSettingById(ctx context.Context, query *models.GetPluginSettingByIdQuery) error {
|
||||
return s.sqlStore.GetPluginSettingById(ctx, query)
|
||||
func (s *Service) GetPluginSettingByPluginID(ctx context.Context, args *pluginsettings.GetByPluginIDArgs) (*pluginsettings.DTO, error) {
|
||||
query := &models.GetPluginSettingByIdQuery{
|
||||
OrgId: args.OrgID,
|
||||
PluginId: args.PluginID,
|
||||
}
|
||||
|
||||
err := s.sqlStore.GetPluginSettingById(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pluginsettings.DTO{
|
||||
ID: query.Result.Id,
|
||||
OrgID: query.Result.OrgId,
|
||||
PluginID: query.Result.PluginId,
|
||||
PluginVersion: query.Result.PluginVersion,
|
||||
JSONData: query.Result.JsonData,
|
||||
SecureJSONData: query.Result.SecureJsonData,
|
||||
Enabled: query.Result.Enabled,
|
||||
Pinned: query.Result.Pinned,
|
||||
Updated: query.Result.Updated,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdatePluginSetting(ctx context.Context, cmd *models.UpdatePluginSettingCmd) error {
|
||||
var err error
|
||||
cmd.EncryptedSecureJsonData, err = s.secretsService.EncryptJsonData(ctx, cmd.SecureJsonData, secrets.WithoutScope())
|
||||
func (s *Service) UpdatePluginSetting(ctx context.Context, args *pluginsettings.UpdateArgs) error {
|
||||
encryptedSecureJsonData, err := s.secretsService.EncryptJsonData(ctx, args.SecureJSONData, secrets.WithoutScope())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.sqlStore.UpdatePluginSetting(ctx, cmd)
|
||||
return s.sqlStore.UpdatePluginSetting(ctx, &models.UpdatePluginSettingCmd{
|
||||
Enabled: args.Enabled,
|
||||
Pinned: args.Pinned,
|
||||
JsonData: args.JSONData,
|
||||
SecureJsonData: args.SecureJSONData,
|
||||
PluginVersion: args.PluginVersion,
|
||||
PluginId: args.PluginID,
|
||||
OrgId: args.OrgID,
|
||||
EncryptedSecureJsonData: encryptedSecureJsonData,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) UpdatePluginSettingVersion(ctx context.Context, cmd *models.UpdatePluginSettingVersionCmd) error {
|
||||
return s.sqlStore.UpdatePluginSettingVersion(ctx, cmd)
|
||||
func (s *Service) UpdatePluginSettingPluginVersion(ctx context.Context, args *pluginsettings.UpdatePluginVersionArgs) error {
|
||||
return s.sqlStore.UpdatePluginSettingVersion(ctx, &models.UpdatePluginSettingVersionCmd{
|
||||
PluginVersion: args.PluginVersion,
|
||||
PluginId: args.PluginID,
|
||||
OrgId: args.OrgID,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) DecryptedValues(ps *models.PluginSetting) map[string]string {
|
||||
func (s *Service) DecryptedValues(ps *pluginsettings.DTO) map[string]string {
|
||||
s.decryptionCache.Lock()
|
||||
defer s.decryptionCache.Unlock()
|
||||
|
||||
if item, present := s.decryptionCache.cache[ps.Id]; present && ps.Updated.Equal(item.updated) {
|
||||
if item, present := s.decryptionCache.cache[ps.ID]; present && ps.Updated.Equal(item.updated) {
|
||||
return item.json
|
||||
}
|
||||
|
||||
json, err := s.secretsService.DecryptJsonData(context.Background(), ps.SecureJsonData)
|
||||
json, err := s.secretsService.DecryptJsonData(context.Background(), ps.SecureJSONData)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to decrypt secure json data", "error", err)
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
s.decryptionCache.cache[ps.Id] = cachedDecryptedJSON{
|
||||
s.decryptionCache.cache[ps.ID] = cachedDecryptedJSON{
|
||||
updated: ps.Updated,
|
||||
json: json,
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
||||
@ -27,10 +27,10 @@ func TestService_DecryptedValuesCache(t *testing.T) {
|
||||
}, secrets.WithoutScope())
|
||||
require.NoError(t, err)
|
||||
|
||||
ps := models.PluginSetting{
|
||||
Id: 1,
|
||||
JsonData: map[string]interface{}{},
|
||||
SecureJsonData: encryptedJsonData,
|
||||
ps := pluginsettings.DTO{
|
||||
ID: 1,
|
||||
JSONData: map[string]interface{}{},
|
||||
SecureJSONData: encryptedJsonData,
|
||||
}
|
||||
|
||||
// Populate cache
|
||||
@ -45,7 +45,7 @@ func TestService_DecryptedValuesCache(t *testing.T) {
|
||||
}, secrets.WithoutScope())
|
||||
require.NoError(t, err)
|
||||
|
||||
ps.SecureJsonData = encryptedJsonData
|
||||
ps.SecureJSONData = encryptedJsonData
|
||||
|
||||
password, ok = psService.DecryptedValues(&ps)["password"]
|
||||
require.Equal(t, "password", password)
|
||||
@ -65,10 +65,10 @@ func TestService_DecryptedValuesCache(t *testing.T) {
|
||||
}, secrets.WithoutScope())
|
||||
require.NoError(t, err)
|
||||
|
||||
ps := models.PluginSetting{
|
||||
Id: 1,
|
||||
JsonData: map[string]interface{}{},
|
||||
SecureJsonData: encryptedJsonData,
|
||||
ps := pluginsettings.DTO{
|
||||
ID: 1,
|
||||
JSONData: map[string]interface{}{},
|
||||
SecureJSONData: encryptedJsonData,
|
||||
}
|
||||
|
||||
// Populate cache
|
||||
@ -83,7 +83,7 @@ func TestService_DecryptedValuesCache(t *testing.T) {
|
||||
}, secrets.WithoutScope())
|
||||
require.NoError(t, err)
|
||||
|
||||
ps.SecureJsonData = encryptedJsonData
|
||||
ps.SecureJSONData = encryptedJsonData
|
||||
ps.Updated = time.Now()
|
||||
|
||||
password, ok = psService.DecryptedValues(&ps)["password"]
|
||||
|
@ -48,27 +48,28 @@ func (ap *PluginProvisioner) apply(ctx context.Context, cfg *pluginsAsConfig) er
|
||||
app.OrgID = 1
|
||||
}
|
||||
|
||||
query := &models.GetPluginSettingByIdQuery{OrgId: app.OrgID, PluginId: app.PluginID}
|
||||
err := ap.pluginSettings.GetPluginSettingById(ctx, query)
|
||||
ps, err := ap.pluginSettings.GetPluginSettingByPluginID(ctx, &pluginsettings.GetByPluginIDArgs{
|
||||
OrgID: app.OrgID,
|
||||
PluginID: app.PluginID,
|
||||
})
|
||||
if err != nil {
|
||||
if !errors.Is(err, models.ErrPluginSettingNotFound) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
app.PluginVersion = query.Result.PluginVersion
|
||||
app.PluginVersion = ps.PluginVersion
|
||||
}
|
||||
|
||||
ap.log.Info("Updating app from configuration ", "type", app.PluginID, "enabled", app.Enabled)
|
||||
cmd := &models.UpdatePluginSettingCmd{
|
||||
OrgId: app.OrgID,
|
||||
PluginId: app.PluginID,
|
||||
if err := ap.pluginSettings.UpdatePluginSetting(ctx, &pluginsettings.UpdateArgs{
|
||||
OrgID: app.OrgID,
|
||||
PluginID: app.PluginID,
|
||||
Enabled: app.Enabled,
|
||||
Pinned: app.Pinned,
|
||||
JsonData: app.JSONData,
|
||||
SecureJsonData: app.SecureJSONData,
|
||||
JSONData: app.JSONData,
|
||||
SecureJSONData: app.SecureJSONData,
|
||||
PluginVersion: app.PluginVersion,
|
||||
}
|
||||
if err := ap.pluginSettings.UpdatePluginSetting(ctx, cmd); err != nil {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -25,8 +27,8 @@ func TestPluginProvisioner(t *testing.T) {
|
||||
Apps: []*appFromConfig{
|
||||
{PluginID: "test-plugin", OrgID: 2, Enabled: true},
|
||||
{PluginID: "test-plugin-2", OrgID: 3, Enabled: false},
|
||||
{PluginID: "test-plugin", OrgName: "Org 4", Enabled: true},
|
||||
{PluginID: "test-plugin-2", OrgID: 1, Enabled: true},
|
||||
{PluginID: "test-plugin", OrgName: "Org 4", Enabled: true, SecureJSONData: map[string]string{"token": "secret"}},
|
||||
{PluginID: "test-plugin-2", OrgID: 1, Enabled: true, JSONData: map[string]interface{}{"test": true}},
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -36,27 +38,31 @@ func TestPluginProvisioner(t *testing.T) {
|
||||
|
||||
err := ap.applyChanges(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, store.sentCommands, 4)
|
||||
require.Len(t, store.updateRequests, 4)
|
||||
|
||||
testCases := []struct {
|
||||
ExpectedPluginID string
|
||||
ExpectedOrgID int64
|
||||
ExpectedEnabled bool
|
||||
ExpectedPluginVersion string
|
||||
ExpectedPluginID string
|
||||
ExpectedOrgID int64
|
||||
ExpectedEnabled bool
|
||||
ExpectedPluginVersion string
|
||||
ExpectedJSONData map[string]interface{}
|
||||
ExpectedSecureJSONData map[string]string
|
||||
}{
|
||||
{ExpectedPluginID: "test-plugin", ExpectedOrgID: 2, ExpectedEnabled: true, ExpectedPluginVersion: "2.0.1"},
|
||||
{ExpectedPluginID: "test-plugin-2", ExpectedOrgID: 3, ExpectedEnabled: false},
|
||||
{ExpectedPluginID: "test-plugin", ExpectedOrgID: 4, ExpectedEnabled: true},
|
||||
{ExpectedPluginID: "test-plugin-2", ExpectedOrgID: 1, ExpectedEnabled: true},
|
||||
{ExpectedPluginID: "test-plugin", ExpectedOrgID: 4, ExpectedEnabled: true, ExpectedSecureJSONData: map[string]string{"token": "secret"}},
|
||||
{ExpectedPluginID: "test-plugin-2", ExpectedOrgID: 1, ExpectedEnabled: true, ExpectedJSONData: map[string]interface{}{"test": true}},
|
||||
}
|
||||
|
||||
for index, tc := range testCases {
|
||||
cmd := store.sentCommands[index]
|
||||
cmd := store.updateRequests[index]
|
||||
require.NotNil(t, cmd)
|
||||
require.Equal(t, tc.ExpectedPluginID, cmd.PluginId)
|
||||
require.Equal(t, tc.ExpectedOrgID, cmd.OrgId)
|
||||
require.Equal(t, tc.ExpectedPluginID, cmd.PluginID)
|
||||
require.Equal(t, tc.ExpectedOrgID, cmd.OrgID)
|
||||
require.Equal(t, tc.ExpectedEnabled, cmd.Enabled)
|
||||
require.Equal(t, tc.ExpectedPluginVersion, cmd.PluginVersion)
|
||||
require.Equal(t, tc.ExpectedJSONData, cmd.JSONData)
|
||||
require.Equal(t, tc.ExpectedSecureJSONData, cmd.SecureJSONData)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -66,45 +72,44 @@ type testConfigReader struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (tcr *testConfigReader) readConfig(ctx context.Context, path string) ([]*pluginsAsConfig, error) {
|
||||
func (tcr *testConfigReader) readConfig(_ context.Context, _ string) ([]*pluginsAsConfig, error) {
|
||||
return tcr.result, tcr.err
|
||||
}
|
||||
|
||||
type mockStore struct {
|
||||
sentCommands []*models.UpdatePluginSettingCmd
|
||||
updateRequests []*pluginsettings.UpdateArgs
|
||||
}
|
||||
|
||||
func (m *mockStore) GetOrgByNameHandler(ctx context.Context, query *models.GetOrgByNameQuery) error {
|
||||
func (m *mockStore) GetOrgByNameHandler(_ context.Context, query *models.GetOrgByNameQuery) error {
|
||||
if query.Name == "Org 4" {
|
||||
query.Result = &models.Org{Id: 4}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockStore) GetPluginSettingById(ctx context.Context, query *models.GetPluginSettingByIdQuery) error {
|
||||
if query.PluginId == "test-plugin" && query.OrgId == 2 {
|
||||
query.Result = &models.PluginSetting{
|
||||
func (m *mockStore) GetPluginSettingByPluginID(_ context.Context, args *pluginsettings.GetByPluginIDArgs) (*pluginsettings.DTO, error) {
|
||||
if args.PluginID == "test-plugin" && args.OrgID == 2 {
|
||||
return &pluginsettings.DTO{
|
||||
PluginVersion: "2.0.1",
|
||||
}
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
return models.ErrPluginSettingNotFound
|
||||
return nil, models.ErrPluginSettingNotFound
|
||||
}
|
||||
|
||||
func (m *mockStore) UpdatePluginSetting(_ context.Context, cmd *models.UpdatePluginSettingCmd) error {
|
||||
m.sentCommands = append(m.sentCommands, cmd)
|
||||
func (m *mockStore) UpdatePluginSetting(_ context.Context, args *pluginsettings.UpdateArgs) error {
|
||||
m.updateRequests = append(m.updateRequests, args)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockStore) UpdatePluginSettingVersion(_ context.Context, _ *models.UpdatePluginSettingVersionCmd) error {
|
||||
func (m *mockStore) UpdatePluginSettingPluginVersion(_ context.Context, _ *pluginsettings.UpdatePluginVersionArgs) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockStore) GetPluginSettings(_ context.Context, _ int64) ([]*models.PluginSettingInfoDTO, error) {
|
||||
func (m *mockStore) GetPluginSettings(_ context.Context, _ *pluginsettings.GetArgs) ([]*pluginsettings.DTO, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockStore) DecryptedValues(_ *models.PluginSetting) map[string]string {
|
||||
func (m *mockStore) DecryptedValues(_ *pluginsettings.DTO) map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ func (m *SQLStoreMock) PatchPreferences(ctx context.Context, cmd *models.PatchPr
|
||||
return m.ExpectedError
|
||||
}
|
||||
|
||||
func (m *SQLStoreMock) GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error) {
|
||||
func (m *SQLStoreMock) GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSetting, error) {
|
||||
return nil, m.ExpectedError
|
||||
}
|
||||
|
||||
|
@ -7,25 +7,22 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func (ss *SQLStore) GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error) {
|
||||
sql := `SELECT org_id, plugin_id, enabled, pinned, plugin_version
|
||||
FROM plugin_setting `
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
if orgID != 0 {
|
||||
sql += "WHERE org_id=?"
|
||||
params = append(params, orgID)
|
||||
}
|
||||
|
||||
var rslt []*models.PluginSettingInfoDTO
|
||||
func (ss *SQLStore) GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSetting, error) {
|
||||
var rslt = make([]*models.PluginSetting, 0)
|
||||
err := ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
return sess.SQL(sql, params...).Find(&rslt)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if orgID != 0 {
|
||||
sess.Where("org_id", orgID)
|
||||
}
|
||||
|
||||
return rslt, nil
|
||||
err := sess.Find(&rslt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return rslt, err
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetPluginSettingById(ctx context.Context, query *models.GetPluginSettingByIdQuery) error {
|
||||
|
@ -64,7 +64,7 @@ type Store interface {
|
||||
GetPreferences(ctx context.Context, query *models.GetPreferencesQuery) error
|
||||
SavePreferences(ctx context.Context, cmd *models.SavePreferencesCommand) error
|
||||
PatchPreferences(ctx context.Context, cmd *models.PatchPreferencesCommand) error
|
||||
GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error)
|
||||
GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSetting, error)
|
||||
GetPluginSettingById(ctx context.Context, query *models.GetPluginSettingByIdQuery) error
|
||||
UpdatePluginSetting(ctx context.Context, cmd *models.UpdatePluginSettingCmd) error
|
||||
UpdatePluginSettingVersion(ctx context.Context, cmd *models.UpdatePluginSettingVersionCmd) error
|
||||
|
Loading…
Reference in New Issue
Block a user