Chore: Move folder store interface, implementation and test under pkg/services/folder (#62586)

* Chore: Move folder store into folder service package

* Split folder and dashboard store implementations
pull/62686/head
Sofia Papagiannaki 2 years ago committed by GitHub
parent 151e57df70
commit f143b0a5b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      pkg/api/common_test.go
  2. 5
      pkg/api/dashboard_permission_test.go
  3. 2
      pkg/api/dashboard_test.go
  4. 2
      pkg/api/folder_permission_test.go
  5. 5
      pkg/cmd/grafana-cli/runner/wire.go
  6. 4
      pkg/server/wire.go
  7. 10
      pkg/services/dashboards/accesscontrol.go
  8. 46
      pkg/services/dashboards/accesscontrol_test.go
  9. 12
      pkg/services/dashboards/dashboard.go
  10. 68
      pkg/services/dashboards/database/database.go
  11. 72
      pkg/services/dashboards/database/database_folder_test.go
  12. 4
      pkg/services/dashboards/service/dashboard_service.go
  13. 16
      pkg/services/dashboards/service/dashboard_service_integration_test.go
  14. 3
      pkg/services/dashboards/service/dashboard_service_test.go
  15. 86
      pkg/services/folder/folderimpl/dashboard_folder_store.go
  16. 137
      pkg/services/folder/folderimpl/dashboard_folder_store_test.go
  17. 4
      pkg/services/folder/folderimpl/folder.go
  18. 35
      pkg/services/folder/folderimpl/folder_test.go
  19. 5
      pkg/services/folder/foldertest/folder_store_mock.go
  20. 12
      pkg/services/folder/service.go
  21. 3
      pkg/services/guardian/accesscontrol_guardian_test.go
  22. 11
      pkg/services/libraryelements/libraryelements_test.go
  23. 10
      pkg/services/librarypanels/librarypanels_test.go
  24. 6
      pkg/services/ngalert/tests/util.go

@ -40,6 +40,7 @@ import (
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/licensing"
@ -410,7 +411,7 @@ func setupHTTPServerWithCfgDb(
teamPermissionsService: teamPermissionService,
searchUsersService: searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), usertest.NewUserServiceFake()),
DashboardService: dashboardservice.ProvideDashboardService(
cfg, dashboardsStore, dashboardsStore, nil, features,
cfg, dashboardsStore, folderimpl.ProvideDashboardFolderStore(db), nil, features,
folderPermissionsService, dashboardPermissionsService, ac,
folderSvc,
),

@ -21,6 +21,7 @@ import (
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/setting"
@ -40,13 +41,13 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
folderPermissions := accesscontrolmock.NewMockedPermissionsService()
dashboardPermissions := accesscontrolmock.NewMockedPermissionsService()
folderSvc := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), settings, dashboardStore, dashboards.NewFakeFolderStore(t), mockSQLStore, featuremgmt.WithFeatures())
folderSvc := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), settings, dashboardStore, foldertest.NewFakeFolderStore(t), mockSQLStore, featuremgmt.WithFeatures())
hs := &HTTPServer{
Cfg: settings,
SQLStore: mockSQLStore,
Features: features,
DashboardService: dashboardservice.ProvideDashboardService(
settings, dashboardStore, dashboards.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac,
settings, dashboardStore, foldertest.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac,
folderSvc,
),
AccessControl: accesscontrolmock.New().WithDisabled(),

@ -960,7 +960,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
})
}
func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, provisioningService provisioning.ProvisioningService, dashboardStore dashboards.Store, dashboardService dashboards.DashboardService, folderStore dashboards.FolderStore) dtos.DashboardFullWithMeta {
func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, provisioningService provisioning.ProvisioningService, dashboardStore dashboards.Store, dashboardService dashboards.DashboardService, folderStore folder.FolderStore) dtos.DashboardFullWithMeta {
t.Helper()
if provisioningService == nil {

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

@ -5,6 +5,8 @@ package runner
import (
"context"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/google/wire"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
@ -257,7 +259,8 @@ var wireSet = wire.NewSet(
dashboardservice.ProvideDashboardProvisioningService,
dashboardservice.ProvideDashboardPluginService,
wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)),
wire.Bind(new(dashboards.FolderStore), new(*dashboardstore.DashboardStore)),
folderimpl.ProvideDashboardFolderStore,
wire.Bind(new(folder.FolderStore), new(*folderimpl.DashboardFolderStoreImpl)),
dashboardimportservice.ProvideService,
wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)),
plugindashboardsservice.ProvideService,

