mirror of https://github.com/grafana/grafana.git
NestedFolders: Add library panels counting and deletion to folder registry (#69149)
* Expose library element service's folder service * Register library panels, add count implementation * Expand folder counts test * Update registry deletion method interface * Allow getting library elements from any folder * Add test for library panel deletion * Add test for library panel counting
This commit is contained in:
parent
32e2304f10
commit
20ffbbc41e
|
|
@ -1359,7 +1359,7 @@ func (l *mockLibraryElementService) CreateElement(c context.Context, signedInUse
|
|||
}
|
||||
|
||||
// GetElement gets an element from a UID.
|
||||
func (l *mockLibraryElementService) GetElement(c context.Context, signedInUser *user.SignedInUser, UID string) (model.LibraryElementDTO, error) {
|
||||
func (l *mockLibraryElementService) GetElement(c context.Context, signedInUser *user.SignedInUser, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
|
||||
return model.LibraryElementDTO{}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -281,6 +281,12 @@ func (hs *HTTPServer) DeleteFolder(c *contextmodel.ReqContext) response.Response
|
|||
}
|
||||
return apierrors.ToFolderErrorResponse(err)
|
||||
}
|
||||
/* TODO: after a decision regarding folder deletion permissions has been made
|
||||
(https://github.com/grafana/grafana-enterprise/issues/5144),
|
||||
remove the previous call to hs.LibraryElementService.DeleteLibraryElementsInFolder
|
||||
and remove "user" from the signature of DeleteInFolder in the folder RegistryService.
|
||||
Context: https://github.com/grafana/grafana/pull/69149#discussion_r1235057903
|
||||
*/
|
||||
|
||||
uid := web.Params(c.Req)[":uid"]
|
||||
err = hs.folderService.Delete(c.Req.Context(), &folder.DeleteFolderCommand{UID: uid, OrgID: c.OrgID, ForceDeleteRules: c.QueryBool("forceDeleteRules"), SignedInUser: c.SignedInUser})
|
||||
|
|
|
|||
|
|
@ -635,7 +635,7 @@ func (dr DashboardServiceImpl) CountInFolder(ctx context.Context, orgID int64, f
|
|||
return dr.dashboardStore.CountDashboardsInFolder(ctx, &dashboards.CountDashboardsInFolderRequest{FolderID: folder.ID, OrgID: orgID})
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) DeleteInFolder(ctx context.Context, orgID int64, folderUID string) error {
|
||||
func (dr *DashboardServiceImpl) DeleteInFolder(ctx context.Context, orgID int64, folderUID string, u *user.SignedInUser) error {
|
||||
return dr.dashboardStore.DeleteDashboardsInFolder(ctx, &dashboards.DeleteDashboardsInFolderRequest{FolderUID: folderUID, OrgID: orgID})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ func TestDashboardService(t *testing.T) {
|
|||
t.Run("Delete dashboards in folder", func(t *testing.T) {
|
||||
args := &dashboards.DeleteDashboardsInFolderRequest{OrgID: 1, FolderUID: "uid"}
|
||||
fakeStore.On("DeleteDashboardsInFolder", mock.Anything, args).Return(nil).Once()
|
||||
err := service.DeleteInFolder(context.Background(), 1, "uid")
|
||||
err := service.DeleteInFolder(context.Background(), 1, "uid", nil)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -509,7 +509,7 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e
|
|||
}
|
||||
|
||||
if cmd.ForceDeleteRules {
|
||||
if err := s.deleteChildrenInFolder(ctx, dashFolder.OrgID, dashFolder.UID); err != nil {
|
||||
if err := s.deleteChildrenInFolder(ctx, dashFolder.OrgID, dashFolder.UID, cmd.SignedInUser); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -525,9 +525,9 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e
|
|||
return err
|
||||
}
|
||||
|
||||
func (s *Service) deleteChildrenInFolder(ctx context.Context, orgID int64, folderUID string) error {
|
||||
func (s *Service) deleteChildrenInFolder(ctx context.Context, orgID int64, folderUID string, user *user.SignedInUser) error {
|
||||
for _, v := range s.registry {
|
||||
if err := v.DeleteInFolder(ctx, orgID, folderUID); err != nil {
|
||||
if err := v.DeleteInFolder(ctx, orgID, folderUID, user); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
||||
|
|
@ -28,10 +29,14 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
|
@ -356,7 +361,9 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
}
|
||||
|
||||
signedInUser := user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{
|
||||
orgID: {dashboards.ActionFoldersCreate: {}, dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersAll}},
|
||||
orgID: {
|
||||
dashboards.ActionFoldersCreate: {},
|
||||
dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersAll}},
|
||||
}}
|
||||
createCmd := folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
|
|
@ -364,6 +371,20 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
SignedInUser: &signedInUser,
|
||||
}
|
||||
|
||||
libraryElementCmd := model.CreateLibraryElementCommand{
|
||||
Model: []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": 1,
|
||||
"title": "Text - Library Panel",
|
||||
"type": "text",
|
||||
"description": "A description"
|
||||
}
|
||||
`),
|
||||
Kind: int64(model.PanelElement),
|
||||
}
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
|
||||
folderPermissions := acmock.NewMockedPermissionsService()
|
||||
dashboardPermissions := acmock.NewMockedPermissionsService()
|
||||
|
||||
|
|
@ -371,7 +392,12 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
depth := 5
|
||||
t.Run("With nested folder feature flag on", func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true, CanViewValue: true})
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
||||
CanSaveValue: true,
|
||||
CanViewValue: true,
|
||||
// CanEditValue is required to create library elements
|
||||
CanEditValue: true,
|
||||
})
|
||||
|
||||
dashSrv, err := service.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, nil, featuresFlagOn, folderPermissions, dashboardPermissions, ac, serviceWithFlagOn)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -379,6 +405,10 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOn, db, serviceWithFlagOn, ac, dashSrv)
|
||||
require.NoError(t, err)
|
||||
|
||||
elementService := libraryelements.ProvideService(cfg, db, routeRegister, serviceWithFlagOn, featuresFlagOn)
|
||||
lps, err := librarypanels.ProvideService(cfg, db, routeRegister, elementService, serviceWithFlagOn)
|
||||
require.NoError(t, err)
|
||||
|
||||
ancestorUIDs := CreateSubtreeInStore(t, nestedFolderStore, serviceWithFlagOn, depth, "getDescendantCountsOn", createCmd)
|
||||
|
||||
parent, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[0])
|
||||
|
|
@ -390,6 +420,13 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
||||
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
||||
|
||||
libraryElementCmd.FolderID = parent.ID
|
||||
_, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
libraryElementCmd.FolderID = subfolder.ID
|
||||
_, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
countCmd := folder.GetDescendantCountsQuery{
|
||||
UID: &ancestorUIDs[0],
|
||||
OrgID: orgID,
|
||||
|
|
@ -397,9 +434,10 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
}
|
||||
m, err := serviceWithFlagOn.GetDescendantCounts(context.Background(), &countCmd)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(depth-1), m["folder"])
|
||||
require.Equal(t, int64(2), m["dashboard"])
|
||||
require.Equal(t, int64(2), m["alertrule"])
|
||||
require.Equal(t, int64(depth-1), m[entity.StandardKindFolder])
|
||||
require.Equal(t, int64(2), m[entity.StandardKindDashboard])
|
||||
require.Equal(t, int64(2), m[entity.StandardKindAlertRule])
|
||||
require.Equal(t, int64(2), m[entity.StandardKindLibraryPanel])
|
||||
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
|
|
@ -428,7 +466,12 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
}
|
||||
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true, CanViewValue: true})
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
||||
CanSaveValue: true,
|
||||
CanViewValue: true,
|
||||
// CanEditValue is required to create library elements
|
||||
CanEditValue: true,
|
||||
})
|
||||
|
||||
dashSrv, err := service.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, nil, featuresFlagOff,
|
||||
folderPermissions, dashboardPermissions, ac, serviceWithFlagOff)
|
||||
|
|
@ -437,6 +480,10 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOff, db, serviceWithFlagOff, ac, dashSrv)
|
||||
require.NoError(t, err)
|
||||
|
||||
elementService := libraryelements.ProvideService(cfg, db, routeRegister, serviceWithFlagOff, featuresFlagOff)
|
||||
lps, err := librarypanels.ProvideService(cfg, db, routeRegister, elementService, serviceWithFlagOff)
|
||||
require.NoError(t, err)
|
||||
|
||||
ancestorUIDs := CreateSubtreeInStore(t, nestedFolderStore, serviceWithFlagOn, depth, "getDescendantCountsOff", createCmd)
|
||||
|
||||
parent, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[0])
|
||||
|
|
@ -448,6 +495,13 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
||||
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
||||
|
||||
libraryElementCmd.FolderID = parent.ID
|
||||
_, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
libraryElementCmd.FolderID = subfolder.ID
|
||||
_, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
countCmd := folder.GetDescendantCountsQuery{
|
||||
UID: &ancestorUIDs[0],
|
||||
OrgID: orgID,
|
||||
|
|
@ -455,9 +509,10 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
}
|
||||
m, err := serviceWithFlagOff.GetDescendantCounts(context.Background(), &countCmd)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(0), m["folder"])
|
||||
require.Equal(t, int64(1), m["dashboard"])
|
||||
require.Equal(t, int64(1), m["alertrule"])
|
||||
require.Equal(t, int64(0), m[entity.StandardKindFolder])
|
||||
require.Equal(t, int64(1), m[entity.StandardKindDashboard])
|
||||
require.Equal(t, int64(1), m[entity.StandardKindAlertRule])
|
||||
require.Equal(t, int64(1), m[entity.StandardKindLibraryPanel])
|
||||
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
|
|
@ -470,169 +525,158 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("Should delete folders", func(t *testing.T) {
|
||||
t.Run("With nested folder feature flag on", func(t *testing.T) {
|
||||
dashSrv, err := service.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, nil, featuresFlagOn, folderPermissions, dashboardPermissions, ac, serviceWithFlagOn)
|
||||
require.NoError(t, err)
|
||||
featuresFlagOff := featuremgmt.WithFeatures()
|
||||
serviceWithFlagOff := &Service{
|
||||
cfg: cfg,
|
||||
log: log.New("test-folder-service"),
|
||||
dashboardFolderStore: folderStore,
|
||||
features: featuresFlagOff,
|
||||
bus: b,
|
||||
db: db,
|
||||
registry: make(map[string]folder.RegistryService),
|
||||
}
|
||||
|
||||
alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOn, db, serviceWithFlagOn, ac, dashSrv)
|
||||
require.NoError(t, err)
|
||||
t.Run("With force deletion of rules", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
service *Service
|
||||
featuresFlag *featuremgmt.FeatureManager
|
||||
prefix string
|
||||
depth int
|
||||
forceDelete bool
|
||||
deletionErr error
|
||||
dashboardErr error
|
||||
folderErr error
|
||||
libPanelParentErr error
|
||||
libPanelSubErr error
|
||||
desc string
|
||||
}{
|
||||
{
|
||||
service: serviceWithFlagOn,
|
||||
featuresFlag: featuresFlagOn,
|
||||
prefix: "flagon-force",
|
||||
depth: 3,
|
||||
forceDelete: true,
|
||||
dashboardErr: dashboards.ErrFolderNotFound,
|
||||
folderErr: folder.ErrFolderNotFound,
|
||||
libPanelParentErr: model.ErrLibraryElementNotFound,
|
||||
libPanelSubErr: model.ErrLibraryElementNotFound,
|
||||
desc: "With nested folder feature flag on and force deletion of rules",
|
||||
},
|
||||
{
|
||||
service: serviceWithFlagOn,
|
||||
featuresFlag: featuresFlagOn,
|
||||
prefix: "flagon-noforce",
|
||||
depth: 3,
|
||||
forceDelete: false,
|
||||
deletionErr: dashboards.ErrFolderContainsAlertRules,
|
||||
desc: "With nested folder feature flag on and no force deletion of rules",
|
||||
},
|
||||
{
|
||||
service: serviceWithFlagOff,
|
||||
featuresFlag: featuresFlagOff,
|
||||
prefix: "flagoff-force",
|
||||
depth: 1,
|
||||
forceDelete: true,
|
||||
dashboardErr: dashboards.ErrFolderNotFound,
|
||||
libPanelParentErr: model.ErrLibraryElementNotFound,
|
||||
desc: "With nested folder feature flag off and force deletion of rules",
|
||||
},
|
||||
{
|
||||
service: serviceWithFlagOff,
|
||||
featuresFlag: featuresFlagOff,
|
||||
prefix: "flagoff-noforce",
|
||||
depth: 1,
|
||||
forceDelete: false,
|
||||
deletionErr: dashboards.ErrFolderContainsAlertRules,
|
||||
desc: "With nested folder feature flag off and no force deletion of rules",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true, CanViewValue: true})
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
||||
CanSaveValue: true,
|
||||
CanViewValue: true,
|
||||
// CanEditValue is required to create library elements
|
||||
CanEditValue: true,
|
||||
})
|
||||
|
||||
ancestorUIDs := CreateSubtreeInStore(t, nestedFolderStore, serviceWithFlagOn, 3, "with-force", createCmd)
|
||||
elementService := libraryelements.ProvideService(cfg, db, routeRegister, tc.service, tc.featuresFlag)
|
||||
lps, err := librarypanels.ProvideService(cfg, db, routeRegister, elementService, tc.service)
|
||||
require.NoError(t, err)
|
||||
|
||||
dashStore, err := database.ProvideDashboardStore(db, db.Cfg, tc.featuresFlag, tagimpl.ProvideService(db, db.Cfg), quotaService)
|
||||
require.NoError(t, err)
|
||||
nestedFolderStore := ProvideStore(db, db.Cfg, tc.featuresFlag)
|
||||
tc.service.dashboardStore = dashStore
|
||||
tc.service.store = nestedFolderStore
|
||||
|
||||
dashSrv, err := service.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, nil, tc.featuresFlag, folderPermissions, dashboardPermissions, ac, tc.service)
|
||||
require.NoError(t, err)
|
||||
alertStore, err := ngstore.ProvideDBStore(cfg, tc.featuresFlag, db, tc.service, ac, dashSrv)
|
||||
require.NoError(t, err)
|
||||
|
||||
ancestorUIDs := CreateSubtreeInStore(t, nestedFolderStore, serviceWithFlagOn, tc.depth, tc.prefix, createCmd)
|
||||
|
||||
parent, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[0])
|
||||
require.NoError(t, err)
|
||||
subfolder, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[1])
|
||||
require.NoError(t, err)
|
||||
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
||||
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
||||
|
||||
var (
|
||||
subfolder *folder.Folder
|
||||
subPanel model.LibraryElementDTO
|
||||
)
|
||||
if tc.depth > 1 {
|
||||
subfolder, err = serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[1])
|
||||
require.NoError(t, err)
|
||||
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
||||
libraryElementCmd.FolderID = subfolder.ID
|
||||
subPanel, err = lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
libraryElementCmd.FolderID = parent.ID
|
||||
parentPanel, err := lps.LibraryElementService.CreateElement(context.Background(), &signedInUser, libraryElementCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
deleteCmd := folder.DeleteFolderCommand{
|
||||
UID: ancestorUIDs[0],
|
||||
OrgID: orgID,
|
||||
SignedInUser: &signedInUser,
|
||||
ForceDeleteRules: true,
|
||||
ForceDeleteRules: tc.forceDelete,
|
||||
}
|
||||
err = serviceWithFlagOn.Delete(context.Background(), &deleteCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = tc.service.Delete(context.Background(), &deleteCmd)
|
||||
require.ErrorIs(t, err, tc.deletionErr)
|
||||
|
||||
for i, uid := range ancestorUIDs {
|
||||
// dashboard table
|
||||
_, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, uid)
|
||||
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
|
||||
_, err := tc.service.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, uid)
|
||||
require.ErrorIs(t, err, tc.dashboardErr)
|
||||
// folder table
|
||||
_, err = serviceWithFlagOn.store.Get(context.Background(), folder.GetFolderQuery{UID: &ancestorUIDs[i], OrgID: orgID})
|
||||
require.ErrorIs(t, err, folder.ErrFolderNotFound)
|
||||
_, err = tc.service.store.Get(context.Background(), folder.GetFolderQuery{UID: &ancestorUIDs[i], OrgID: orgID})
|
||||
require.ErrorIs(t, err, tc.folderErr)
|
||||
}
|
||||
|
||||
_, err = lps.LibraryElementService.GetElement(context.Background(), &signedInUser, model.GetLibraryElementCommand{
|
||||
FolderName: parent.Title,
|
||||
FolderID: parent.ID,
|
||||
UID: parentPanel.UID,
|
||||
})
|
||||
require.ErrorIs(t, err, tc.libPanelParentErr)
|
||||
if tc.depth > 1 {
|
||||
_, err = lps.LibraryElementService.GetElement(context.Background(), &signedInUser, model.GetLibraryElementCommand{
|
||||
FolderName: subfolder.Title,
|
||||
FolderID: subfolder.ID,
|
||||
UID: subPanel.UID,
|
||||
})
|
||||
require.ErrorIs(t, err, tc.libPanelSubErr)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
})
|
||||
t.Run("Without force deletion of rules", func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true, CanViewValue: true})
|
||||
|
||||
ancestorUIDs := CreateSubtreeInStore(t, nestedFolderStore, serviceWithFlagOn, 3, "without-force", createCmd)
|
||||
|
||||
parent, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[0])
|
||||
require.NoError(t, err)
|
||||
subfolder, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[1])
|
||||
require.NoError(t, err)
|
||||
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
||||
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
||||
|
||||
deleteCmd := folder.DeleteFolderCommand{
|
||||
UID: ancestorUIDs[0],
|
||||
OrgID: orgID,
|
||||
SignedInUser: &signedInUser,
|
||||
ForceDeleteRules: false,
|
||||
}
|
||||
err = serviceWithFlagOn.Delete(context.Background(), &deleteCmd)
|
||||
require.Error(t, dashboards.ErrFolderContainsAlertRules, err)
|
||||
|
||||
for i, uid := range ancestorUIDs {
|
||||
// dashboard table
|
||||
_, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, uid)
|
||||
require.NoError(t, err)
|
||||
// folder table
|
||||
_, err = serviceWithFlagOn.store.Get(context.Background(), folder.GetFolderQuery{UID: &ancestorUIDs[i], OrgID: orgID})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
})
|
||||
})
|
||||
t.Run("With nested folder feature flag off", func(t *testing.T) {
|
||||
featuresFlagOff := featuremgmt.WithFeatures()
|
||||
dashStore, err := database.ProvideDashboardStore(db, db.Cfg, featuresFlagOff, tagimpl.ProvideService(db, db.Cfg), quotaService)
|
||||
require.NoError(t, err)
|
||||
nestedFolderStore := ProvideStore(db, db.Cfg, featuresFlagOff)
|
||||
|
||||
dashSrv, err := service.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, nil, featuresFlagOff, folderPermissions, dashboardPermissions, ac, serviceWithFlagOn)
|
||||
require.NoError(t, err)
|
||||
alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOff, db, serviceWithFlagOn, ac, dashSrv)
|
||||
require.NoError(t, err)
|
||||
|
||||
serviceWithFlagOff := &Service{
|
||||
cfg: cfg,
|
||||
log: log.New("test-folder-service"),
|
||||
dashboardStore: dashStore,
|
||||
dashboardFolderStore: folderStore,
|
||||
store: nestedFolderStore,
|
||||
features: featuresFlagOff,
|
||||
bus: b,
|
||||
db: db,
|
||||
}
|
||||
t.Run("With force deletion of rules", func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true, CanViewValue: true})
|
||||
|
||||
ancestorUIDs := CreateSubtreeInStore(t, nestedFolderStore, serviceWithFlagOn, 1, "off-force", createCmd)
|
||||
|
||||
parent, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[0])
|
||||
require.NoError(t, err)
|
||||
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
||||
|
||||
deleteCmd := folder.DeleteFolderCommand{
|
||||
UID: ancestorUIDs[0],
|
||||
OrgID: orgID,
|
||||
SignedInUser: &signedInUser,
|
||||
ForceDeleteRules: true,
|
||||
}
|
||||
err = serviceWithFlagOff.Delete(context.Background(), &deleteCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
// dashboard table
|
||||
_, err = serviceWithFlagOff.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[0])
|
||||
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
|
||||
// folder table
|
||||
_, err = serviceWithFlagOff.store.Get(context.Background(), folder.GetFolderQuery{UID: &ancestorUIDs[0], OrgID: orgID})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
for _, uid := range ancestorUIDs {
|
||||
err := serviceWithFlagOff.store.Delete(context.Background(), uid, orgID)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
})
|
||||
t.Run("Without force deletion of rules", func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true, CanViewValue: true})
|
||||
|
||||
ancestorUIDs := CreateSubtreeInStore(t, nestedFolderStore, serviceWithFlagOn, 1, "off-no-force", createCmd)
|
||||
|
||||
parent, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[0])
|
||||
require.NoError(t, err)
|
||||
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
||||
|
||||
deleteCmd := folder.DeleteFolderCommand{
|
||||
UID: ancestorUIDs[0],
|
||||
OrgID: orgID,
|
||||
SignedInUser: &signedInUser,
|
||||
ForceDeleteRules: false,
|
||||
}
|
||||
err = serviceWithFlagOff.Delete(context.Background(), &deleteCmd)
|
||||
require.Error(t, dashboards.ErrFolderContainsAlertRules, err)
|
||||
|
||||
// dashboard table
|
||||
_, err = serviceWithFlagOff.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[0])
|
||||
require.NoError(t, err)
|
||||
// folder table
|
||||
_, err = serviceWithFlagOff.store.Get(context.Background(), folder.GetFolderQuery{UID: &ancestorUIDs[0], OrgID: orgID})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
for _, uid := range ancestorUIDs {
|
||||
err := serviceWithFlagOff.store.Delete(context.Background(), uid, orgID)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
type RegistryService interface {
|
||||
DeleteInFolder(ctx context.Context, orgID int64, folderUID string) error
|
||||
DeleteInFolder(ctx context.Context, orgID int64, folderUID string, user *user.SignedInUser) error
|
||||
CountInFolder(ctx context.Context, orgID int64, folderUID string, user *user.SignedInUser) (int64, error)
|
||||
Kind() string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,12 @@ func (l *LibraryElementService) deleteHandler(c *contextmodel.ReqContext) respon
|
|||
// 404: notFoundError
|
||||
// 500: internalServerError
|
||||
func (l *LibraryElementService) getHandler(c *contextmodel.ReqContext) response.Response {
|
||||
element, err := l.getLibraryElementByUid(c.Req.Context(), c.SignedInUser, web.Params(c.Req)[":uid"])
|
||||
element, err := l.getLibraryElementByUid(c.Req.Context(), c.SignedInUser,
|
||||
model.GetLibraryElementCommand{
|
||||
UID: web.Params(c.Req)[":uid"],
|
||||
FolderName: dashboards.RootFolderName,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return toLibraryElementError(err, "Failed to get library element")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ func (l *LibraryElementService) deleteLibraryElement(c context.Context, signedIn
|
|||
}
|
||||
|
||||
// getLibraryElements gets a Library Element where param == value
|
||||
func getLibraryElements(c context.Context, store db.DB, cfg *setting.Cfg, signedInUser *user.SignedInUser, params []Pair, features featuremgmt.FeatureToggles) ([]model.LibraryElementDTO, error) {
|
||||
func getLibraryElements(c context.Context, store db.DB, cfg *setting.Cfg, signedInUser *user.SignedInUser, params []Pair, features featuremgmt.FeatureToggles, cmd model.GetLibraryElementCommand) ([]model.LibraryElementDTO, error) {
|
||||
libraryElements := make([]model.LibraryElementWithMeta, 0)
|
||||
|
||||
recursiveQueriesAreSupported, err := store.RecursiveQueriesAreSupported()
|
||||
|
|
@ -239,10 +239,10 @@ func getLibraryElements(c context.Context, store db.DB, cfg *setting.Cfg, signed
|
|||
err = store.WithDbSession(c, func(session *db.Session) error {
|
||||
builder := db.NewSqlBuilder(cfg, features, store.GetDialect(), recursiveQueriesAreSupported)
|
||||
builder.Write(selectLibraryElementDTOWithMeta)
|
||||
builder.Write(", 'General' as folder_name ")
|
||||
builder.Write(", ? as folder_name ", cmd.FolderName)
|
||||
builder.Write(", '' as folder_uid ")
|
||||
builder.Write(getFromLibraryElementDTOWithMeta(store.GetDialect()))
|
||||
writeParamSelectorSQL(&builder, append(params, Pair{"folder_id", 0})...)
|
||||
writeParamSelectorSQL(&builder, append(params, Pair{"folder_id", cmd.FolderID})...)
|
||||
builder.Write(" UNION ")
|
||||
builder.Write(selectLibraryElementDTOWithMeta)
|
||||
builder.Write(", dashboard.title as folder_name ")
|
||||
|
|
@ -303,8 +303,8 @@ func getLibraryElements(c context.Context, store db.DB, cfg *setting.Cfg, signed
|
|||
}
|
||||
|
||||
// getLibraryElementByUid gets a Library Element by uid.
|
||||
func (l *LibraryElementService) getLibraryElementByUid(c context.Context, signedInUser *user.SignedInUser, UID string) (model.LibraryElementDTO, error) {
|
||||
libraryElements, err := getLibraryElements(c, l.SQLStore, l.Cfg, signedInUser, []Pair{{key: "org_id", value: signedInUser.OrgID}, {key: "uid", value: UID}}, l.features)
|
||||
func (l *LibraryElementService) getLibraryElementByUid(c context.Context, signedInUser *user.SignedInUser, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
|
||||
libraryElements, err := getLibraryElements(c, l.SQLStore, l.Cfg, signedInUser, []Pair{{key: "org_id", value: signedInUser.OrgID}, {key: "uid", value: cmd.UID}}, l.features, cmd)
|
||||
if err != nil {
|
||||
return model.LibraryElementDTO{}, err
|
||||
}
|
||||
|
|
@ -317,7 +317,10 @@ func (l *LibraryElementService) getLibraryElementByUid(c context.Context, signed
|
|||
|
||||
// getLibraryElementByName gets a Library Element by name.
|
||||
func (l *LibraryElementService) getLibraryElementsByName(c context.Context, signedInUser *user.SignedInUser, name string) ([]model.LibraryElementDTO, error) {
|
||||
return getLibraryElements(c, l.SQLStore, l.Cfg, signedInUser, []Pair{{"org_id", signedInUser.OrgID}, {"name", name}}, l.features)
|
||||
return getLibraryElements(c, l.SQLStore, l.Cfg, signedInUser, []Pair{{"org_id", signedInUser.OrgID}, {"name", name}}, l.features,
|
||||
model.GetLibraryElementCommand{
|
||||
FolderName: dashboards.RootFolderName,
|
||||
})
|
||||
}
|
||||
|
||||
// getAllLibraryElements gets all Library Elements.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, routeRegister routing.Rout
|
|||
// Service is a service for operating on library elements.
|
||||
type Service interface {
|
||||
CreateElement(c context.Context, signedInUser *user.SignedInUser, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error)
|
||||
GetElement(c context.Context, signedInUser *user.SignedInUser, UID string) (model.LibraryElementDTO, error)
|
||||
GetElement(c context.Context, signedInUser *user.SignedInUser, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error)
|
||||
GetElementsForDashboard(c context.Context, dashboardID int64) (map[string]model.LibraryElementDTO, error)
|
||||
ConnectElementsToDashboard(c context.Context, signedInUser *user.SignedInUser, elementUIDs []string, dashboardID int64) error
|
||||
DisconnectElementsFromDashboard(c context.Context, dashboardID int64) error
|
||||
|
|
@ -52,8 +52,8 @@ func (l *LibraryElementService) CreateElement(c context.Context, signedInUser *u
|
|||
}
|
||||
|
||||
// GetElement gets an element from a UID.
|
||||
func (l *LibraryElementService) GetElement(c context.Context, signedInUser *user.SignedInUser, UID string) (model.LibraryElementDTO, error) {
|
||||
return l.getLibraryElementByUid(c, signedInUser, UID)
|
||||
func (l *LibraryElementService) GetElement(c context.Context, signedInUser *user.SignedInUser, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
|
||||
return l.getLibraryElementByUid(c, signedInUser, cmd)
|
||||
}
|
||||
|
||||
// GetElementsForDashboard gets all connected elements for a specific dashboard.
|
||||
|
|
|
|||
|
|
@ -230,6 +230,13 @@ type PatchLibraryElementCommand struct {
|
|||
UID string `json:"uid"`
|
||||
}
|
||||
|
||||
// GetLibraryElementCommand is the command for getting a library element.
|
||||
type GetLibraryElementCommand struct {
|
||||
FolderName string
|
||||
FolderID int64
|
||||
UID string
|
||||
}
|
||||
|
||||
// SearchLibraryElementsQuery is the query used for searching for Elements
|
||||
type SearchLibraryElementsQuery struct {
|
||||
PerPage int
|
||||
|
|
|
|||
|
|
@ -10,21 +10,30 @@ import (
|
|||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, sqlStore db.DB, routeRegister routing.RouteRegister,
|
||||
libraryElementService libraryelements.Service) *LibraryPanelService {
|
||||
return &LibraryPanelService{
|
||||
libraryElementService libraryelements.Service, folderService folder.Service) (*LibraryPanelService, error) {
|
||||
lps := LibraryPanelService{
|
||||
Cfg: cfg,
|
||||
SQLStore: sqlStore,
|
||||
RouteRegister: routeRegister,
|
||||
LibraryElementService: libraryElementService,
|
||||
FolderService: folderService,
|
||||
log: log.New("library-panels"),
|
||||
}
|
||||
|
||||
if err := folderService.RegisterService(lps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lps, nil
|
||||
}
|
||||
|
||||
// Service is a service for operating on library panels.
|
||||
|
|
@ -44,6 +53,7 @@ type LibraryPanelService struct {
|
|||
SQLStore db.DB
|
||||
RouteRegister routing.RouteRegister
|
||||
LibraryElementService libraryelements.Service
|
||||
FolderService folder.Service
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +140,7 @@ func importLibraryPanelsRecursively(c context.Context, service libraryelements.S
|
|||
return errLibraryPanelHeaderUIDMissing
|
||||
}
|
||||
|
||||
_, err := service.GetElement(c, signedInUser, UID)
|
||||
_, err := service.GetElement(c, signedInUser, model.GetLibraryElementCommand{UID: UID, FolderName: dashboards.RootFolderName})
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
|
@ -171,3 +181,28 @@ func importLibraryPanelsRecursively(c context.Context, service libraryelements.S
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CountInFolder is a handler for retrieving the number of library panels contained
|
||||
// within a given folder and for a specific organisation.
|
||||
func (lps LibraryPanelService) CountInFolder(ctx context.Context, orgID int64, folderUID string, u *user.SignedInUser) (int64, error) {
|
||||
var count int64
|
||||
return count, lps.SQLStore.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
folder, err := lps.FolderService.Get(ctx, &folder.GetFolderQuery{UID: &folderUID, OrgID: orgID, SignedInUser: u})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q := sess.Table("library_element").Where("org_id = ?", u.OrgID).
|
||||
Where("folder_id = ?", folder.ID).Where("kind = ?", int64(model.PanelElement))
|
||||
count, err = q.Count()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteInFolder deletes the library panels contained in a given folder.
|
||||
func (lps LibraryPanelService) DeleteInFolder(ctx context.Context, orgID int64, folderUID string, user *user.SignedInUser) error {
|
||||
return lps.LibraryElementService.DeleteLibraryElementsInFolder(ctx, user, folderUID)
|
||||
}
|
||||
|
||||
// Kind returns the name of the library panel type of entity.
|
||||
func (lps LibraryPanelService) Kind() string { return entity.StandardKindLibraryPanel }
|
||||
|
|
|
|||
|
|
@ -319,6 +319,23 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
|
|||
require.Len(t, elements, 1)
|
||||
require.Equal(t, sc.initialResult.Result.UID, elements[sc.initialResult.Result.UID].UID)
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "It should return the correct count of library panels in a folder",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
count, err := sc.lps.CountInFolder(context.Background(), sc.user.OrgID, sc.folder.UID, sc.user)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), count)
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "It should delete library panels in a folder",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
err := sc.lps.DeleteInFolder(context.Background(), sc.user.OrgID, sc.folder.UID, sc.user)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = sc.elementService.GetElement(sc.ctx, sc.user,
|
||||
model.GetLibraryElementCommand{UID: sc.initialResult.Result.UID, FolderName: sc.folder.Title})
|
||||
require.EqualError(t, err, model.ErrLibraryElementNotFound.Error())
|
||||
})
|
||||
}
|
||||
|
||||
func TestImportLibraryPanelsForDashboard(t *testing.T) {
|
||||
|
|
@ -367,14 +384,16 @@ func TestImportLibraryPanelsForDashboard(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
_, err := sc.elementService.GetElement(sc.ctx, sc.user, missingUID)
|
||||
_, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||
model.GetLibraryElementCommand{UID: missingUID, FolderName: dashboards.RootFolderName})
|
||||
|
||||
require.EqualError(t, err, model.ErrLibraryElementNotFound.Error())
|
||||
|
||||
err = sc.service.ImportLibraryPanelsForDashboard(sc.ctx, sc.user, simplejson.NewFromAny(libraryElements), panels, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user, missingUID)
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||
model.GetLibraryElementCommand{UID: missingUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
var expected = getExpected(t, element, missingUID, missingName, missingModel)
|
||||
var result = toLibraryElement(t, element)
|
||||
|
|
@ -406,13 +425,15 @@ func TestImportLibraryPanelsForDashboard(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
_, err := sc.elementService.GetElement(sc.ctx, sc.user, existingUID)
|
||||
_, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||
model.GetLibraryElementCommand{UID: existingUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = sc.service.ImportLibraryPanelsForDashboard(sc.ctx, sc.user, simplejson.New(), panels, sc.folder.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user, existingUID)
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||
model.GetLibraryElementCommand{UID: existingUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
var expected = getExpected(t, element, existingUID, existingName, sc.initialResult.Result.Model)
|
||||
expected.FolderID = sc.initialResult.Result.FolderID
|
||||
|
|
@ -519,16 +540,15 @@ func TestImportLibraryPanelsForDashboard(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := sc.elementService.GetElement(sc.ctx, sc.user, outsideUID)
|
||||
_, err := sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: outsideUID, FolderName: dashboards.RootFolderName})
|
||||
require.EqualError(t, err, model.ErrLibraryElementNotFound.Error())
|
||||
_, err = sc.elementService.GetElement(sc.ctx, sc.user, insideUID)
|
||||
_, err = sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: insideUID, FolderName: dashboards.RootFolderName})
|
||||
require.EqualError(t, err, model.ErrLibraryElementNotFound.Error())
|
||||
|
||||
err = sc.service.ImportLibraryPanelsForDashboard(sc.ctx, sc.user, simplejson.NewFromAny(libraryElements), panels, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user, outsideUID)
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: outsideUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
expected := getExpected(t, element, outsideUID, outsideName, outsideModel)
|
||||
result := toLibraryElement(t, element)
|
||||
|
|
@ -536,7 +556,7 @@ func TestImportLibraryPanelsForDashboard(t *testing.T) {
|
|||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
element, err = sc.elementService.GetElement(sc.ctx, sc.user, insideUID)
|
||||
element, err = sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: insideUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
expected = getExpected(t, element, insideUID, insideName, insideModel)
|
||||
result = toLibraryElement(t, element)
|
||||
|
|
@ -607,6 +627,7 @@ type scenarioContext struct {
|
|||
folder *folder.Folder
|
||||
initialResult libraryPanelResult
|
||||
sqlStore db.DB
|
||||
lps LibraryPanelService
|
||||
}
|
||||
|
||||
func toLibraryElement(t *testing.T, res model.LibraryElementDTO) libraryElement {
|
||||
|
|
@ -814,6 +835,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||
Cfg: cfg,
|
||||
SQLStore: sqlStore,
|
||||
LibraryElementService: elementService,
|
||||
FolderService: folderService,
|
||||
}
|
||||
|
||||
usr := &user.SignedInUser{
|
||||
|
|
@ -853,6 +875,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||
service: &service,
|
||||
elementService: elementService,
|
||||
sqlStore: sqlStore,
|
||||
lps: service,
|
||||
}
|
||||
|
||||
foldr := createFolder(t, sc, "ScenarioFolder")
|
||||
|
|
|
|||
|
|
@ -586,7 +586,7 @@ func (st DBstore) GetAlertRulesForScheduling(ctx context.Context, query *ngmodel
|
|||
}
|
||||
|
||||
// DeleteInFolder deletes the rules contained in a given folder along with their associated data.
|
||||
func (st DBstore) DeleteInFolder(ctx context.Context, orgID int64, folderUID string) error {
|
||||
func (st DBstore) DeleteInFolder(ctx context.Context, orgID int64, folderUID string, user *user.SignedInUser) error {
|
||||
rules, err := st.ListAlertRules(ctx, &ngmodels.ListAlertRulesQuery{
|
||||
OrgID: orgID,
|
||||
NamespaceUIDs: []string{folderUID},
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@ func TestIntegration_DeleteInFolder(t *testing.T) {
|
|||
}
|
||||
rule := createRule(t, store, nil)
|
||||
|
||||
err := store.DeleteInFolder(context.Background(), rule.OrgID, rule.NamespaceUID)
|
||||
err := store.DeleteInFolder(context.Background(), rule.OrgID, rule.NamespaceUID, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := store.CountInFolder(context.Background(), rule.OrgID, rule.NamespaceUID, nil)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ const (
|
|||
// the kind may need to change to better encapsulate { targets:[], transforms:[] }
|
||||
StandardKindQuery = "query"
|
||||
|
||||
// KindAlertRule is not a real kind. It's used to refer to alert rules, for instance
|
||||
// StandardKindAlertRule is not a real kind. It's used to refer to alert rules, for instance
|
||||
// in the folder registry service.
|
||||
StandardKindAlertRule = "alertrule"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue