Folders: Able to fetch folders available for user as "shared" folder (#77774)
* Folders: Show folders user has access to at the root level * Refactor * Refactor * Hide parent folders user has no access to * Skip expensive computation if possible * Fix tests * Fix potential nil access * Fix duplicated folders * Fix linter error * Fix querying folders if no managed permissions set * Update benchmark * Add special shared with me folder and fetch available non-root folders on demand * Fix parents query * Improve db query for folders * Reset benchmark changes * Fix permissions for shared with me folder * Simplify dedup * Add option to include shared folder permission to user's permissions * Fix nil UID * Remove duplicated folders from shared list * Only left the base part * Apply suggestions from code review Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> * Add tests * Fix linter errors --------- Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
This commit is contained in:
@@ -7,6 +7,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/events"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
@@ -246,6 +248,85 @@ func (s *Service) GetChildren(ctx context.Context, cmd *folder.GetChildrenQuery)
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
// GetSharedWithMe returns folders available to user, which cannot be accessed from the root folders
|
||||
func (s *Service) GetSharedWithMe(ctx context.Context, cmd *folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
availableNonRootFolders, err := s.getAvailableNonRootFolders(ctx, cmd.OrgID, cmd.SignedInUser)
|
||||
if err != nil {
|
||||
return nil, folder.ErrInternal.Errorf("failed to fetch subfolders to which the user has explicit access: %w", err)
|
||||
}
|
||||
rootFolders, err := s.GetChildren(ctx, &folder.GetChildrenQuery{UID: "", OrgID: cmd.OrgID, SignedInUser: cmd.SignedInUser})
|
||||
if err != nil {
|
||||
return nil, folder.ErrInternal.Errorf("failed to fetch root folders to which the user has access: %w", err)
|
||||
}
|
||||
availableNonRootFolders = s.deduplicateAvailableFolders(ctx, availableNonRootFolders, rootFolders)
|
||||
return availableNonRootFolders, nil
|
||||
}
|
||||
|
||||
func (s *Service) getAvailableNonRootFolders(ctx context.Context, orgID int64, user identity.Requester) ([]*folder.Folder, error) {
|
||||
permissions := user.GetPermissions()
|
||||
folderPermissions := permissions["folders:read"]
|
||||
folderPermissions = append(folderPermissions, permissions["dashboards:read"]...)
|
||||
nonRootFolders := make([]*folder.Folder, 0)
|
||||
folderUids := make([]string, 0)
|
||||
for _, p := range folderPermissions {
|
||||
if folderUid, found := strings.CutPrefix(p, "folders:uid:"); found {
|
||||
if !slices.Contains(folderUids, folderUid) {
|
||||
folderUids = append(folderUids, folderUid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(folderUids) == 0 {
|
||||
return nonRootFolders, nil
|
||||
}
|
||||
|
||||
dashFolders, err := s.store.GetFolders(ctx, orgID, folderUids)
|
||||
if err != nil {
|
||||
return nil, folder.ErrInternal.Errorf("failed to fetch subfolders: %w", err)
|
||||
}
|
||||
|
||||
for _, f := range dashFolders {
|
||||
if f.ParentUID != "" {
|
||||
nonRootFolders = append(nonRootFolders, f)
|
||||
}
|
||||
}
|
||||
|
||||
return nonRootFolders, nil
|
||||
}
|
||||
|
||||
func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*folder.Folder, rootFolders []*folder.Folder) []*folder.Folder {
|
||||
allFolders := append(folders, rootFolders...)
|
||||
foldersDedup := make([]*folder.Folder, 0)
|
||||
for _, f := range folders {
|
||||
isSubfolder := slices.ContainsFunc(allFolders, func(folder *folder.Folder) bool {
|
||||
return f.ParentUID == folder.UID
|
||||
})
|
||||
|
||||
if !isSubfolder {
|
||||
parents, err := s.GetParents(ctx, folder.GetParentsQuery{UID: f.UID, OrgID: f.OrgID})
|
||||
if err != nil {
|
||||
s.log.Error("failed to fetch folder parents", "uid", f.UID, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, parent := range parents {
|
||||
contains := slices.ContainsFunc(allFolders, func(f *folder.Folder) bool {
|
||||
return f.UID == parent.UID
|
||||
})
|
||||
if contains {
|
||||
isSubfolder = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !isSubfolder {
|
||||
foldersDedup = append(foldersDedup, f)
|
||||
}
|
||||
}
|
||||
return foldersDedup
|
||||
}
|
||||
|
||||
func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
||||
if !s.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
return nil, nil
|
||||
|
||||
Reference in New Issue
Block a user