Chore: Split folder store and dashboard store interfaces (#61655)

* update folder store mock

* Split folder store and dashboard store interfaces
This commit is contained in:
Sofia Papagiannaki 2023-01-19 18:38:07 +02:00 committed by GitHub
parent a6102105af
commit c104cc7020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 238 additions and 268 deletions

View File

@ -432,7 +432,7 @@ func setupHTTPServerWithCfgDb(
teamPermissionsService: teamPermissionService, teamPermissionsService: teamPermissionService,
searchUsersService: searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), usertest.NewUserServiceFake()), searchUsersService: searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), usertest.NewUserServiceFake()),
DashboardService: dashboardservice.ProvideDashboardService( DashboardService: dashboardservice.ProvideDashboardService(
cfg, dashboardsStore, nil, features, cfg, dashboardsStore, dashboardsStore, nil, features,
folderPermissionsService, dashboardPermissionsService, ac, folderPermissionsService, dashboardPermissionsService, ac,
), ),
preferenceService: preftest.NewPreferenceServiceFake(), preferenceService: preftest.NewPreferenceServiceFake(),

View File

@ -47,7 +47,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
SQLStore: mockSQLStore, SQLStore: mockSQLStore,
Features: features, Features: features,
DashboardService: dashboardservice.ProvideDashboardService( DashboardService: dashboardservice.ProvideDashboardService(
settings, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac, settings, dashboardStore, dashboards.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac,
), ),
AccessControl: accesscontrolmock.New().WithDisabled(), AccessControl: accesscontrolmock.New().WithDisabled(),
} }

View File

@ -177,7 +177,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUp() setUp()
sc.sqlStore = mockSQLStore sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService) dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil)
assert.False(t, dash.Meta.CanEdit) assert.False(t, dash.Meta.CanEdit)
assert.False(t, dash.Meta.CanSave) assert.False(t, dash.Meta.CanSave)
@ -209,7 +209,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUp() setUp()
sc.sqlStore = mockSQLStore sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService) dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil)
assert.True(t, dash.Meta.CanEdit) assert.True(t, dash.Meta.CanEdit)
assert.True(t, dash.Meta.CanSave) assert.True(t, dash.Meta.CanSave)
@ -393,7 +393,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUpInner() setUpInner()
sc.sqlStore = mockSQLStore sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService) dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil)
assert.True(t, dash.Meta.CanEdit) assert.True(t, dash.Meta.CanEdit)
assert.True(t, dash.Meta.CanSave) assert.True(t, dash.Meta.CanSave)
@ -456,7 +456,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
require.True(t, setting.ViewersCanEdit) require.True(t, setting.ViewersCanEdit)
sc.sqlStore = mockSQLStore sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService) dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil)
assert.True(t, dash.Meta.CanEdit) assert.True(t, dash.Meta.CanEdit)
assert.False(t, dash.Meta.CanSave) assert.False(t, dash.Meta.CanSave)
@ -494,7 +494,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUpInner() setUpInner()
sc.sqlStore = mockSQLStore sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService) dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil)
assert.True(t, dash.Meta.CanEdit) assert.True(t, dash.Meta.CanEdit)
assert.True(t, dash.Meta.CanSave) assert.True(t, dash.Meta.CanSave)
@ -547,7 +547,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) { loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUpInner() setUpInner()
sc.sqlStore = mockSQLStore sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService) dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService, nil)
assert.False(t, dash.Meta.CanEdit) assert.False(t, dash.Meta.CanEdit)
assert.False(t, dash.Meta.CanSave) assert.False(t, dash.Meta.CanSave)
@ -952,7 +952,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
return "/tmp/grafana/dashboards" return "/tmp/grafana/dashboards"
} }
dash := getDashboardShouldReturn200WithConfig(t, sc, fakeProvisioningService, dashboardStore, dashboardService) dash := getDashboardShouldReturn200WithConfig(t, sc, fakeProvisioningService, dashboardStore, dashboardService, nil)
assert.Equal(t, "../../../dashboard1.json", dash.Meta.ProvisionedExternalId, mockSQLStore) assert.Equal(t, "../../../dashboard1.json", dash.Meta.ProvisionedExternalId, mockSQLStore)
}, mockSQLStore) }, mockSQLStore)
@ -992,7 +992,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
}) })
} }
func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, provisioningService provisioning.ProvisioningService, dashboardStore dashboards.Store, dashboardService dashboards.DashboardService) dtos.DashboardFullWithMeta { func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, provisioningService provisioning.ProvisioningService, dashboardStore dashboards.Store, dashboardService dashboards.DashboardService, folderStore dashboards.FolderStore) dtos.DashboardFullWithMeta {
t.Helper() t.Helper()
if provisioningService == nil { if provisioningService == nil {
@ -1017,7 +1017,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
if dashboardService == nil { if dashboardService == nil {
dashboardService = service.ProvideDashboardService( dashboardService = service.ProvideDashboardService(
cfg, dashboardStore, nil, features, cfg, dashboardStore, folderStore, nil, features,
folderPermissions, dashboardPermissions, ac, folderPermissions, dashboardPermissions, ac,
) )
} }
@ -1030,7 +1030,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
ProvisioningService: provisioningService, ProvisioningService: provisioningService,
AccessControl: accesscontrolmock.New(), AccessControl: accesscontrolmock.New(),
dashboardProvisioningService: service.ProvideDashboardService( dashboardProvisioningService: service.ProvideDashboardService(
cfg, dashboardStore, nil, features, cfg, dashboardStore, folderStore, nil, features,
folderPermissions, dashboardPermissions, ac, folderPermissions, dashboardPermissions, ac,
), ),
DashboardService: dashboardService, DashboardService: dashboardService,

View File

@ -45,7 +45,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
folderPermissionsService: folderPermissions, folderPermissionsService: folderPermissions,
dashboardPermissionsService: dashboardPermissions, dashboardPermissionsService: dashboardPermissions,
DashboardService: service.ProvideDashboardService( DashboardService: service.ProvideDashboardService(
settings, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac, settings, dashboardStore, dashboards.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac,
), ),
AccessControl: accesscontrolmock.New().WithDisabled(), AccessControl: accesscontrolmock.New().WithDisabled(),
} }

View File

@ -257,6 +257,7 @@ var wireSet = wire.NewSet(
wire.Bind(new(dashboards.DashboardProvisioningService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.DashboardProvisioningService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.PluginService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.PluginService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)), wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)),
wire.Bind(new(dashboards.FolderStore), new(*dashboardstore.DashboardStore)),
dashboardimportservice.ProvideService, dashboardimportservice.ProvideService,
wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)), wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)),
plugindashboardsservice.ProvideService, plugindashboardsservice.ProvideService,

View File

@ -293,6 +293,7 @@ var wireBasicSet = wire.NewSet(
wire.Bind(new(dashboards.DashboardProvisioningService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.DashboardProvisioningService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.PluginService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.PluginService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)), wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)),
wire.Bind(new(dashboards.FolderStore), new(*dashboardstore.DashboardStore)),
dashboardimportservice.ProvideService, dashboardimportservice.ProvideService,
wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)), wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)),
plugindashboardsservice.ProvideService, plugindashboardsservice.ProvideService,

View File

@ -38,7 +38,7 @@ var (
) )
// NewFolderNameScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:name:" into an uid based scope. // NewFolderNameScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:name:" into an uid based scope.
func NewFolderNameScopeResolver(db Store) (string, ac.ScopeAttributeResolver) { func NewFolderNameScopeResolver(db Store, folderDB FolderStore) (string, ac.ScopeAttributeResolver) {
prefix := ScopeFoldersProvider.GetResourceScopeName("") prefix := ScopeFoldersProvider.GetResourceScopeName("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) { if !strings.HasPrefix(scope, prefix) {
@ -48,7 +48,7 @@ func NewFolderNameScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
if len(nsName) == 0 { if len(nsName) == 0 {
return nil, ac.ErrInvalidScope return nil, ac.ErrInvalidScope
} }
folder, err := db.GetFolderByTitle(ctx, orgID, nsName) folder, err := folderDB.GetFolderByTitle(ctx, orgID, nsName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -57,7 +57,7 @@ func NewFolderNameScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
} }
// NewFolderIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:id:" into an uid based scope. // NewFolderIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:id:" into an uid based scope.
func NewFolderIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) { func NewFolderIDScopeResolver(db Store, folderDB FolderStore) (string, ac.ScopeAttributeResolver) {
prefix := ScopeFoldersProvider.GetResourceScope("") prefix := ScopeFoldersProvider.GetResourceScope("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) { if !strings.HasPrefix(scope, prefix) {
@ -73,7 +73,7 @@ func NewFolderIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
return []string{ScopeFoldersProvider.GetResourceScopeUID(ac.GeneralFolderUID)}, nil return []string{ScopeFoldersProvider.GetResourceScopeUID(ac.GeneralFolderUID)}, nil
} }
folder, err := db.GetFolderByID(ctx, orgID, id) folder, err := folderDB.GetFolderByID(ctx, orgID, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -84,7 +84,7 @@ func NewFolderIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
// NewDashboardIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:id:" // NewDashboardIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:id:"
// into uid based scopes for both dashboard and folder // into uid based scopes for both dashboard and folder
func NewDashboardIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) { func NewDashboardIDScopeResolver(db Store, folderDB FolderStore) (string, ac.ScopeAttributeResolver) {
prefix := ScopeDashboardsProvider.GetResourceScope("") prefix := ScopeDashboardsProvider.GetResourceScope("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) { if !strings.HasPrefix(scope, prefix) {
@ -101,13 +101,13 @@ func NewDashboardIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
return nil, err return nil, err
} }
return resolveDashboardScope(ctx, db, orgID, dashboard) return resolveDashboardScope(ctx, db, folderDB, orgID, dashboard)
}) })
} }
// NewDashboardUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:uid:" // NewDashboardUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:uid:"
// into uid based scopes for both dashboard and folder // into uid based scopes for both dashboard and folder
func NewDashboardUIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) { func NewDashboardUIDScopeResolver(db Store, folderDB FolderStore) (string, ac.ScopeAttributeResolver) {
prefix := ScopeDashboardsProvider.GetResourceScopeUID("") prefix := ScopeDashboardsProvider.GetResourceScopeUID("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) { return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) { if !strings.HasPrefix(scope, prefix) {
@ -124,11 +124,11 @@ func NewDashboardUIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver)
return nil, err return nil, err
} }
return resolveDashboardScope(ctx, db, orgID, dashboard) return resolveDashboardScope(ctx, db, folderDB, orgID, dashboard)
}) })
} }
func resolveDashboardScope(ctx context.Context, db Store, orgID int64, dashboard *Dashboard) ([]string, error) { func resolveDashboardScope(ctx context.Context, db Store, folderDB FolderStore, orgID int64, dashboard *Dashboard) ([]string, error) {
var folderUID string var folderUID string
if dashboard.FolderID < 0 { if dashboard.FolderID < 0 {
return []string{ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID)}, nil return []string{ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID)}, nil
@ -137,7 +137,7 @@ func resolveDashboardScope(ctx context.Context, db Store, orgID int64, dashboard
if dashboard.FolderID == 0 { if dashboard.FolderID == 0 {
folderUID = ac.GeneralFolderUID folderUID = ac.GeneralFolderUID
} else { } else {
folder, err := db.GetFolderByID(ctx, orgID, dashboard.FolderID) folder, err := folderDB.GetFolderByID(ctx, orgID, dashboard.FolderID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,41 +17,42 @@ import (
func TestNewFolderNameScopeResolver(t *testing.T) { func TestNewFolderNameScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewFolderNameScopeResolver(&FakeDashboardStore{}) prefix, _ := NewFolderNameScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t))
require.Equal(t, "folders:name:", prefix) require.Equal(t, "folders:name:", prefix)
}) })
t.Run("resolver should convert to uid scope", func(t *testing.T) { t.Run("resolver should convert to uid scope", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderNameScopeResolver(dashboardStore)
orgId := rand.Int63() orgId := rand.Int63()
title := "Very complex :title with: and /" + util.GenerateShortUID() title := "Very complex :title with: and /" + util.GenerateShortUID()
db := &folder.Folder{Title: title, ID: rand.Int63(), UID: util.GenerateShortUID()} db := &folder.Folder{Title: title, ID: rand.Int63(), UID: util.GenerateShortUID()}
dashboardStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
folderStore := NewFakeFolderStore(t)
folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
scope := "folders:name:" + title scope := "folders:name:" + title
_, resolver := NewFolderNameScopeResolver(dashboardStore, folderStore)
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, resolvedScopes, 1) require.Len(t, resolvedScopes, 1)
require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0]) require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0])
dashboardStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title) folderStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title)
}) })
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderNameScopeResolver(dashboardStore) _, resolver := NewFolderNameScopeResolver(dashboardStore, NewFakeFolderStore(t))
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:123") _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:123")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
}) })
t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) { t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderNameScopeResolver(dashboardStore) _, resolver := NewFolderNameScopeResolver(dashboardStore, NewFakeFolderStore(t))
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:name:") _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:name:")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
@ -59,10 +60,11 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
t.Run("returns 'not found' if folder does not exist", func(t *testing.T) { t.Run("returns 'not found' if folder does not exist", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderNameScopeResolver(dashboardStore) folderStore := NewFakeFolderStore(t)
_, resolver := NewFolderNameScopeResolver(dashboardStore, folderStore)
orgId := rand.Int63() orgId := rand.Int63()
dashboardStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once() folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once()
scope := "folders:name:" + util.GenerateShortUID() scope := "folders:name:" + util.GenerateShortUID()
@ -74,20 +76,21 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
func TestNewFolderIDScopeResolver(t *testing.T) { func TestNewFolderIDScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewFolderIDScopeResolver(&FakeDashboardStore{}) prefix, _ := NewFolderIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t))
require.Equal(t, "folders:id:", prefix) require.Equal(t, "folders:id:", prefix)
}) })
t.Run("resolver should convert to uid scope", func(t *testing.T) { t.Run("resolver should convert to uid scope", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
_, resolver := NewFolderIDScopeResolver(dashboardStore) _, resolver := NewFolderIDScopeResolver(dashboardStore, folderStore)
orgId := rand.Int63() orgId := rand.Int63()
uid := util.GenerateShortUID() uid := util.GenerateShortUID()
db := &folder.Folder{ID: rand.Int63(), UID: uid} db := &folder.Folder{ID: rand.Int63(), UID: uid}
dashboardStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once() folderStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
scope := "folders:id:" + strconv.FormatInt(db.ID, 10) scope := "folders:id:" + strconv.FormatInt(db.ID, 10)
@ -96,11 +99,11 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
require.Len(t, resolvedScopes, 1) require.Len(t, resolvedScopes, 1)
require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0]) require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0])
dashboardStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.ID) folderStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.ID)
}) })
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderIDScopeResolver(dashboardStore) _, resolver := NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t))
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:uid:123") _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:uid:123")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
@ -111,7 +114,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
dashboardStore = &FakeDashboardStore{} dashboardStore = &FakeDashboardStore{}
orgId = rand.Int63() orgId = rand.Int63()
scope = "folders:id:0" scope = "folders:id:0"
_, resolver = NewFolderIDScopeResolver(dashboardStore) _, resolver = NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t))
) )
resolved, err := resolver.Resolve(context.Background(), orgId, scope) resolved, err := resolver.Resolve(context.Background(), orgId, scope)
@ -123,18 +126,19 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) { t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderIDScopeResolver(dashboardStore) _, resolver := NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t))
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:") _, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
}) })
t.Run("returns 'not found' if folder does not exist", func(t *testing.T) { t.Run("returns 'not found' if folder does not exist", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{} dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
_, resolver := NewFolderIDScopeResolver(dashboardStore) _, resolver := NewFolderIDScopeResolver(dashboardStore, folderStore)
orgId := rand.Int63() orgId := rand.Int63()
dashboardStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once() folderStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once()
scope := "folders:id:10" scope := "folders:id:10"
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
@ -145,21 +149,22 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
func TestNewDashboardIDScopeResolver(t *testing.T) { func TestNewDashboardIDScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewDashboardIDScopeResolver(&FakeDashboardStore{}) prefix, _ := NewDashboardIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t))
require.Equal(t, "dashboards:id:", prefix) require.Equal(t, "dashboards:id:", prefix)
}) })
t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) { t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) {
store := &FakeDashboardStore{} store := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
_, resolver := NewDashboardIDScopeResolver(store) _, resolver := NewDashboardIDScopeResolver(store, folderStore)
orgID := rand.Int63() orgID := rand.Int63()
folder := &folder.Folder{ID: 2, UID: "2"} folder := &folder.Folder{ID: 2, UID: "2"}
dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"} dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"}
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once()
store.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once() folderStore.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once()
scope := ac.Scope("dashboards", "id", strconv.FormatInt(dashboard.ID, 10)) scope := ac.Scope("dashboards", "id", strconv.FormatInt(dashboard.ID, 10))
resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope) resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope)
@ -170,14 +175,14 @@ func TestNewDashboardIDScopeResolver(t *testing.T) {
}) })
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
_, resolver := NewDashboardIDScopeResolver(&FakeDashboardStore{}) _, resolver := NewDashboardIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t))
_, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:uid:123") _, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:uid:123")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
}) })
t.Run("resolver should convert folderID 0 to general uid scope for the folder scope", func(t *testing.T) { t.Run("resolver should convert folderID 0 to general uid scope for the folder scope", func(t *testing.T) {
store := &FakeDashboardStore{} store := &FakeDashboardStore{}
_, resolver := NewDashboardIDScopeResolver(store) _, resolver := NewDashboardIDScopeResolver(store, NewFakeFolderStore(t))
dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"} dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"}
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil) store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil)
@ -192,20 +197,21 @@ func TestNewDashboardIDScopeResolver(t *testing.T) {
func TestNewDashboardUIDScopeResolver(t *testing.T) { func TestNewDashboardUIDScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) { t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewDashboardUIDScopeResolver(&FakeDashboardStore{}) prefix, _ := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t))
require.Equal(t, "dashboards:uid:", prefix) require.Equal(t, "dashboards:uid:", prefix)
}) })
t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) { t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) {
store := &FakeDashboardStore{} store := &FakeDashboardStore{}
_, resolver := NewDashboardUIDScopeResolver(store) folderStore := NewFakeFolderStore(t)
_, resolver := NewDashboardUIDScopeResolver(store, folderStore)
orgID := rand.Int63() orgID := rand.Int63()
folder := &folder.Folder{ID: 2, UID: "2"} folder := &folder.Folder{ID: 2, UID: "2"}
dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"} dashboard := &Dashboard{ID: 1, FolderID: folder.ID, UID: "1"}
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once()
store.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once() folderStore.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once()
scope := ac.Scope("dashboards", "uid", dashboard.UID) scope := ac.Scope("dashboards", "uid", dashboard.UID)
resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope) resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope)
@ -216,14 +222,14 @@ func TestNewDashboardUIDScopeResolver(t *testing.T) {
}) })
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
_, resolver := NewDashboardUIDScopeResolver(&FakeDashboardStore{}) _, resolver := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t))
_, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:id:123") _, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:id:123")
require.ErrorIs(t, err, ac.ErrInvalidScope) require.ErrorIs(t, err, ac.ErrInvalidScope)
}) })
t.Run("resolver should convert folderID 0 to general uid scope for the folder scope", func(t *testing.T) { t.Run("resolver should convert folderID 0 to general uid scope for the folder scope", func(t *testing.T) {
store := &FakeDashboardStore{} store := &FakeDashboardStore{}
_, resolver := NewDashboardUIDScopeResolver(store) _, resolver := NewDashboardUIDScopeResolver(store, NewFakeFolderStore(t))
dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"} dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"}
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil) store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil)

View File

@ -83,8 +83,6 @@ type Store interface {
// CountDashboardsInFolder returns the number of dashboards associated with // CountDashboardsInFolder returns the number of dashboards associated with
// the given parent folder ID. // the given parent folder ID.
CountDashboardsInFolder(ctx context.Context, request *CountDashboardsInFolderRequest) (int64, error) CountDashboardsInFolder(ctx context.Context, request *CountDashboardsInFolderRequest) (int64, error)
FolderStore
} }
// FolderStore is a folder store. // FolderStore is a folder store.

View File

@ -37,6 +37,7 @@ type DashboardServiceImpl struct {
cfg *setting.Cfg cfg *setting.Cfg
log log.Logger log log.Logger
dashboardStore dashboards.Store dashboardStore dashboards.Store
folderStore dashboards.FolderStore
dashAlertExtractor alerting.DashAlertExtractor dashAlertExtractor alerting.DashAlertExtractor
features featuremgmt.FeatureToggles features featuremgmt.FeatureToggles
folderPermissions accesscontrol.FolderPermissionsService folderPermissions accesscontrol.FolderPermissionsService
@ -45,17 +46,17 @@ type DashboardServiceImpl struct {
} }
func ProvideDashboardService( func ProvideDashboardService(
cfg *setting.Cfg, store dashboards.Store, dashAlertExtractor alerting.DashAlertExtractor, cfg *setting.Cfg, dashboardStore dashboards.Store, folderStore dashboards.FolderStore, dashAlertExtractor alerting.DashAlertExtractor,
features featuremgmt.FeatureToggles, folderPermissionsService accesscontrol.FolderPermissionsService, features featuremgmt.FeatureToggles, folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService, ac accesscontrol.AccessControl, dashboardPermissionsService accesscontrol.DashboardPermissionsService, ac accesscontrol.AccessControl,
) *DashboardServiceImpl { ) *DashboardServiceImpl {
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(store)) ac.RegisterScopeAttributeResolver(dashboards.NewDashboardIDScopeResolver(dashboardStore, folderStore))
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(store)) ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashboardStore, folderStore))
return &DashboardServiceImpl{ return &DashboardServiceImpl{
cfg: cfg, cfg: cfg,
log: log.New("dashboard-service"), log: log.New("dashboard-service"),
dashboardStore: store, dashboardStore: dashboardStore,
dashAlertExtractor: dashAlertExtractor, dashAlertExtractor: dashAlertExtractor,
features: features, features: features,
folderPermissions: folderPermissionsService, folderPermissions: folderPermissionsService,
@ -623,7 +624,7 @@ func (dr DashboardServiceImpl) CountDashboardsInFolder(ctx context.Context, quer
return 0, err return 0, err
} }
folder, err := dr.dashboardStore.GetFolderByUID(ctx, u.OrgID, query.FolderUID) folder, err := dr.folderStore.GetFolderByUID(ctx, u.OrgID, query.FolderUID)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -826,7 +826,7 @@ func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionSc
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err) require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
@ -885,7 +885,7 @@ func callSaveWithResult(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSt
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err) require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
@ -906,7 +906,7 @@ func callSaveWithError(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSto
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err) require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
@ -945,7 +945,7 @@ func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlSto
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err) require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
@ -985,7 +985,7 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore db.DB) *da
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err) require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),