@ -6,6 +6,7 @@ package server
import (
"github.com/google/wire"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/avatar"
@ -290,11 +291,12 @@ var wireBasicSet = wire.NewSet(
dashboardservice.ProvideDashboardService, // DashboardServiceImpl
dashboardstore.ProvideDashboardStore,
folderimpl.ProvideService,
folderimpl.ProvideDashboardFolderStore,
dashboardservice.ProvideSimpleDashboardService,
dashboardservice.ProvideDashboardProvisioningService,
dashboardservice.ProvideDashboardPluginService,
wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)),
wire.Bind(new(dashboards.FolderStore), new(*dashboardstore.DashboardStore)),
wire.Bind(new(folder.FolderStore), new(*folderimpl.DashboardFolderStoreImpl)),
dashboardimportservice.ProvideService,
wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)),
plugindashboardsservice.ProvideService,

@ -39,7 +39,7 @@ var (
)
// NewFolderNameScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:name:" into an uid based scope.
func NewFolderNameScopeResolver(db Store, folderDB FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
func NewFolderNameScopeResolver(db Store, folderDB folder.FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
prefix := ScopeFoldersProvider.GetResourceScopeName("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) {
@ -65,7 +65,7 @@ func NewFolderNameScopeResolver(db Store, folderDB FolderStore, folderSvc folder
}
// NewFolderIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:id:" into an uid based scope.
func NewFolderIDScopeResolver(db Store, folderDB FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
func NewFolderIDScopeResolver(db Store, folderDB folder.FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
prefix := ScopeFoldersProvider.GetResourceScope("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) {
@ -98,7 +98,7 @@ func NewFolderIDScopeResolver(db Store, folderDB FolderStore, folderSvc folder.S
// NewDashboardIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:id:"
// into uid based scopes for both dashboard and folder
func NewDashboardIDScopeResolver(db Store, folderDB FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
func NewDashboardIDScopeResolver(db Store, folderDB folder.FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
prefix := ScopeDashboardsProvider.GetResourceScope("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) {
@ -121,7 +121,7 @@ func NewDashboardIDScopeResolver(db Store, folderDB FolderStore, folderSvc folde
// NewDashboardUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:uid:"
// into uid based scopes for both dashboard and folder
func NewDashboardUIDScopeResolver(db Store, folderDB FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
func NewDashboardUIDScopeResolver(db Store, folderDB folder.FolderStore, folderSvc folder.Service) (string, ac.ScopeAttributeResolver) {
prefix := ScopeDashboardsProvider.GetResourceScopeUID("")
return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
if !strings.HasPrefix(scope, prefix) {
@ -142,7 +142,7 @@ func NewDashboardUIDScopeResolver(db Store, folderDB FolderStore, folderSvc fold
})
}
func resolveDashboardScope(ctx context.Context, db Store, folderDB FolderStore, orgID int64, dashboard *Dashboard, folderSvc folder.Service) ([]string, error) {
func resolveDashboardScope(ctx context.Context, db Store, folderDB folder.FolderStore, orgID int64, dashboard *Dashboard, folderSvc folder.Service) ([]string, error) {
var folderUID string
if dashboard.FolderID < 0 {
return []string{ScopeDashboardsProvider.GetResourceScopeUID(dashboard.UID)}, nil

@ -19,7 +19,7 @@ import (
func TestNewFolderNameScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewFolderNameScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
prefix, _ := NewFolderNameScopeResolver(&FakeDashboardStore{}, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
require.Equal(t, "folders:name:", prefix)
})
@ -30,7 +30,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
db := &folder.Folder{Title: title, ID: rand.Int63(), UID: util.GenerateShortUID()}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
scope := "folders:name:" + title
@ -52,7 +52,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
db := &folder.Folder{Title: title, ID: rand.Int63(), UID: util.GenerateShortUID()}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
folderStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
scope := "folders:name:" + title
@ -84,14 +84,14 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
})
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderNameScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
_, resolver := NewFolderNameScopeResolver(dashboardStore, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:123")
require.ErrorIs(t, err, ac.ErrInvalidScope)
})
t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderNameScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
_, resolver := NewFolderNameScopeResolver(dashboardStore, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:name:")
require.ErrorIs(t, err, ac.ErrInvalidScope)
@ -99,7 +99,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
t.Run("returns 'not found' if folder does not exist", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
_, resolver := NewFolderNameScopeResolver(dashboardStore, folderStore, foldertest.NewFakeService())
orgId := rand.Int63()
@ -115,13 +115,13 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
func TestNewFolderIDScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewFolderIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
prefix, _ := NewFolderIDScopeResolver(&FakeDashboardStore{}, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
require.Equal(t, "folders:id:", prefix)
})
t.Run("resolver should convert to uid scope", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
_, resolver := NewFolderIDScopeResolver(dashboardStore, folderStore, foldertest.NewFakeService())
@ -142,7 +142,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
})
t.Run("resolver should should include inherited scopes if any", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
folderSvc := foldertest.NewFakeService()
folderSvc.ExpectedFolders = []*folder.Folder{
@ -179,7 +179,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
})
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
_, resolver := NewFolderIDScopeResolver(dashboardStore, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:uid:123")
require.ErrorIs(t, err, ac.ErrInvalidScope)
@ -190,7 +190,7 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
dashboardStore = &FakeDashboardStore{}
orgId = rand.Int63()
scope = "folders:id:0"
_, resolver = NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
_, resolver = NewFolderIDScopeResolver(dashboardStore, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
)
resolved, err := resolver.Resolve(context.Background(), orgId, scope)
@ -202,14 +202,14 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
_, resolver := NewFolderIDScopeResolver(dashboardStore, NewFakeFolderStore(t), foldertest.NewFakeService())
_, resolver := NewFolderIDScopeResolver(dashboardStore, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "folders:id:")
require.ErrorIs(t, err, ac.ErrInvalidScope)
})
t.Run("returns 'not found' if folder does not exist", func(t *testing.T) {
dashboardStore := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
_, resolver := NewFolderIDScopeResolver(dashboardStore, folderStore, foldertest.NewFakeService())
@ -225,13 +225,13 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
func TestNewDashboardIDScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewDashboardIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
prefix, _ := NewDashboardIDScopeResolver(&FakeDashboardStore{}, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
require.Equal(t, "dashboards:id:", prefix)
})
t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) {
store := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
_, resolver := NewDashboardIDScopeResolver(store, folderStore, foldertest.NewFakeService())
@ -252,7 +252,7 @@ func TestNewDashboardIDScopeResolver(t *testing.T) {
t.Run("resolver should inlude inherited scopes if any", func(t *testing.T) {
store := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
folderSvc := foldertest.NewFakeService()
folderSvc.ExpectedFolders = []*folder.Folder{
@ -288,14 +288,14 @@ func TestNewDashboardIDScopeResolver(t *testing.T) {
})
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
_, resolver := NewDashboardIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
_, resolver := NewDashboardIDScopeResolver(&FakeDashboardStore{}, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:uid:123")
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) {
store := &FakeDashboardStore{}
_, resolver := NewDashboardIDScopeResolver(store, NewFakeFolderStore(t), foldertest.NewFakeService())
_, resolver := NewDashboardIDScopeResolver(store, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"}
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil)
@ -310,13 +310,13 @@ func TestNewDashboardIDScopeResolver(t *testing.T) {
func TestNewDashboardUIDScopeResolver(t *testing.T) {
t.Run("prefix should be expected", func(t *testing.T) {
prefix, _ := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
prefix, _ := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
require.Equal(t, "dashboards:uid:", prefix)
})
t.Run("resolver should convert to uid dashboard and folder scope", func(t *testing.T) {
store := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
_, resolver := NewDashboardUIDScopeResolver(store, folderStore, foldertest.NewFakeService())
orgID := rand.Int63()
@ -336,7 +336,7 @@ func TestNewDashboardUIDScopeResolver(t *testing.T) {
t.Run("resolver should include inherited scopes if any", func(t *testing.T) {
store := &FakeDashboardStore{}
folderStore := NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
folderSvc := foldertest.NewFakeService()
folderSvc.ExpectedFolders = []*folder.Folder{
@ -373,14 +373,14 @@ func TestNewDashboardUIDScopeResolver(t *testing.T) {
})
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
_, resolver := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, NewFakeFolderStore(t), foldertest.NewFakeService())
_, resolver := NewDashboardUIDScopeResolver(&FakeDashboardStore{}, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
_, err := resolver.Resolve(context.Background(), rand.Int63(), "dashboards:id:123")
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) {
store := &FakeDashboardStore{}
_, resolver := NewDashboardUIDScopeResolver(store, NewFakeFolderStore(t), foldertest.NewFakeService())
_, resolver := NewDashboardUIDScopeResolver(store, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService())
dashboard := &Dashboard{ID: 1, FolderID: 0, UID: "1"}
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil)

@ -84,15 +84,3 @@ type Store interface {
// the given parent folder ID.
CountDashboardsInFolder(ctx context.Context, request *CountDashboardsInFolderRequest) (int64, error)
}
// FolderStore is a folder store.
//
//go:generate mockery --name FolderStore --structname FakeFolderStore --inpackage --filename folder_store_mock.go
type FolderStore interface {
// GetFolderByTitle retrieves a folder by its title
GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error)
// GetFolderByUID retrieves a folder by its UID
GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error)
// GetFolderByID retrieves a folder by its ID
GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error)
}

@ -16,7 +16,6 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
@ -93,73 +92,6 @@ func (d *DashboardStore) ValidateDashboardBeforeSave(ctx context.Context, dashbo
return isParentFolderChanged, nil
}
func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error) {
if title == "" {
return nil, dashboards.ErrFolderTitleEmpty
}
// there is a unique constraint on org_id, folder_id, title
// there are no nested folders so the parent folder id is always 0
dashboard := dashboards.Dashboard{OrgID: orgID, FolderID: 0, Title: title}
err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
has, err := sess.Table(&dashboards.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)).Where("folder_id=0").Get(&dashboard)
if err != nil {
return err
}
if !has {
return dashboards.ErrFolderNotFound
}
dashboard.SetID(dashboard.ID)
dashboard.SetUID(dashboard.UID)
return nil
})
return dashboards.FromDashboard(&dashboard), err
}
func (d *DashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error) {
dashboard := dashboards.Dashboard{OrgID: orgID, FolderID: 0, ID: id}
err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
has, err := sess.Table(&dashboards.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)).Where("folder_id=0").Get(&dashboard)
if err != nil {
return err
}
if !has {
return dashboards.ErrFolderNotFound
}
dashboard.SetID(dashboard.ID)
dashboard.SetUID(dashboard.UID)
return nil
})
if err != nil {
return nil, err
}
return dashboards.FromDashboard(&dashboard), nil
}
func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error) {
if uid == "" {
return nil, dashboards.ErrDashboardIdentifierNotSet
}
dashboard := dashboards.Dashboard{OrgID: orgID, FolderID: 0, UID: uid}
err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
has, err := sess.Table(&dashboards.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)).Where("folder_id=0").Get(&dashboard)
if err != nil {
return err
}
if !has {
return dashboards.ErrFolderNotFound
}
dashboard.SetID(dashboard.ID)
dashboard.SetUID(dashboard.UID)
return nil
})
if err != nil {
return nil, err
}
return dashboards.FromDashboard(&dashboard), nil
}
func (d *DashboardStore) GetProvisionedDataByDashboardID(ctx context.Context, dashboardID int64) (*dashboards.DashboardProvisioning, error) {
var data dashboards.DashboardProvisioning
err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {

@ -474,78 +474,6 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
})
})
})
t.Run("Given dashboard and folder with the same title", func(t *testing.T) {
var orgId int64 = 1
title := "Very Unique Name"
var sqlStore *sqlstore.SQLStore
var folder1, folder2 *dashboards.Dashboard
sqlStore = db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folder2 = insertTestDashboard(t, dashboardStore, "TEST", orgId, 0, true, "prod")
_ = insertTestDashboard(t, dashboardStore, title, orgId, folder2.ID, false, "prod")
folder1 = insertTestDashboard(t, dashboardStore, title, orgId, 0, true, "prod")
t.Run("GetFolderByTitle should find the folder", func(t *testing.T) {
result, err := dashboardStore.GetFolderByTitle(context.Background(), orgId, title)
require.NoError(t, err)
require.Equal(t, folder1.ID, result.ID)
})
})
t.Run("GetFolderByUID", func(t *testing.T) {
var orgId int64 = 1
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folder := insertTestDashboard(t, dashboardStore, "TEST", orgId, 0, true, "prod")
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.ID, false, "prod")
t.Run("should return folder by UID", func(t *testing.T) {
d, err := dashboardStore.GetFolderByUID(context.Background(), orgId, folder.UID)
require.Equal(t, folder.ID, d.ID)
require.NoError(t, err)
})
t.Run("should not find dashboard", func(t *testing.T) {
d, err := dashboardStore.GetFolderByUID(context.Background(), orgId, dash.UID)
require.Nil(t, d)
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
})
t.Run("should search in organization", func(t *testing.T) {
d, err := dashboardStore.GetFolderByUID(context.Background(), orgId+1, folder.UID)
require.Nil(t, d)
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
})
})
t.Run("GetFolderByID", func(t *testing.T) {
var orgId int64 = 1
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folder := insertTestDashboard(t, dashboardStore, "TEST", orgId, 0, true, "prod")
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.ID, false, "prod")
t.Run("should return folder by ID", func(t *testing.T) {
d, err := dashboardStore.GetFolderByID(context.Background(), orgId, folder.ID)
require.Equal(t, folder.ID, d.ID)
require.NoError(t, err)
})
t.Run("should not find dashboard", func(t *testing.T) {
d, err := dashboardStore.GetFolderByID(context.Background(), orgId, dash.ID)
require.Nil(t, d)
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
})
t.Run("should search in organization", func(t *testing.T) {
d, err := dashboardStore.GetFolderByID(context.Background(), orgId+1, folder.ID)
require.Nil(t, d)
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
})
})
})
}

