Nested Folder: Enforce maximum nested folder depth (#59213)

* Nested Folder: Enforce maximum nested folder depth

* Cleanup

* Fix logging
This commit is contained in:
Sofia Papagiannaki
2022-11-23 16:44:45 +02:00
committed by GitHub
parent 980a2cb949
commit ad96b240fc
5 changed files with 101 additions and 83 deletions
+52 -27
View File
@@ -3,6 +3,7 @@ package folderimpl
import (
"context"
"errors"
"fmt"
"math/rand"
"testing"
@@ -12,7 +13,6 @@ import (
"github.com/xorcare/pointer"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/appcontext"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models"
@@ -117,8 +117,7 @@ func TestIntegrationFolderService(t *testing.T) {
t.Run("When creating folder should return access denied error", func(t *testing.T) {
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*models.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil).Times(2)
ctx := appcontext.WithUser(context.Background(), usr)
_, err := service.Create(ctx, &folder.CreateFolderCommand{
_, err := service.Create(context.Background(), &folder.CreateFolderCommand{
OrgID: orgID,
Title: f.Title,
UID: folderUID,
@@ -141,16 +140,13 @@ func TestIntegrationFolderService(t *testing.T) {
})
t.Run("When deleting folder by uid should return access denied error", func(t *testing.T) {
ctx := context.Background()
ctx = appcontext.WithUser(ctx, usr)
newFolder := models.NewFolder("Folder")
newFolder.Uid = folderUID
dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(newFolder, nil)
dashStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(newFolder, nil)
err := service.DeleteFolder(ctx, &folder.DeleteFolderCommand{
err := service.DeleteFolder(context.Background(), &folder.DeleteFolderCommand{
UID: folderUID,
OrgID: orgID,
ForceDeleteRules: false,
@@ -178,8 +174,7 @@ func TestIntegrationFolderService(t *testing.T) {
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(dash, nil).Once()
dashStore.On("GetFolderByID", mock.Anything, orgID, dash.Id).Return(f, nil)
ctx := appcontext.WithUser(context.Background(), usr)
actualFolder, err := service.Create(ctx, &folder.CreateFolderCommand{
actualFolder, err := service.Create(context.Background(), &folder.CreateFolderCommand{
OrgID: orgID,
Title: dash.Title,
UID: "someuid",
@@ -193,8 +188,7 @@ func TestIntegrationFolderService(t *testing.T) {
dash := models.NewDashboardFolder("Test-Folder")
dash.Id = rand.Int63()
ctx := appcontext.WithUser(context.Background(), usr)
_, err := service.Create(ctx, &folder.CreateFolderCommand{
_, err := service.Create(context.Background(), &folder.CreateFolderCommand{
OrgID: orgID,
Title: dash.Title,
UID: "general",
@@ -235,9 +229,7 @@ func TestIntegrationFolderService(t *testing.T) {
}).Return(nil).Once()
expectedForceDeleteRules := rand.Int63()%2 == 0
ctx := context.Background()
ctx = appcontext.WithUser(ctx, usr)
err := service.DeleteFolder(ctx, &folder.DeleteFolderCommand{
err := service.DeleteFolder(context.Background(), &folder.DeleteFolderCommand{
UID: f.UID,
OrgID: orgID,
ForceDeleteRules: expectedForceDeleteRules,
@@ -338,11 +330,11 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
dashboardStore: &dashStore,
dashboardService: &dashboardsvc,
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders),
log: log.New("test-folder-service"),
}
t.Run("create folder", func(t *testing.T) {
folderStore.ExpectedFolder = &folder.Folder{}
ctx := appcontext.WithUser(context.Background(), usr)
res, err := folderService.Create(ctx, &folder.CreateFolderCommand{SignedInUser: usr})
res, err := folderService.Create(context.Background(), &folder.CreateFolderCommand{SignedInUser: usr})
require.NoError(t, err)
require.NotNil(t, res.UID)
})
@@ -379,7 +371,6 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
func TestNestedFolderService(t *testing.T) {
t.Run("with feature flag unset", func(t *testing.T) {
ctx := appcontext.WithUser(context.Background(), usr)
store := &FakeStore{}
dashStore := dashboards.FakeDashboardStore{}
dashboardsvc := dashboards.FakeDashboardService{}
@@ -403,8 +394,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil)
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
ctx = appcontext.WithUser(ctx, usr)
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
_, err := foldersvc.Create(context.Background(), &folder.CreateFolderCommand{
OrgID: orgID,
Title: "myFolder",
UID: "myFolder",
@@ -425,7 +415,7 @@ func TestNestedFolderService(t *testing.T) {
g := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
err := foldersvc.DeleteFolder(ctx, &folder.DeleteFolderCommand{UID: "myFolder", OrgID: orgID, SignedInUser: usr})
err := foldersvc.DeleteFolder(context.Background(), &folder.DeleteFolderCommand{UID: "myFolder", OrgID: orgID, SignedInUser: usr})
require.NoError(t, err)
require.NotNil(t, actualCmd)
@@ -437,7 +427,6 @@ func TestNestedFolderService(t *testing.T) {
})
t.Run("with nested folder feature flag on", func(t *testing.T) {
ctx := appcontext.WithUser(context.Background(), usr)
store := &FakeStore{}
dashStore := &dashboards.FakeDashboardStore{}
dashboardsvc := &dashboards.FakeDashboardService{}
@@ -463,8 +452,7 @@ func TestNestedFolderService(t *testing.T) {
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil)
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
ctx = appcontext.WithUser(ctx, usr)
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
_, err := foldersvc.Create(context.Background(), &folder.CreateFolderCommand{
OrgID: orgID,
Title: "myFolder",
UID: "myFolder",
@@ -492,8 +480,7 @@ func TestNestedFolderService(t *testing.T) {
store.ExpectedError = errors.New("FAILED")
// the service return success as long as the legacy create succeeds
ctx = appcontext.WithUser(ctx, usr)
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
_, err := foldersvc.Create(context.Background(), &folder.CreateFolderCommand{
OrgID: orgID,
Title: "myFolder",
UID: "myFolder",
@@ -512,7 +499,7 @@ func TestNestedFolderService(t *testing.T) {
t.Run("move, no error", func(t *testing.T) {
store.ExpectedError = nil
store.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
f, err := foldersvc.Move(ctx, &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder", OrgID: orgID, SignedInUser: usr})
f, err := foldersvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder", OrgID: orgID, SignedInUser: usr})
require.NoError(t, err)
require.NotNil(t, f)
})
@@ -527,7 +514,7 @@ func TestNestedFolderService(t *testing.T) {
g := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
err := foldersvc.DeleteFolder(ctx, &folder.DeleteFolderCommand{UID: "myFolder", OrgID: orgID, SignedInUser: usr})
err := foldersvc.DeleteFolder(context.Background(), &folder.DeleteFolderCommand{UID: "myFolder", OrgID: orgID, SignedInUser: usr})
require.NoError(t, err)
require.NotNil(t, actualCmd)
@@ -536,5 +523,43 @@ func TestNestedFolderService(t *testing.T) {
})
require.True(t, store.DeleteCalled)
})
t.Run("create returns error if maximum depth reached", func(t *testing.T) {
// This test creates and deletes the dashboard, so needs some extra setup.
g := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
t.Cleanup(func() {
guardian.New = g
})
// dashboard store & service commands that should be called.
dashboardsvc.On("BuildSaveDashboardCommand",
mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"),
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil)
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
var actualCmd *models.DeleteDashboardCommand
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
actualCmd = args.Get(1).(*models.DeleteDashboardCommand)
}).Return(nil).Once()
parents := make([]*folder.Folder, 0, folder.MaxNestedFolderDepth)
for i := 0; i < folder.MaxNestedFolderDepth; i++ {
parents = append(parents, &folder.Folder{UID: fmt.Sprintf("folder%d", i)})
}
store.ExpectedFolders = parents
store.ExpectedError = nil
_, err := foldersvc.Create(context.Background(), &folder.CreateFolderCommand{
Title: "folder",
OrgID: orgID,
ParentUID: parents[len(parents)-1].UID,
UID: util.GenerateShortUID(),
SignedInUser: usr,
})
assert.ErrorIs(t, err, folder.ErrMaximumDepthReached)
require.NotNil(t, actualCmd)
})
})
}