View File

@ -25,10 +25,13 @@ func TestDashboardService(t *testing.T) {
fakeStore := dashboards.FakeDashboardStore{} fakeStore := dashboards.FakeDashboardStore{}
defer fakeStore.AssertExpectations(t) defer fakeStore.AssertExpectations(t)
folderStore := dashboards.NewFakeFolderStore(t)
service := &DashboardServiceImpl{ service := &DashboardServiceImpl{
cfg: setting.NewCfg(), cfg: setting.NewCfg(),
log: log.New("test.logger"), log: log.New("test.logger"),
dashboardStore: &fakeStore, dashboardStore: &fakeStore,
folderStore: folderStore,
dashAlertExtractor: &dummyDashAlertExtractor{}, dashAlertExtractor: &dummyDashAlertExtractor{},
} }
@ -227,7 +230,7 @@ func TestDashboardService(t *testing.T) {
}) })
t.Run("Count dashboards in folder", func(t *testing.T) { t.Run("Count dashboards in folder", func(t *testing.T) {
fakeStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil) folderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
fakeStore.On("CountDashboardsInFolder", mock.Anything, mock.AnythingOfType("*dashboards.CountDashboardsInFolderRequest")).Return(int64(3), nil) fakeStore.On("CountDashboardsInFolder", mock.Anything, mock.AnythingOfType("*dashboards.CountDashboardsInFolderRequest")).Return(int64(3), nil)
// set up a ctx with signed in user // set up a ctx with signed in user

View File

@ -5,10 +5,8 @@ package dashboards
import ( import (
context "context" context "context"
folder "github.com/grafana/grafana/pkg/services/folder"
mock "github.com/stretchr/testify/mock"
models "github.com/grafana/grafana/pkg/models" models "github.com/grafana/grafana/pkg/models"
mock "github.com/stretchr/testify/mock"
quota "github.com/grafana/grafana/pkg/services/quota" quota "github.com/grafana/grafana/pkg/services/quota"
) )
@ -220,75 +218,6 @@ func (_m *FakeDashboardStore) GetDashboardsByPluginID(ctx context.Context, query
return r0 return r0
} }
// GetFolderByID provides a mock function with given fields: ctx, orgID, id
func (_m *FakeDashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error) {
ret := _m.Called(ctx, orgID, id)
var r0 *folder.Folder
if rf, ok := ret.Get(0).(func(context.Context, int64, int64) *folder.Folder); ok {
r0 = rf(ctx, orgID, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*folder.Folder)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64, int64) error); ok {
r1 = rf(ctx, orgID, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetFolderByTitle provides a mock function with given fields: ctx, orgID, title
func (_m *FakeDashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error) {
ret := _m.Called(ctx, orgID, title)
var r0 *folder.Folder
if rf, ok := ret.Get(0).(func(context.Context, int64, string) *folder.Folder); ok {
r0 = rf(ctx, orgID, title)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*folder.Folder)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok {
r1 = rf(ctx, orgID, title)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetFolderByUID provides a mock function with given fields: ctx, orgID, uid
func (_m *FakeDashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error) {
ret := _m.Called(ctx, orgID, uid)
var r0 *folder.Folder
if rf, ok := ret.Get(0).(func(context.Context, int64, string) *folder.Folder); ok {
r0 = rf(ctx, orgID, uid)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*folder.Folder)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok {
r1 = rf(ctx, orgID, uid)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetProvisionedDashboardData provides a mock function with given fields: ctx, name // GetProvisionedDashboardData provides a mock function with given fields: ctx, name
func (_m *FakeDashboardStore) GetProvisionedDashboardData(ctx context.Context, name string) ([]*DashboardProvisioning, error) { func (_m *FakeDashboardStore) GetProvisionedDashboardData(ctx context.Context, name string) ([]*DashboardProvisioning, error) {
ret := _m.Called(ctx, name) ret := _m.Called(ctx, name)

View File

@ -30,13 +30,14 @@ import (
type Service struct { type Service struct {
store store store store
log log.Logger log log.Logger
cfg *setting.Cfg cfg *setting.Cfg
dashboardStore dashboards.Store dashboardStore dashboards.Store
searchService *search.SearchService dashboardFolderStore dashboards.FolderStore
features featuremgmt.FeatureToggles searchService *search.SearchService
permissions accesscontrol.FolderPermissionsService features featuremgmt.FeatureToggles
accessControl accesscontrol.AccessControl permissions accesscontrol.FolderPermissionsService
accessControl accesscontrol.AccessControl
// bus is currently used to publish events that cause scheduler to update rules. // bus is currently used to publish events that cause scheduler to update rules.
bus bus.Bus bus bus.Bus
@ -47,24 +48,26 @@ func ProvideService(
bus bus.Bus, bus bus.Bus,
cfg *setting.Cfg, cfg *setting.Cfg,
dashboardStore dashboards.Store, dashboardStore dashboards.Store,
folderStore dashboards.FolderStore,
db db.DB, // DB for the (new) nested folder store db db.DB, // DB for the (new) nested folder store
features featuremgmt.FeatureToggles, features featuremgmt.FeatureToggles,
folderPermissionsService accesscontrol.FolderPermissionsService, folderPermissionsService accesscontrol.FolderPermissionsService,
searchService *search.SearchService, searchService *search.SearchService,
) folder.Service { ) folder.Service {
ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(dashboardStore)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(dashboardStore, folderStore))
ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(dashboardStore)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(dashboardStore, folderStore))
store := ProvideStore(db, cfg, features) store := ProvideStore(db, cfg, features)
svr := &Service{ svr := &Service{
cfg: cfg, cfg: cfg,
log: log.New("folder-service"), log: log.New("folder-service"),
dashboardStore: dashboardStore, dashboardStore: dashboardStore,
store: store, dashboardFolderStore: folderStore,
searchService: searchService, store: store,
features: features, searchService: searchService,
permissions: folderPermissionsService, features: features,
accessControl: ac, permissions: folderPermissionsService,
bus: bus, accessControl: ac,
bus: bus,
} }
if features.IsEnabled(featuremgmt.FlagNestedFolders) { if features.IsEnabled(featuremgmt.FlagNestedFolders) {
svr.DBMigration(db) svr.DBMigration(db)
@ -166,7 +169,7 @@ func (s *Service) GetChildren(ctx context.Context, cmd *folder.GetChildrenQuery)
filtered := make([]*folder.Folder, 0, len(children)) filtered := make([]*folder.Folder, 0, len(children))
for _, f := range children { for _, f := range children {
// fetch folder from dashboard store // fetch folder from dashboard store
dashFolder, err := s.dashboardStore.GetFolderByUID(ctx, f.OrgID, f.UID) dashFolder, err := s.dashboardFolderStore.GetFolderByUID(ctx, f.OrgID, f.UID)
if err != nil { if err != nil {
s.log.Error("failed to fetch folder by UID: %s from dashboard store", f.UID, err) s.log.Error("failed to fetch folder by UID: %s from dashboard store", f.UID, err)
continue continue
@ -220,7 +223,7 @@ func (s *Service) getFolderByID(ctx context.Context, user *user.SignedInUser, id
return &folder.Folder{ID: id, Title: "General"}, nil return &folder.Folder{ID: id, Title: "General"}, nil
} }
dashFolder, err := s.dashboardStore.GetFolderByID(ctx, orgID, id) dashFolder, err := s.dashboardFolderStore.GetFolderByID(ctx, orgID, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -244,7 +247,7 @@ func (s *Service) getFolderByID(ctx context.Context, user *user.SignedInUser, id
} }
func (s *Service) getFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*folder.Folder, error) { func (s *Service) getFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*folder.Folder, error) {
dashFolder, err := s.dashboardStore.GetFolderByUID(ctx, orgID, uid) dashFolder, err := s.dashboardFolderStore.GetFolderByUID(ctx, orgID, uid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -268,7 +271,7 @@ func (s *Service) getFolderByUID(ctx context.Context, user *user.SignedInUser, o
} }
func (s *Service) getFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*folder.Folder, error) { func (s *Service) getFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*folder.Folder, error) {
dashFolder, err := s.dashboardStore.GetFolderByTitle(ctx, orgID, title) dashFolder, err := s.dashboardFolderStore.GetFolderByTitle(ctx, orgID, title)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -330,7 +333,7 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
} }
var createdFolder *folder.Folder var createdFolder *folder.Folder
createdFolder, err = s.dashboardStore.GetFolderByID(ctx, cmd.OrgID, dash.ID) createdFolder, err = s.dashboardFolderStore.GetFolderByID(ctx, cmd.OrgID, dash.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -475,7 +478,7 @@ func (s *Service) legacyUpdate(ctx context.Context, cmd *folder.UpdateFolderComm
} }
var foldr *folder.Folder var foldr *folder.Folder
foldr, err = s.dashboardStore.GetFolderByID(ctx, cmd.OrgID, dash.ID) foldr, err = s.dashboardFolderStore.GetFolderByID(ctx, cmd.OrgID, dash.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -534,7 +537,7 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e
} }
} }
dashFolder, err := s.dashboardStore.GetFolderByUID(ctx, cmd.OrgID, cmd.UID) dashFolder, err := s.dashboardFolderStore.GetFolderByUID(ctx, cmd.OrgID, cmd.UID)
if err != nil { if err != nil {
return err return err
} }

View File

@ -38,7 +38,7 @@ func TestIntegrationProvideFolderService(t *testing.T) {
t.Run("should register scope resolvers", func(t *testing.T) { t.Run("should register scope resolvers", func(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
ac := acmock.New() ac := acmock.New()
ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, nil, nil, &featuremgmt.FeatureManager{}, nil, nil) ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, nil, nil, nil, &featuremgmt.FeatureManager{}, nil, nil)
require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 2) require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 2)
}) })
@ -51,7 +51,9 @@ func TestIntegrationFolderService(t *testing.T) {
t.Run("Folder service tests", func(t *testing.T) { t.Run("Folder service tests", func(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
db := sqlstore.InitTestDB(t) db := sqlstore.InitTestDB(t)
store := ProvideStore(db, db.Cfg, featuremgmt.WithFeatures([]interface{}{"nestedFolders"})) nestedFolderStore := ProvideStore(db, db.Cfg, featuremgmt.WithFeatures([]interface{}{"nestedFolders"}))
folderStore := dashboards.NewFakeFolderStore(t)
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = false cfg.RBACEnabled = false
@ -59,14 +61,15 @@ func TestIntegrationFolderService(t *testing.T) {
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
service := &Service{ service := &Service{
cfg: cfg, cfg: cfg,
log: log.New("test-folder-service"), log: log.New("test-folder-service"),
dashboardStore: dashStore, dashboardStore: dashStore,
store: store, dashboardFolderStore: folderStore,
searchService: nil, store: nestedFolderStore,
features: features, searchService: nil,
permissions: folderPermissions, features: features,
bus: bus.ProvideBus(tracing.InitializeTracerForTest()), permissions: folderPermissions,
bus: bus.ProvideBus(tracing.InitializeTracerForTest()),
} }
t.Run("Given user has no permissions", func(t *testing.T) { t.Run("Given user has no permissions", func(t *testing.T) {
@ -80,8 +83,8 @@ func TestIntegrationFolderService(t *testing.T) {
f.ID = folderId f.ID = folderId
f.UID = folderUID f.UID = folderUID
dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(f, nil) folderStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(f, nil)
dashStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(f, nil) folderStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(f, nil)
t.Run("When get folder by id should return access denied error", func(t *testing.T) { t.Run("When get folder by id should return access denied error", func(t *testing.T) {
_, err := service.Get(context.Background(), &folder.GetFolderQuery{ _, err := service.Get(context.Background(), &folder.GetFolderQuery{
@ -143,8 +146,8 @@ func TestIntegrationFolderService(t *testing.T) {
newFolder := models.NewFolder("Folder") newFolder := models.NewFolder("Folder")
newFolder.Uid = folderUID newFolder.Uid = folderUID
dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(newFolder, nil) folderStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(newFolder, nil)
dashStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(newFolder, nil) folderStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(newFolder, nil)
err := service.Delete(context.Background(), &folder.DeleteFolderCommand{ err := service.Delete(context.Background(), &folder.DeleteFolderCommand{
UID: folderUID, UID: folderUID,
@ -172,7 +175,7 @@ func TestIntegrationFolderService(t *testing.T) {
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(dash, nil).Once() dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(dash, nil).Once()
dashStore.On("GetFolderByID", mock.Anything, orgID, dash.ID).Return(f, nil) folderStore.On("GetFolderByID", mock.Anything, orgID, dash.ID).Return(f, nil)
actualFolder, err := service.Create(context.Background(), &folder.CreateFolderCommand{ actualFolder, err := service.Create(context.Background(), &folder.CreateFolderCommand{
OrgID: orgID, OrgID: orgID,
@ -205,7 +208,7 @@ func TestIntegrationFolderService(t *testing.T) {
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(dashboardFolder, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(dashboardFolder, nil)
dashStore.On("GetFolderByID", mock.Anything, orgID, dashboardFolder.ID).Return(f, nil) folderStore.On("GetFolderByID", mock.Anything, orgID, dashboardFolder.ID).Return(f, nil)
title := "TEST-Folder" title := "TEST-Folder"
req := &folder.UpdateFolderCommand{ req := &folder.UpdateFolderCommand{
@ -224,7 +227,7 @@ func TestIntegrationFolderService(t *testing.T) {
f := folder.NewFolder(util.GenerateShortUID(), "") f := folder.NewFolder(util.GenerateShortUID(), "")
f.ID = rand.Int63() f.ID = rand.Int63()
f.UID = util.GenerateShortUID() f.UID = util.GenerateShortUID()
dashStore.On("GetFolderByUID", mock.Anything, orgID, f.UID).Return(f, nil) folderStore.On("GetFolderByUID", mock.Anything, orgID, f.UID).Return(f, nil)
var actualCmd *dashboards.DeleteDashboardCommand var actualCmd *dashboards.DeleteDashboardCommand
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
@ -258,7 +261,7 @@ func TestIntegrationFolderService(t *testing.T) {
expected := folder.NewFolder(util.GenerateShortUID(), "") expected := folder.NewFolder(util.GenerateShortUID(), "")
expected.ID = rand.Int63() expected.ID = rand.Int63()
dashStore.On("GetFolderByID", mock.Anything, orgID, expected.ID).Return(expected, nil) folderStore.On("GetFolderByID", mock.Anything, orgID, expected.ID).Return(expected, nil)
actual, err := service.getFolderByID(context.Background(), usr, expected.ID, orgID) actual, err := service.getFolderByID(context.Background(), usr, expected.ID, orgID)
require.Equal(t, expected, actual) require.Equal(t, expected, actual)
@ -269,7 +272,7 @@ func TestIntegrationFolderService(t *testing.T) {
expected := folder.NewFolder(util.GenerateShortUID(), "") expected := folder.NewFolder(util.GenerateShortUID(), "")
expected.UID = util.GenerateShortUID() expected.UID = util.GenerateShortUID()
dashStore.On("GetFolderByUID", mock.Anything, orgID, expected.UID).Return(expected, nil) folderStore.On("GetFolderByUID", mock.Anything, orgID, expected.UID).Return(expected, nil)
actual, err := service.getFolderByUID(context.Background(), usr, orgID, expected.UID) actual, err := service.getFolderByUID(context.Background(), usr, orgID, expected.UID)
require.Equal(t, expected, actual) require.Equal(t, expected, actual)
@ -279,7 +282,7 @@ func TestIntegrationFolderService(t *testing.T) {
t.Run("When get folder by title should return folder", func(t *testing.T) { t.Run("When get folder by title should return folder", func(t *testing.T) {
expected := folder.NewFolder("TEST-"+util.GenerateShortUID(), "") expected := folder.NewFolder("TEST-"+util.GenerateShortUID(), "")
dashStore.On("GetFolderByTitle", mock.Anything, orgID, expected.Title).Return(expected, nil) folderStore.On("GetFolderByTitle", mock.Anything, orgID, expected.Title).Return(expected, nil)
actual, err := service.getFolderByTitle(context.Background(), usr, orgID, expected.Title) actual, err := service.getFolderByTitle(context.Background(), usr, orgID, expected.Title)
require.Equal(t, expected, actual) require.Equal(t, expected, actual)
@ -322,23 +325,27 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
guardian.New = g guardian.New = g
}) })
folderStore := NewFakeStore() nestedFolderStore := NewFakeStore()
dashStore := dashboards.FakeDashboardStore{} dashStore := dashboards.FakeDashboardStore{}
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{}, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{}, nil)
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = false cfg.RBACEnabled = false
folderService := &Service{ folderService := &Service{
cfg: cfg, cfg: cfg,
store: folderStore, store: nestedFolderStore,
dashboardStore: &dashStore, dashboardStore: &dashStore,
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), dashboardFolderStore: dashboardFolderStore,
log: log.New("test-folder-service"), 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{ParentUID: util.GenerateShortUID()} nestedFolderStore.ExpectedFolder = &folder.Folder{ParentUID: util.GenerateShortUID()}
res, err := folderService.Create(context.Background(), &folder.CreateFolderCommand{SignedInUser: usr, Title: "my folder"}) res, err := folderService.Create(context.Background(), &folder.CreateFolderCommand{SignedInUser: usr, Title: "my folder"})
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, res.UID) require.NotNil(t, res.UID)
@ -359,11 +366,13 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{}, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{}, nil)
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
folderStore := NewFakeStore() dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures(), nil) nestedFolderStore := NewFakeStore()
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures(), nil)
_, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{ _, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
OrgID: orgID, OrgID: orgID,
Title: "myFolder", Title: "myFolder",
@ -372,7 +381,7 @@ func TestNestedFolderService(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
// CreateFolder should not call the folder store create if the feature toggle is not enabled. // CreateFolder should not call the folder store create if the feature toggle is not enabled.
require.False(t, folderStore.CreateCalled) require.False(t, nestedFolderStore.CreateCalled)
}) })
t.Run("When delete folder, no delete in folder table done", func(t *testing.T) { t.Run("When delete folder, no delete in folder table done", func(t *testing.T) {
@ -381,16 +390,18 @@ func TestNestedFolderService(t *testing.T) {
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand) actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once() }).Return(nil).Once()
dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
folderStore := NewFakeStore() dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures(), nil) nestedFolderStore := NewFakeStore()
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures(), nil)
err := folderSvc.Delete(context.Background(), &folder.DeleteFolderCommand{UID: "myFolder", OrgID: orgID, SignedInUser: usr}) err := folderSvc.Delete(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)
require.False(t, folderStore.DeleteCalled) require.False(t, nestedFolderStore.DeleteCalled)
}) })
}) })
@ -406,11 +417,13 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{}, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{}, nil)
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
folderStore := NewFakeStore() dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ nestedFolderStore := NewFakeStore()
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
_, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{ _, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
@ -421,7 +434,7 @@ func TestNestedFolderService(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
// CreateFolder should also call the folder store's create method. // CreateFolder should also call the folder store's create method.
require.True(t, folderStore.CreateCalled) require.True(t, nestedFolderStore.CreateCalled)
}) })
t.Run("create without UID, no error", func(t *testing.T) { t.Run("create without UID, no error", func(t *testing.T) {
@ -435,11 +448,13 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{UID: "newUID"}, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{UID: "newUID"}, nil)
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
folderStore := NewFakeStore() dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ nestedFolderStore := NewFakeStore()
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
f, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{ f, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
@ -449,7 +464,7 @@ func TestNestedFolderService(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
// CreateFolder should also call the folder store's create method. // CreateFolder should also call the folder store's create method.
require.True(t, folderStore.CreateCalled) require.True(t, nestedFolderStore.CreateCalled)
require.Equal(t, "newUID", f.UID) require.Equal(t, "newUID", f.UID)
}) })
@ -469,14 +484,16 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(dashboardFolder, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(dashboardFolder, nil)
dashStore.On("GetFolderByID", mock.Anything, orgID, dashboardFolder.ID).Return(f, nil)
var actualCmd *dashboards.DeleteDashboardCommand var actualCmd *dashboards.DeleteDashboardCommand
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand) actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once() }).Return(nil).Once()
folderStore := NewFakeStore() dashboardFolderStore := dashboards.NewFakeFolderStore(t)
folderStore.ExpectedParentFolders = []*folder.Folder{ dashboardFolderStore.On("GetFolderByID", mock.Anything, orgID, dashboardFolder.ID).Return(f, nil)
nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedParentFolders = []*folder.Folder{
{UID: "newFolder", ParentUID: "newFolder"}, {UID: "newFolder", ParentUID: "newFolder"},
{UID: "newFolder2", ParentUID: "newFolder2"}, {UID: "newFolder2", ParentUID: "newFolder2"},
{UID: "newFolder3", ParentUID: "newFolder3"}, {UID: "newFolder3", ParentUID: "newFolder3"},
@ -491,13 +508,13 @@ func TestNestedFolderService(t *testing.T) {
SignedInUser: usr, SignedInUser: usr,
} }
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
_, err := folderSvc.Create(context.Background(), &cmd) _, err := folderSvc.Create(context.Background(), &cmd)
require.Error(t, err, folder.ErrCircularReference) require.Error(t, err, folder.ErrCircularReference)
// CreateFolder should not call the folder store's create method. // CreateFolder should not call the folder store's create method.
require.False(t, folderStore.CreateCalled) require.False(t, nestedFolderStore.CreateCalled)
require.NotNil(t, actualCmd) require.NotNil(t, actualCmd)
}) })
@ -513,19 +530,20 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{}, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.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 *dashboards.DeleteDashboardCommand var actualCmd *dashboards.DeleteDashboardCommand
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand) actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once() }).Return(nil).Once()
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
// return an error from the folder store // return an error from the folder store
folderStore := NewFakeStore() nestedFolderStore := NewFakeStore()
folderStore.ExpectedError = errors.New("FAILED") nestedFolderStore.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
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
_, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{ _, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
@ -537,7 +555,7 @@ func TestNestedFolderService(t *testing.T) {
require.Error(t, err, "FAILED") require.Error(t, err, "FAILED")
// CreateFolder should also call the folder store's create method. // CreateFolder should also call the folder store's create method.
require.True(t, folderStore.CreateCalled) require.True(t, nestedFolderStore.CreateCalled)
require.NotNil(t, actualCmd) require.NotNil(t, actualCmd)
}) })
@ -550,11 +568,12 @@ func TestNestedFolderService(t *testing.T) {
}) })
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
folderStore := NewFakeStore() nestedFolderStore := NewFakeStore()
folderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"} nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
_, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder", OrgID: orgID, SignedInUser: usr}) _, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder", OrgID: orgID, SignedInUser: usr})
@ -570,11 +589,12 @@ func TestNestedFolderService(t *testing.T) {
}) })
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
folderStore := NewFakeStore() nestedFolderStore := NewFakeStore()
folderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"} nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
_, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder", OrgID: orgID, SignedInUser: usr}) _, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder", OrgID: orgID, SignedInUser: usr})
@ -590,16 +610,17 @@ func TestNestedFolderService(t *testing.T) {
}) })
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
folderStore := NewFakeStore() nestedFolderStore := NewFakeStore()
folderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"} nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
folderStore.ExpectedParentFolders = []*folder.Folder{ nestedFolderStore.ExpectedParentFolders = []*folder.Folder{
{UID: "newFolder", ParentUID: "newFolder"}, {UID: "newFolder", ParentUID: "newFolder"},
{UID: "newFolder2", ParentUID: "newFolder2"}, {UID: "newFolder2", ParentUID: "newFolder2"},
{UID: "newFolder3", ParentUID: "newFolder3"}, {UID: "newFolder3", ParentUID: "newFolder3"},
} }
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
f, err := folderSvc.Move(context.Background(), &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})
@ -615,12 +636,13 @@ func TestNestedFolderService(t *testing.T) {
}) })
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
folderStore := NewFakeStore() nestedFolderStore := NewFakeStore()
folderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"} nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
folderStore.ExpectedError = folder.ErrCircularReference nestedFolderStore.ExpectedError = folder.ErrCircularReference
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
f, err := folderSvc.Move(context.Background(), &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})
@ -636,16 +658,17 @@ func TestNestedFolderService(t *testing.T) {
}) })
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
folderStore := NewFakeStore() nestedFolderStore := NewFakeStore()
folderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"} nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
folderStore.ExpectedParentFolders = []*folder.Folder{ nestedFolderStore.ExpectedParentFolders = []*folder.Folder{
{UID: "newFolder", ParentUID: "newFolder"}, {UID: "newFolder", ParentUID: "newFolder"},
{UID: "newFolder2", ParentUID: "newFolder2"}, {UID: "newFolder2", ParentUID: "newFolder2"},
} }
folderStore.ExpectedFolderHeight = 5 nestedFolderStore.ExpectedFolderHeight = 5
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
f, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder2", OrgID: orgID, SignedInUser: usr}) f, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder2", OrgID: orgID, SignedInUser: usr})
@ -661,12 +684,13 @@ func TestNestedFolderService(t *testing.T) {
}) })
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
folderStore := NewFakeStore() nestedFolderStore := NewFakeStore()
folderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"} nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
folderStore.ExpectedParentFolders = []*folder.Folder{{UID: "myFolder", ParentUID: "12345"}, {UID: "12345", ParentUID: ""}} nestedFolderStore.ExpectedParentFolders = []*folder.Folder{{UID: "myFolder", ParentUID: "12345"}, {UID: "12345", ParentUID: ""}}
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
f, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder2", OrgID: orgID, SignedInUser: usr}) f, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder2", OrgID: orgID, SignedInUser: usr})
@ -686,19 +710,21 @@ func TestNestedFolderService(t *testing.T) {
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand) actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once() }).Return(nil).Once()
dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
folderStore := NewFakeStore() dashboardFolderStore := dashboards.NewFakeFolderStore(t)
folderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"} dashboardFolderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
err := folderSvc.Delete(context.Background(), &folder.DeleteFolderCommand{UID: "myFolder", OrgID: orgID, SignedInUser: usr}) err := folderSvc.Delete(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)
require.True(t, folderStore.DeleteCalled) require.True(t, nestedFolderStore.DeleteCalled)
}) })
t.Run("create returns error if maximum depth reached", func(t *testing.T) { t.Run("create returns error if maximum depth reached", func(t *testing.T) {
@ -713,23 +739,24 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{} dashStore := &dashboards.FakeDashboardStore{}
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil).Times(2) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil).Times(2)
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{}, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.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 *dashboards.DeleteDashboardCommand var actualCmd *dashboards.DeleteDashboardCommand
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand) actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once() }).Return(nil).Once()
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
parents := make([]*folder.Folder, 0, folder.MaxNestedFolderDepth) parents := make([]*folder.Folder, 0, folder.MaxNestedFolderDepth)
for i := 0; i < folder.MaxNestedFolderDepth; i++ { for i := 0; i < folder.MaxNestedFolderDepth; i++ {
parents = append(parents, &folder.Folder{UID: fmt.Sprintf("folder%d", i)}) parents = append(parents, &folder.Folder{UID: fmt.Sprintf("folder%d", i)})
} }
folderStore := NewFakeStore() nestedFolderStore := NewFakeStore()
//folderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"} //nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
folderStore.ExpectedParentFolders = parents nestedFolderStore.ExpectedParentFolders = parents
folderSvc := setup(t, dashStore, folderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
ExpectedEvaluate: true, ExpectedEvaluate: true,
}) })
_, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{ _, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
@ -745,18 +772,19 @@ func TestNestedFolderService(t *testing.T) {
}) })
} }
func setup(t *testing.T, dashStore dashboards.Store, folderStore store, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) folder.Service { func setup(t *testing.T, dashStore dashboards.Store, dashboardFolderStore dashboards.FolderStore, nestedFolderStore store, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) folder.Service {
t.Helper() t.Helper()
// nothing enabled yet // nothing enabled yet
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = false cfg.RBACEnabled = false
return &Service{ return &Service{
cfg: cfg, cfg: cfg,
log: log.New("test-folder-service"), log: log.New("test-folder-service"),
dashboardStore: dashStore, dashboardStore: dashStore,
store: folderStore, dashboardFolderStore: dashboardFolderStore,
features: features, store: nestedFolderStore,
accessControl: ac, features: features,
accessControl: ac,
} }
} }

