backend/sqlstore split: move dashboard snapshot funcs to dashboardsnapshotservice (#50727)

* backend/sqlstore split: move dashboard snapshot funcs to dashboardsnapshotservice

This commit moves the dashboard snapshot related sql functions in the dashboardsnapshots service. I split the dashboards package up so the interfaces live in dashboarsnapshots and the store and service implementations are in their own packages. This took some minor refactoring, but none of the actual underlying code has changed, just where it lives.
pull/50824/head
Kristin Laemmert 4 years ago committed by GitHub
parent 65a5ac462a
commit 08c7a54c47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      pkg/api/dashboard_snapshot.go
  2. 5
      pkg/api/dashboard_snapshot_test.go
  3. 6
      pkg/api/http_server.go
  4. 2
      pkg/server/backgroundsvcs/background_services.go
  5. 8
      pkg/server/wire.go
  6. 35
      pkg/services/cleanup/cleanup.go
  7. 75
      pkg/services/dashboardsnapshots/dashboardsnapshots.go
  8. 42
      pkg/services/dashboardsnapshots/database/database.go
  9. 51
      pkg/services/dashboardsnapshots/database/database_test.go
  10. 15
      pkg/services/dashboardsnapshots/service.go
  11. 78
      pkg/services/dashboardsnapshots/service/service.go
  12. 16
      pkg/services/dashboardsnapshots/service/service_test.go
  13. 15
      pkg/services/dashboardsnapshots/store.go
  14. 1
      pkg/services/sqlstore/dashboard_provisioning.go
  15. 5
      pkg/services/sqlstore/store.go

@ -133,7 +133,7 @@ func (hs *HTTPServer) CreateDashboardSnapshot(c *models.ReqContext) response.Res
metrics.MApiDashboardSnapshotCreate.Inc() metrics.MApiDashboardSnapshotCreate.Inc()
} }
if err := hs.DashboardsnapshotsService.CreateDashboardSnapshot(c.Req.Context(), &cmd); err != nil { if err := hs.dashboardsnapshotsService.CreateDashboardSnapshot(c.Req.Context(), &cmd); err != nil {
c.JsonApiErr(500, "Failed to create snapshot", err) c.JsonApiErr(500, "Failed to create snapshot", err)
return nil return nil
} }
@ -157,7 +157,7 @@ func (hs *HTTPServer) GetDashboardSnapshot(c *models.ReqContext) response.Respon
query := &models.GetDashboardSnapshotQuery{Key: key} query := &models.GetDashboardSnapshotQuery{Key: key}
err := hs.DashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query) err := hs.dashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query)
if err != nil { if err != nil {
return response.Error(500, "Failed to get dashboard snapshot", err) return response.Error(500, "Failed to get dashboard snapshot", err)
} }
@ -224,7 +224,7 @@ func (hs *HTTPServer) DeleteDashboardSnapshotByDeleteKey(c *models.ReqContext) r
} }
query := &models.GetDashboardSnapshotQuery{DeleteKey: key} query := &models.GetDashboardSnapshotQuery{DeleteKey: key}
err := hs.DashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query) err := hs.dashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query)
if err != nil { if err != nil {
return response.Error(500, "Failed to get dashboard snapshot", err) return response.Error(500, "Failed to get dashboard snapshot", err)
} }
@ -238,7 +238,7 @@ func (hs *HTTPServer) DeleteDashboardSnapshotByDeleteKey(c *models.ReqContext) r
cmd := &models.DeleteDashboardSnapshotCommand{DeleteKey: query.Result.DeleteKey} cmd := &models.DeleteDashboardSnapshotCommand{DeleteKey: query.Result.DeleteKey}
if err := hs.DashboardsnapshotsService.DeleteDashboardSnapshot(c.Req.Context(), cmd); err != nil { if err := hs.dashboardsnapshotsService.DeleteDashboardSnapshot(c.Req.Context(), cmd); err != nil {
return response.Error(500, "Failed to delete dashboard snapshot", err) return response.Error(500, "Failed to delete dashboard snapshot", err)
} }
@ -257,7 +257,7 @@ func (hs *HTTPServer) DeleteDashboardSnapshot(c *models.ReqContext) response.Res
query := &models.GetDashboardSnapshotQuery{Key: key} query := &models.GetDashboardSnapshotQuery{Key: key}
err := hs.DashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query) err := hs.dashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query)
if err != nil { if err != nil {
return response.Error(500, "Failed to get dashboard snapshot", err) return response.Error(500, "Failed to get dashboard snapshot", err)
} }
@ -286,7 +286,7 @@ func (hs *HTTPServer) DeleteDashboardSnapshot(c *models.ReqContext) response.Res
cmd := &models.DeleteDashboardSnapshotCommand{DeleteKey: query.Result.DeleteKey} cmd := &models.DeleteDashboardSnapshotCommand{DeleteKey: query.Result.DeleteKey}
if err := hs.DashboardsnapshotsService.DeleteDashboardSnapshot(c.Req.Context(), cmd); err != nil { if err := hs.dashboardsnapshotsService.DeleteDashboardSnapshot(c.Req.Context(), cmd); err != nil {
return response.Error(500, "Failed to delete dashboard snapshot", err) return response.Error(500, "Failed to delete dashboard snapshot", err)
} }
@ -312,7 +312,7 @@ func (hs *HTTPServer) SearchDashboardSnapshots(c *models.ReqContext) response.Re
SignedInUser: c.SignedInUser, SignedInUser: c.SignedInUser,
} }
err := hs.DashboardsnapshotsService.SearchDashboardSnapshots(c.Req.Context(), &searchQuery) err := hs.dashboardsnapshotsService.SearchDashboardSnapshots(c.Req.Context(), &searchQuery)
if err != nil { if err != nil {
return response.Error(500, "Search failed", err) return response.Error(500, "Search failed", err)
} }

