2022-05-25 03:41:51 -05:00
|
|
|
package dashverimpl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-02-07 11:27:38 -06:00
|
|
|
"errors"
|
2022-05-25 03:41:51 -05:00
|
|
|
|
2022-10-19 08:02:15 -05:00
|
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
2023-02-07 11:27:38 -06:00
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
2022-05-25 03:41:51 -05:00
|
|
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
|
|
)
|
|
|
|
|
2022-05-31 04:56:05 -05:00
|
|
|
const (
|
|
|
|
maxVersionsToDeletePerBatch = 100
|
|
|
|
maxVersionDeletionBatches = 50
|
|
|
|
)
|
|
|
|
|
2022-05-25 03:41:51 -05:00
|
|
|
type Service struct {
|
2023-02-07 11:27:38 -06:00
|
|
|
store store
|
|
|
|
dashSvc dashboards.DashboardService
|
|
|
|
log log.Logger
|
2022-05-25 03:41:51 -05:00
|
|
|
}
|
|
|
|
|
2023-02-07 11:27:38 -06:00
|
|
|
func ProvideService(db db.DB, dashboardService dashboards.DashboardService) dashver.Service {
|
2022-05-25 03:41:51 -05:00
|
|
|
return &Service{
|
|
|
|
store: &sqlStore{
|
2022-06-02 08:59:05 -05:00
|
|
|
db: db,
|
|
|
|
dialect: db.GetDialect(),
|
2022-05-25 03:41:51 -05:00
|
|
|
},
|
2023-02-07 11:27:38 -06:00
|
|
|
dashSvc: dashboardService,
|
|
|
|
log: log.New("dashboard-version"),
|
2022-05-25 03:41:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-27 10:17:24 -06:00
|
|
|
func (s *Service) Get(ctx context.Context, query *dashver.GetDashboardVersionQuery) (*dashver.DashboardVersionDTO, error) {
|
2023-02-07 11:27:38 -06:00
|
|
|
// Get the DashboardUID if not populated
|
|
|
|
if query.DashboardUID == "" {
|
|
|
|
u, err := s.getDashUIDMaybeEmpty(ctx, query.DashboardID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
query.DashboardUID = u
|
|
|
|
}
|
|
|
|
|
|
|
|
// The store methods require the dashboard ID (uid is not in the dashboard
|
|
|
|
// versions table, at time of this writing), so get the DashboardID if it
|
|
|
|
// was not populated.
|
|
|
|
if query.DashboardID == 0 {
|
|
|
|
id, err := s.getDashIDMaybeEmpty(ctx, query.DashboardUID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
query.DashboardID = id
|
|
|
|
}
|
|
|
|
|
2022-05-25 03:41:51 -05:00
|
|
|
version, err := s.store.Get(ctx, query)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
version.Data.Set("id", version.DashboardID)
|
2023-02-07 11:27:38 -06:00
|
|
|
return version.ToDTO(query.DashboardUID), nil
|
2022-05-25 03:41:51 -05:00
|
|
|
}
|
2022-05-31 04:56:05 -05:00
|
|
|
|
|
|
|
func (s *Service) DeleteExpired(ctx context.Context, cmd *dashver.DeleteExpiredVersionsCommand) error {
|
|
|
|
versionsToKeep := setting.DashboardVersionsToKeep
|
|
|
|
if versionsToKeep < 1 {
|
|
|
|
versionsToKeep = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
for batch := 0; batch < maxVersionDeletionBatches; batch++ {
|
|
|
|
versionIdsToDelete, batchErr := s.store.GetBatch(ctx, cmd, maxVersionsToDeletePerBatch, versionsToKeep)
|
|
|
|
if batchErr != nil {
|
|
|
|
return batchErr
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(versionIdsToDelete) < 1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
deleted, err := s.store.DeleteBatch(ctx, cmd, versionIdsToDelete)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.DeletedRows += deleted
|
|
|
|
|
|
|
|
if deleted < int64(maxVersionsToDeletePerBatch) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2022-06-02 08:59:05 -05:00
|
|
|
|
|
|
|
// List all dashboard versions for the given dashboard ID.
|
|
|
|
func (s *Service) List(ctx context.Context, query *dashver.ListDashboardVersionsQuery) ([]*dashver.DashboardVersionDTO, error) {
|
2023-02-07 11:27:38 -06:00
|
|
|
// Get the DashboardUID if not populated
|
|
|
|
if query.DashboardUID == "" {
|
|
|
|
u, err := s.getDashUIDMaybeEmpty(ctx, query.DashboardID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
query.DashboardUID = u
|
|
|
|
}
|
|
|
|
|
|
|
|
// The store methods require the dashboard ID (uid is not in the dashboard
|
|
|
|
// versions table, at time of this writing), so get the DashboardID if it
|
|
|
|
// was not populated.
|
|
|
|
if query.DashboardID == 0 {
|
|
|
|
id, err := s.getDashIDMaybeEmpty(ctx, query.DashboardUID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
query.DashboardID = id
|
|
|
|
}
|
2022-06-02 08:59:05 -05:00
|
|
|
if query.Limit == 0 {
|
|
|
|
query.Limit = 1000
|
|
|
|
}
|
2022-12-27 10:17:24 -06:00
|
|
|
dvs, err := s.store.List(ctx, query)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dtos := make([]*dashver.DashboardVersionDTO, len(dvs))
|
|
|
|
for i, v := range dvs {
|
2023-02-07 11:27:38 -06:00
|
|
|
dtos[i] = v.ToDTO(query.DashboardUID)
|
2022-12-27 10:17:24 -06:00
|
|
|
}
|
|
|
|
return dtos, nil
|
2022-06-02 08:59:05 -05:00
|
|
|
}
|
2023-02-07 11:27:38 -06:00
|
|
|
|
|
|
|
// getDashUIDMaybeEmpty is a helper function which takes a dashboardID and
|
|
|
|
// returns the UID. If the dashboard is not found, it will return an empty
|
|
|
|
// string.
|
|
|
|
func (s *Service) getDashUIDMaybeEmpty(ctx context.Context, id int64) (string, error) {
|
|
|
|
q := dashboards.GetDashboardRefByIDQuery{ID: id}
|
|
|
|
result, err := s.dashSvc.GetDashboardUIDByID(ctx, &q)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
|
|
|
s.log.Debug("dashboard not found")
|
|
|
|
return "", nil
|
|
|
|
} else {
|
|
|
|
s.log.Error("error getting dashboard", err)
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result.UID, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// getDashIDMaybeEmpty is a helper function which takes a dashboardUID and
|
|
|
|
// returns the ID. If the dashboard is not found, it will return -1.
|
|
|
|
func (s *Service) getDashIDMaybeEmpty(ctx context.Context, uid string) (int64, error) {
|
|
|
|
q := dashboards.GetDashboardQuery{UID: uid}
|
|
|
|
result, err := s.dashSvc.GetDashboard(ctx, &q)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
|
|
|
s.log.Debug("dashboard not found")
|
|
|
|
return -1, nil
|
|
|
|
} else {
|
|
|
|
s.log.Error("error getting dashboard", err)
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result.ID, nil
|
|
|
|
}
|