View File

@ -601,7 +601,7 @@ func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []acce
}) })
require.NoError(t, err) require.NoError(t, err)
ac := accesscontrolmock.New().WithPermissions(permissions) ac := accesscontrolmock.New().WithPermissions(permissions)
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashStore)) ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashStore, dashStore))
license := licensingtest.NewFakeLicensing() license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
teamSvc := teamimpl.ProvideService(store, store.Cfg) teamSvc := teamimpl.ProvideService(store, store.Cfg)

View File

@ -291,7 +291,7 @@ func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
service := dashboardservice.ProvideDashboardService( service := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, dashAlertExtractor, cfg, dashboardStore, dashboardStore, dashAlertExtractor,
features, folderPermissions, dashboardPermissions, ac, features, folderPermissions, dashboardPermissions, ac,
) )
dashboard, err := service.SaveDashboard(context.Background(), dashItem, true) dashboard, err := service.SaveDashboard(context.Background(), dashItem, true)
@ -314,7 +314,7 @@ func createFolderWithACL(t *testing.T, sqlStore db.DB, title string, user user.S
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err) require.NoError(t, err)
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, dashboardStore, nil, features, folderPermissions, nil) s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, dashboardStore, dashboardStore, nil, features, folderPermissions, nil)
t.Logf("Creating folder with title and UID %q", title) t.Logf("Creating folder with title and UID %q", title)
ctx := appcontext.WithUser(context.Background(), &user) ctx := appcontext.WithUser(context.Background(), &user)
folder, err := s.Create(ctx, &folder.CreateFolderCommand{ folder, err := s.Create(ctx, &folder.CreateFolderCommand{
@ -435,14 +435,14 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
dashboardService := dashboardservice.ProvideDashboardService( dashboardService := dashboardservice.ProvideDashboardService(
sqlStore.Cfg, dashboardStore, nil, sqlStore.Cfg, dashboardStore, dashboardStore, nil,
features, folderPermissions, dashboardPermissions, ac, features, folderPermissions, dashboardPermissions, ac,
) )
guardian.InitLegacyGuardian(sqlStore, dashboardService, &teamtest.FakeService{}) guardian.InitLegacyGuardian(sqlStore, dashboardService, &teamtest.FakeService{})
service := LibraryElementService{ service := LibraryElementService{
Cfg: sqlStore.Cfg, Cfg: sqlStore.Cfg,
SQLStore: sqlStore, SQLStore: sqlStore,
folderService: folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), sqlStore.Cfg, dashboardStore, nil, features, folderPermissions, nil), folderService: folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), sqlStore.Cfg, dashboardStore, dashboardStore, nil, features, folderPermissions, nil),
} }
// deliberate difference between signed in user and user in db to make it crystal clear // deliberate difference between signed in user and user in db to make it crystal clear