@ -40,7 +40,7 @@ type DashboardServiceImpl struct {
cfg *setting.Cfg
log log.Logger
dashboardStore dashboards.Store
folderStore dashboards.FolderStore
folderStore folder.FolderStore
dashAlertExtractor alerting.DashAlertExtractor
features featuremgmt.FeatureToggles
folderPermissions accesscontrol.FolderPermissionsService
@ -50,7 +50,7 @@ type DashboardServiceImpl struct {
// This is the uber service that implements a three smaller services
func ProvideDashboardService(
cfg *setting.Cfg, dashboardStore dashboards.Store, folderStore dashboards.FolderStore, dashAlertExtractor alerting.DashAlertExtractor,
cfg *setting.Cfg, dashboardStore dashboards.Store, folderStore folder.FolderStore, dashAlertExtractor alerting.DashAlertExtractor,
features featuremgmt.FeatureToggles, folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService, ac accesscontrol.AccessControl,
folderSvc folder.Service,

@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org"
@ -826,8 +827,9 @@ func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionSc
quotaService := quotatest.New(false, nil)
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
service := ProvideDashboardService(
cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(),
@ -886,8 +888,9 @@ func callSaveWithResult(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSt
quotaService := quotatest.New(false, nil)
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
service := ProvideDashboardService(
cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(),
@ -908,8 +911,9 @@ func callSaveWithError(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSto
quotaService := quotatest.New(false, nil)
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
service := ProvideDashboardService(
cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(),
@ -948,8 +952,9 @@ func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlSto
quotaService := quotatest.New(false, nil)
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
service := ProvideDashboardService(
cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(),
@ -989,8 +994,9 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore db.DB) *da
quotaService := quotatest.New(false, nil)
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
service := ProvideDashboardService(
cfg, dashboardStore, dashboardStore, &dummyDashAlertExtractor{},
cfg, dashboardStore, folderStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(),
accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.NewMockedPermissionsService(),

@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
@ -24,7 +25,7 @@ func TestDashboardService(t *testing.T) {
fakeStore := dashboards.FakeDashboardStore{}
defer fakeStore.AssertExpectations(t)
folderStore := dashboards.NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
service := &DashboardServiceImpl{
cfg: setting.NewCfg(),

@ -0,0 +1,86 @@
package folderimpl
import (
"context"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
)
// DashboardStore implements the FolderStore interface
// It fetches folders from the dashboard DB table
type DashboardFolderStoreImpl struct {
store db.DB
}
func ProvideDashboardFolderStore(sqlStore db.DB) *DashboardFolderStoreImpl {
return &DashboardFolderStoreImpl{store: sqlStore}
}
func (d *DashboardFolderStoreImpl) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error) {
if title == "" {
return nil, dashboards.ErrFolderTitleEmpty
}
// there is a unique constraint on org_id, folder_id, title
// there are no nested folders so the parent folder id is always 0
dashboard := dashboards.Dashboard{OrgID: orgID, FolderID: 0, Title: title}
err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
has, err := sess.Table(&dashboards.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)).Where("folder_id=0").Get(&dashboard)
if err != nil {
return err
}
if !has {
return dashboards.ErrFolderNotFound
}
dashboard.SetID(dashboard.ID)
dashboard.SetUID(dashboard.UID)
return nil
})
return dashboards.FromDashboard(&dashboard), err
}
func (d *DashboardFolderStoreImpl) GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error) {
dashboard := dashboards.Dashboard{OrgID: orgID, FolderID: 0, ID: id}
err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
has, err := sess.Table(&dashboards.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)).Where("folder_id=0").Get(&dashboard)
if err != nil {
return err
}
if !has {
return dashboards.ErrFolderNotFound
}
dashboard.SetID(dashboard.ID)
dashboard.SetUID(dashboard.UID)
return nil
})
if err != nil {
return nil, err
}
return dashboards.FromDashboard(&dashboard), nil
}
func (d *DashboardFolderStoreImpl) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error) {
if uid == "" {
return nil, dashboards.ErrDashboardIdentifierNotSet
}
dashboard := dashboards.Dashboard{OrgID: orgID, FolderID: 0, UID: uid}
err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
has, err := sess.Table(&dashboards.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)).Where("folder_id=0").Get(&dashboard)
if err != nil {
return err
}
if !has {
return dashboards.ErrFolderNotFound
}
dashboard.SetID(dashboard.ID)
dashboard.SetUID(dashboard.UID)
return nil
})
if err != nil {
return nil, err
}
return dashboards.FromDashboard(&dashboard), nil
}

