|
|
|
@ -3,6 +3,7 @@ package folderimpl |
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"context" |
|
|
|
"context" |
|
|
|
"errors" |
|
|
|
"errors" |
|
|
|
|
|
|
|
"fmt" |
|
|
|
"math/rand" |
|
|
|
"math/rand" |
|
|
|
"testing" |
|
|
|
"testing" |
|
|
|
|
|
|
|
|
|
|
|
@ -12,7 +13,6 @@ import ( |
|
|
|
"github.com/xorcare/pointer" |
|
|
|
"github.com/xorcare/pointer" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/bus" |
|
|
|
"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/log" |
|
|
|
"github.com/grafana/grafana/pkg/infra/tracing" |
|
|
|
"github.com/grafana/grafana/pkg/infra/tracing" |
|
|
|
"github.com/grafana/grafana/pkg/models" |
|
|
|
"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) { |
|
|
|
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) |
|
|
|
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(context.Background(), &folder.CreateFolderCommand{ |
|
|
|
_, err := service.Create(ctx, &folder.CreateFolderCommand{ |
|
|
|
|
|
|
|
OrgID: orgID, |
|
|
|
OrgID: orgID, |
|
|
|
Title: f.Title, |
|
|
|
Title: f.Title, |
|
|
|
UID: folderUID, |
|
|
|
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) { |
|
|
|
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 := models.NewFolder("Folder") |
|
|
|
newFolder.Uid = folderUID |
|
|
|
newFolder.Uid = folderUID |
|
|
|
|
|
|
|
|
|
|
|
dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(newFolder, nil) |
|
|
|
dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(newFolder, nil) |
|
|
|
dashStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).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, |
|
|
|
UID: folderUID, |
|
|
|
OrgID: orgID, |
|
|
|
OrgID: orgID, |
|
|
|
ForceDeleteRules: false, |
|
|
|
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("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(dash, nil).Once() |
|
|
|
dashStore.On("GetFolderByID", mock.Anything, orgID, dash.Id).Return(f, nil) |
|
|
|
dashStore.On("GetFolderByID", mock.Anything, orgID, dash.Id).Return(f, nil) |
|
|
|
|
|
|
|
|
|
|
|
ctx := appcontext.WithUser(context.Background(), usr) |
|
|
|
actualFolder, err := service.Create(context.Background(), &folder.CreateFolderCommand{ |
|
|
|
actualFolder, err := service.Create(ctx, &folder.CreateFolderCommand{ |
|
|
|
|
|
|
|
OrgID: orgID, |
|
|
|
OrgID: orgID, |
|
|
|
Title: dash.Title, |
|
|
|
Title: dash.Title, |
|
|
|
UID: "someuid", |
|
|
|
UID: "someuid", |
|
|
|
@ -193,8 +188,7 @@ func TestIntegrationFolderService(t *testing.T) { |
|
|
|
dash := models.NewDashboardFolder("Test-Folder") |
|
|
|
dash := models.NewDashboardFolder("Test-Folder") |
|
|
|
dash.Id = rand.Int63() |
|
|
|
dash.Id = rand.Int63() |
|
|
|
|
|
|
|
|
|
|
|
ctx := appcontext.WithUser(context.Background(), usr) |
|
|
|
_, err := service.Create(context.Background(), &folder.CreateFolderCommand{ |
|
|
|
_, err := service.Create(ctx, &folder.CreateFolderCommand{ |
|
|
|
|
|
|
|
OrgID: orgID, |
|
|
|
OrgID: orgID, |
|
|
|
Title: dash.Title, |
|
|
|
Title: dash.Title, |
|
|
|
UID: "general", |
|
|
|
UID: "general", |
|
|
|
@ -235,9 +229,7 @@ func TestIntegrationFolderService(t *testing.T) { |
|
|
|
}).Return(nil).Once() |
|
|
|
}).Return(nil).Once() |
|
|
|
|
|
|
|
|
|
|
|
expectedForceDeleteRules := rand.Int63()%2 == 0 |
|
|
|
expectedForceDeleteRules := rand.Int63()%2 == 0 |
|
|
|
ctx := context.Background() |
|
|
|
err := service.DeleteFolder(context.Background(), &folder.DeleteFolderCommand{ |
|
|
|
ctx = appcontext.WithUser(ctx, usr) |
|
|
|
|
|
|
|
err := service.DeleteFolder(ctx, &folder.DeleteFolderCommand{ |
|
|
|
|
|
|
|
UID: f.UID, |
|
|
|
UID: f.UID, |
|
|
|
OrgID: orgID, |
|
|
|
OrgID: orgID, |
|
|
|
ForceDeleteRules: expectedForceDeleteRules, |
|
|
|
ForceDeleteRules: expectedForceDeleteRules, |
|
|
|
@ -338,11 +330,11 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) { |
|
|
|
dashboardStore: &dashStore, |
|
|
|
dashboardStore: &dashStore, |
|
|
|
dashboardService: &dashboardsvc, |
|
|
|
dashboardService: &dashboardsvc, |
|
|
|
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), |
|
|
|
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), |
|
|
|
|
|
|
|
log: log.New("test-folder-service"), |
|
|
|
} |
|
|
|
} |
|
|
|
t.Run("create folder", func(t *testing.T) { |
|
|
|
t.Run("create folder", func(t *testing.T) { |
|
|
|
folderStore.ExpectedFolder = &folder.Folder{} |
|
|
|
folderStore.ExpectedFolder = &folder.Folder{} |
|
|
|
ctx := appcontext.WithUser(context.Background(), usr) |
|
|
|
res, err := folderService.Create(context.Background(), &folder.CreateFolderCommand{SignedInUser: usr}) |
|
|
|
res, err := folderService.Create(ctx, &folder.CreateFolderCommand{SignedInUser: usr}) |
|
|
|
|
|
|
|
require.NoError(t, err) |
|
|
|
require.NoError(t, err) |
|
|
|
require.NotNil(t, res.UID) |
|
|
|
require.NotNil(t, res.UID) |
|
|
|
}) |
|
|
|
}) |
|
|
|
@ -379,7 +371,6 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) { |
|
|
|
|
|
|
|
|
|
|
|
func TestNestedFolderService(t *testing.T) { |
|
|
|
func TestNestedFolderService(t *testing.T) { |
|
|
|
t.Run("with feature flag unset", func(t *testing.T) { |
|
|
|
t.Run("with feature flag unset", func(t *testing.T) { |
|
|
|
ctx := appcontext.WithUser(context.Background(), usr) |
|
|
|
|
|
|
|
store := &FakeStore{} |
|
|
|
store := &FakeStore{} |
|
|
|
dashStore := dashboards.FakeDashboardStore{} |
|
|
|
dashStore := dashboards.FakeDashboardStore{} |
|
|
|
dashboardsvc := dashboards.FakeDashboardService{} |
|
|
|
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("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("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil) |
|
|
|
|
|
|
|
|
|
|
|
ctx = appcontext.WithUser(ctx, usr) |
|
|
|
_, err := foldersvc.Create(context.Background(), &folder.CreateFolderCommand{ |
|
|
|
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{ |
|
|
|
|
|
|
|
OrgID: orgID, |
|
|
|
OrgID: orgID, |
|
|
|
Title: "myFolder", |
|
|
|
Title: "myFolder", |
|
|
|
UID: "myFolder", |
|
|
|
UID: "myFolder", |
|
|
|
@ -425,7 +415,7 @@ func TestNestedFolderService(t *testing.T) { |
|
|
|
g := guardian.New |
|
|
|
g := guardian.New |
|
|
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true}) |
|
|
|
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.NoError(t, err) |
|
|
|
require.NotNil(t, actualCmd) |
|
|
|
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) { |
|
|
|
t.Run("with nested folder feature flag on", func(t *testing.T) { |
|
|
|
ctx := appcontext.WithUser(context.Background(), usr) |
|
|
|
|
|
|
|
store := &FakeStore{} |
|
|
|
store := &FakeStore{} |
|
|
|
dashStore := &dashboards.FakeDashboardStore{} |
|
|
|
dashStore := &dashboards.FakeDashboardStore{} |
|
|
|
dashboardsvc := &dashboards.FakeDashboardService{} |
|
|
|
dashboardsvc := &dashboards.FakeDashboardService{} |
|
|
|
@ -463,8 +452,7 @@ func TestNestedFolderService(t *testing.T) { |
|
|
|
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil) |
|
|
|
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("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("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil) |
|
|
|
ctx = appcontext.WithUser(ctx, usr) |
|
|
|
_, err := foldersvc.Create(context.Background(), &folder.CreateFolderCommand{ |
|
|
|
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{ |
|
|
|
|
|
|
|
OrgID: orgID, |
|
|
|
OrgID: orgID, |
|
|
|
Title: "myFolder", |
|
|
|
Title: "myFolder", |
|
|
|
UID: "myFolder", |
|
|
|
UID: "myFolder", |
|
|
|
@ -492,8 +480,7 @@ func TestNestedFolderService(t *testing.T) { |
|
|
|
store.ExpectedError = errors.New("FAILED") |
|
|
|
store.ExpectedError = errors.New("FAILED") |
|
|
|
|
|
|
|
|
|
|
|
// the service return success as long as the legacy create succeeds
|
|
|
|
// the service return success as long as the legacy create succeeds
|
|
|
|
ctx = appcontext.WithUser(ctx, usr) |
|
|
|
_, err := foldersvc.Create(context.Background(), &folder.CreateFolderCommand{ |
|
|
|
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{ |
|
|
|
|
|
|
|
OrgID: orgID, |
|
|
|
OrgID: orgID, |
|
|
|
Title: "myFolder", |
|
|
|
Title: "myFolder", |
|
|
|
UID: "myFolder", |
|
|
|
UID: "myFolder", |
|
|
|
@ -512,7 +499,7 @@ func TestNestedFolderService(t *testing.T) { |
|
|
|
t.Run("move, no error", func(t *testing.T) { |
|
|
|
t.Run("move, no error", func(t *testing.T) { |
|
|
|
store.ExpectedError = nil |
|
|
|
store.ExpectedError = nil |
|
|
|
store.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"} |
|
|
|
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.NoError(t, err) |
|
|
|
require.NotNil(t, f) |
|
|
|
require.NotNil(t, f) |
|
|
|
}) |
|
|
|
}) |
|
|
|
@ -527,7 +514,7 @@ func TestNestedFolderService(t *testing.T) { |
|
|
|
g := guardian.New |
|
|
|
g := guardian.New |
|
|
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true}) |
|
|
|
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.NoError(t, err) |
|
|
|
require.NotNil(t, actualCmd) |
|
|
|
require.NotNil(t, actualCmd) |
|
|
|
|
|
|
|
|
|
|
|
@ -536,5 +523,43 @@ func TestNestedFolderService(t *testing.T) { |
|
|
|
}) |
|
|
|
}) |
|
|
|
require.True(t, store.DeleteCalled) |
|
|
|
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) |
|
|
|
|
|
|
|
}) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|