View File

@ -704,7 +704,7 @@ func createDashboard(t *testing.T, sqlStore db.DB, user *user.SignedInUser, dash
dashAlertService := alerting.ProvideDashAlertExtractorService(nil, nil, nil) dashAlertService := alerting.ProvideDashAlertExtractorService(nil, nil, nil)
ac := acmock.New() ac := acmock.New()
service := dashboardservice.ProvideDashboardService( service := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, dashAlertService, cfg, dashboardStore, dashboardStore, dashAlertService,
featuremgmt.WithFeatures(), acmock.NewMockedPermissionsService(), acmock.NewMockedPermissionsService(), ac, featuremgmt.WithFeatures(), acmock.NewMockedPermissionsService(), acmock.NewMockedPermissionsService(), ac,
) )
dashboard, err := service.SaveDashboard(context.Background(), dashItem, true) dashboard, err := service.SaveDashboard(context.Background(), dashItem, true)
@ -726,7 +726,7 @@ func createFolderWithACL(t *testing.T, sqlStore db.DB, title string, user *user.
quotaService := quotatest.New(false, nil) quotaService := quotatest.New(false, nil)
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService) dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err) require.NoError(t, err)
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, dashboardStore, nil, features, folderPermissions, nil) s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, dashboardStore, dashboardStore, nil, features, folderPermissions, nil)
t.Logf("Creating folder with title and UID %q", title) t.Logf("Creating folder with title and UID %q", title)
ctx := appcontext.WithUser(context.Background(), user) ctx := appcontext.WithUser(context.Background(), user)
@ -835,7 +835,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
ac := acmock.New() ac := acmock.New()
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
folderService := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, dashboardStore, nil, features, folderPermissions, nil) folderService := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, dashboardStore, dashboardStore, nil, features, folderPermissions, nil)
elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService) elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService)
service := LibraryPanelService{ service := LibraryPanelService{

View File

@ -79,13 +79,13 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG,
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
dashboardService := dashboardservice.ProvideDashboardService( dashboardService := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, nil, cfg, dashboardStore, dashboardStore, nil,
features, folderPermissions, dashboardPermissions, ac, features, folderPermissions, dashboardPermissions, ac,
) )
tracer := tracing.InitializeTracerForTest() tracer := tracing.InitializeTracerForTest()
bus := bus.ProvideBus(tracer) bus := bus.ProvideBus(tracer)
folderService := folderimpl.ProvideService(ac, bus, cfg, dashboardStore, nil, features, folderPermissions, nil) folderService := folderimpl.ProvideService(ac, bus, cfg, dashboardStore, dashboardStore, nil, features, folderPermissions, nil)
ng, err := ngalert.ProvideService( ng, err := ngalert.ProvideService(
cfg, featuremgmt.WithFeatures(), nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, quotatest.New(false, nil), cfg, featuremgmt.WithFeatures(), nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, quotatest.New(false, nil),