|
|
|
@ -8,6 +8,7 @@ import ( |
|
|
|
|
|
|
|
|
|
authzv1 "github.com/grafana/authlib/authz/proto/v1" |
|
|
|
|
"github.com/grafana/authlib/claims" |
|
|
|
|
"golang.org/x/sync/singleflight" |
|
|
|
|
"google.golang.org/grpc/codes" |
|
|
|
|
"google.golang.org/grpc/status" |
|
|
|
|
"k8s.io/apiserver/pkg/endpoints/request" |
|
|
|
@ -49,6 +50,9 @@ type Service struct { |
|
|
|
|
teamCache *localcache.CacheService |
|
|
|
|
basicRoleCache *localcache.CacheService |
|
|
|
|
folderCache *localcache.CacheService |
|
|
|
|
|
|
|
|
|
// Deduplication of concurrent requests
|
|
|
|
|
sf *singleflight.Group |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func NewService(sql legacysql.LegacyDatabaseProvider, identityStore legacy.LegacyIdentityStore, logger log.Logger, tracer tracing.Tracer) *Service { |
|
|
|
@ -63,6 +67,7 @@ func NewService(sql legacysql.LegacyDatabaseProvider, identityStore legacy.Legac |
|
|
|
|
teamCache: localcache.New(shortCacheTTL, shortCleanupInterval), |
|
|
|
|
basicRoleCache: localcache.New(longCacheTTL, longCleanupInterval), |
|
|
|
|
folderCache: localcache.New(shortCacheTTL, shortCleanupInterval), |
|
|
|
|
sf: new(singleflight.Group), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -339,38 +344,45 @@ func (s *Service) buildFolderTree(ctx context.Context, ns claims.NamespaceInfo) |
|
|
|
|
return cached.(map[string]FolderNode), nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
folders, err := s.store.GetFolders(ctx, ns) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, fmt.Errorf("could not get folders: %w", err) |
|
|
|
|
} |
|
|
|
|
res, err, _ := s.sf.Do(ns.Value+"_buildFolderTree", func() (interface{}, error) { |
|
|
|
|
folders, err := s.store.GetFolders(ctx, ns) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, fmt.Errorf("could not get folders: %w", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
folderMap := make(map[string]FolderNode, len(folders)) |
|
|
|
|
for _, folder := range folders { |
|
|
|
|
if node, has := folderMap[folder.UID]; !has { |
|
|
|
|
folderMap[folder.UID] = FolderNode{ |
|
|
|
|
uid: folder.UID, |
|
|
|
|
parentUID: folder.ParentUID, |
|
|
|
|
folderMap := make(map[string]FolderNode, len(folders)) |
|
|
|
|
for _, folder := range folders { |
|
|
|
|
if node, has := folderMap[folder.UID]; !has { |
|
|
|
|
folderMap[folder.UID] = FolderNode{ |
|
|
|
|
uid: folder.UID, |
|
|
|
|
parentUID: folder.ParentUID, |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
node.parentUID = folder.ParentUID |
|
|
|
|
folderMap[folder.UID] = node |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
node.parentUID = folder.ParentUID |
|
|
|
|
folderMap[folder.UID] = node |
|
|
|
|
} |
|
|
|
|
// Register that the parent has this child node
|
|
|
|
|
if folder.ParentUID == nil { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
if parent, has := folderMap[*folder.ParentUID]; has { |
|
|
|
|
parent.childrenUIDs = append(parent.childrenUIDs, folder.UID) |
|
|
|
|
folderMap[*folder.ParentUID] = parent |
|
|
|
|
} else { |
|
|
|
|
folderMap[*folder.ParentUID] = FolderNode{ |
|
|
|
|
uid: *folder.ParentUID, |
|
|
|
|
childrenUIDs: []string{folder.UID}, |
|
|
|
|
// Register that the parent has this child node
|
|
|
|
|
if folder.ParentUID == nil { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
if parent, has := folderMap[*folder.ParentUID]; has { |
|
|
|
|
parent.childrenUIDs = append(parent.childrenUIDs, folder.UID) |
|
|
|
|
folderMap[*folder.ParentUID] = parent |
|
|
|
|
} else { |
|
|
|
|
folderMap[*folder.ParentUID] = FolderNode{ |
|
|
|
|
uid: *folder.ParentUID, |
|
|
|
|
childrenUIDs: []string{folder.UID}, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
s.folderCache.Set(key, folderMap, 0) |
|
|
|
|
s.folderCache.Set(key, folderMap, 0) |
|
|
|
|
return folderMap, nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return folderMap, nil |
|
|
|
|
return res.(map[string]FolderNode), nil |
|
|
|
|
} |
|
|
|
|