@ -15,7 +15,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboardsnapshots" dashboardsnapshots "github.com/grafana/grafana/pkg/services/dashboardsnapshots/service"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore" "github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
) )
@ -35,7 +35,8 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
sqlmock := mockstore.NewSQLStoreMock() sqlmock := mockstore.NewSQLStoreMock()
dashSvc := &dashboards.FakeDashboardService{} dashSvc := &dashboards.FakeDashboardService{}
dashSvc.On("GetDashboardAclInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardAclInfoListQuery")).Return(nil) dashSvc.On("GetDashboardAclInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardAclInfoListQuery")).Return(nil)
hs := &HTTPServer{DashboardsnapshotsService: &dashboardsnapshots.Service{SQLStore: sqlmock}} dashSnapSvc := dashboardsnapshots.ProvideService(sqlmock, nil)
hs := &HTTPServer{dashboardsnapshotsService: dashSnapSvc}
setUpSnapshotTest := func(t *testing.T) *models.DashboardSnapshot { setUpSnapshotTest := func(t *testing.T) *models.DashboardSnapshot {
t.Helper() t.Helper()

@ -151,7 +151,7 @@ type HTTPServer struct {
DatasourcePermissionsService permissions.DatasourcePermissionsService DatasourcePermissionsService permissions.DatasourcePermissionsService
commentsService *comments.Service commentsService *comments.Service
AlertNotificationService *alerting.AlertNotificationService AlertNotificationService *alerting.AlertNotificationService
DashboardsnapshotsService *dashboardsnapshots.Service dashboardsnapshotsService dashboardsnapshots.Service
PluginSettings *pluginSettings.Service PluginSettings *pluginSettings.Service
AvatarCacheServer *avatar.AvatarCacheServer AvatarCacheServer *avatar.AvatarCacheServer
preferenceService pref.Service preferenceService pref.Service
@ -191,7 +191,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService, notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService,
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService dashboards.FolderService, dashboardProvisioningService dashboards.DashboardProvisioningService, folderService dashboards.FolderService,
datasourcePermissionsService permissions.DatasourcePermissionsService, alertNotificationService *alerting.AlertNotificationService, datasourcePermissionsService permissions.DatasourcePermissionsService, alertNotificationService *alerting.AlertNotificationService,
dashboardsnapshotsService *dashboardsnapshots.Service, commentsService *comments.Service, pluginSettings *pluginSettings.Service, dashboardsnapshotsService dashboardsnapshots.Service, commentsService *comments.Service, pluginSettings *pluginSettings.Service,
avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service, entityEventsService store.EntityEventsService, avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service, entityEventsService store.EntityEventsService,
teamsPermissionsService accesscontrol.TeamPermissionsService, folderPermissionsService accesscontrol.FolderPermissionsService, teamsPermissionsService accesscontrol.TeamPermissionsService, folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardVersionService dashver.Service, dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardVersionService dashver.Service,
@ -266,7 +266,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
commentsService: commentsService, commentsService: commentsService,
teamPermissionsService: teamsPermissionsService, teamPermissionsService: teamsPermissionsService,
AlertNotificationService: alertNotificationService, AlertNotificationService: alertNotificationService,
DashboardsnapshotsService: dashboardsnapshotsService, dashboardsnapshotsService: dashboardsnapshotsService,
PluginSettings: pluginSettings, PluginSettings: pluginSettings,
AvatarCacheServer: avatarCacheServer, AvatarCacheServer: avatarCacheServer,
preferenceService: preferenceService, preferenceService: preferenceService,

@ -39,7 +39,7 @@ func ProvideBackgroundServiceRegistry(
secretsService *secretsManager.SecretsService, remoteCache *remotecache.RemoteCache, secretsService *secretsManager.SecretsService, remoteCache *remotecache.RemoteCache,
thumbnailsService thumbs.Service, StorageService store.StorageService, searchService searchV2.SearchService, entityEventsService store.EntityEventsService, thumbnailsService thumbs.Service, StorageService store.StorageService, searchService searchV2.SearchService, entityEventsService store.EntityEventsService,
// Need to make sure these are initialized, is there a better place to put them? // Need to make sure these are initialized, is there a better place to put them?
_ *dashboardsnapshots.Service, _ *alerting.AlertNotificationService, _ dashboardsnapshots.Service, _ *alerting.AlertNotificationService,
_ serviceaccounts.Service, _ *guardian.Provider, _ serviceaccounts.Service, _ *guardian.Provider,
_ *plugindashboardsservice.DashboardUpdater, _ *plugindashboardsservice.DashboardUpdater,
) *BackgroundServiceRegistry { ) *BackgroundServiceRegistry {

@ -6,6 +6,7 @@ package server
import ( import (
"github.com/google/wire" "github.com/google/wire"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/api" "github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/avatar" "github.com/grafana/grafana/pkg/api/avatar"
"github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/api/routing"
@ -49,6 +50,8 @@ import (
dashboardstore "github.com/grafana/grafana/pkg/services/dashboards/database" dashboardstore "github.com/grafana/grafana/pkg/services/dashboards/database"
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service" dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
"github.com/grafana/grafana/pkg/services/dashboardsnapshots" "github.com/grafana/grafana/pkg/services/dashboardsnapshots"
dashsnapstore "github.com/grafana/grafana/pkg/services/dashboardsnapshots/database"
dashsnapsvc "github.com/grafana/grafana/pkg/services/dashboardsnapshots/service"
"github.com/grafana/grafana/pkg/services/dashboardversion/dashverimpl" "github.com/grafana/grafana/pkg/services/dashboardversion/dashverimpl"
"github.com/grafana/grafana/pkg/services/datasourceproxy" "github.com/grafana/grafana/pkg/services/datasourceproxy"
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
@ -217,7 +220,10 @@ var wireBasicSet = wire.NewSet(
secretsDatabase.ProvideSecretsStore, secretsDatabase.ProvideSecretsStore,
wire.Bind(new(secrets.Store), new(*secretsDatabase.SecretsStoreImpl)), wire.Bind(new(secrets.Store), new(*secretsDatabase.SecretsStoreImpl)),
grafanads.ProvideService, grafanads.ProvideService,
dashboardsnapshots.ProvideService, wire.Bind(new(dashboardsnapshots.Store), new(*dashsnapstore.DashboardSnapshotStore)),
dashsnapstore.ProvideStore,
wire.Bind(new(dashboardsnapshots.Service), new(*dashsnapsvc.ServiceImpl)),
dashsnapsvc.ProvideService,
datasourceservice.ProvideService, datasourceservice.ProvideService,
wire.Bind(new(datasources.DataSourceService), new(*datasourceservice.Service)), wire.Bind(new(datasources.DataSourceService), new(*datasourceservice.Service)),
pluginSettings.ProvideService, pluginSettings.ProvideService,

@ -8,6 +8,7 @@ import (
"path" "path"
"time" "time"
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion" dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/queryhistory" "github.com/grafana/grafana/pkg/services/queryhistory"
"github.com/grafana/grafana/pkg/services/shorturls" "github.com/grafana/grafana/pkg/services/shorturls"
@ -22,27 +23,29 @@ import (
func ProvideService(cfg *setting.Cfg, serverLockService *serverlock.ServerLockService, func ProvideService(cfg *setting.Cfg, serverLockService *serverlock.ServerLockService,
shortURLService shorturls.Service, store sqlstore.Store, queryHistoryService queryhistory.Service, shortURLService shorturls.Service, store sqlstore.Store, queryHistoryService queryhistory.Service,
dashboardVersionService dashver.Service) *CleanUpService { dashboardVersionService dashver.Service, dashSnapSvc dashboardsnapshots.Service) *CleanUpService {
s := &CleanUpService{ s := &CleanUpService{
Cfg: cfg, Cfg: cfg,
ServerLockService: serverLockService, ServerLockService: serverLockService,
ShortURLService: shortURLService, ShortURLService: shortURLService,
QueryHistoryService: queryHistoryService, QueryHistoryService: queryHistoryService,
store: store, store: store,
log: log.New("cleanup"), log: log.New("cleanup"),
dashboardVersionService: dashboardVersionService, dashboardVersionService: dashboardVersionService,
dashboardSnapshotService: dashSnapSvc,
} }
return s return s
} }
type CleanUpService struct { type CleanUpService struct {
log log.Logger log log.Logger
store sqlstore.Store store sqlstore.Store
Cfg *setting.Cfg Cfg *setting.Cfg
ServerLockService *serverlock.ServerLockService ServerLockService *serverlock.ServerLockService
ShortURLService shorturls.Service ShortURLService shorturls.Service
QueryHistoryService queryhistory.Service QueryHistoryService queryhistory.Service
dashboardVersionService dashver.Service dashboardVersionService dashver.Service
dashboardSnapshotService dashboardsnapshots.Service
} }
func (srv *CleanUpService) Run(ctx context.Context) error { func (srv *CleanUpService) Run(ctx context.Context) error {
@ -137,7 +140,7 @@ func (srv *CleanUpService) shouldCleanupTempFile(filemtime time.Time, now time.T
func (srv *CleanUpService) deleteExpiredSnapshots(ctx context.Context) { func (srv *CleanUpService) deleteExpiredSnapshots(ctx context.Context) {
cmd := models.DeleteExpiredSnapshotsCommand{} cmd := models.DeleteExpiredSnapshotsCommand{}
if err := srv.store.DeleteExpiredSnapshots(ctx, &cmd); err != nil { if err := srv.dashboardSnapshotService.DeleteExpiredSnapshots(ctx, &cmd); err != nil {
srv.log.Error("Failed to delete expired snapshots", "error", err.Error()) srv.log.Error("Failed to delete expired snapshots", "error", err.Error())
} else { } else {
srv.log.Debug("Deleted expired snapshots", "rows affected", cmd.DeletedRows) srv.log.Debug("Deleted expired snapshots", "rows affected", cmd.DeletedRows)

@ -1,75 +0,0 @@
package dashboardsnapshots
import (
"context"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
type Service struct {
SQLStore sqlstore.Store
SecretsService secrets.Service
}
func ProvideService(store sqlstore.Store, secretsService secrets.Service) *Service {
s := &Service{
SQLStore: store,
SecretsService: secretsService,
}
return s
}
func (s *Service) CreateDashboardSnapshot(ctx context.Context, cmd *models.CreateDashboardSnapshotCommand) error {
marshalledData, err := cmd.Dashboard.Encode()
if err != nil {
return err
}
encryptedDashboard, err := s.SecretsService.Encrypt(ctx, marshalledData, secrets.WithoutScope())
if err != nil {
return err
}
cmd.DashboardEncrypted = encryptedDashboard
return s.SQLStore.CreateDashboardSnapshot(ctx, cmd)
}
func (s *Service) GetDashboardSnapshot(ctx context.Context, query *models.GetDashboardSnapshotQuery) error {
err := s.SQLStore.GetDashboardSnapshot(ctx, query)
if err != nil {
return err
}
if query.Result.DashboardEncrypted != nil {
decryptedDashboard, err := s.SecretsService.Decrypt(ctx, query.Result.DashboardEncrypted)
if err != nil {
return err
}
dashboard, err := simplejson.NewJson(decryptedDashboard)
if err != nil {
return err
}
query.Result.Dashboard = dashboard
}
return err
}
func (s *Service) DeleteDashboardSnapshot(ctx context.Context, cmd *models.DeleteDashboardSnapshotCommand) error {
return s.SQLStore.DeleteDashboardSnapshot(ctx, cmd)
}
func (s *Service) SearchDashboardSnapshots(ctx context.Context, query *models.GetDashboardSnapshotsQuery) error {
return s.SQLStore.SearchDashboardSnapshots(ctx, query)
}
func (s *Service) DeleteExpiredSnapshots(ctx context.Context, cmd *models.DeleteExpiredSnapshotsCommand) error {
return s.SQLStore.DeleteExpiredSnapshots(ctx, cmd)
}

@ -1,21 +1,37 @@
package sqlstore package database
import ( import (
"context" "context"
"time" "time"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/db"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
type DashboardSnapshotStore struct {
store db.DB
log log.Logger
}
// DashboardStore implements the Store interface
var _ dashboardsnapshots.Store = (*DashboardSnapshotStore)(nil)
func ProvideStore(db db.DB) *DashboardSnapshotStore {
return &DashboardSnapshotStore{store: db, log: log.New("dashboardsnapshot.store")}
}
// DeleteExpiredSnapshots removes snapshots with old expiry dates. // DeleteExpiredSnapshots removes snapshots with old expiry dates.
// SnapShotRemoveExpired is deprecated and should be removed in the future. // SnapShotRemoveExpired is deprecated and should be removed in the future.
// Snapshot expiry is decided by the user when they share the snapshot. // Snapshot expiry is decided by the user when they share the snapshot.
func (ss *SQLStore) DeleteExpiredSnapshots(ctx context.Context, cmd *models.DeleteExpiredSnapshotsCommand) error { func (d *DashboardSnapshotStore) DeleteExpiredSnapshots(ctx context.Context, cmd *models.DeleteExpiredSnapshotsCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error { return d.store.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
if !setting.SnapShotRemoveExpired { if !setting.SnapShotRemoveExpired {
sqlog.Warn("[Deprecated] The snapshot_remove_expired setting is outdated. Please remove from your config.") d.log.Warn("[Deprecated] The snapshot_remove_expired setting is outdated. Please remove from your config.")
return nil return nil
} }
@ -30,8 +46,8 @@ func (ss *SQLStore) DeleteExpiredSnapshots(ctx context.Context, cmd *models.Dele
}) })
} }
func (ss *SQLStore) CreateDashboardSnapshot(ctx context.Context, cmd *models.CreateDashboardSnapshotCommand) error { func (d *DashboardSnapshotStore) CreateDashboardSnapshot(ctx context.Context, cmd *models.CreateDashboardSnapshotCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error { return d.store.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
// never // never
var expires = time.Now().Add(time.Hour * 24 * 365 * 50) var expires = time.Now().Add(time.Hour * 24 * 365 * 50)
if cmd.Expires > 0 { if cmd.Expires > 0 {
@ -60,16 +76,16 @@ func (ss *SQLStore) CreateDashboardSnapshot(ctx context.Context, cmd *models.Cre
}) })
} }
func (ss *SQLStore) DeleteDashboardSnapshot(ctx context.Context, cmd *models.DeleteDashboardSnapshotCommand) error { func (d *DashboardSnapshotStore) DeleteDashboardSnapshot(ctx context.Context, cmd *models.DeleteDashboardSnapshotCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error { return d.store.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
var rawSQL = "DELETE FROM dashboard_snapshot WHERE delete_key=?" var rawSQL = "DELETE FROM dashboard_snapshot WHERE delete_key=?"
_, err := sess.Exec(rawSQL, cmd.DeleteKey) _, err := sess.Exec(rawSQL, cmd.DeleteKey)
return err return err
}) })
} }
func (ss *SQLStore) GetDashboardSnapshot(ctx context.Context, query *models.GetDashboardSnapshotQuery) error { func (d *DashboardSnapshotStore) GetDashboardSnapshot(ctx context.Context, query *models.GetDashboardSnapshotQuery) error {
return ss.WithDbSession(ctx, func(dbSess *DBSession) error { return d.store.WithDbSession(ctx, func(dbSess *sqlstore.DBSession) error {
snapshot := models.DashboardSnapshot{Key: query.Key, DeleteKey: query.DeleteKey} snapshot := models.DashboardSnapshot{Key: query.Key, DeleteKey: query.DeleteKey}
has, err := dbSess.Get(&snapshot) has, err := dbSess.Get(&snapshot)
@ -86,11 +102,9 @@ func (ss *SQLStore) GetDashboardSnapshot(ctx context.Context, query *models.GetD
// SearchDashboardSnapshots returns a list of all snapshots for admins // SearchDashboardSnapshots returns a list of all snapshots for admins
// for other roles, it returns snapshots created by the user // for other roles, it returns snapshots created by the user
func (ss *SQLStore) SearchDashboardSnapshots(ctx context.Context, query *models.GetDashboardSnapshotsQuery) error { func (d *DashboardSnapshotStore) SearchDashboardSnapshots(ctx context.Context, query *models.GetDashboardSnapshotsQuery) error {
return ss.WithDbSession(ctx, func(dbSess *DBSession) error { return d.store.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
var snapshots = make(models.DashboardSnapshotsList, 0) var snapshots = make(models.DashboardSnapshotsList, 0)
sess := ss.NewSession(ctx)
if query.Limit > 0 { if query.Limit > 0 {
sess.Limit(query.Limit) sess.Limit(query.Limit)
} }

@ -1,24 +1,27 @@
package sqlstore package database
import ( import (
"context" "context"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/secrets/fakes" "github.com/grafana/grafana/pkg/services/secrets/fakes"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) { func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("skipping integration test") t.Skip("skipping integration test")
} }
sqlstore := InitTestDB(t) sqlstore := sqlstore.InitTestDB(t)
dashStore := ProvideStore(sqlstore)
origSecret := setting.SecretKey origSecret := setting.SecretKey
setting.SecretKey = "dashboard_snapshot_testing" setting.SecretKey = "dashboard_snapshot_testing"
@ -42,12 +45,12 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
} }
err = sqlstore.CreateDashboardSnapshot(context.Background(), &cmd) err = dashStore.CreateDashboardSnapshot(context.Background(), &cmd)
require.NoError(t, err) require.NoError(t, err)
t.Run("Should be able to get snapshot by key", func(t *testing.T) { t.Run("Should be able to get snapshot by key", func(t *testing.T) {
query := models.GetDashboardSnapshotQuery{Key: "hej"} query := models.GetDashboardSnapshotQuery{Key: "hej"}
err := sqlstore.GetDashboardSnapshot(context.Background(), &query) err := dashStore.GetDashboardSnapshot(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
assert.NotNil(t, query.Result) assert.NotNil(t, query.Result)
@ -69,7 +72,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_ADMIN},
} }
err := sqlstore.SearchDashboardSnapshots(context.Background(), &query) err := dashStore.SearchDashboardSnapshots(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
t.Run("Should return all the snapshots", func(t *testing.T) { t.Run("Should return all the snapshots", func(t *testing.T) {
@ -83,7 +86,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR, UserId: 1000}, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR, UserId: 1000},
} }
err := sqlstore.SearchDashboardSnapshots(context.Background(), &query) err := dashStore.SearchDashboardSnapshots(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
t.Run("Should return all the snapshots", func(t *testing.T) { t.Run("Should return all the snapshots", func(t *testing.T) {
@ -97,7 +100,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR, UserId: 2}, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR, UserId: 2},
} }
err := sqlstore.SearchDashboardSnapshots(context.Background(), &query) err := dashStore.SearchDashboardSnapshots(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
t.Run("Should not return any snapshots", func(t *testing.T) { t.Run("Should not return any snapshots", func(t *testing.T) {
@ -116,7 +119,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) {
UserId: 0, UserId: 0,
OrgId: 1, OrgId: 1,
} }
err := sqlstore.CreateDashboardSnapshot(context.Background(), &cmd) err := dashStore.CreateDashboardSnapshot(context.Background(), &cmd)
require.NoError(t, err) require.NoError(t, err)
t.Run("Should not return any snapshots", func(t *testing.T) { t.Run("Should not return any snapshots", func(t *testing.T) {
@ -124,7 +127,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR, IsAnonymous: true, UserId: 0}, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR, IsAnonymous: true, UserId: 0},
} }
err := sqlstore.SearchDashboardSnapshots(context.Background(), &query) err := dashStore.SearchDashboardSnapshots(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, query.Result) require.NotNil(t, query.Result)
@ -148,36 +151,37 @@ func TestIntegrationDeleteExpiredSnapshots(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("skipping integration test") t.Skip("skipping integration test")
} }
sqlstore := InitTestDB(t) sqlstore := sqlstore.InitTestDB(t)
dashStore := ProvideStore(sqlstore)
t.Run("Testing dashboard snapshots clean up", func(t *testing.T) { t.Run("Testing dashboard snapshots clean up", func(t *testing.T) {
setting.SnapShotRemoveExpired = true setting.SnapShotRemoveExpired = true
nonExpiredSnapshot := createTestSnapshot(t, sqlstore, "key1", 48000) nonExpiredSnapshot := createTestSnapshot(t, dashStore, "key1", 48000)
createTestSnapshot(t, sqlstore, "key2", -1200) createTestSnapshot(t, dashStore, "key2", -1200)
createTestSnapshot(t, sqlstore, "key3", -1200) createTestSnapshot(t, dashStore, "key3", -1200)
err := sqlstore.DeleteExpiredSnapshots(context.Background(), &models.DeleteExpiredSnapshotsCommand{}) err := dashStore.DeleteExpiredSnapshots(context.Background(), &models.DeleteExpiredSnapshotsCommand{})
require.NoError(t, err) require.NoError(t, err)
query := models.GetDashboardSnapshotsQuery{ query := models.GetDashboardSnapshotsQuery{
OrgId: 1, OrgId: 1,
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_ADMIN},
} }
err = sqlstore.SearchDashboardSnapshots(context.Background(), &query) err = dashStore.SearchDashboardSnapshots(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
assert.Len(t, query.Result, 1) assert.Len(t, query.Result, 1)
assert.Equal(t, nonExpiredSnapshot.Key, query.Result[0].Key) assert.Equal(t, nonExpiredSnapshot.Key, query.Result[0].Key)
err = sqlstore.DeleteExpiredSnapshots(context.Background(), &models.DeleteExpiredSnapshotsCommand{}) err = dashStore.DeleteExpiredSnapshots(context.Background(), &models.DeleteExpiredSnapshotsCommand{})
require.NoError(t, err) require.NoError(t, err)
query = models.GetDashboardSnapshotsQuery{ query = models.GetDashboardSnapshotsQuery{
OrgId: 1, OrgId: 1,
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_ADMIN},
} }
err = sqlstore.SearchDashboardSnapshots(context.Background(), &query) err = dashStore.SearchDashboardSnapshots(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, query.Result, 1) require.Len(t, query.Result, 1)
@ -185,7 +189,7 @@ func TestIntegrationDeleteExpiredSnapshots(t *testing.T) {
}) })
} }
func createTestSnapshot(t *testing.T, sqlstore *SQLStore, key string, expires int64) *models.DashboardSnapshot { func createTestSnapshot(t *testing.T, dashStore *DashboardSnapshotStore, key string, expires int64) *models.DashboardSnapshot {
cmd := models.CreateDashboardSnapshotCommand{ cmd := models.CreateDashboardSnapshotCommand{
Key: key, Key: key,
DeleteKey: "delete" + key, DeleteKey: "delete" + key,
@ -196,13 +200,16 @@ func createTestSnapshot(t *testing.T, sqlstore *SQLStore, key string, expires in
OrgId: 1, OrgId: 1,
Expires: expires, Expires: expires,
} }
err := sqlstore.CreateDashboardSnapshot(context.Background(), &cmd) err := dashStore.CreateDashboardSnapshot(context.Background(), &cmd)
require.NoError(t, err) require.NoError(t, err)
// Set expiry date manually - to be able to create expired snapshots // Set expiry date manually - to be able to create expired snapshots
if expires < 0 { if expires < 0 {
expireDate := time.Now().Add(time.Second * time.Duration(expires)) expireDate := time.Now().Add(time.Second * time.Duration(expires))
_, err = sqlstore.engine.Exec("UPDATE dashboard_snapshot SET expires = ? WHERE id = ?", expireDate, cmd.Result.Id) err = dashStore.store.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
_, err := sess.Exec("UPDATE dashboard_snapshot SET expires = ? WHERE id = ?", expireDate, cmd.Result.Id)
return err
})
require.NoError(t, err) require.NoError(t, err)
} }

@ -0,0 +1,15 @@
package dashboardsnapshots
import (
"context"
"github.com/grafana/grafana/pkg/models"
)
type Service interface {
CreateDashboardSnapshot(context.Context, *models.CreateDashboardSnapshotCommand) error
DeleteDashboardSnapshot(context.Context, *models.DeleteDashboardSnapshotCommand) error
DeleteExpiredSnapshots(context.Context, *models.DeleteExpiredSnapshotsCommand) error
GetDashboardSnapshot(context.Context, *models.GetDashboardSnapshotQuery) error
SearchDashboardSnapshots(context.Context, *models.GetDashboardSnapshotsQuery) error
}

@ -0,0 +1,78 @@
package service
import (
"context"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
"github.com/grafana/grafana/pkg/services/secrets"
)
type ServiceImpl struct {
store dashboardsnapshots.Store
secretsService secrets.Service
}
// ServiceImpl implements the dashboardsnapshots Service interface
var _ dashboardsnapshots.Service = (*ServiceImpl)(nil)
func ProvideService(store dashboardsnapshots.Store, secretsService secrets.Service) *ServiceImpl {
s := &ServiceImpl{
store: store,
secretsService: secretsService,
}
return s
}
func (s *ServiceImpl) CreateDashboardSnapshot(ctx context.Context, cmd *models.CreateDashboardSnapshotCommand) error {
marshalledData, err := cmd.Dashboard.Encode()
if err != nil {
return err
}
encryptedDashboard, err := s.secretsService.Encrypt(ctx, marshalledData, secrets.WithoutScope())
if err != nil {
return err
}
cmd.DashboardEncrypted = encryptedDashboard
return s.store.CreateDashboardSnapshot(ctx, cmd)
}
func (s *ServiceImpl) GetDashboardSnapshot(ctx context.Context, query *models.GetDashboardSnapshotQuery) error {
err := s.store.GetDashboardSnapshot(ctx, query)
if err != nil {
return err
}
if query.Result.DashboardEncrypted != nil {
decryptedDashboard, err := s.secretsService.Decrypt(ctx, query.Result.DashboardEncrypted)
if err != nil {
return err
}
dashboard, err := simplejson.NewJson(decryptedDashboard)
if err != nil {
return err
}
query.Result.Dashboard = dashboard
}
return err
}
func (s *ServiceImpl) DeleteDashboardSnapshot(ctx context.Context, cmd *models.DeleteDashboardSnapshotCommand) error {
return s.store.DeleteDashboardSnapshot(ctx, cmd)
}
func (s *ServiceImpl) SearchDashboardSnapshots(ctx context.Context, query *models.GetDashboardSnapshotsQuery) error {
return s.store.SearchDashboardSnapshots(ctx, query)
}
func (s *ServiceImpl) DeleteExpiredSnapshots(ctx context.Context, cmd *models.DeleteExpiredSnapshotsCommand) error {
return s.store.DeleteExpiredSnapshots(ctx, cmd)
}

@ -1,27 +1,25 @@
package dashboardsnapshots package service
import ( import (
"context" "context"
"testing" "testing"
"github.com/grafana/grafana/pkg/services/secrets/database" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
dashsnapdb "github.com/grafana/grafana/pkg/services/dashboardsnapshots/database"
"github.com/grafana/grafana/pkg/services/secrets/database"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
) )
func TestDashboardSnapshotsService(t *testing.T) { func TestDashboardSnapshotsService(t *testing.T) {
sqlStore := sqlstore.InitTestDB(t) sqlStore := sqlstore.InitTestDB(t)
dsStore := dashsnapdb.ProvideStore(sqlStore)
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore)) secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
s := ProvideService(dsStore, secretsService)
s := &Service{
SQLStore: sqlStore,
SecretsService: secretsService,
}
origSecret := setting.SecretKey origSecret := setting.SecretKey
setting.SecretKey = "dashboard_snapshot_service_test" setting.SecretKey = "dashboard_snapshot_service_test"
@ -47,7 +45,7 @@ func TestDashboardSnapshotsService(t *testing.T) {
err = s.CreateDashboardSnapshot(ctx, &cmd) err = s.CreateDashboardSnapshot(ctx, &cmd)
require.NoError(t, err) require.NoError(t, err)
decrypted, err := s.SecretsService.Decrypt(ctx, cmd.Result.DashboardEncrypted) decrypted, err := s.secretsService.Decrypt(ctx, cmd.Result.DashboardEncrypted)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, rawDashboard, decrypted) require.Equal(t, rawDashboard, decrypted)

@ -0,0 +1,15 @@
package dashboardsnapshots
import (
"context"
"github.com/grafana/grafana/pkg/models"
)
type Store interface {
CreateDashboardSnapshot(context.Context, *models.CreateDashboardSnapshotCommand) error
DeleteDashboardSnapshot(context.Context, *models.DeleteDashboardSnapshotCommand) error
DeleteExpiredSnapshots(context.Context, *models.DeleteExpiredSnapshotsCommand) error
GetDashboardSnapshot(context.Context, *models.GetDashboardSnapshotQuery) error
SearchDashboardSnapshots(context.Context, *models.GetDashboardSnapshotsQuery) error
}

@ -12,11 +12,6 @@ type Store interface {
GetDataSourceStats(ctx context.Context, query *models.GetDataSourceStatsQuery) error GetDataSourceStats(ctx context.Context, query *models.GetDataSourceStatsQuery) error
GetDataSourceAccessStats(ctx context.Context, query *models.GetDataSourceAccessStatsQuery) error GetDataSourceAccessStats(ctx context.Context, query *models.GetDataSourceAccessStatsQuery) error
GetSystemStats(ctx context.Context, query *models.GetSystemStatsQuery) error GetSystemStats(ctx context.Context, query *models.GetSystemStatsQuery) error
DeleteExpiredSnapshots(ctx context.Context, cmd *models.DeleteExpiredSnapshotsCommand) error
CreateDashboardSnapshot(ctx context.Context, cmd *models.CreateDashboardSnapshotCommand) error
DeleteDashboardSnapshot(ctx context.Context, cmd *models.DeleteDashboardSnapshotCommand) error
GetDashboardSnapshot(ctx context.Context, query *models.GetDashboardSnapshotQuery) error
SearchDashboardSnapshots(ctx context.Context, query *models.GetDashboardSnapshotsQuery) error
GetOrgByName(name string) (*models.Org, error) GetOrgByName(name string) (*models.Org, error)
CreateOrg(ctx context.Context, cmd *models.CreateOrgCommand) error CreateOrg(ctx context.Context, cmd *models.CreateOrgCommand) error
CreateOrgWithMember(name string, userID int64) (models.Org, error) CreateOrgWithMember(name string, userID int64) (models.Org, error)

Loading…
Cancel
Save