@ -0,0 +1,137 @@
package folderimpl
import (
"context"
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
)
func TestIntegrationDashboardFolderStore(t *testing.T) {
var sqlStore *sqlstore.SQLStore
var cfg *setting.Cfg
var dashboardStore *database.DashboardStore
setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t)
quotaService := quotatest.New(false, nil)
var err error
dashboardStore, err = database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(featuremgmt.FlagPanelTitleSearch), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
}
t.Run("Given dashboard and folder with the same title", func(t *testing.T) {
setup()
var orgId int64 = 1
title := "Very Unique Name"
var sqlStore *sqlstore.SQLStore
var folder1, folder2 *dashboards.Dashboard
sqlStore = db.InitTestDB(t)
folderStore := ProvideDashboardFolderStore(sqlStore)
folder2 = insertTestFolder(t, dashboardStore, "TEST", orgId, 0, "prod")
_ = insertTestDashboard(t, dashboardStore, title, orgId, folder2.ID, "prod")
folder1 = insertTestFolder(t, dashboardStore, title, orgId, 0, "prod")
t.Run("GetFolderByTitle should find the folder", func(t *testing.T) {
result, err := folderStore.GetFolderByTitle(context.Background(), orgId, title)
require.NoError(t, err)
require.Equal(t, folder1.ID, result.ID)
})
})
t.Run("GetFolderByUID", func(t *testing.T) {
var orgId int64 = 1
sqlStore := db.InitTestDB(t)
folderStore := ProvideDashboardFolderStore(sqlStore)
folder := insertTestFolder(t, dashboardStore, "TEST", orgId, 0, "prod")
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.ID, "prod")
t.Run("should return folder by UID", func(t *testing.T) {
d, err := folderStore.GetFolderByUID(context.Background(), orgId, folder.UID)
require.Equal(t, folder.ID, d.ID)
require.NoError(t, err)
})
t.Run("should not find dashboard", func(t *testing.T) {
d, err := folderStore.GetFolderByUID(context.Background(), orgId, dash.UID)
require.Nil(t, d)
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
})
t.Run("should search in organization", func(t *testing.T) {
d, err := folderStore.GetFolderByUID(context.Background(), orgId+1, folder.UID)
require.Nil(t, d)
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
})
})
t.Run("GetFolderByID", func(t *testing.T) {
var orgId int64 = 1
sqlStore := db.InitTestDB(t)
folderStore := ProvideDashboardFolderStore(sqlStore)
folder := insertTestFolder(t, dashboardStore, "TEST", orgId, 0, "prod")
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.ID, "prod")
t.Run("should return folder by ID", func(t *testing.T) {
d, err := folderStore.GetFolderByID(context.Background(), orgId, folder.ID)
require.Equal(t, folder.ID, d.ID)
require.NoError(t, err)
})
t.Run("should not find dashboard", func(t *testing.T) {
d, err := folderStore.GetFolderByID(context.Background(), orgId, dash.ID)
require.Nil(t, d)
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
})
t.Run("should search in organization", func(t *testing.T) {
d, err := folderStore.GetFolderByID(context.Background(), orgId+1, folder.ID)
require.Nil(t, d)
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
})
})
}
func insertTestDashboard(t *testing.T, dashboardStore *database.DashboardStore, title string, orgId int64, folderId int64, tags ...interface{}) *dashboards.Dashboard {
t.Helper()
cmd := dashboards.SaveDashboardCommand{
OrgID: orgId,
FolderID: folderId,
IsFolder: false,
Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil,
"title": title,
"tags": tags,
}),
}
dash, err := dashboardStore.SaveDashboard(context.Background(), cmd)
require.NoError(t, err)
require.NotNil(t, dash)
dash.Data.Set("id", dash.ID)
dash.Data.Set("uid", dash.UID)
return dash
}
func insertTestFolder(t *testing.T, dashboardStore *database.DashboardStore, title string, orgId int64, folderId int64, tags ...interface{}) *dashboards.Dashboard {
t.Helper()
cmd := dashboards.SaveDashboardCommand{
OrgID: orgId,
FolderID: folderId,
IsFolder: true,
Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil,
"title": title,
"tags": tags,
}),
}
dash, err := dashboardStore.SaveDashboard(context.Background(), cmd)
require.NoError(t, err)
require.NotNil(t, dash)
dash.Data.Set("id", dash.ID)
dash.Data.Set("uid", dash.UID)
return dash
}

