Folders: Export folder store implementation (#93897)

* Export folder store implementation

* Rename folder store

* Add folder store as a parameter to folder service

* Add folder store to dash service implementation

* Fix folder store comments
This commit is contained in:
Arati R.
2024-09-30 10:28:47 +02:00
committed by GitHub
parent daf9273cd1
commit ed75aea21d
24 changed files with 226 additions and 187 deletions

View File

@@ -40,7 +40,7 @@ import (
const FULLPATH_SEPARATOR = "/"
type Service struct {
store store
store folder.Store
db db.DB
log *slog.Logger
dashboardStore dashboards.Store
@@ -58,6 +58,7 @@ type Service struct {
}
func ProvideService(
store *FolderStoreImpl,
ac accesscontrol.AccessControl,
bus bus.Bus,
dashboardStore dashboards.Store,
@@ -68,7 +69,6 @@ func ProvideService(
r prometheus.Registerer,
tracer tracing.Tracer,
) folder.Service {
store := ProvideStore(db)
srv := &Service{
log: slog.Default().With("logger", "folder-service"),
dashboardStore: dashboardStore,
@@ -143,10 +143,10 @@ func (s *Service) GetFolders(ctx context.Context, q folder.GetFoldersQuery) ([]*
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
}
qry := NewGetFoldersQuery(q)
qry := folder.NewGetFoldersQuery(q)
permissions := q.SignedInUser.GetPermissions()
folderPermissions := permissions[dashboards.ActionFoldersRead]
qry.ancestorUIDs = make([]string, 0, len(folderPermissions))
qry.AncestorUIDs = make([]string, 0, len(folderPermissions))
if len(folderPermissions) == 0 && !q.SignedInUser.GetIsGrafanaAdmin() {
return nil, nil
}
@@ -154,12 +154,12 @@ func (s *Service) GetFolders(ctx context.Context, q folder.GetFoldersQuery) ([]*
if p == dashboards.ScopeFoldersAll {
// no need to query for folders with permissions
// the user has permission to access all folders
qry.ancestorUIDs = nil
qry.AncestorUIDs = nil
break
}
if folderUid, found := strings.CutPrefix(p, dashboards.ScopeFoldersPrefix); found {
if !slices.Contains(qry.ancestorUIDs, folderUid) {
qry.ancestorUIDs = append(qry.ancestorUIDs, folderUid)
if !slices.Contains(qry.AncestorUIDs, folderUid) {
qry.AncestorUIDs = append(qry.AncestorUIDs, folderUid)
}
}
}

View File

@@ -62,8 +62,10 @@ func TestIntegrationProvideFolderService(t *testing.T) {
}
t.Run("should register scope resolvers", func(t *testing.T) {
ac := acmock.New()
db := db.InitTestDB(t)
ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), nil, nil, db, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
db, _ := db.InitTestDBWithCfg(t)
store := ProvideStore(db)
ProvideService(store, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), nil, nil, db,
featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 3)
})
@@ -486,7 +488,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
CanEditValue: true,
})
dashSrv, err := dashboardservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuresFlagOn, folderPermissions, dashboardPermissions, ac, serviceWithFlagOn, nil)
dashSrv, err := dashboardservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuresFlagOn, folderPermissions, dashboardPermissions, ac, serviceWithFlagOn, nestedFolderStore, nil)
require.NoError(t, err)
alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOn, db, serviceWithFlagOn, dashSrv, ac)
@@ -568,7 +570,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
})
dashSrv, err := dashboardservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuresFlagOff,
folderPermissions, dashboardPermissions, ac, serviceWithFlagOff, nil)
folderPermissions, dashboardPermissions, ac, serviceWithFlagOff, nestedFolderStore, nil)
require.NoError(t, err)
alertStore, err := ngstore.ProvideDBStore(cfg, featuresFlagOff, db, serviceWithFlagOff, dashSrv, ac)
@@ -713,7 +715,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
tc.service.dashboardStore = dashStore
tc.service.store = nestedFolderStore
dashSrv, err := dashboardservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, tc.featuresFlag, folderPermissions, dashboardPermissions, ac, tc.service, nil)
dashSrv, err := dashboardservice.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, tc.featuresFlag, folderPermissions, dashboardPermissions, ac, tc.service, tc.service.store, nil)
require.NoError(t, err)
alertStore, err := ngstore.ProvideDBStore(cfg, tc.featuresFlag, db, tc.service, dashSrv, ac)
require.NoError(t, err)
@@ -792,7 +794,7 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
guardian.New = g
})
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
dashStore := dashboards.FakeDashboardStore{}
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*dashboards.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
@@ -906,7 +908,7 @@ func TestNestedFolderService(t *testing.T) {
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
features := featuremgmt.WithFeatures()
db, _ := sqlstore.InitTestDB(t)
@@ -944,7 +946,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(dash, nil)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
features := featuremgmt.WithFeatures("nestedFolders")
tempUser := &user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{}}
@@ -974,7 +976,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
features := featuremgmt.WithFeatures("nestedFolders")
tempUser := &user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{}}
@@ -1044,7 +1046,7 @@ func TestNestedFolderService(t *testing.T) {
nestedFolderUser := &user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{}}
nestedFolderUser.Permissions[orgID] = map[string][]string{dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("some_parent")}}
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
db, _ := sqlstore.InitTestDB(t)
features := featuremgmt.WithFeatures("nestedFolders")
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, features, acimpl.ProvideAccessControl(features, zanzana.NewNoopClient()), db)
@@ -1085,7 +1087,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{UID: "newUID"}, nil)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
db, _ := sqlstore.InitTestDB(t)
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
@@ -1122,7 +1124,7 @@ func TestNestedFolderService(t *testing.T) {
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByUID", mock.Anything, orgID, dashboardFolder.UID).Return(f, nil)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedParentFolders = []*folder.Folder{
{UID: "newFolder", ParentUID: "newFolder"},
{UID: "newFolder2", ParentUID: "newFolder2"},
@@ -1164,7 +1166,7 @@ func TestNestedFolderService(t *testing.T) {
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
// return an error from the folder store
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedError = errors.New("FAILED")
// the service return success as long as the legacy create succeeds
@@ -1189,7 +1191,7 @@ func TestNestedFolderService(t *testing.T) {
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
//dashboardFolderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
nestedFolderUser := &user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{}}
@@ -1205,7 +1207,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
nestedFolderStore.ExpectedParentFolders = []*folder.Folder{
{UID: "newFolder", ParentUID: "newFolder"},
@@ -1236,7 +1238,7 @@ func TestNestedFolderService(t *testing.T) {
nestedFolderUser.Permissions[orgID] = map[string][]string{dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceAllScope()}}
features := featuremgmt.WithFeatures("nestedFolders")
folderSvc := setup(t, &dashboards.FakeDashboardStore{}, foldertest.NewFakeFolderStore(t), NewFakeStore(), features, acimpl.ProvideAccessControl(features, zanzana.NewNoopClient()), dbtest.NewFakeDB())
folderSvc := setup(t, &dashboards.FakeDashboardStore{}, foldertest.NewFakeFolderStore(t), folder.NewFakeStore(), features, acimpl.ProvideAccessControl(features, zanzana.NewNoopClient()), dbtest.NewFakeDB())
_, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: accesscontrol.K6FolderUID, NewParentUID: "newFolder", OrgID: orgID, SignedInUser: nestedFolderUser})
require.Error(t, err, folder.ErrBadRequest)
})
@@ -1246,7 +1248,7 @@ func TestNestedFolderService(t *testing.T) {
nestedFolderUser.Permissions[orgID] = map[string][]string{dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceAllScope()}}
childUID := "k6-app-child"
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{
OrgID: orgID,
UID: childUID,
@@ -1263,7 +1265,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
nestedFolderUser := &user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{}}
@@ -1279,7 +1281,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
nestedFolderStore.ExpectedParentFolders = []*folder.Folder{
{UID: "newFolder", ParentUID: "newFolder"},
@@ -1307,7 +1309,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderUser := &user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{}}
nestedFolderUser.Permissions[orgID] = map[string][]string{dashboards.ActionFoldersCreate: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("some_subfolder")}}
@@ -1328,7 +1330,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
nestedFolderStore.ExpectedError = folder.ErrCircularReference
@@ -1350,7 +1352,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
nestedFolderStore.ExpectedParentFolders = []*folder.Folder{
{UID: "newFolder", ParentUID: "newFolder"},
@@ -1376,7 +1378,7 @@ func TestNestedFolderService(t *testing.T) {
dashStore := &dashboards.FakeDashboardStore{}
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
nestedFolderStore.ExpectedParentFolders = []*folder.Folder{{UID: "myFolder", ParentUID: "12345"}, {UID: "12345", ParentUID: ""}}
@@ -1409,7 +1411,7 @@ func TestNestedFolderService(t *testing.T) {
parents = append(parents, &folder.Folder{UID: fmt.Sprintf("folder%d", i)})
}
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
//nestedFolderStore.ExpectedFolder = &folder.Folder{UID: "myFolder", ParentUID: "newFolder"}
nestedFolderStore.ExpectedParentFolders = parents
@@ -1439,7 +1441,7 @@ func TestNestedFolderService(t *testing.T) {
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
nestedFolderStore := NewFakeStore()
nestedFolderStore := folder.NewFakeStore()
nestedFolderStore.ExpectedError = folder.ErrFolderNotFound
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{
@@ -1493,6 +1495,7 @@ func TestIntegrationNestedFolderSharedWithMe(t *testing.T) {
dashboardPermissions,
actest.FakeAccessControl{},
serviceWithFlagOn,
nestedFolderStore,
nil,
)
require.NoError(t, err)
@@ -2496,7 +2499,7 @@ func TestSupportBundle(t *testing.T) {
}
}
func CreateSubtreeInStore(t *testing.T, store store, service *Service, depth int, prefix string, cmd folder.CreateFolderCommand) []*folder.Folder {
func CreateSubtreeInStore(t *testing.T, store folder.Store, service *Service, depth int, prefix string, cmd folder.CreateFolderCommand) []*folder.Folder {
t.Helper()
folders := make([]*folder.Folder, 0, depth)
@@ -2520,7 +2523,7 @@ func CreateSubtreeInStore(t *testing.T, store store, service *Service, depth int
return folders
}
func setup(t *testing.T, dashStore dashboards.Store, dashboardFolderStore folder.FolderStore, nestedFolderStore store, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl, db db.DB) folder.Service {
func setup(t *testing.T, dashStore dashboards.Store, dashboardFolderStore folder.FolderStore, nestedFolderStore folder.Store, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl, db db.DB) folder.Service {
t.Helper()
// nothing enabled yet

View File

@@ -22,19 +22,19 @@ import (
const DEFAULT_BATCH_SIZE = 999
type sqlStore struct {
type FolderStoreImpl struct {
db db.DB
log log.Logger
}
// sqlStore implements the store interface.
var _ store = (*sqlStore)(nil)
var _ folder.Store = (*FolderStoreImpl)(nil)
func ProvideStore(db db.DB) *sqlStore {
return &sqlStore{db: db, log: log.New("folder-store")}
func ProvideStore(db db.DB) *FolderStoreImpl {
return &FolderStoreImpl{db: db, log: log.New("folder-store")}
}
func (ss *sqlStore) Create(ctx context.Context, cmd folder.CreateFolderCommand) (*folder.Folder, error) {
func (ss *FolderStoreImpl) Create(ctx context.Context, cmd folder.CreateFolderCommand) (*folder.Folder, error) {
if cmd.UID == "" {
return nil, folder.ErrBadRequest.Errorf("missing UID")
}
@@ -83,7 +83,7 @@ func (ss *sqlStore) Create(ctx context.Context, cmd folder.CreateFolderCommand)
return foldr.WithURL(), err
}
func (ss *sqlStore) Delete(ctx context.Context, UIDs []string, orgID int64) error {
func (ss *FolderStoreImpl) Delete(ctx context.Context, UIDs []string, orgID int64) error {
if len(UIDs) == 0 {
return nil
}
@@ -103,7 +103,7 @@ func (ss *sqlStore) Delete(ctx context.Context, UIDs []string, orgID int64) erro
})
}
func (ss *sqlStore) Update(ctx context.Context, cmd folder.UpdateFolderCommand) (*folder.Folder, error) {
func (ss *FolderStoreImpl) Update(ctx context.Context, cmd folder.UpdateFolderCommand) (*folder.Folder, error) {
updated := time.Now()
uid := cmd.UID
@@ -191,7 +191,7 @@ func (ss *sqlStore) Update(ctx context.Context, cmd folder.UpdateFolderCommand)
// └── B/C
//
// The full path of C is "A/B\/C".
func (ss *sqlStore) Get(ctx context.Context, q folder.GetFolderQuery) (*folder.Folder, error) {
func (ss *FolderStoreImpl) Get(ctx context.Context, q folder.GetFolderQuery) (*folder.Folder, error) {
foldr := &folder.Folder{}
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
exists := false
@@ -244,7 +244,7 @@ func (ss *sqlStore) Get(ctx context.Context, q folder.GetFolderQuery) (*folder.F
return foldr.WithURL(), err
}
func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
func (ss *FolderStoreImpl) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
if q.UID == "" {
return []*folder.Folder{}, nil
}
@@ -295,7 +295,7 @@ func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([
return util.Reverse(folders[1:]), nil
}
func (ss *sqlStore) GetChildren(ctx context.Context, q folder.GetChildrenQuery) ([]*folder.Folder, error) {
func (ss *FolderStoreImpl) GetChildren(ctx context.Context, q folder.GetChildrenQuery) ([]*folder.Folder, error) {
var folders []*folder.Folder
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
@@ -353,7 +353,7 @@ func (ss *sqlStore) GetChildren(ctx context.Context, q folder.GetChildrenQuery)
return folders, err
}
func (ss *sqlStore) getParentsMySQL(ctx context.Context, q folder.GetParentsQuery) (folders []*folder.Folder, err error) {
func (ss *FolderStoreImpl) getParentsMySQL(ctx context.Context, q folder.GetParentsQuery) (folders []*folder.Folder, err error) {
err = ss.db.WithDbSession(ctx, func(sess *db.Session) error {
uid := ""
// covered by UQE_folder_org_id_uid
@@ -387,7 +387,7 @@ func (ss *sqlStore) getParentsMySQL(ctx context.Context, q folder.GetParentsQuer
}
// TODO use a single query to get the height of a folder
func (ss *sqlStore) GetHeight(ctx context.Context, foldrUID string, orgID int64, parentUID *string) (int, error) {
func (ss *FolderStoreImpl) GetHeight(ctx context.Context, foldrUID string, orgID int64, parentUID *string) (int, error) {
height := -1
queue := []string{foldrUID}
for len(queue) > 0 && height <= folder.MaxNestedFolderDepth {
@@ -445,7 +445,7 @@ func (ss *sqlStore) GetHeight(ctx context.Context, foldrUID string, orgID int64,
// The full path UIDs of C is "uid1/uid2/uid3".
// The full path UIDs of B is "uid1/uid2".
// The full path UIDs of A is "uid1".
func (ss *sqlStore) GetFolders(ctx context.Context, q getFoldersQuery) ([]*folder.Folder, error) {
func (ss *FolderStoreImpl) GetFolders(ctx context.Context, q folder.GetFoldersFromStoreQuery) ([]*folder.Folder, error) {
if q.BatchSize == 0 {
q.BatchSize = DEFAULT_BATCH_SIZE
}
@@ -467,7 +467,7 @@ func (ss *sqlStore) GetFolders(ctx context.Context, q getFoldersQuery) ([]*folde
}
s.WriteString(` FROM folder f0`)
// join the same table multiple times to compute the full path of a folder
if q.WithFullpath || q.WithFullpathUIDs || len(q.ancestorUIDs) > 0 {
if q.WithFullpath || q.WithFullpathUIDs || len(q.AncestorUIDs) > 0 {
s.WriteString(getFullpathJoinsSQL())
}
// covered by UQE_folder_org_id_uid
@@ -489,7 +489,7 @@ func (ss *sqlStore) GetFolders(ctx context.Context, q getFoldersQuery) ([]*folde
args = append(args, accesscontrol.K6FolderUID, accesscontrol.K6FolderUID)
}
if len(q.ancestorUIDs) == 0 {
if len(q.AncestorUIDs) == 0 {
if q.OrderByTitle {
s.WriteString(` ORDER BY f0.title ASC`)
}
@@ -503,8 +503,8 @@ func (ss *sqlStore) GetFolders(ctx context.Context, q getFoldersQuery) ([]*folde
}
// filter out folders if they are not in the subtree of the given ancestor folders
if err := batch(len(q.ancestorUIDs), int(q.BatchSize), func(start2, end2 int) error {
s2, args2 := getAncestorsSQL(ss.db.GetDialect(), q.ancestorUIDs, start2, end2, s.String(), args)
if err := batch(len(q.AncestorUIDs), int(q.BatchSize), func(start2, end2 int) error {
s2, args2 := getAncestorsSQL(ss.db.GetDialect(), q.AncestorUIDs, start2, end2, s.String(), args)
if q.OrderByTitle {
s2 += " ORDER BY f0.title ASC"
}
@@ -533,7 +533,7 @@ func (ss *sqlStore) GetFolders(ctx context.Context, q getFoldersQuery) ([]*folde
return folders, nil
}
func (ss *sqlStore) GetDescendants(ctx context.Context, orgID int64, ancestor_uid string) ([]*folder.Folder, error) {
func (ss *FolderStoreImpl) GetDescendants(ctx context.Context, orgID int64, ancestor_uid string) ([]*folder.Folder, error) {
var folders []*folder.Folder
recursiveQueriesAreSupported, err := ss.db.RecursiveQueriesAreSupported()

View File

@@ -865,7 +865,7 @@ func TestIntegrationGetFolders(t *testing.T) {
})
t.Run("get folders by UIDs should succeed", func(t *testing.T) {
actualFolders, err := folderStore.GetFolders(context.Background(), NewGetFoldersQuery(folder.GetFoldersQuery{OrgID: orgID, UIDs: uids[1:]}))
actualFolders, err := folderStore.GetFolders(context.Background(), folder.NewGetFoldersQuery(folder.GetFoldersQuery{OrgID: orgID, UIDs: uids[1:]}))
require.NoError(t, err)
assert.Equal(t, len(uids[1:]), len(actualFolders))
for _, f := range folders[1:] {
@@ -885,7 +885,7 @@ func TestIntegrationGetFolders(t *testing.T) {
})
t.Run("get folders by UIDs batching should work as expected", func(t *testing.T) {
q := NewGetFoldersQuery(folder.GetFoldersQuery{OrgID: orgID, UIDs: uids[1:], BatchSize: 3})
q := folder.NewGetFoldersQuery(folder.GetFoldersQuery{OrgID: orgID, UIDs: uids[1:], BatchSize: 3})
actualFolders, err := folderStore.GetFolders(context.Background(), q)
require.NoError(t, err)
assert.Equal(t, len(uids[1:]), len(actualFolders))
@@ -906,7 +906,7 @@ func TestIntegrationGetFolders(t *testing.T) {
})
t.Run("get folders by UIDs with fullpath should succeed", func(t *testing.T) {
q := NewGetFoldersQuery(folder.GetFoldersQuery{OrgID: orgID, UIDs: uids[1:], WithFullpath: true})
q := folder.NewGetFoldersQuery(folder.GetFoldersQuery{OrgID: orgID, UIDs: uids[1:], WithFullpath: true})
q.BatchSize = 3
actualFolders, err := folderStore.GetFolders(context.Background(), q)
require.NoError(t, err)
@@ -929,12 +929,12 @@ func TestIntegrationGetFolders(t *testing.T) {
})
t.Run("get folders by UIDs and ancestor UIDs should work as expected", func(t *testing.T) {
q := NewGetFoldersQuery(folder.GetFoldersQuery{OrgID: orgID, UIDs: uids[1:], BatchSize: 3})
q.ancestorUIDs = make([]string, 0, int(q.BatchSize)+1)
q := folder.NewGetFoldersQuery(folder.GetFoldersQuery{OrgID: orgID, UIDs: uids[1:], BatchSize: 3})
q.AncestorUIDs = make([]string, 0, int(q.BatchSize)+1)
for i := 0; i < int(q.BatchSize); i++ {
q.ancestorUIDs = append(q.ancestorUIDs, uuid.New().String())
q.AncestorUIDs = append(q.AncestorUIDs, uuid.New().String())
}
q.ancestorUIDs = append(q.ancestorUIDs, folders[len(folders)-1].UID)
q.AncestorUIDs = append(q.AncestorUIDs, folders[len(folders)-1].UID)
actualFolders, err := folderStore.GetFolders(context.Background(), q)
require.NoError(t, err)
@@ -967,7 +967,7 @@ func CreateOrg(t *testing.T, db db.DB, cfg *setting.Cfg) int64 {
return orgID
}
func CreateSubtree(t *testing.T, store *sqlStore, orgID int64, parentUID string, depth int, prefix string) []string {
func CreateSubtree(t *testing.T, store *FolderStoreImpl, orgID int64, parentUID string, depth int, prefix string) []string {
t.Helper()
ancestorUIDs := []string{}
@@ -1006,7 +1006,7 @@ func CreateSubtree(t *testing.T, store *sqlStore, orgID int64, parentUID string,
return ancestorUIDs
}
func CreateLeaves(t *testing.T, store *sqlStore, parent *folder.Folder, num int) []string {
func CreateLeaves(t *testing.T, store *FolderStoreImpl, parent *folder.Folder, num int) []string {
t.Helper()
leaves := make([]string, 0)
@@ -1024,7 +1024,7 @@ func CreateLeaves(t *testing.T, store *sqlStore, parent *folder.Folder, num int)
return leaves
}
func assertAncestorUIDs(t *testing.T, store *sqlStore, f *folder.Folder, expected []string) {
func assertAncestorUIDs(t *testing.T, store *FolderStoreImpl, f *folder.Folder, expected []string) {
t.Helper()
ancestors, err := store.GetParents(context.Background(), folder.GetParentsQuery{
@@ -1039,7 +1039,7 @@ func assertAncestorUIDs(t *testing.T, store *sqlStore, f *folder.Folder, expecte
assert.Equal(t, expected, actualAncestorsUIDs)
}
func assertChildrenUIDs(t *testing.T, store *sqlStore, f *folder.Folder, expected []string) {
func assertChildrenUIDs(t *testing.T, store *FolderStoreImpl, f *folder.Folder, expected []string) {
t.Helper()
ancestors, err := store.GetChildren(context.Background(), folder.GetChildrenQuery{

View File

@@ -1,66 +0,0 @@
package folderimpl
import (
"context"
"github.com/grafana/grafana/pkg/services/folder"
)
type fakeStore struct {
ExpectedChildFolders []*folder.Folder
ExpectedParentFolders []*folder.Folder
ExpectedFolders []*folder.Folder
ExpectedFolder *folder.Folder
ExpectedError error
ExpectedFolderHeight int
CreateCalled bool
DeleteCalled bool
}
func NewFakeStore() *fakeStore {
return &fakeStore{}
}
var _ store = (*fakeStore)(nil)
func (f *fakeStore) Create(ctx context.Context, cmd folder.CreateFolderCommand) (*folder.Folder, error) {
f.CreateCalled = true
return f.ExpectedFolder, f.ExpectedError
}
func (f *fakeStore) Delete(ctx context.Context, UIDs []string, orgID int64) error {
f.DeleteCalled = true
return f.ExpectedError
}
func (f *fakeStore) Update(ctx context.Context, cmd folder.UpdateFolderCommand) (*folder.Folder, error) {
return f.ExpectedFolder, f.ExpectedError
}
func (f *fakeStore) Move(ctx context.Context, cmd folder.MoveFolderCommand) error {
return f.ExpectedError
}
func (f *fakeStore) Get(ctx context.Context, cmd folder.GetFolderQuery) (*folder.Folder, error) {
return f.ExpectedFolder, f.ExpectedError
}
func (f *fakeStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
return f.ExpectedParentFolders, f.ExpectedError
}
func (f *fakeStore) GetChildren(ctx context.Context, cmd folder.GetChildrenQuery) ([]*folder.Folder, error) {
return f.ExpectedChildFolders, f.ExpectedError
}
func (f *fakeStore) GetHeight(ctx context.Context, folderUID string, orgID int64, parentUID *string) (int, error) {
return f.ExpectedFolderHeight, f.ExpectedError
}
func (f *fakeStore) GetFolders(ctx context.Context, q getFoldersQuery) ([]*folder.Folder, error) {
return f.ExpectedFolders, f.ExpectedError
}
func (f *fakeStore) GetDescendants(ctx context.Context, orgID int64, ancestor_uid string) ([]*folder.Folder, error) {
return f.ExpectedFolders, f.ExpectedError
}

View File

@@ -1,27 +1,25 @@
package folderimpl
package folder
import (
"context"
"github.com/grafana/grafana/pkg/services/folder"
)
type getFoldersQuery struct {
folder.GetFoldersQuery
ancestorUIDs []string
type GetFoldersFromStoreQuery struct {
GetFoldersQuery
AncestorUIDs []string
}
func NewGetFoldersQuery(q folder.GetFoldersQuery) getFoldersQuery {
return getFoldersQuery{
func NewGetFoldersQuery(q GetFoldersQuery) GetFoldersFromStoreQuery {
return GetFoldersFromStoreQuery{
GetFoldersQuery: q,
ancestorUIDs: []string{},
AncestorUIDs: []string{},
}
}
// store is the interface which a folder store must implement.
type store interface {
// Store is the interface which a folder Store must implement.
type Store interface {
// Create creates a folder and returns the newly-created folder.
Create(ctx context.Context, cmd folder.CreateFolderCommand) (*folder.Folder, error)
Create(ctx context.Context, cmd CreateFolderCommand) (*Folder, error)
// Delete folders with the specified UIDs and orgID from the folder store.
Delete(ctx context.Context, UIDs []string, orgID int64) error
@@ -30,24 +28,24 @@ type store interface {
// If the NewParentUID field is not nil, it updates also the parent UID (move mode).
// If it's a non empty string, it moves the folder under the folder with the specific UID
// otherwise, it moves the folder under the root folder (parent_uid column is set to NULL).
Update(ctx context.Context, cmd folder.UpdateFolderCommand) (*folder.Folder, error)
Update(ctx context.Context, cmd UpdateFolderCommand) (*Folder, error)
// Get returns a folder.
Get(ctx context.Context, q folder.GetFolderQuery) (*folder.Folder, error)
Get(ctx context.Context, q GetFolderQuery) (*Folder, error)
// GetParents returns an ordered list of parent folder of the given folder.
GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error)
GetParents(ctx context.Context, q GetParentsQuery) ([]*Folder, error)
// GetChildren returns the set of immediate children folders (depth=1) of the
// given folder.
GetChildren(ctx context.Context, q folder.GetChildrenQuery) ([]*folder.Folder, error)
GetChildren(ctx context.Context, q GetChildrenQuery) ([]*Folder, error)
// GetHeight returns the height of the folder tree. When parentUID is set, the function would
// verify in the meanwhile that parentUID is not present in the subtree of the folder with the given UID.
GetHeight(ctx context.Context, foldrUID string, orgID int64, parentUID *string) (int, error)
// GetFolders returns folders with given uids
GetFolders(ctx context.Context, q getFoldersQuery) ([]*folder.Folder, error)
GetFolders(ctx context.Context, q GetFoldersFromStoreQuery) ([]*Folder, error)
// GetDescendants returns all descendants of a folder
GetDescendants(ctx context.Context, orgID int64, anchestor_uid string) ([]*folder.Folder, error)
GetDescendants(ctx context.Context, orgID int64, anchestor_uid string) ([]*Folder, error)
}

View File

@@ -0,0 +1,64 @@
package folder
import (
"context"
)
type fakeStore struct {
ExpectedChildFolders []*Folder
ExpectedParentFolders []*Folder
ExpectedFolders []*Folder
ExpectedFolder *Folder
ExpectedError error
ExpectedFolderHeight int
CreateCalled bool
DeleteCalled bool
}
func NewFakeStore() *fakeStore {
return &fakeStore{}
}
var _ Store = (*fakeStore)(nil)
func (f *fakeStore) Create(ctx context.Context, cmd CreateFolderCommand) (*Folder, error) {
f.CreateCalled = true
return f.ExpectedFolder, f.ExpectedError
}
func (f *fakeStore) Delete(ctx context.Context, UIDs []string, orgID int64) error {
f.DeleteCalled = true
return f.ExpectedError
}
func (f *fakeStore) Update(ctx context.Context, cmd UpdateFolderCommand) (*Folder, error) {
return f.ExpectedFolder, f.ExpectedError
}
func (f *fakeStore) Move(ctx context.Context, cmd MoveFolderCommand) error {
return f.ExpectedError
}
func (f *fakeStore) Get(ctx context.Context, cmd GetFolderQuery) (*Folder, error) {
return f.ExpectedFolder, f.ExpectedError
}
func (f *fakeStore) GetParents(ctx context.Context, q GetParentsQuery) ([]*Folder, error) {
return f.ExpectedParentFolders, f.ExpectedError
}
func (f *fakeStore) GetChildren(ctx context.Context, cmd GetChildrenQuery) ([]*Folder, error) {
return f.ExpectedChildFolders, f.ExpectedError
}
func (f *fakeStore) GetHeight(ctx context.Context, folderUID string, orgID int64, parentUID *string) (int, error) {
return f.ExpectedFolderHeight, f.ExpectedError
}
func (f *fakeStore) GetFolders(ctx context.Context, q GetFoldersFromStoreQuery) ([]*Folder, error) {
return f.ExpectedFolders, f.ExpectedError
}
func (f *fakeStore) GetDescendants(ctx context.Context, orgID int64, ancestor_uid string) ([]*Folder, error) {
return f.ExpectedFolders, f.ExpectedError
}