mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Remove bus from dashboard service (#46829)
* Move DeleteDashboard funtion into dashboards store service, remove bus and update tests * Remove bus from folder service and update more tests * Fix mock
This commit is contained in:
parent
bfb03d779d
commit
b2af18f129
@ -47,12 +47,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
})
|
||||
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
|
||||
|
||||
getDashboardQueryResult := models.NewDashboard("Dash")
|
||||
mockSQLStore := mockstore.NewSQLStoreMock()
|
||||
mockSQLStore.ExpectedDashboard = getDashboardQueryResult
|
||||
mockSQLStore.ExpectedError = nil
|
||||
hs.SQLStore = mockSQLStore
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
|
||||
"/api/dashboards/id/:dashboardId/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||
callGetDashboardPermissions(sc, hs)
|
||||
@ -96,10 +90,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
mockSQLStore := mockstore.NewSQLStoreMock()
|
||||
mockSQLStore.ExpectedDashboard = models.NewDashboard("Dash")
|
||||
hs.SQLStore = mockSQLStore
|
||||
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
|
||||
"/api/dashboards/id/:dashboardId/permissions", models.ROLE_ADMIN, func(sc *scenarioContext) {
|
||||
callGetDashboardPermissions(sc, hs)
|
||||
|
@ -51,6 +51,7 @@ type Store interface {
|
||||
UnprovisionDashboard(ctx context.Context, id int64) error
|
||||
// GetDashboardsByPluginID retrieves dashboards identified by plugin.
|
||||
GetDashboardsByPluginID(ctx context.Context, query *models.GetDashboardsByPluginIdQuery) error
|
||||
DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error
|
||||
FolderStore
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,14 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@ -223,7 +226,7 @@ func (d *DashboardStore) SaveAlerts(ctx context.Context, dashID int64, alerts []
|
||||
return err
|
||||
}
|
||||
|
||||
if err := deleteMissingAlerts(existingAlerts, alerts, sess, d.log); err != nil {
|
||||
if err := d.deleteMissingAlerts(existingAlerts, alerts, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -255,7 +258,7 @@ func (d *DashboardStore) DeleteOrphanedProvisionedDashboards(ctx context.Context
|
||||
}
|
||||
|
||||
for _, deleteDashCommand := range result {
|
||||
err := d.sqlStore.DeleteDashboard(ctx, &models.DeleteDashboardCommand{Id: deleteDashCommand.DashboardId})
|
||||
err := d.DeleteDashboard(ctx, &models.DeleteDashboardCommand{Id: deleteDashCommand.DashboardId})
|
||||
if err != nil && !errors.Is(err, models.ErrDashboardNotFound) {
|
||||
return err
|
||||
}
|
||||
@ -613,7 +616,7 @@ func updateAlerts(existingAlerts []*models.Alert, alerts []*models.Alert, sess *
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert, sess *sqlstore.DBSession, log log.Logger) error {
|
||||
func (d *DashboardStore) deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert, sess *sqlstore.DBSession) error {
|
||||
for _, missingAlert := range alerts {
|
||||
missing := true
|
||||
|
||||
@ -625,7 +628,7 @@ func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert,
|
||||
}
|
||||
|
||||
if missing {
|
||||
if err := deleteAlertByIdInternal(missingAlert.Id, "Removed from dashboard", sess, log); err != nil {
|
||||
if err := d.deleteAlertByIdInternal(missingAlert.Id, "Removed from dashboard", sess); err != nil {
|
||||
// No use trying to delete more, since we're in a transaction and it will be
|
||||
// rolled back on error.
|
||||
return err
|
||||
@ -636,8 +639,8 @@ func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert,
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteAlertByIdInternal(alertId int64, reason string, sess *sqlstore.DBSession, log log.Logger) error {
|
||||
log.Debug("Deleting alert", "id", alertId, "reason", reason)
|
||||
func (d *DashboardStore) deleteAlertByIdInternal(alertId int64, reason string, sess *sqlstore.DBSession) error {
|
||||
d.log.Debug("Deleting alert", "id", alertId, "reason", reason)
|
||||
|
||||
if _, err := sess.Exec("DELETE FROM alert WHERE id = ?", alertId); err != nil {
|
||||
return err
|
||||
@ -690,3 +693,138 @@ func (d *DashboardStore) GetDashboardsByPluginID(ctx context.Context, query *mod
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DashboardStore) DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
|
||||
return d.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
return d.deleteDashboard(cmd, sess)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DashboardStore) deleteDashboard(cmd *models.DeleteDashboardCommand, sess *sqlstore.DBSession) error {
|
||||
dashboard := models.Dashboard{Id: cmd.Id, OrgId: cmd.OrgId}
|
||||
has, err := sess.Get(&dashboard)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return models.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
deletes := []string{
|
||||
"DELETE FROM dashboard_tag WHERE dashboard_id = ? ",
|
||||
"DELETE FROM star WHERE dashboard_id = ? ",
|
||||
"DELETE FROM dashboard WHERE id = ?",
|
||||
"DELETE FROM playlist_item WHERE type = 'dashboard_by_id' AND value = ?",
|
||||
"DELETE FROM dashboard_version WHERE dashboard_id = ?",
|
||||
"DELETE FROM annotation WHERE dashboard_id = ?",
|
||||
"DELETE FROM dashboard_provisioning WHERE dashboard_id = ?",
|
||||
"DELETE FROM dashboard_acl WHERE dashboard_id = ?",
|
||||
}
|
||||
|
||||
if dashboard.IsFolder {
|
||||
deletes = append(deletes, "DELETE FROM dashboard WHERE folder_id = ?")
|
||||
|
||||
var dashIds []struct {
|
||||
Id int64
|
||||
}
|
||||
err := sess.SQL("SELECT id FROM dashboard WHERE folder_id = ?", dashboard.Id).Find(&dashIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, id := range dashIds {
|
||||
if err := d.deleteAlertDefinition(id.Id, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// remove all access control permission with folder scope
|
||||
_, err = sess.Exec("DELETE FROM permission WHERE scope = ?", dashboards.ScopeFoldersProvider.GetResourceScope(strconv.FormatInt(dashboard.Id, 10)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, dash := range dashIds {
|
||||
// remove all access control permission with child dashboard scopes
|
||||
_, err = sess.Exec("DELETE FROM permission WHERE scope = ?", ac.Scope("dashboards", "id", strconv.FormatInt(dash.Id, 10)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(dashIds) > 0 {
|
||||
childrenDeletes := []string{
|
||||
"DELETE FROM dashboard_tag WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM star WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM dashboard_version WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM annotation WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM dashboard_provisioning WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM dashboard_acl WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
}
|
||||
for _, sql := range childrenDeletes {
|
||||
_, err := sess.Exec(sql, dashboard.OrgId, dashboard.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var existingRuleID int64
|
||||
exists, err := sess.Table("alert_rule").Where("namespace_uid = (SELECT uid FROM dashboard WHERE id = ?)", dashboard.Id).Cols("id").Get(&existingRuleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
if !cmd.ForceDeleteFolderRules {
|
||||
return fmt.Errorf("folder cannot be deleted: %w", models.ErrFolderContainsAlertRules)
|
||||
}
|
||||
|
||||
// Delete all rules under this folder.
|
||||
deleteNGAlertsByFolder := []string{
|
||||
"DELETE FROM alert_rule WHERE namespace_uid = (SELECT uid FROM dashboard WHERE id = ?)",
|
||||
"DELETE FROM alert_rule_version WHERE rule_namespace_uid = (SELECT uid FROM dashboard WHERE id = ?)",
|
||||
}
|
||||
|
||||
for _, sql := range deleteNGAlertsByFolder {
|
||||
_, err := sess.Exec(sql, dashboard.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_, err = sess.Exec("DELETE FROM permission WHERE scope = ?", ac.Scope("dashboards", "id", strconv.FormatInt(dashboard.Id, 10)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := d.deleteAlertDefinition(dashboard.Id, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sql := range deletes {
|
||||
_, err := sess.Exec(sql, dashboard.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DashboardStore) deleteAlertDefinition(dashboardId int64, sess *sqlstore.DBSession) error {
|
||||
alerts := make([]*models.Alert, 0)
|
||||
if err := sess.Where("dashboard_id = ?", dashboardId).Find(&alerts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, alert := range alerts {
|
||||
if err := d.deleteAlertByIdInternal(alert.Id, "Dashboard deleted", sess); err != nil {
|
||||
// If we return an error, the current transaction gets rolled back, so no use
|
||||
// trying to delete more
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, dashboardStore, "delete me", 1, 0, false, "delete this")
|
||||
|
||||
err := sqlStore.DeleteDashboard(context.Background(), &models.DeleteDashboardCommand{
|
||||
err := dashboardStore.DeleteDashboard(context.Background(), &models.DeleteDashboardCommand{
|
||||
Id: dash.Id,
|
||||
OrgId: 1,
|
||||
})
|
||||
@ -193,21 +193,21 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
emptyFolder := insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
|
||||
deleteCmd := &models.DeleteDashboardCommand{Id: emptyFolder.Id}
|
||||
err := sqlStore.DeleteDashboard(context.Background(), deleteCmd)
|
||||
err := dashboardStore.DeleteDashboard(context.Background(), deleteCmd)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Should be not able to delete a dashboard if force delete rules is disabled", func(t *testing.T) {
|
||||
setup()
|
||||
deleteCmd := &models.DeleteDashboardCommand{Id: savedFolder.Id, ForceDeleteFolderRules: false}
|
||||
err := sqlStore.DeleteDashboard(context.Background(), deleteCmd)
|
||||
err := dashboardStore.DeleteDashboard(context.Background(), deleteCmd)
|
||||
require.True(t, errors.Is(err, models.ErrFolderContainsAlertRules))
|
||||
})
|
||||
|
||||
t.Run("Should be able to delete a dashboard folder and its children if force delete rules is enabled", func(t *testing.T) {
|
||||
setup()
|
||||
deleteCmd := &models.DeleteDashboardCommand{Id: savedFolder.Id, ForceDeleteFolderRules: true}
|
||||
err := sqlStore.DeleteDashboard(context.Background(), deleteCmd)
|
||||
err := dashboardStore.DeleteDashboard(context.Background(), deleteCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
query := models.FindPersistedDashboardsQuery{
|
||||
|
@ -120,7 +120,7 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
||||
OrgId: 1,
|
||||
}
|
||||
|
||||
require.Nil(t, sqlStore.DeleteDashboard(context.Background(), deleteCmd))
|
||||
require.Nil(t, dashboardStore.DeleteDashboard(context.Background(), deleteCmd))
|
||||
|
||||
data, err := dashboardStore.GetProvisionedDataByDashboardID(dash.Id)
|
||||
require.Nil(t, err)
|
||||
|
@ -14,6 +14,20 @@ type FakeDashboardStore struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// DeleteDashboard provides a mock function with given fields: ctx, cmd
|
||||
func (_m *FakeDashboardStore) DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
|
||||
ret := _m.Called(ctx, cmd)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.DeleteDashboardCommand) error); ok {
|
||||
r0 = rf(ctx, cmd)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteOrphanedProvisionedDashboards provides a mock function with given fields: ctx, cmd
|
||||
func (_m *FakeDashboardStore) DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *models.DeleteOrphanedProvisionedDashboardsCommand) error {
|
||||
ret := _m.Called(ctx, cmd)
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
@ -408,7 +407,7 @@ func (dr *DashboardServiceImpl) deleteDashboard(ctx context.Context, dashboardId
|
||||
}
|
||||
}
|
||||
cmd := &models.DeleteDashboardCommand{OrgId: orgId, Id: dashboardId}
|
||||
return bus.Dispatch(ctx, cmd)
|
||||
return dr.dashboardStore.DeleteDashboard(ctx, cmd)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) ImportDashboard(ctx context.Context, dto *m.SaveDashboardDTO) (
|
||||
|
@ -176,35 +176,34 @@ func TestDashboardService(t *testing.T) {
|
||||
|
||||
t.Run("Given provisioned dashboard", func(t *testing.T) {
|
||||
t.Run("DeleteProvisionedDashboard should delete it", func(t *testing.T) {
|
||||
result := setupDeleteHandlers(t)
|
||||
args := &models.DeleteDashboardCommand{OrgId: 1, Id: 1}
|
||||
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
||||
err := service.DeleteProvisionedDashboard(context.Background(), 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.True(t, result.deleteWasCalled)
|
||||
})
|
||||
|
||||
t.Run("DeleteDashboard should fail to delete it", func(t *testing.T) {
|
||||
t.Run("DeleteDashboard should fail to delete it when provisioning information is missing", func(t *testing.T) {
|
||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything).Return(&models.DashboardProvisioning{}, nil).Once()
|
||||
result := setupDeleteHandlers(t)
|
||||
err := service.DeleteDashboard(context.Background(), 1, 1)
|
||||
require.Equal(t, err, models.ErrDashboardCannotDeleteProvisionedDashboard)
|
||||
require.False(t, result.deleteWasCalled)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Given non provisioned dashboard", func(t *testing.T) {
|
||||
result := setupDeleteHandlers(t)
|
||||
|
||||
t.Run("DeleteProvisionedDashboard should delete it", func(t *testing.T) {
|
||||
t.Run("DeleteProvisionedDashboard should delete the dashboard", func(t *testing.T) {
|
||||
args := &models.DeleteDashboardCommand{OrgId: 1, Id: 1, ForceDeleteFolderRules: false}
|
||||
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
||||
err := service.DeleteProvisionedDashboard(context.Background(), 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.True(t, result.deleteWasCalled)
|
||||
})
|
||||
|
||||
t.Run("DeleteDashboard should delete it", func(t *testing.T) {
|
||||
args := &models.DeleteDashboardCommand{OrgId: 1, Id: 1}
|
||||
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything).Return(nil, nil).Once()
|
||||
err := service.DeleteDashboard(context.Background(), 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.True(t, result.deleteWasCalled)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -213,17 +212,3 @@ func TestDashboardService(t *testing.T) {
|
||||
type Result struct {
|
||||
deleteWasCalled bool
|
||||
}
|
||||
|
||||
func setupDeleteHandlers(t *testing.T) *Result {
|
||||
t.Helper()
|
||||
|
||||
result := &Result{}
|
||||
bus.AddHandler("test", func(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
|
||||
require.Equal(t, cmd.Id, int64(1))
|
||||
require.Equal(t, cmd.OrgId, int64(1))
|
||||
result.deleteWasCalled = true
|
||||
return nil
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
@ -233,7 +233,8 @@ func (f *FolderServiceImpl) DeleteFolder(ctx context.Context, user *models.Signe
|
||||
}
|
||||
|
||||
deleteCmd := models.DeleteDashboardCommand{OrgId: orgID, Id: dashFolder.Id, ForceDeleteFolderRules: forceDeleteRules}
|
||||
if err := bus.Dispatch(ctx, &deleteCmd); err != nil {
|
||||
|
||||
if err := f.dashboardStore.DeleteDashboard(ctx, &deleteCmd); err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
|
@ -184,12 +184,11 @@ func TestFolderService(t *testing.T) {
|
||||
f.Id = rand.Int63()
|
||||
f.Uid = util.GenerateShortUID()
|
||||
store.On("GetFolderByUID", mock.Anything, orgID, f.Uid).Return(f, nil)
|
||||
|
||||
var actualCmd *models.DeleteDashboardCommand
|
||||
bus.AddHandler("test", func(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
|
||||
actualCmd = cmd
|
||||
return nil
|
||||
})
|
||||
defer bus.ClearBusHandlers()
|
||||
store.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||
actualCmd = args.Get(1).(*models.DeleteDashboardCommand)
|
||||
}).Return(nil).Once()
|
||||
|
||||
expectedForceDeleteRules := rand.Int63()%2 == 0
|
||||
_, err := service.DeleteFolder(context.Background(), user, orgID, f.Uid, expectedForceDeleteRules)
|
||||
|
@ -155,23 +155,6 @@ func (ss *SQLStore) HandleAlertsQuery(ctx context.Context, query *models.GetAler
|
||||
})
|
||||
}
|
||||
|
||||
func deleteAlertDefinition(dashboardId int64, sess *DBSession) error {
|
||||
alerts := make([]*models.Alert, 0)
|
||||
if err := sess.Where("dashboard_id = ?", dashboardId).Find(&alerts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, alert := range alerts {
|
||||
if err := deleteAlertByIdInternal(alert.Id, "Dashboard deleted", sess); err != nil {
|
||||
// If we return an error, the current transaction gets rolled back, so no use
|
||||
// trying to delete more
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *SQLStore) SaveAlerts(ctx context.Context, dashID int64, alerts []*models.Alert) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
existingAlerts, err := GetAlertsByDashboardId2(dashID, sess)
|
||||
|
@ -245,9 +245,10 @@ func TestAlertingDataAccess(t *testing.T) {
|
||||
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
|
||||
require.Nil(t, err)
|
||||
|
||||
err = sqlStore.DeleteDashboard(context.Background(), &models.DeleteDashboardCommand{
|
||||
OrgId: 1,
|
||||
Id: testDash.Id,
|
||||
err = sqlStore.WithDbSession(context.Background(), func(sess *DBSession) error {
|
||||
dash := models.Dashboard{Id: testDash.Id, OrgId: 1}
|
||||
_, err := sess.Delete(dash)
|
||||
return err
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
|
@ -2,16 +2,12 @@ package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||
@ -35,7 +31,6 @@ func (ss *SQLStore) addDashboardQueryAndCommandHandlers() {
|
||||
bus.AddHandler("sql", ss.GetDashboardUIDById)
|
||||
bus.AddHandler("sql", ss.GetDashboardTags)
|
||||
bus.AddHandler("sql", ss.SearchDashboards)
|
||||
bus.AddHandler("sql", ss.DeleteDashboard)
|
||||
bus.AddHandler("sql", ss.GetDashboards)
|
||||
bus.AddHandler("sql", ss.HasEditPermissionInFolders)
|
||||
bus.AddHandler("sql", ss.GetDashboardPermissionsForUser)
|
||||
@ -239,124 +234,6 @@ func (ss *SQLStore) GetDashboardTags(ctx context.Context, query *models.GetDashb
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
return deleteDashboard(cmd, sess)
|
||||
})
|
||||
}
|
||||
|
||||
func deleteDashboard(cmd *models.DeleteDashboardCommand, sess *DBSession) error {
|
||||
dashboard := models.Dashboard{Id: cmd.Id, OrgId: cmd.OrgId}
|
||||
has, err := sess.Get(&dashboard)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return models.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
deletes := []string{
|
||||
"DELETE FROM dashboard_tag WHERE dashboard_id = ? ",
|
||||
"DELETE FROM star WHERE dashboard_id = ? ",
|
||||
"DELETE FROM dashboard WHERE id = ?",
|
||||
"DELETE FROM playlist_item WHERE type = 'dashboard_by_id' AND value = ?",
|
||||
"DELETE FROM dashboard_version WHERE dashboard_id = ?",
|
||||
"DELETE FROM annotation WHERE dashboard_id = ?",
|
||||
"DELETE FROM dashboard_provisioning WHERE dashboard_id = ?",
|
||||
"DELETE FROM dashboard_acl WHERE dashboard_id = ?",
|
||||
}
|
||||
|
||||
if dashboard.IsFolder {
|
||||
deletes = append(deletes, "DELETE FROM dashboard WHERE folder_id = ?")
|
||||
|
||||
dashIds := []struct {
|
||||
Id int64
|
||||
}{}
|
||||
err := sess.SQL("SELECT id FROM dashboard WHERE folder_id = ?", dashboard.Id).Find(&dashIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, id := range dashIds {
|
||||
if err := deleteAlertDefinition(id.Id, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// remove all access control permission with folder scope
|
||||
_, err = sess.Exec("DELETE FROM permission WHERE scope = ?", dashboards.ScopeFoldersProvider.GetResourceScope(strconv.FormatInt(dashboard.Id, 10)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, dash := range dashIds {
|
||||
// remove all access control permission with child dashboard scopes
|
||||
_, err = sess.Exec("DELETE FROM permission WHERE scope = ?", ac.Scope("dashboards", "id", strconv.FormatInt(dash.Id, 10)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(dashIds) > 0 {
|
||||
childrenDeletes := []string{
|
||||
"DELETE FROM dashboard_tag WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM star WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM dashboard_version WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM annotation WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM dashboard_provisioning WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM dashboard_acl WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
}
|
||||
for _, sql := range childrenDeletes {
|
||||
_, err := sess.Exec(sql, dashboard.OrgId, dashboard.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var existingRuleID int64
|
||||
exists, err := sess.Table("alert_rule").Where("namespace_uid = (SELECT uid FROM dashboard WHERE id = ?)", dashboard.Id).Cols("id").Get(&existingRuleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
if !cmd.ForceDeleteFolderRules {
|
||||
return fmt.Errorf("folder cannot be deleted: %w", models.ErrFolderContainsAlertRules)
|
||||
}
|
||||
|
||||
// Delete all rules under this folder.
|
||||
deleteNGAlertsByFolder := []string{
|
||||
"DELETE FROM alert_rule WHERE namespace_uid = (SELECT uid FROM dashboard WHERE id = ?)",
|
||||
"DELETE FROM alert_rule_version WHERE rule_namespace_uid = (SELECT uid FROM dashboard WHERE id = ?)",
|
||||
}
|
||||
|
||||
for _, sql := range deleteNGAlertsByFolder {
|
||||
_, err := sess.Exec(sql, dashboard.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_, err = sess.Exec("DELETE FROM permission WHERE scope = ?", ac.Scope("dashboards", "id", strconv.FormatInt(dashboard.Id, 10)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := deleteAlertDefinition(dashboard.Id, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sql := range deletes {
|
||||
_, err := sess.Exec(sql, dashboard.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetDashboards(ctx context.Context, query *models.GetDashboardsQuery) error {
|
||||
return ss.WithDbSession(ctx, func(dbSession *DBSession) error {
|
||||
if len(query.DashboardIds) == 0 {
|
||||
|
@ -482,12 +482,6 @@ func (m *SQLStoreMock) GetDashboardTags(ctx context.Context, query *models.GetDa
|
||||
return nil // TODO: Implement
|
||||
}
|
||||
|
||||
func (m *SQLStoreMock) DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
|
||||
cmd.Id = m.ExpectedDashboard.Id
|
||||
cmd.OrgId = m.ExpectedDashboard.OrgId
|
||||
return m.ExpectedError
|
||||
}
|
||||
|
||||
func (m *SQLStoreMock) GetDashboards(ctx context.Context, query *models.GetDashboardsQuery) error {
|
||||
return m.ExpectedError
|
||||
}
|
||||
|
@ -105,7 +105,6 @@ type Store interface {
|
||||
GetDashboard(ctx context.Context, query *models.GetDashboardQuery) error
|
||||
GetDashboardTags(ctx context.Context, query *models.GetDashboardTagsQuery) error
|
||||
SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error
|
||||
DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error
|
||||
GetDashboards(ctx context.Context, query *models.GetDashboardsQuery) error
|
||||
GetDashboardUIDById(ctx context.Context, query *models.GetDashboardRefByIdQuery) error
|
||||
GetDataSource(ctx context.Context, query *models.GetDataSourceQuery) error
|
||||
|
Loading…
Reference in New Issue
Block a user