mirror of
https://github.com/grafana/grafana.git
synced 2024-11-22 08:56:43 -06:00
Chore: Split dashboard thumbnail service (#55344)
* Chore: split dashboard thumbnail service * fix typo * move tests * make linter happy
This commit is contained in:
parent
1ee6a1f7c2
commit
96b032e103
@ -5,24 +5,23 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
dashboardthumbs "github.com/grafana/grafana/pkg/services/dashboard_thumbs"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
// TODO remove sqlstore
|
||||
sqlStore *sqlstore.SQLStore
|
||||
store store
|
||||
}
|
||||
|
||||
func ProvideService(
|
||||
ss *sqlstore.SQLStore,
|
||||
db db.DB,
|
||||
) dashboardthumbs.Service {
|
||||
return &Service{
|
||||
sqlStore: ss,
|
||||
store: &xormStore{db: db},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GetThumbnail(ctx context.Context, query *models.GetDashboardThumbnailCommand) (*models.DashboardThumbnail, error) {
|
||||
dt, err := s.sqlStore.GetThumbnail(ctx, query)
|
||||
dt, err := s.store.Get(ctx, query)
|
||||
if err != nil {
|
||||
return dt, err
|
||||
}
|
||||
@ -30,7 +29,7 @@ func (s *Service) GetThumbnail(ctx context.Context, query *models.GetDashboardTh
|
||||
}
|
||||
|
||||
func (s *Service) SaveThumbnail(ctx context.Context, cmd *models.SaveDashboardThumbnailCommand) (*models.DashboardThumbnail, error) {
|
||||
dt, err := s.sqlStore.SaveThumbnail(ctx, cmd)
|
||||
dt, err := s.store.Save(ctx, cmd)
|
||||
if err != nil {
|
||||
return dt, err
|
||||
}
|
||||
@ -38,7 +37,7 @@ func (s *Service) SaveThumbnail(ctx context.Context, cmd *models.SaveDashboardTh
|
||||
}
|
||||
|
||||
func (s *Service) UpdateThumbnailState(ctx context.Context, cmd *models.UpdateThumbnailStateCommand) error {
|
||||
err := s.sqlStore.UpdateThumbnailState(ctx, cmd)
|
||||
err := s.store.UpdateState(ctx, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -46,7 +45,7 @@ func (s *Service) UpdateThumbnailState(ctx context.Context, cmd *models.UpdateTh
|
||||
}
|
||||
|
||||
func (s *Service) FindThumbnailCount(ctx context.Context, cmd *models.FindDashboardThumbnailCountCommand) (int64, error) {
|
||||
i, err := s.sqlStore.FindThumbnailCount(ctx, cmd)
|
||||
i, err := s.store.Count(ctx, cmd)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
@ -54,7 +53,7 @@ func (s *Service) FindThumbnailCount(ctx context.Context, cmd *models.FindDashbo
|
||||
}
|
||||
|
||||
func (s *Service) FindDashboardsWithStaleThumbnails(ctx context.Context, cmd *models.FindDashboardsWithStaleThumbnailsCommand) ([]*models.DashboardWithStaleThumbnail, error) {
|
||||
d, err := s.sqlStore.FindDashboardsWithStaleThumbnails(ctx, cmd)
|
||||
d, err := s.store.FindDashboardsWithStaleThumbnails(ctx, cmd)
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
|
@ -1 +1,225 @@
|
||||
package dashboardthumbsimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||
)
|
||||
|
||||
type store interface {
|
||||
Get(ctx context.Context, query *models.GetDashboardThumbnailCommand) (*models.DashboardThumbnail, error)
|
||||
Save(ctx context.Context, cmd *models.SaveDashboardThumbnailCommand) (*models.DashboardThumbnail, error)
|
||||
UpdateState(ctx context.Context, cmd *models.UpdateThumbnailStateCommand) error
|
||||
Count(ctx context.Context, cmd *models.FindDashboardThumbnailCountCommand) (int64, error)
|
||||
FindDashboardsWithStaleThumbnails(ctx context.Context, cmd *models.FindDashboardsWithStaleThumbnailsCommand) ([]*models.DashboardWithStaleThumbnail, error)
|
||||
}
|
||||
|
||||
type xormStore struct {
|
||||
db db.DB
|
||||
}
|
||||
|
||||
func (ss *xormStore) Get(ctx context.Context, query *models.GetDashboardThumbnailCommand) (*models.DashboardThumbnail, error) {
|
||||
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
result, err := findThumbnailByMeta(sess, query.DashboardThumbnailMeta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query.Result = result
|
||||
return nil
|
||||
})
|
||||
|
||||
return query.Result, err
|
||||
}
|
||||
|
||||
func marshalDatasourceUids(dsUids []string) (string, error) {
|
||||
if dsUids == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
b, err := json.Marshal(dsUids)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func (ss *xormStore) Save(ctx context.Context, cmd *models.SaveDashboardThumbnailCommand) (*models.DashboardThumbnail, error) {
|
||||
err := ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
existing, err := findThumbnailByMeta(sess, cmd.DashboardThumbnailMeta)
|
||||
|
||||
if err != nil && !errors.Is(err, dashboards.ErrDashboardThumbnailNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
dsUids, err := marshalDatasourceUids(cmd.DatasourceUIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if existing != nil {
|
||||
existing.Image = cmd.Image
|
||||
existing.MimeType = cmd.MimeType
|
||||
existing.Updated = time.Now()
|
||||
existing.DashboardVersion = cmd.DashboardVersion
|
||||
existing.DsUIDs = dsUids
|
||||
existing.State = models.ThumbnailStateDefault
|
||||
_, err = sess.ID(existing.Id).Update(existing)
|
||||
cmd.Result = existing
|
||||
return err
|
||||
}
|
||||
|
||||
thumb := &models.DashboardThumbnail{}
|
||||
|
||||
dash, err := findDashboardIdByThumbMeta(sess, cmd.DashboardThumbnailMeta)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
thumb.Updated = time.Now()
|
||||
thumb.Theme = cmd.Theme
|
||||
thumb.Kind = cmd.Kind
|
||||
thumb.Image = cmd.Image
|
||||
thumb.DsUIDs = dsUids
|
||||
thumb.MimeType = cmd.MimeType
|
||||
thumb.DashboardId = dash.Id
|
||||
thumb.DashboardVersion = cmd.DashboardVersion
|
||||
thumb.State = models.ThumbnailStateDefault
|
||||
thumb.PanelId = cmd.PanelID
|
||||
_, err = sess.Insert(thumb)
|
||||
cmd.Result = thumb
|
||||
return err
|
||||
})
|
||||
|
||||
return cmd.Result, err
|
||||
}
|
||||
|
||||
func (ss *xormStore) UpdateState(ctx context.Context, cmd *models.UpdateThumbnailStateCommand) error {
|
||||
err := ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
existing, err := findThumbnailByMeta(sess, cmd.DashboardThumbnailMeta)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existing.State = cmd.State
|
||||
_, err = sess.ID(existing.Id).Update(existing)
|
||||
return err
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (ss *xormStore) Count(ctx context.Context, cmd *models.FindDashboardThumbnailCountCommand) (int64, error) {
|
||||
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
count, err := sess.Count(&models.DashboardThumbnail{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Result = count
|
||||
return nil
|
||||
})
|
||||
|
||||
return cmd.Result, err
|
||||
}
|
||||
|
||||
func (ss *xormStore) FindDashboardsWithStaleThumbnails(ctx context.Context, cmd *models.FindDashboardsWithStaleThumbnailsCommand) ([]*models.DashboardWithStaleThumbnail, error) {
|
||||
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
sess.Table("dashboard")
|
||||
sess.Join("LEFT", "dashboard_thumbnail", "dashboard.id = dashboard_thumbnail.dashboard_id AND dashboard_thumbnail.theme = ? AND dashboard_thumbnail.kind = ?", cmd.Theme, cmd.Kind)
|
||||
sess.Where("dashboard.is_folder = ?", ss.db.GetDialect().BooleanStr(false))
|
||||
|
||||
query := "(dashboard.version != dashboard_thumbnail.dashboard_version " +
|
||||
"OR dashboard_thumbnail.state = ? " +
|
||||
"OR dashboard_thumbnail.id IS NULL"
|
||||
args := []interface{}{models.ThumbnailStateStale}
|
||||
|
||||
if cmd.IncludeThumbnailsWithEmptyDsUIDs {
|
||||
query += " OR dashboard_thumbnail.ds_uids = ? OR dashboard_thumbnail.ds_uids IS NULL"
|
||||
args = append(args, "")
|
||||
}
|
||||
sess.Where(query+")", args...)
|
||||
|
||||
if !cmd.IncludeManuallyUploadedThumbnails {
|
||||
sess.Where("(dashboard_thumbnail.id is not null AND dashboard_thumbnail.dashboard_version != ?) "+
|
||||
"OR dashboard_thumbnail.id is null "+
|
||||
"OR dashboard_thumbnail.state = ?", models.DashboardVersionForManualThumbnailUpload, models.ThumbnailStateStale)
|
||||
}
|
||||
|
||||
sess.Where("(dashboard_thumbnail.id IS NULL OR dashboard_thumbnail.state != ?)", models.ThumbnailStateLocked)
|
||||
|
||||
sess.Cols("dashboard.id",
|
||||
"dashboard.uid",
|
||||
"dashboard.org_id",
|
||||
"dashboard.version",
|
||||
"dashboard.slug")
|
||||
|
||||
var result = make([]*models.DashboardWithStaleThumbnail, 0)
|
||||
err := sess.Find(&result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Result = result
|
||||
return err
|
||||
})
|
||||
|
||||
return cmd.Result, err
|
||||
}
|
||||
|
||||
func findThumbnailByMeta(sess *sqlstore.DBSession, meta models.DashboardThumbnailMeta) (*models.DashboardThumbnail, error) {
|
||||
result := &models.DashboardThumbnail{}
|
||||
|
||||
sess.Table("dashboard_thumbnail")
|
||||
sess.Join("INNER", "dashboard", "dashboard.id = dashboard_thumbnail.dashboard_id")
|
||||
sess.Where("dashboard.uid = ? AND dashboard.org_id = ? AND panel_id = ? AND kind = ? AND theme = ?", meta.DashboardUID, meta.OrgId, meta.PanelID, meta.Kind, meta.Theme)
|
||||
sess.Cols("dashboard_thumbnail.id",
|
||||
"dashboard_thumbnail.dashboard_id",
|
||||
"dashboard_thumbnail.panel_id",
|
||||
"dashboard_thumbnail.image",
|
||||
"dashboard_thumbnail.dashboard_version",
|
||||
"dashboard_thumbnail.state",
|
||||
"dashboard_thumbnail.kind",
|
||||
"dashboard_thumbnail.ds_uids",
|
||||
"dashboard_thumbnail.mime_type",
|
||||
"dashboard_thumbnail.theme",
|
||||
"dashboard_thumbnail.updated")
|
||||
exists, err := sess.Get(result)
|
||||
|
||||
if !exists {
|
||||
return nil, dashboards.ErrDashboardThumbnailNotFound
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type dash struct {
|
||||
Id int64
|
||||
}
|
||||
|
||||
func findDashboardIdByThumbMeta(sess *sqlstore.DBSession, meta models.DashboardThumbnailMeta) (*dash, error) {
|
||||
result := &dash{}
|
||||
|
||||
sess.Table("dashboard").Where("dashboard.uid = ? AND dashboard.org_id = ?", meta.DashboardUID, meta.OrgId).Cols("id")
|
||||
exists, err := sess.Get(result)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, dashboards.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
@ -1 +1,400 @@
|
||||
package dashboardthumbsimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var theme = models.ThemeDark
|
||||
var kind = models.ThumbnailKindDefault
|
||||
|
||||
func TestIntegrationSqlStorage(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
var sqlStore *sqlstore.SQLStore
|
||||
var store store
|
||||
var savedFolder *models.Dashboard
|
||||
|
||||
setup := func() {
|
||||
sqlStore = sqlstore.InitTestDB(t)
|
||||
store = &xormStore{db: sqlStore}
|
||||
savedFolder = insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
}
|
||||
|
||||
t.Run("Should insert dashboard in default state", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version)
|
||||
thumb := getThumbnail(t, store, dash.Uid, dash.OrgId)
|
||||
|
||||
require.Positive(t, thumb.Id)
|
||||
require.Equal(t, models.ThumbnailStateDefault, thumb.State)
|
||||
require.Equal(t, dash.Version, thumb.DashboardVersion)
|
||||
})
|
||||
|
||||
t.Run("Should be able to update the thumbnail", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version)
|
||||
thumb := getThumbnail(t, store, dash.Uid, dash.OrgId)
|
||||
|
||||
insertedThumbnailId := thumb.Id
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version+1)
|
||||
|
||||
updatedThumb := getThumbnail(t, store, dash.Uid, dash.OrgId)
|
||||
require.Equal(t, insertedThumbnailId, updatedThumb.Id)
|
||||
require.Equal(t, dash.Version+1, updatedThumb.DashboardVersion)
|
||||
})
|
||||
|
||||
t.Run("Should return empty array if all dashboards have thumbnails", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version)
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := store.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 0)
|
||||
})
|
||||
|
||||
t.Run("Should return dashboards with thumbnails with empty ds_uids array", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version)
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
IncludeThumbnailsWithEmptyDsUIDs: true,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := store.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should return dashboards with thumbnails marked as stale", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version)
|
||||
updateThumbnailState(t, store, dash.Uid, dash.OrgId, models.ThumbnailStateStale)
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := store.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should not return dashboards with updated thumbnails that had been marked as stale", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version)
|
||||
updateThumbnailState(t, store, dash.Uid, dash.OrgId, models.ThumbnailStateStale)
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version)
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := store.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 0)
|
||||
})
|
||||
|
||||
t.Run("Should find dashboards without thumbnails", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := store.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should find dashboards with outdated thumbnails", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := store.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should not return dashboards with locked thumbnails even if they are outdated", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, dash.Version)
|
||||
updateThumbnailState(t, store, dash.Uid, dash.OrgId, models.ThumbnailStateLocked)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := store.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 0)
|
||||
})
|
||||
|
||||
t.Run("Should not return dashboards with manually uploaded thumbnails by default", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, models.DashboardVersionForManualThumbnailUpload)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := store.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 0)
|
||||
})
|
||||
|
||||
t.Run("Should return dashboards with manually uploaded thumbnails if requested", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, models.DashboardVersionForManualThumbnailUpload)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
IncludeManuallyUploadedThumbnails: true,
|
||||
}
|
||||
res, err := store.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should count all dashboard thumbnails", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash.Uid, dash.OrgId, 1)
|
||||
dash2 := insertTestDashboard(t, sqlStore, "test dash 23", 2, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, store, dash2.Uid, dash2.OrgId, 1)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardThumbnailCountCommand{}
|
||||
res, err := store.Count(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, res, int64(2))
|
||||
})
|
||||
}
|
||||
|
||||
func getThumbnail(t *testing.T, store store, dashboardUID string, orgId int64) *models.DashboardThumbnail {
|
||||
t.Helper()
|
||||
cmd := models.GetDashboardThumbnailCommand{
|
||||
DashboardThumbnailMeta: models.DashboardThumbnailMeta{
|
||||
DashboardUID: dashboardUID,
|
||||
OrgId: orgId,
|
||||
PanelID: 0,
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
},
|
||||
}
|
||||
|
||||
thumb, err := store.Get(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
return thumb
|
||||
}
|
||||
|
||||
func upsertTestDashboardThumbnail(t *testing.T, store store, dashboardUID string, orgId int64, dashboardVersion int) *models.DashboardThumbnail {
|
||||
t.Helper()
|
||||
cmd := models.SaveDashboardThumbnailCommand{
|
||||
DashboardThumbnailMeta: models.DashboardThumbnailMeta{
|
||||
DashboardUID: dashboardUID,
|
||||
OrgId: orgId,
|
||||
PanelID: 0,
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
},
|
||||
DashboardVersion: dashboardVersion,
|
||||
Image: make([]byte, 0),
|
||||
MimeType: "image/png",
|
||||
}
|
||||
dash, err := store.Save(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
|
||||
return dash
|
||||
}
|
||||
|
||||
func updateThumbnailState(t *testing.T, store store, dashboardUID string, orgId int64, state models.ThumbnailState) {
|
||||
t.Helper()
|
||||
cmd := models.UpdateThumbnailStateCommand{
|
||||
DashboardThumbnailMeta: models.DashboardThumbnailMeta{
|
||||
DashboardUID: dashboardUID,
|
||||
OrgId: orgId,
|
||||
PanelID: 0,
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
},
|
||||
State: state,
|
||||
}
|
||||
err := store.UpdateState(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func updateTestDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, dashModel *models.Dashboard, data map[string]interface{}) {
|
||||
t.Helper()
|
||||
|
||||
data["id"] = dashModel.Id
|
||||
|
||||
parentVersion := dashModel.Version
|
||||
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: dashModel.OrgId,
|
||||
Overwrite: true,
|
||||
Dashboard: simplejson.NewFromAny(data),
|
||||
}
|
||||
var dash *models.Dashboard
|
||||
err := sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||
var existing models.Dashboard
|
||||
dash = cmd.GetDashboardModel()
|
||||
dashWithIdExists, err := sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existing)
|
||||
require.NoError(t, err)
|
||||
require.True(t, dashWithIdExists)
|
||||
|
||||
if dash.Version != existing.Version {
|
||||
dash.SetVersion(existing.Version)
|
||||
dash.Version = existing.Version
|
||||
}
|
||||
|
||||
dash.SetVersion(dash.Version + 1)
|
||||
dash.Created = time.Now()
|
||||
dash.Updated = time.Now()
|
||||
dash.Id = dashModel.Id
|
||||
dash.Uid = util.GenerateShortUID()
|
||||
|
||||
_, err = sess.MustCols("folder_id").ID(dash.Id).Update(dash)
|
||||
return err
|
||||
})
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||
dashVersion := &dashver.DashboardVersion{
|
||||
DashboardID: dash.Id,
|
||||
ParentVersion: parentVersion,
|
||||
RestoredFrom: cmd.RestoredFrom,
|
||||
Version: dash.Version,
|
||||
Created: time.Now(),
|
||||
CreatedBy: dash.UpdatedBy,
|
||||
Message: cmd.Message,
|
||||
Data: dash.Data,
|
||||
}
|
||||
|
||||
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
||||
return err
|
||||
} else if affectedRows == 0 {
|
||||
return dashboards.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func insertTestDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, title string, orgId int64,
|
||||
folderId int64, isFolder bool, tags ...interface{}) *models.Dashboard {
|
||||
t.Helper()
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: orgId,
|
||||
FolderId: folderId,
|
||||
IsFolder: isFolder,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": title,
|
||||
"tags": tags,
|
||||
}),
|
||||
}
|
||||
|
||||
var dash *models.Dashboard
|
||||
err := sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||
dash = cmd.GetDashboardModel()
|
||||
dash.SetVersion(1)
|
||||
dash.Created = time.Now()
|
||||
dash.Updated = time.Now()
|
||||
dash.Uid = util.GenerateShortUID()
|
||||
_, err := sess.Insert(dash)
|
||||
return err
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
dash.Data.Set("id", dash.Id)
|
||||
dash.Data.Set("uid", dash.Uid)
|
||||
|
||||
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||
dashVersion := &dashver.DashboardVersion{
|
||||
DashboardID: dash.Id,
|
||||
ParentVersion: dash.Version,
|
||||
RestoredFrom: cmd.RestoredFrom,
|
||||
Version: dash.Version,
|
||||
Created: time.Now(),
|
||||
CreatedBy: dash.UpdatedBy,
|
||||
Message: cmd.Message,
|
||||
Data: dash.Data,
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
||||
return err
|
||||
} else if affectedRows == 0 {
|
||||
return dashboards.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return dash
|
||||
}
|
||||
|
@ -1,211 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
)
|
||||
|
||||
func (ss *SQLStore) GetThumbnail(ctx context.Context, query *models.GetDashboardThumbnailCommand) (*models.DashboardThumbnail, error) {
|
||||
err := ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
result, err := findThumbnailByMeta(sess, query.DashboardThumbnailMeta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query.Result = result
|
||||
return nil
|
||||
})
|
||||
|
||||
return query.Result, err
|
||||
}
|
||||
|
||||
func marshalDatasourceUids(dsUids []string) (string, error) {
|
||||
if dsUids == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
b, err := json.Marshal(dsUids)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func (ss *SQLStore) SaveThumbnail(ctx context.Context, cmd *models.SaveDashboardThumbnailCommand) (*models.DashboardThumbnail, error) {
|
||||
err := ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
existing, err := findThumbnailByMeta(sess, cmd.DashboardThumbnailMeta)
|
||||
|
||||
if err != nil && !errors.Is(err, dashboards.ErrDashboardThumbnailNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
dsUids, err := marshalDatasourceUids(cmd.DatasourceUIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if existing != nil {
|
||||
existing.Image = cmd.Image
|
||||
existing.MimeType = cmd.MimeType
|
||||
existing.Updated = time.Now()
|
||||
existing.DashboardVersion = cmd.DashboardVersion
|
||||
existing.DsUIDs = dsUids
|
||||
existing.State = models.ThumbnailStateDefault
|
||||
_, err = sess.ID(existing.Id).Update(existing)
|
||||
cmd.Result = existing
|
||||
return err
|
||||
}
|
||||
|
||||
thumb := &models.DashboardThumbnail{}
|
||||
|
||||
dash, err := findDashboardIdByThumbMeta(sess, cmd.DashboardThumbnailMeta)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
thumb.Updated = time.Now()
|
||||
thumb.Theme = cmd.Theme
|
||||
thumb.Kind = cmd.Kind
|
||||
thumb.Image = cmd.Image
|
||||
thumb.DsUIDs = dsUids
|
||||
thumb.MimeType = cmd.MimeType
|
||||
thumb.DashboardId = dash.Id
|
||||
thumb.DashboardVersion = cmd.DashboardVersion
|
||||
thumb.State = models.ThumbnailStateDefault
|
||||
thumb.PanelId = cmd.PanelID
|
||||
_, err = sess.Insert(thumb)
|
||||
cmd.Result = thumb
|
||||
return err
|
||||
})
|
||||
|
||||
return cmd.Result, err
|
||||
}
|
||||
|
||||
func (ss *SQLStore) UpdateThumbnailState(ctx context.Context, cmd *models.UpdateThumbnailStateCommand) error {
|
||||
err := ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
existing, err := findThumbnailByMeta(sess, cmd.DashboardThumbnailMeta)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existing.State = cmd.State
|
||||
_, err = sess.ID(existing.Id).Update(existing)
|
||||
return err
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (ss *SQLStore) FindThumbnailCount(ctx context.Context, cmd *models.FindDashboardThumbnailCountCommand) (int64, error) {
|
||||
err := ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
count, err := sess.Count(&models.DashboardThumbnail{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Result = count
|
||||
return nil
|
||||
})
|
||||
|
||||
return cmd.Result, err
|
||||
}
|
||||
|
||||
func (ss *SQLStore) FindDashboardsWithStaleThumbnails(ctx context.Context, cmd *models.FindDashboardsWithStaleThumbnailsCommand) ([]*models.DashboardWithStaleThumbnail, error) {
|
||||
err := ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
sess.Table("dashboard")
|
||||
sess.Join("LEFT", "dashboard_thumbnail", "dashboard.id = dashboard_thumbnail.dashboard_id AND dashboard_thumbnail.theme = ? AND dashboard_thumbnail.kind = ?", cmd.Theme, cmd.Kind)
|
||||
sess.Where("dashboard.is_folder = ?", dialect.BooleanStr(false))
|
||||
|
||||
query := "(dashboard.version != dashboard_thumbnail.dashboard_version " +
|
||||
"OR dashboard_thumbnail.state = ? " +
|
||||
"OR dashboard_thumbnail.id IS NULL"
|
||||
args := []interface{}{models.ThumbnailStateStale}
|
||||
|
||||
if cmd.IncludeThumbnailsWithEmptyDsUIDs {
|
||||
query += " OR dashboard_thumbnail.ds_uids = ? OR dashboard_thumbnail.ds_uids IS NULL"
|
||||
args = append(args, "")
|
||||
}
|
||||
sess.Where(query+")", args...)
|
||||
|
||||
if !cmd.IncludeManuallyUploadedThumbnails {
|
||||
sess.Where("(dashboard_thumbnail.id is not null AND dashboard_thumbnail.dashboard_version != ?) "+
|
||||
"OR dashboard_thumbnail.id is null "+
|
||||
"OR dashboard_thumbnail.state = ?", models.DashboardVersionForManualThumbnailUpload, models.ThumbnailStateStale)
|
||||
}
|
||||
|
||||
sess.Where("(dashboard_thumbnail.id IS NULL OR dashboard_thumbnail.state != ?)", models.ThumbnailStateLocked)
|
||||
|
||||
sess.Cols("dashboard.id",
|
||||
"dashboard.uid",
|
||||
"dashboard.org_id",
|
||||
"dashboard.version",
|
||||
"dashboard.slug")
|
||||
|
||||
var result = make([]*models.DashboardWithStaleThumbnail, 0)
|
||||
err := sess.Find(&result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Result = result
|
||||
return err
|
||||
})
|
||||
|
||||
return cmd.Result, err
|
||||
}
|
||||
|
||||
func findThumbnailByMeta(sess *DBSession, meta models.DashboardThumbnailMeta) (*models.DashboardThumbnail, error) {
|
||||
result := &models.DashboardThumbnail{}
|
||||
|
||||
sess.Table("dashboard_thumbnail")
|
||||
sess.Join("INNER", "dashboard", "dashboard.id = dashboard_thumbnail.dashboard_id")
|
||||
sess.Where("dashboard.uid = ? AND dashboard.org_id = ? AND panel_id = ? AND kind = ? AND theme = ?", meta.DashboardUID, meta.OrgId, meta.PanelID, meta.Kind, meta.Theme)
|
||||
sess.Cols("dashboard_thumbnail.id",
|
||||
"dashboard_thumbnail.dashboard_id",
|
||||
"dashboard_thumbnail.panel_id",
|
||||
"dashboard_thumbnail.image",
|
||||
"dashboard_thumbnail.dashboard_version",
|
||||
"dashboard_thumbnail.state",
|
||||
"dashboard_thumbnail.kind",
|
||||
"dashboard_thumbnail.ds_uids",
|
||||
"dashboard_thumbnail.mime_type",
|
||||
"dashboard_thumbnail.theme",
|
||||
"dashboard_thumbnail.updated")
|
||||
exists, err := sess.Get(result)
|
||||
|
||||
if !exists {
|
||||
return nil, dashboards.ErrDashboardThumbnailNotFound
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type dash struct {
|
||||
Id int64
|
||||
}
|
||||
|
||||
func findDashboardIdByThumbMeta(sess *DBSession, meta models.DashboardThumbnailMeta) (*dash, error) {
|
||||
result := &dash{}
|
||||
|
||||
sess.Table("dashboard").Where("dashboard.uid = ? AND dashboard.org_id = ?", meta.DashboardUID, meta.OrgId).Cols("id")
|
||||
exists, err := sess.Get(result)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, dashboards.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
@ -1,342 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
var theme = models.ThemeDark
|
||||
var kind = models.ThumbnailKindDefault
|
||||
|
||||
func TestIntegrationSqlStorage(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
var sqlStore *SQLStore
|
||||
var savedFolder *models.Dashboard
|
||||
|
||||
setup := func() {
|
||||
sqlStore = InitTestDB(t)
|
||||
savedFolder = insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
}
|
||||
|
||||
t.Run("Should insert dashboard in default state", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version)
|
||||
thumb := getThumbnail(t, sqlStore, dash.Uid, dash.OrgId)
|
||||
|
||||
require.Positive(t, thumb.Id)
|
||||
require.Equal(t, models.ThumbnailStateDefault, thumb.State)
|
||||
require.Equal(t, dash.Version, thumb.DashboardVersion)
|
||||
})
|
||||
|
||||
t.Run("Should be able to update the thumbnail", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version)
|
||||
thumb := getThumbnail(t, sqlStore, dash.Uid, dash.OrgId)
|
||||
|
||||
insertedThumbnailId := thumb.Id
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version+1)
|
||||
|
||||
updatedThumb := getThumbnail(t, sqlStore, dash.Uid, dash.OrgId)
|
||||
require.Equal(t, insertedThumbnailId, updatedThumb.Id)
|
||||
require.Equal(t, dash.Version+1, updatedThumb.DashboardVersion)
|
||||
})
|
||||
|
||||
t.Run("Should return empty array if all dashboards have thumbnails", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version)
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := sqlStore.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 0)
|
||||
})
|
||||
|
||||
t.Run("Should return dashboards with thumbnails with empty ds_uids array", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version)
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
IncludeThumbnailsWithEmptyDsUIDs: true,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := sqlStore.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should return dashboards with thumbnails marked as stale", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version)
|
||||
updateThumbnailState(t, sqlStore, dash.Uid, dash.OrgId, models.ThumbnailStateStale)
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := sqlStore.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should not return dashboards with updated thumbnails that had been marked as stale", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version)
|
||||
updateThumbnailState(t, sqlStore, dash.Uid, dash.OrgId, models.ThumbnailStateStale)
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version)
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := sqlStore.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 0)
|
||||
})
|
||||
|
||||
t.Run("Should find dashboards without thumbnails", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := sqlStore.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should find dashboards with outdated thumbnails", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := sqlStore.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should not return dashboards with locked thumbnails even if they are outdated", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, dash.Version)
|
||||
updateThumbnailState(t, sqlStore, dash.Uid, dash.OrgId, models.ThumbnailStateLocked)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := sqlStore.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 0)
|
||||
})
|
||||
|
||||
t.Run("Should not return dashboards with manually uploaded thumbnails by default", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, models.DashboardVersionForManualThumbnailUpload)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
}
|
||||
res, err := sqlStore.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 0)
|
||||
})
|
||||
|
||||
t.Run("Should return dashboards with manually uploaded thumbnails if requested", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, models.DashboardVersionForManualThumbnailUpload)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardsWithStaleThumbnailsCommand{
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
IncludeManuallyUploadedThumbnails: true,
|
||||
}
|
||||
res, err := sqlStore.FindDashboardsWithStaleThumbnails(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, dash.Id, res[0].Id)
|
||||
})
|
||||
|
||||
t.Run("Should count all dashboard thumbnails", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash.Uid, dash.OrgId, 1)
|
||||
dash2 := insertTestDashboard(t, sqlStore, "test dash 23", 2, savedFolder.Id, false, "prod", "webapp")
|
||||
upsertTestDashboardThumbnail(t, sqlStore, dash2.Uid, dash2.OrgId, 1)
|
||||
|
||||
updateTestDashboard(t, sqlStore, dash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
cmd := models.FindDashboardThumbnailCountCommand{}
|
||||
res, err := sqlStore.FindThumbnailCount(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, res, int64(2))
|
||||
})
|
||||
}
|
||||
|
||||
func getThumbnail(t *testing.T, sqlStore *SQLStore, dashboardUID string, orgId int64) *models.DashboardThumbnail {
|
||||
t.Helper()
|
||||
cmd := models.GetDashboardThumbnailCommand{
|
||||
DashboardThumbnailMeta: models.DashboardThumbnailMeta{
|
||||
DashboardUID: dashboardUID,
|
||||
OrgId: orgId,
|
||||
PanelID: 0,
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
},
|
||||
}
|
||||
|
||||
thumb, err := sqlStore.GetThumbnail(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
return thumb
|
||||
}
|
||||
|
||||
func upsertTestDashboardThumbnail(t *testing.T, sqlStore *SQLStore, dashboardUID string, orgId int64, dashboardVersion int) *models.DashboardThumbnail {
|
||||
t.Helper()
|
||||
cmd := models.SaveDashboardThumbnailCommand{
|
||||
DashboardThumbnailMeta: models.DashboardThumbnailMeta{
|
||||
DashboardUID: dashboardUID,
|
||||
OrgId: orgId,
|
||||
PanelID: 0,
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
},
|
||||
DashboardVersion: dashboardVersion,
|
||||
Image: make([]byte, 0),
|
||||
MimeType: "image/png",
|
||||
}
|
||||
dash, err := sqlStore.SaveThumbnail(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
|
||||
return dash
|
||||
}
|
||||
|
||||
func updateThumbnailState(t *testing.T, sqlStore *SQLStore, dashboardUID string, orgId int64, state models.ThumbnailState) {
|
||||
t.Helper()
|
||||
cmd := models.UpdateThumbnailStateCommand{
|
||||
DashboardThumbnailMeta: models.DashboardThumbnailMeta{
|
||||
DashboardUID: dashboardUID,
|
||||
OrgId: orgId,
|
||||
PanelID: 0,
|
||||
Kind: kind,
|
||||
Theme: theme,
|
||||
},
|
||||
State: state,
|
||||
}
|
||||
err := sqlStore.UpdateThumbnailState(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func updateTestDashboard(t *testing.T, sqlStore *SQLStore, dashboard *models.Dashboard, data map[string]interface{}) {
|
||||
t.Helper()
|
||||
|
||||
data["id"] = dashboard.Id
|
||||
|
||||
parentVersion := dashboard.Version
|
||||
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: dashboard.OrgId,
|
||||
Overwrite: true,
|
||||
Dashboard: simplejson.NewFromAny(data),
|
||||
}
|
||||
var dash *models.Dashboard
|
||||
err := sqlStore.WithDbSession(context.Background(), func(sess *DBSession) error {
|
||||
var existing models.Dashboard
|
||||
dash = cmd.GetDashboardModel()
|
||||
dashWithIdExists, err := sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existing)
|
||||
require.NoError(t, err)
|
||||
require.True(t, dashWithIdExists)
|
||||
|
||||
if dash.Version != existing.Version {
|
||||
dash.SetVersion(existing.Version)
|
||||
dash.Version = existing.Version
|
||||
}
|
||||
|
||||
dash.SetVersion(dash.Version + 1)
|
||||
dash.Created = time.Now()
|
||||
dash.Updated = time.Now()
|
||||
dash.Id = dashboard.Id
|
||||
dash.Uid = util.GenerateShortUID()
|
||||
|
||||
_, err = sess.MustCols("folder_id").ID(dash.Id).Update(dash)
|
||||
return err
|
||||
})
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
err = sqlStore.WithDbSession(context.Background(), func(sess *DBSession) error {
|
||||
dashVersion := &dashver.DashboardVersion{
|
||||
DashboardID: dash.Id,
|
||||
ParentVersion: parentVersion,
|
||||
RestoredFrom: cmd.RestoredFrom,
|
||||
Version: dash.Version,
|
||||
Created: time.Now(),
|
||||
CreatedBy: dash.UpdatedBy,
|
||||
Message: cmd.Message,
|
||||
Data: dash.Data,
|
||||
}
|
||||
|
||||
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
||||
return err
|
||||
} else if affectedRows == 0 {
|
||||
return dashboards.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
@ -9,13 +9,13 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
dashboardthumbs "github.com/grafana/grafana/pkg/services/dashboard_thumbs"
|
||||
"github.com/grafana/grafana/pkg/services/searchV2"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
)
|
||||
|
||||
func newThumbnailRepo(store *sqlstore.SQLStore, search searchV2.SearchService) thumbnailRepo {
|
||||
func newThumbnailRepo(thumbsService dashboardthumbs.Service, search searchV2.SearchService) thumbnailRepo {
|
||||
repo := &sqlThumbnailRepository{
|
||||
store: store,
|
||||
store: thumbsService,
|
||||
search: search,
|
||||
log: log.New("thumbnails_repo"),
|
||||
}
|
||||
@ -23,7 +23,7 @@ func newThumbnailRepo(store *sqlstore.SQLStore, search searchV2.SearchService) t
|
||||
}
|
||||
|
||||
type sqlThumbnailRepository struct {
|
||||
store *sqlstore.SQLStore
|
||||
store dashboardthumbs.Service
|
||||
search searchV2.SearchService
|
||||
log log.Logger
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
dashboardthumbs "github.com/grafana/grafana/pkg/services/dashboard_thumbs"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/searchV2"
|
||||
"github.com/segmentio/encoding/json"
|
||||
@ -76,14 +77,14 @@ type crawlerScheduleOptions struct {
|
||||
func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles,
|
||||
lockService *serverlock.ServerLockService, renderService rendering.Service,
|
||||
gl *live.GrafanaLive, store *sqlstore.SQLStore, authSetupService CrawlerAuthSetupService,
|
||||
dashboardService dashboards.DashboardService, searchService searchV2.SearchService,
|
||||
dashboardService dashboards.DashboardService, dashboardThumbsService dashboardthumbs.Service, searchService searchV2.SearchService,
|
||||
dsPermissionsService permissions.DatasourcePermissionsService, licensing models.Licensing) Service {
|
||||
if !features.IsEnabled(featuremgmt.FlagDashboardPreviews) {
|
||||
return &dummyService{}
|
||||
}
|
||||
logger := log.New("previews_service")
|
||||
|
||||
thumbnailRepo := newThumbnailRepo(store, searchService)
|
||||
thumbnailRepo := newThumbnailRepo(dashboardThumbsService, searchService)
|
||||
|
||||
canRunCrawler := true
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user