@ -30,7 +30,7 @@ type Service struct {
log log.Logger
cfg *setting.Cfg
dashboardStore dashboards.Store
dashboardFolderStore dashboards.FolderStore
dashboardFolderStore folder.FolderStore
features featuremgmt.FeatureToggles
accessControl accesscontrol.AccessControl
@ -43,7 +43,7 @@ func ProvideService(
bus bus.Bus,
cfg *setting.Cfg,
dashboardStore dashboards.Store,
folderStore dashboards.FolderStore,
folderStore folder.FolderStore,
db db.DB, // DB for the (new) nested folder store
features featuremgmt.FeatureToggles,
) folder.Service {

@ -20,6 +20,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user"
@ -52,7 +53,7 @@ func TestIntegrationFolderService(t *testing.T) {
db := sqlstore.InitTestDB(t)
nestedFolderStore := ProvideStore(db, db.Cfg, featuremgmt.WithFeatures([]interface{}{"nestedFolders"}))
folderStore := dashboards.NewFakeFolderStore(t)
folderStore := foldertest.NewFakeFolderStore(t)
cfg := setting.NewCfg()
cfg.RBACEnabled = false
@ -325,7 +326,7 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
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)
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
cfg := setting.NewCfg()
@ -361,7 +362,7 @@ func TestNestedFolderService(t *testing.T) {
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)
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
nestedFolderStore := NewFakeStore()
@ -385,7 +386,7 @@ func TestNestedFolderService(t *testing.T) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once()
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
nestedFolderStore := NewFakeStore()
@ -412,7 +413,7 @@ func TestNestedFolderService(t *testing.T) {
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)
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
nestedFolderStore := NewFakeStore()
@ -443,7 +444,7 @@ func TestNestedFolderService(t *testing.T) {
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)
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
nestedFolderStore := NewFakeStore()
@ -483,7 +484,7 @@ func TestNestedFolderService(t *testing.T) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once()
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, orgID, dashboardFolder.ID).Return(f, nil)
nestedFolderStore := NewFakeStore()
@ -529,7 +530,7 @@ func TestNestedFolderService(t *testing.T) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once()
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
// return an error from the folder store
@ -562,7 +563,7 @@ func TestNestedFolderService(t *testing.T) {
})
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
@ -583,7 +584,7 @@ func TestNestedFolderService(t *testing.T) {
})
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
@ -604,7 +605,7 @@ func TestNestedFolderService(t *testing.T) {
})
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
@ -630,7 +631,7 @@ func TestNestedFolderService(t *testing.T) {
})
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
@ -652,7 +653,7 @@ func TestNestedFolderService(t *testing.T) {
})
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
@ -678,7 +679,7 @@ func TestNestedFolderService(t *testing.T) {
})
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
@ -705,7 +706,7 @@ func TestNestedFolderService(t *testing.T) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once()
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
nestedFolderStore := NewFakeStore()
@ -738,7 +739,7 @@ func TestNestedFolderService(t *testing.T) {
actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand)
}).Return(nil).Once()
dashboardFolderStore := dashboards.NewFakeFolderStore(t)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
parents := make([]*folder.Folder, 0, folder.MaxNestedFolderDepth)
@ -766,7 +767,7 @@ func TestNestedFolderService(t *testing.T) {
})
}
func setup(t *testing.T, dashStore dashboards.Store, dashboardFolderStore dashboards.FolderStore, nestedFolderStore store, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) folder.Service {
func setup(t *testing.T, dashStore dashboards.Store, dashboardFolderStore folder.FolderStore, nestedFolderStore store, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) folder.Service {
t.Helper()
// nothing enabled yet

@ -1,13 +1,12 @@
// Code generated by mockery v2.16.0. DO NOT EDIT.
package dashboards
package foldertest
import (
context "context"
mock "github.com/stretchr/testify/mock"
folder "github.com/grafana/grafana/pkg/services/folder"
mock "github.com/stretchr/testify/mock"
)
// FakeFolderStore is an autogenerated mock type for the FolderStore type

@ -26,3 +26,15 @@ type Service interface {
// Move changes a folder's parent folder to the requested new parent.
Move(ctx context.Context, cmd *MoveFolderCommand) (*Folder, error)
}
// FolderStore is a folder store.
//
//go:generate mockery --name FolderStore --structname FakeFolderStore --outpkg foldertest --output foldertest --filename folder_store_mock.go
type FolderStore interface {
// GetFolderByTitle retrieves a folder by its title
GetFolderByTitle(ctx context.Context, orgID int64, title string) (*Folder, error)
// GetFolderByUID retrieves a folder by its UID
GetFolderByUID(ctx context.Context, orgID int64, uid string) (*Folder, error)
// GetFolderByID retrieves a folder by its ID
GetFolderByID(ctx context.Context, orgID int64, id int64) (*Folder, error)
}

@ -602,7 +602,8 @@ func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []acce
})
require.NoError(t, err)
ac := accesscontrolmock.New().WithPermissions(permissions)
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashStore, dashStore, foldertest.NewFakeService()))
// TODO replace with actual folder store implementation after resolving import cycles
ac.RegisterScopeAttributeResolver(dashboards.NewDashboardUIDScopeResolver(dashStore, foldertest.NewFakeFolderStore(t), foldertest.NewFakeService()))
license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
teamSvc := teamimpl.ProvideService(store, store.Cfg)

