Nested Folders: Support listing nested folder children (#58566)
* Nested Folders: Support listing nested folder children * Filter out subfolders with no permissions * Apply suggestion from code review
This commit is contained in:
committed by
GitHub
parent
0743c4eb87
commit
b1ef5ab320
@@ -134,28 +134,53 @@ func (s *Service) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) {
|
||||
func (s *Service) GetChildren(ctx context.Context, cmd *folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
if cmd.SignedInUser == nil {
|
||||
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
children, err := s.store.GetChildren(ctx, *cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filtered := make([]*folder.Folder, 0, len(children))
|
||||
for _, f := range children {
|
||||
g, err := guardian.New(ctx, f.ID, f.OrgID, cmd.SignedInUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
canView, err := g.CanView()
|
||||
if err != nil || canView {
|
||||
filtered = append(filtered, f)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
searchQuery := search.Query{
|
||||
SignedInUser: user,
|
||||
SignedInUser: cmd.SignedInUser,
|
||||
DashboardIds: make([]int64, 0),
|
||||
FolderIds: make([]int64, 0),
|
||||
Limit: limit,
|
||||
OrgId: orgID,
|
||||
Limit: cmd.Limit,
|
||||
OrgId: cmd.OrgID,
|
||||
Type: "dash-folder",
|
||||
Permission: models.PERMISSION_VIEW,
|
||||
Page: page,
|
||||
Page: cmd.Page,
|
||||
}
|
||||
|
||||
if err := s.searchService.SearchHandler(ctx, &searchQuery); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folders := make([]*models.Folder, 0)
|
||||
folders := make([]*folder.Folder, 0)
|
||||
|
||||
for _, hit := range searchQuery.Result {
|
||||
folders = append(folders, &models.Folder{
|
||||
Id: hit.ID,
|
||||
Uid: hit.UID,
|
||||
folders = append(folders, &folder.Folder{
|
||||
ID: hit.ID,
|
||||
UID: hit.UID,
|
||||
Title: hit.Title,
|
||||
})
|
||||
}
|
||||
@@ -533,7 +558,7 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e
|
||||
return err
|
||||
}
|
||||
|
||||
folders, err := s.store.GetChildren(ctx, folder.GetTreeQuery{UID: cmd.UID, OrgID: cmd.OrgID})
|
||||
folders, err := s.store.GetChildren(ctx, folder.GetChildrenQuery{UID: cmd.UID, OrgID: cmd.OrgID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -560,12 +585,6 @@ func (s *Service) GetParents(ctx context.Context, cmd *folder.GetParentsQuery) (
|
||||
return s.store.GetParents(ctx, *cmd)
|
||||
}
|
||||
|
||||
func (s *Service) GetTree(ctx context.Context, cmd *folder.GetTreeQuery) ([]*folder.Folder, error) {
|
||||
// check the flag, if old - do whatever did before
|
||||
// for new only the store
|
||||
return s.store.GetChildren(ctx, *cmd)
|
||||
}
|
||||
|
||||
func (s *Service) MakeUserAdmin(ctx context.Context, orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error {
|
||||
return s.dashboardService.MakeUserAdmin(ctx, orgID, userID, folderID, setViewAndEditPermissions)
|
||||
}
|
||||
|
||||
@@ -361,9 +361,30 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
|
||||
UID: "test4",
|
||||
},
|
||||
}
|
||||
res, err := folderService.GetTree(context.Background(),
|
||||
&folder.GetTreeQuery{
|
||||
UID: "test",
|
||||
|
||||
g := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{})
|
||||
t.Cleanup(func() {
|
||||
guardian.New = g
|
||||
})
|
||||
|
||||
res, err := folderService.GetChildren(context.Background(),
|
||||
&folder.GetChildrenQuery{
|
||||
UID: "test",
|
||||
SignedInUser: usr,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(res))
|
||||
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true})
|
||||
t.Cleanup(func() {
|
||||
guardian.New = g
|
||||
})
|
||||
|
||||
res, err = folderService.GetChildren(context.Background(),
|
||||
&folder.GetChildrenQuery{
|
||||
UID: "test",
|
||||
SignedInUser: usr,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 4, len(res))
|
||||
|
||||
@@ -212,21 +212,28 @@ 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.GetTreeQuery) ([]*folder.Folder, error) {
|
||||
func (ss *sqlStore) GetChildren(ctx context.Context, q folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
var folders []*folder.Folder
|
||||
|
||||
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
sql := strings.Builder{}
|
||||
sql.Write([]byte("SELECT * FROM folder WHERE parent_uid=? AND org_id=? ORDER BY id"))
|
||||
args := make([]interface{}, 0, 2)
|
||||
if q.UID == "" {
|
||||
sql.Write([]byte("SELECT * FROM folder WHERE parent_uid IS NULL AND org_id=?"))
|
||||
args = append(args, q.OrgID)
|
||||
} else {
|
||||
sql.Write([]byte("SELECT * FROM folder WHERE parent_uid=? AND org_id=?"))
|
||||
args = append(args, q.UID, q.OrgID)
|
||||
}
|
||||
|
||||
if q.Limit != 0 {
|
||||
var offset int64 = 1
|
||||
if q.Page != 0 {
|
||||
offset = q.Page
|
||||
var offset int64 = 0
|
||||
if q.Page > 0 {
|
||||
offset = q.Limit * (q.Page - 1)
|
||||
}
|
||||
sql.Write([]byte(ss.db.GetDialect().LimitOffset(q.Limit, offset)))
|
||||
}
|
||||
err := sess.SQL(sql.String(), q.UID, q.OrgID).Find(&folders)
|
||||
err := sess.SQL(sql.String(), args...).Find(&folders)
|
||||
if err != nil {
|
||||
return folder.ErrDatabaseError.Errorf("failed to get folder children: %w", err)
|
||||
}
|
||||
@@ -277,7 +284,7 @@ func (ss *sqlStore) GetHeight(ctx context.Context, foldrUID string, orgID int64,
|
||||
if parentUID != nil && *parentUID == ele {
|
||||
return 0, folder.ErrCircularReference
|
||||
}
|
||||
folders, err := ss.GetChildren(ctx, folder.GetTreeQuery{UID: ele, OrgID: orgID})
|
||||
folders, err := ss.GetChildren(ctx, folder.GetChildrenQuery{UID: ele, OrgID: orgID})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ func TestIntegrationDelete(t *testing.T) {
|
||||
err := folderStore.Delete(context.Background(), ancestorUIDs[len(ancestorUIDs)-1], orgID)
|
||||
require.NoError(t, err)
|
||||
|
||||
children, err := folderStore.GetChildren(context.Background(), folder.GetTreeQuery{
|
||||
children, err := folderStore.GetChildren(context.Background(), folder.GetChildrenQuery{
|
||||
UID: ancestorUIDs[len(ancestorUIDs)-2],
|
||||
OrgID: orgID,
|
||||
})
|
||||
@@ -456,7 +456,7 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
treeLeaves := CreateLeaves(t, folderStore, parent, 4)
|
||||
treeLeaves := CreateLeaves(t, folderStore, parent, 8)
|
||||
|
||||
t.Cleanup(func() {
|
||||
for _, uid := range treeLeaves {
|
||||
@@ -473,7 +473,7 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
*/
|
||||
|
||||
t.Run("should successfully get all children", func(t *testing.T) {
|
||||
children, err := folderStore.GetChildren(context.Background(), folder.GetTreeQuery{
|
||||
children, err := folderStore.GetChildren(context.Background(), folder.GetChildrenQuery{
|
||||
UID: parent.UID,
|
||||
OrgID: orgID,
|
||||
})
|
||||
@@ -489,12 +489,24 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should default to general folder if UID is missing", func(t *testing.T) {
|
||||
children, err := folderStore.GetChildren(context.Background(), folder.GetChildrenQuery{
|
||||
OrgID: orgID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
childrenUIDs := make([]string, 0, len(children))
|
||||
for _, c := range children {
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
assert.Equal(t, []string{parent.UID}, childrenUIDs)
|
||||
})
|
||||
|
||||
t.Run("query with pagination should work as expected", func(t *testing.T) {
|
||||
children, err := folderStore.GetChildren(context.Background(), folder.GetTreeQuery{
|
||||
children, err := folderStore.GetChildren(context.Background(), folder.GetChildrenQuery{
|
||||
UID: parent.UID,
|
||||
OrgID: orgID,
|
||||
Limit: 1,
|
||||
Page: 1,
|
||||
Limit: 2,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -503,14 +515,31 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(treeLeaves[1:2], childrenUIDs); diff != "" {
|
||||
if diff := cmp.Diff(treeLeaves[:2], childrenUIDs); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
children, err = folderStore.GetChildren(context.Background(), folder.GetTreeQuery{
|
||||
children, err = folderStore.GetChildren(context.Background(), folder.GetChildrenQuery{
|
||||
UID: parent.UID,
|
||||
OrgID: orgID,
|
||||
Limit: 1,
|
||||
Limit: 2,
|
||||
Page: 1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
childrenUIDs = make([]string, 0, len(children))
|
||||
for _, c := range children {
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(treeLeaves[:2], childrenUIDs); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
children, err = folderStore.GetChildren(context.Background(), folder.GetChildrenQuery{
|
||||
UID: parent.UID,
|
||||
OrgID: orgID,
|
||||
Limit: 2,
|
||||
Page: 2,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -520,12 +549,12 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(treeLeaves[2:3], childrenUIDs); diff != "" {
|
||||
if diff := cmp.Diff(treeLeaves[2:4], childrenUIDs); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
// no page is set
|
||||
children, err = folderStore.GetChildren(context.Background(), folder.GetTreeQuery{
|
||||
children, err = folderStore.GetChildren(context.Background(), folder.GetChildrenQuery{
|
||||
UID: parent.UID,
|
||||
OrgID: orgID,
|
||||
Limit: 1,
|
||||
@@ -537,12 +566,12 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(treeLeaves[1:2], childrenUIDs); diff != "" {
|
||||
if diff := cmp.Diff(treeLeaves[:1], childrenUIDs); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
// page is set but limit is not set, it should return them all
|
||||
children, err = folderStore.GetChildren(context.Background(), folder.GetTreeQuery{
|
||||
children, err = folderStore.GetChildren(context.Background(), folder.GetChildrenQuery{
|
||||
UID: parent.UID,
|
||||
OrgID: orgID,
|
||||
Page: 1,
|
||||
@@ -679,7 +708,7 @@ func assertAncestorUIDs(t *testing.T, store *sqlStore, f *folder.Folder, expecte
|
||||
func assertChildrenUIDs(t *testing.T, store *sqlStore, f *folder.Folder, expected []string) {
|
||||
t.Helper()
|
||||
|
||||
ancestors, err := store.GetChildren(context.Background(), folder.GetTreeQuery{
|
||||
ancestors, err := store.GetChildren(context.Background(), folder.GetChildrenQuery{
|
||||
UID: f.UID,
|
||||
OrgID: f.OrgID,
|
||||
})
|
||||
|
||||
@@ -26,7 +26,7 @@ type store interface {
|
||||
|
||||
// GetChildren returns the set of immediate children folders (depth=1) of the
|
||||
// given folder.
|
||||
GetChildren(ctx context.Context, cmd folder.GetTreeQuery) ([]*folder.Folder, error)
|
||||
GetChildren(ctx context.Context, cmd folder.GetChildrenQuery) ([]*folder.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.
|
||||
|
||||
@@ -48,7 +48,7 @@ func (f *FakeStore) GetParents(ctx context.Context, cmd folder.GetParentsQuery)
|
||||
return f.ExpectedParentFolders, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakeStore) GetChildren(ctx context.Context, cmd folder.GetTreeQuery) ([]*folder.Folder, error) {
|
||||
func (f *FakeStore) GetChildren(ctx context.Context, cmd folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
return f.ExpectedChildFolders, f.ExpectedError
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user