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)