@ -293,8 +293,9 @@ func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash
ac := acmock.New()
folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService()
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
service := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, dashboardStore, dashAlertExtractor,
cfg, dashboardStore, folderStore, dashAlertExtractor,
features, folderPermissions, dashboardPermissions, ac,
foldertest.NewFakeService(),
)
@ -317,7 +318,8 @@ 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)
require.NoError(t, err)
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, dashboardStore, dashboardStore, nil, features)
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, dashboardStore, folderStore, nil, features)
t.Logf("Creating folder with title and UID %q", title)
ctx := appcontext.WithUser(context.Background(), &user)
folder, err := s.Create(ctx, &folder.CreateFolderCommand{
@ -437,8 +439,9 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
sqlStore.Cfg.RBACEnabled = false
folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService()
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
dashboardService := dashboardservice.ProvideDashboardService(
sqlStore.Cfg, dashboardStore, dashboardStore, nil,
sqlStore.Cfg, dashboardStore, folderStore, nil,
features, folderPermissions, dashboardPermissions, ac,
foldertest.NewFakeService(),
)
@ -446,7 +449,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
service := LibraryElementService{
Cfg: sqlStore.Cfg,
SQLStore: sqlStore,
folderService: folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), sqlStore.Cfg, dashboardStore, dashboardStore, nil, features),
folderService: folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), sqlStore.Cfg, dashboardStore, folderStore, nil, features),
}
// deliberate difference between signed in user and user in db to make it crystal clear

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

@ -79,15 +79,17 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG,
folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
dashboardPermissions := acmock.NewMockedPermissionsService()
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
dashboardService := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, dashboardStore, nil,
cfg, dashboardStore, folderStore, nil,
features, folderPermissions, dashboardPermissions, ac,
foldertest.NewFakeService(),
)
tracer := tracing.InitializeTracerForTest()
bus := bus.ProvideBus(tracer)
folderService := folderimpl.ProvideService(ac, bus, cfg, dashboardStore, dashboardStore, nil, features)
folderService := folderimpl.ProvideService(ac, bus, cfg, dashboardStore, folderStore, nil, features)
ng, err := ngalert.ProvideService(
cfg, featuremgmt.WithFeatures(), nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, quotatest.New(false, nil),

Loading…
Cancel
Save