From 08c7a54c47f96093d33e9d1ae38c0af4b23f17d4 Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Tue, 14 Jun 2022 13:41:29 -0400 Subject: [PATCH] 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. --- pkg/api/dashboard_snapshot.go | 14 ++-- pkg/api/dashboard_snapshot_test.go | 5 +- pkg/api/http_server.go | 6 +- .../backgroundsvcs/background_services.go | 2 +- pkg/server/wire.go | 8 +- pkg/services/cleanup/cleanup.go | 35 +++++---- .../dashboardsnapshots/dashboardsnapshots.go | 75 ------------------ .../database/database.go} | 42 ++++++---- .../database/database_test.go} | 51 ++++++------ pkg/services/dashboardsnapshots/service.go | 15 ++++ .../dashboardsnapshots/service/service.go | 78 +++++++++++++++++++ .../service_test.go} | 16 ++-- pkg/services/dashboardsnapshots/store.go | 15 ++++ .../sqlstore/dashboard_provisioning.go | 1 - pkg/services/sqlstore/store.go | 5 -- 15 files changed, 212 insertions(+), 156 deletions(-) delete mode 100644 pkg/services/dashboardsnapshots/dashboardsnapshots.go rename pkg/services/{sqlstore/dashboard_snapshot.go => dashboardsnapshots/database/database.go} (61%) rename pkg/services/{sqlstore/dashboard_snapshot_test.go => dashboardsnapshots/database/database_test.go} (76%) create mode 100644 pkg/services/dashboardsnapshots/service.go create mode 100644 pkg/services/dashboardsnapshots/service/service.go rename pkg/services/dashboardsnapshots/{dashboardsnapshots_test.go => service/service_test.go} (87%) create mode 100644 pkg/services/dashboardsnapshots/store.go delete mode 100644 pkg/services/sqlstore/dashboard_provisioning.go diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go index 93fa34cd385..c69140be74a 100644 --- a/pkg/api/dashboard_snapshot.go +++ b/pkg/api/dashboard_snapshot.go @@ -133,7 +133,7 @@ func (hs *HTTPServer) CreateDashboardSnapshot(c *models.ReqContext) response.Res 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) return nil } @@ -157,7 +157,7 @@ func (hs *HTTPServer) GetDashboardSnapshot(c *models.ReqContext) response.Respon query := &models.GetDashboardSnapshotQuery{Key: key} - err := hs.DashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query) + err := hs.dashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query) if err != nil { 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} - err := hs.DashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query) + err := hs.dashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query) if err != nil { 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} - 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) } @@ -257,7 +257,7 @@ func (hs *HTTPServer) DeleteDashboardSnapshot(c *models.ReqContext) response.Res query := &models.GetDashboardSnapshotQuery{Key: key} - err := hs.DashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query) + err := hs.dashboardsnapshotsService.GetDashboardSnapshot(c.Req.Context(), query) if err != nil { 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} - 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) } @@ -312,7 +312,7 @@ func (hs *HTTPServer) SearchDashboardSnapshots(c *models.ReqContext) response.Re SignedInUser: c.SignedInUser, } - err := hs.DashboardsnapshotsService.SearchDashboardSnapshots(c.Req.Context(), &searchQuery) + err := hs.dashboardsnapshotsService.SearchDashboardSnapshots(c.Req.Context(), &searchQuery) if err != nil { return response.Error(500, "Search failed", err) } diff --git a/pkg/api/dashboard_snapshot_test.go b/pkg/api/dashboard_snapshot_test.go index 538e49ed305..4634b9e68e9 100644 --- a/pkg/api/dashboard_snapshot_test.go +++ b/pkg/api/dashboard_snapshot_test.go @@ -15,7 +15,7 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" "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/sqlstore/mockstore" ) @@ -35,7 +35,8 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) { sqlmock := mockstore.NewSQLStoreMock() dashSvc := &dashboards.FakeDashboardService{} 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 { t.Helper() diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 5773f9a94b1..21415cf10ea 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -151,7 +151,7 @@ type HTTPServer struct { DatasourcePermissionsService permissions.DatasourcePermissionsService commentsService *comments.Service AlertNotificationService *alerting.AlertNotificationService - DashboardsnapshotsService *dashboardsnapshots.Service + dashboardsnapshotsService dashboardsnapshots.Service PluginSettings *pluginSettings.Service AvatarCacheServer *avatar.AvatarCacheServer preferenceService pref.Service @@ -191,7 +191,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService, dashboardProvisioningService dashboards.DashboardProvisioningService, folderService dashboards.FolderService, 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, teamsPermissionsService accesscontrol.TeamPermissionsService, folderPermissionsService accesscontrol.FolderPermissionsService, dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardVersionService dashver.Service, @@ -266,7 +266,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi commentsService: commentsService, teamPermissionsService: teamsPermissionsService, AlertNotificationService: alertNotificationService, - DashboardsnapshotsService: dashboardsnapshotsService, + dashboardsnapshotsService: dashboardsnapshotsService, PluginSettings: pluginSettings, AvatarCacheServer: avatarCacheServer, preferenceService: preferenceService, diff --git a/pkg/server/backgroundsvcs/background_services.go b/pkg/server/backgroundsvcs/background_services.go index d891a17c4ca..c6970da787b 100644 --- a/pkg/server/backgroundsvcs/background_services.go +++ b/pkg/server/backgroundsvcs/background_services.go @@ -39,7 +39,7 @@ func ProvideBackgroundServiceRegistry( secretsService *secretsManager.SecretsService, remoteCache *remotecache.RemoteCache, 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? - _ *dashboardsnapshots.Service, _ *alerting.AlertNotificationService, + _ dashboardsnapshots.Service, _ *alerting.AlertNotificationService, _ serviceaccounts.Service, _ *guardian.Provider, _ *plugindashboardsservice.DashboardUpdater, ) *BackgroundServiceRegistry { diff --git a/pkg/server/wire.go b/pkg/server/wire.go index 39c9ed81824..cf9d6eaa3eb 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -6,6 +6,7 @@ package server import ( "github.com/google/wire" sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana/pkg/api" "github.com/grafana/grafana/pkg/api/avatar" "github.com/grafana/grafana/pkg/api/routing" @@ -49,6 +50,8 @@ import ( dashboardstore "github.com/grafana/grafana/pkg/services/dashboards/database" dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service" "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/datasourceproxy" "github.com/grafana/grafana/pkg/services/datasources" @@ -217,7 +220,10 @@ var wireBasicSet = wire.NewSet( secretsDatabase.ProvideSecretsStore, wire.Bind(new(secrets.Store), new(*secretsDatabase.SecretsStoreImpl)), 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, wire.Bind(new(datasources.DataSourceService), new(*datasourceservice.Service)), pluginSettings.ProvideService, diff --git a/pkg/services/cleanup/cleanup.go b/pkg/services/cleanup/cleanup.go index 05d7ec0bfe0..6a9d9ab3f40 100644 --- a/pkg/services/cleanup/cleanup.go +++ b/pkg/services/cleanup/cleanup.go @@ -8,6 +8,7 @@ import ( "path" "time" + "github.com/grafana/grafana/pkg/services/dashboardsnapshots" dashver "github.com/grafana/grafana/pkg/services/dashboardversion" "github.com/grafana/grafana/pkg/services/queryhistory" "github.com/grafana/grafana/pkg/services/shorturls" @@ -22,27 +23,29 @@ import ( func ProvideService(cfg *setting.Cfg, serverLockService *serverlock.ServerLockService, shortURLService shorturls.Service, store sqlstore.Store, queryHistoryService queryhistory.Service, - dashboardVersionService dashver.Service) *CleanUpService { + dashboardVersionService dashver.Service, dashSnapSvc dashboardsnapshots.Service) *CleanUpService { s := &CleanUpService{ - Cfg: cfg, - ServerLockService: serverLockService, - ShortURLService: shortURLService, - QueryHistoryService: queryHistoryService, - store: store, - log: log.New("cleanup"), - dashboardVersionService: dashboardVersionService, + Cfg: cfg, + ServerLockService: serverLockService, + ShortURLService: shortURLService, + QueryHistoryService: queryHistoryService, + store: store, + log: log.New("cleanup"), + dashboardVersionService: dashboardVersionService, + dashboardSnapshotService: dashSnapSvc, } return s } type CleanUpService struct { - log log.Logger - store sqlstore.Store - Cfg *setting.Cfg - ServerLockService *serverlock.ServerLockService - ShortURLService shorturls.Service - QueryHistoryService queryhistory.Service - dashboardVersionService dashver.Service + log log.Logger + store sqlstore.Store + Cfg *setting.Cfg + ServerLockService *serverlock.ServerLockService + ShortURLService shorturls.Service + QueryHistoryService queryhistory.Service + dashboardVersionService dashver.Service + dashboardSnapshotService dashboardsnapshots.Service } 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) { 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()) } else { srv.log.Debug("Deleted expired snapshots", "rows affected", cmd.DeletedRows) diff --git a/pkg/services/dashboardsnapshots/dashboardsnapshots.go b/pkg/services/dashboardsnapshots/dashboardsnapshots.go deleted file mode 100644 index 001bf4354cd..00000000000 --- a/pkg/services/dashboardsnapshots/dashboardsnapshots.go +++ /dev/null @@ -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) -} diff --git a/pkg/services/sqlstore/dashboard_snapshot.go b/pkg/services/dashboardsnapshots/database/database.go similarity index 61% rename from pkg/services/sqlstore/dashboard_snapshot.go rename to pkg/services/dashboardsnapshots/database/database.go index 9773b795fa1..89b44438324 100644 --- a/pkg/services/sqlstore/dashboard_snapshot.go +++ b/pkg/services/dashboardsnapshots/database/database.go @@ -1,21 +1,37 @@ -package sqlstore +package database import ( "context" "time" "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/services/dashboardsnapshots" + "github.com/grafana/grafana/pkg/services/sqlstore" + "github.com/grafana/grafana/pkg/services/sqlstore/db" "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. // SnapShotRemoveExpired is deprecated and should be removed in the future. // Snapshot expiry is decided by the user when they share the snapshot. -func (ss *SQLStore) DeleteExpiredSnapshots(ctx context.Context, cmd *models.DeleteExpiredSnapshotsCommand) error { - return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error { +func (d *DashboardSnapshotStore) DeleteExpiredSnapshots(ctx context.Context, cmd *models.DeleteExpiredSnapshotsCommand) error { + return d.store.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { 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 } @@ -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 { - return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error { +func (d *DashboardSnapshotStore) CreateDashboardSnapshot(ctx context.Context, cmd *models.CreateDashboardSnapshotCommand) error { + return d.store.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { // never var expires = time.Now().Add(time.Hour * 24 * 365 * 50) 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 { - return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error { +func (d *DashboardSnapshotStore) DeleteDashboardSnapshot(ctx context.Context, cmd *models.DeleteDashboardSnapshotCommand) error { + return d.store.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { var rawSQL = "DELETE FROM dashboard_snapshot WHERE delete_key=?" _, err := sess.Exec(rawSQL, cmd.DeleteKey) return err }) } -func (ss *SQLStore) GetDashboardSnapshot(ctx context.Context, query *models.GetDashboardSnapshotQuery) error { - return ss.WithDbSession(ctx, func(dbSess *DBSession) error { +func (d *DashboardSnapshotStore) GetDashboardSnapshot(ctx context.Context, query *models.GetDashboardSnapshotQuery) error { + return d.store.WithDbSession(ctx, func(dbSess *sqlstore.DBSession) error { snapshot := models.DashboardSnapshot{Key: query.Key, DeleteKey: query.DeleteKey} 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 // for other roles, it returns snapshots created by the user -func (ss *SQLStore) SearchDashboardSnapshots(ctx context.Context, query *models.GetDashboardSnapshotsQuery) error { - return ss.WithDbSession(ctx, func(dbSess *DBSession) error { +func (d *DashboardSnapshotStore) SearchDashboardSnapshots(ctx context.Context, query *models.GetDashboardSnapshotsQuery) error { + return d.store.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { var snapshots = make(models.DashboardSnapshotsList, 0) - - sess := ss.NewSession(ctx) if query.Limit > 0 { sess.Limit(query.Limit) } diff --git a/pkg/services/sqlstore/dashboard_snapshot_test.go b/pkg/services/dashboardsnapshots/database/database_test.go similarity index 76% rename from pkg/services/sqlstore/dashboard_snapshot_test.go rename to pkg/services/dashboardsnapshots/database/database_test.go index 008dd8cbdc3..e4cd112ada7 100644 --- a/pkg/services/sqlstore/dashboard_snapshot_test.go +++ b/pkg/services/dashboardsnapshots/database/database_test.go @@ -1,24 +1,27 @@ -package sqlstore +package database import ( "context" "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "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/secrets/fakes" + "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - sqlstore := InitTestDB(t) + sqlstore := sqlstore.InitTestDB(t) + dashStore := ProvideStore(sqlstore) origSecret := setting.SecretKey setting.SecretKey = "dashboard_snapshot_testing" @@ -42,12 +45,12 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) { OrgId: 1, } - err = sqlstore.CreateDashboardSnapshot(context.Background(), &cmd) + err = dashStore.CreateDashboardSnapshot(context.Background(), &cmd) require.NoError(t, err) t.Run("Should be able to get snapshot by key", func(t *testing.T) { query := models.GetDashboardSnapshotQuery{Key: "hej"} - err := sqlstore.GetDashboardSnapshot(context.Background(), &query) + err := dashStore.GetDashboardSnapshot(context.Background(), &query) require.NoError(t, err) assert.NotNil(t, query.Result) @@ -69,7 +72,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) { OrgId: 1, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}, } - err := sqlstore.SearchDashboardSnapshots(context.Background(), &query) + err := dashStore.SearchDashboardSnapshots(context.Background(), &query) require.NoError(t, err) t.Run("Should return all the snapshots", func(t *testing.T) { @@ -83,7 +86,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) { OrgId: 1, 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) t.Run("Should return all the snapshots", func(t *testing.T) { @@ -97,7 +100,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) { OrgId: 1, 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) t.Run("Should not return any snapshots", func(t *testing.T) { @@ -116,7 +119,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) { UserId: 0, OrgId: 1, } - err := sqlstore.CreateDashboardSnapshot(context.Background(), &cmd) + err := dashStore.CreateDashboardSnapshot(context.Background(), &cmd) require.NoError(t, err) t.Run("Should not return any snapshots", func(t *testing.T) { @@ -124,7 +127,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) { OrgId: 1, 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.NotNil(t, query.Result) @@ -148,36 +151,37 @@ func TestIntegrationDeleteExpiredSnapshots(t *testing.T) { if testing.Short() { 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) { setting.SnapShotRemoveExpired = true - nonExpiredSnapshot := createTestSnapshot(t, sqlstore, "key1", 48000) - createTestSnapshot(t, sqlstore, "key2", -1200) - createTestSnapshot(t, sqlstore, "key3", -1200) + nonExpiredSnapshot := createTestSnapshot(t, dashStore, "key1", 48000) + createTestSnapshot(t, dashStore, "key2", -1200) + createTestSnapshot(t, dashStore, "key3", -1200) - err := sqlstore.DeleteExpiredSnapshots(context.Background(), &models.DeleteExpiredSnapshotsCommand{}) + err := dashStore.DeleteExpiredSnapshots(context.Background(), &models.DeleteExpiredSnapshotsCommand{}) require.NoError(t, err) query := models.GetDashboardSnapshotsQuery{ OrgId: 1, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}, } - err = sqlstore.SearchDashboardSnapshots(context.Background(), &query) + err = dashStore.SearchDashboardSnapshots(context.Background(), &query) require.NoError(t, err) assert.Len(t, query.Result, 1) 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) query = models.GetDashboardSnapshotsQuery{ OrgId: 1, SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}, } - err = sqlstore.SearchDashboardSnapshots(context.Background(), &query) + err = dashStore.SearchDashboardSnapshots(context.Background(), &query) require.NoError(t, err) 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{ Key: key, DeleteKey: "delete" + key, @@ -196,13 +200,16 @@ func createTestSnapshot(t *testing.T, sqlstore *SQLStore, key string, expires in OrgId: 1, Expires: expires, } - err := sqlstore.CreateDashboardSnapshot(context.Background(), &cmd) + err := dashStore.CreateDashboardSnapshot(context.Background(), &cmd) require.NoError(t, err) // Set expiry date manually - to be able to create expired snapshots if expires < 0 { 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) } diff --git a/pkg/services/dashboardsnapshots/service.go b/pkg/services/dashboardsnapshots/service.go new file mode 100644 index 00000000000..5fc6f4eaf22 --- /dev/null +++ b/pkg/services/dashboardsnapshots/service.go @@ -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 +} diff --git a/pkg/services/dashboardsnapshots/service/service.go b/pkg/services/dashboardsnapshots/service/service.go new file mode 100644 index 00000000000..a16d5665e81 --- /dev/null +++ b/pkg/services/dashboardsnapshots/service/service.go @@ -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) +} diff --git a/pkg/services/dashboardsnapshots/dashboardsnapshots_test.go b/pkg/services/dashboardsnapshots/service/service_test.go similarity index 87% rename from pkg/services/dashboardsnapshots/dashboardsnapshots_test.go rename to pkg/services/dashboardsnapshots/service/service_test.go index 56969b471bd..ab9053bca12 100644 --- a/pkg/services/dashboardsnapshots/dashboardsnapshots_test.go +++ b/pkg/services/dashboardsnapshots/service/service_test.go @@ -1,27 +1,25 @@ -package dashboardsnapshots +package service import ( "context" "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/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" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" - "github.com/stretchr/testify/require" ) func TestDashboardSnapshotsService(t *testing.T) { sqlStore := sqlstore.InitTestDB(t) + dsStore := dashsnapdb.ProvideStore(sqlStore) secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore)) - - s := &Service{ - SQLStore: sqlStore, - SecretsService: secretsService, - } + s := ProvideService(dsStore, secretsService) origSecret := setting.SecretKey setting.SecretKey = "dashboard_snapshot_service_test" @@ -47,7 +45,7 @@ func TestDashboardSnapshotsService(t *testing.T) { err = s.CreateDashboardSnapshot(ctx, &cmd) 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.Equal(t, rawDashboard, decrypted) diff --git a/pkg/services/dashboardsnapshots/store.go b/pkg/services/dashboardsnapshots/store.go new file mode 100644 index 00000000000..ca2144cba56 --- /dev/null +++ b/pkg/services/dashboardsnapshots/store.go @@ -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 +} diff --git a/pkg/services/sqlstore/dashboard_provisioning.go b/pkg/services/sqlstore/dashboard_provisioning.go deleted file mode 100644 index f79e0bcddbe..00000000000 --- a/pkg/services/sqlstore/dashboard_provisioning.go +++ /dev/null @@ -1 +0,0 @@ -package sqlstore diff --git a/pkg/services/sqlstore/store.go b/pkg/services/sqlstore/store.go index 044450976d5..ef063540abf 100644 --- a/pkg/services/sqlstore/store.go +++ b/pkg/services/sqlstore/store.go @@ -12,11 +12,6 @@ type Store interface { GetDataSourceStats(ctx context.Context, query *models.GetDataSourceStatsQuery) error GetDataSourceAccessStats(ctx context.Context, query *models.GetDataSourceAccessStatsQuery) 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) CreateOrg(ctx context.Context, cmd *models.CreateOrgCommand) error CreateOrgWithMember(name string, userID int64) (models.Org, error)