|
|
|
@ -203,6 +203,8 @@ func (s *Service) DBMigration(db db.DB) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) CountFoldersInOrg(ctx context.Context, orgID int64) (int64, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.CountFoldersInOrg") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
return s.unifiedStore.CountInOrg(ctx, orgID) |
|
|
|
|
} |
|
|
|
@ -211,17 +213,20 @@ func (s *Service) CountFoldersInOrg(ctx context.Context, orgID int64) (int64, er |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) SearchFolders(ctx context.Context, q folder.SearchFoldersQuery) (model.HitList, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.SearchFolders") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
// TODO:
|
|
|
|
|
// - implement filtering by alerting folders and k6 folders (see the dashboards store `FindDashboards` method for reference)
|
|
|
|
|
// - implement fallback on search client in unistore to go to legacy store (will need to read from dashboard store)
|
|
|
|
|
return s.searchFoldersFromApiServer(ctx, q) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("cannot be called on the legacy folder service") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) GetFolders(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetFolders") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
return s.getFoldersFromApiServer(ctx, q) |
|
|
|
|
} |
|
|
|
@ -229,6 +234,8 @@ func (s *Service) GetFolders(ctx context.Context, q folder.GetFoldersQuery) ([]* |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) GetFoldersLegacy(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetFoldersLegacy") |
|
|
|
|
defer span.End() |
|
|
|
|
if q.SignedInUser == nil { |
|
|
|
|
return nil, folder.ErrBadRequest.Errorf("missing signed in user") |
|
|
|
|
} |
|
|
|
@ -281,6 +288,8 @@ func (s *Service) GetFoldersLegacy(ctx context.Context, q folder.GetFoldersQuery |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) Get(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.Get") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
return s.getFromApiServer(ctx, q) |
|
|
|
|
} |
|
|
|
@ -288,6 +297,8 @@ func (s *Service) Get(ctx context.Context, q *folder.GetFolderQuery) (*folder.Fo |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetLegacy") |
|
|
|
|
defer span.End() |
|
|
|
|
if q.SignedInUser == nil { |
|
|
|
|
return nil, folder.ErrBadRequest.Errorf("missing signed in user") |
|
|
|
|
} |
|
|
|
@ -343,6 +354,8 @@ func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*fol |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) setFullpath(ctx context.Context, f *folder.Folder, user identity.Requester, forceLegacy bool) (*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.setFullpath") |
|
|
|
|
defer span.End() |
|
|
|
|
// #TODO is some kind of intermediate conversion required as is the case with user id where
|
|
|
|
|
// it gets parsed using UserIdentifier(). Also is there some kind of validation taking place as
|
|
|
|
|
// part of the parsing?
|
|
|
|
@ -386,6 +399,8 @@ func (s *Service) setFullpath(ctx context.Context, f *folder.Folder, user identi |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetChildren") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
return s.getChildrenFromApiServer(ctx, q) |
|
|
|
|
} |
|
|
|
@ -393,6 +408,8 @@ func (s *Service) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetChildrenLegacy") |
|
|
|
|
defer span.End() |
|
|
|
|
defer func(t time.Time) { |
|
|
|
|
parent := q.UID |
|
|
|
|
if q.UID != folder.SharedWithMeFolderUID { |
|
|
|
@ -466,6 +483,8 @@ func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQu |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) getRootFolders(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.getRootFolders") |
|
|
|
|
defer span.End() |
|
|
|
|
permissions := q.SignedInUser.GetPermissions() |
|
|
|
|
var folderPermissions []string |
|
|
|
|
if q.Permission == dashboardaccess.PERMISSION_EDIT { |
|
|
|
@ -535,6 +554,8 @@ func (s *Service) getRootFolders(ctx context.Context, q *folder.GetChildrenQuery |
|
|
|
|
|
|
|
|
|
// GetSharedWithMe returns folders available to user, which cannot be accessed from the root folders
|
|
|
|
|
func (s *Service) GetSharedWithMe(ctx context.Context, q *folder.GetChildrenQuery, forceLegacy bool) ([]*folder.FolderReference, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetSharedWithMe") |
|
|
|
|
defer span.End() |
|
|
|
|
start := time.Now() |
|
|
|
|
availableNonRootFolders, err := s.getAvailableNonRootFolders(ctx, q, forceLegacy) |
|
|
|
|
if err != nil { |
|
|
|
@ -558,6 +579,8 @@ func (s *Service) GetSharedWithMe(ctx context.Context, q *folder.GetChildrenQuer |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) getAvailableNonRootFolders(ctx context.Context, q *folder.GetChildrenQuery, forceLegacy bool) ([]*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.getAvailableNonRootFolders") |
|
|
|
|
defer span.End() |
|
|
|
|
permissions := q.SignedInUser.GetPermissions() |
|
|
|
|
var folderPermissions []string |
|
|
|
|
if q.Permission == dashboardaccess.PERMISSION_EDIT { |
|
|
|
@ -624,6 +647,8 @@ func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*fo |
|
|
|
|
foldersRef[i] = f.ToFolderReference() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_, span := s.tracer.Start(ctx, "folder.deduplicateAvailableFolders") |
|
|
|
|
defer span.End() |
|
|
|
|
allFolders := append(foldersRef, rootFolders...) |
|
|
|
|
foldersDedup := make([]*folder.FolderReference, 0) |
|
|
|
|
|
|
|
|
@ -661,6 +686,8 @@ func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*fo |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetParents") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
return s.getParentsFromApiServer(ctx, q) |
|
|
|
|
} |
|
|
|
@ -668,6 +695,8 @@ func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]* |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) GetParentsLegacy(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetParentsLegacy") |
|
|
|
|
defer span.End() |
|
|
|
|
if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) || q.UID == accesscontrol.GeneralFolderUID { |
|
|
|
|
return nil, nil |
|
|
|
|
} |
|
|
|
@ -801,6 +830,8 @@ func (s *Service) CreateLegacy(ctx context.Context, cmd *folder.CreateFolderComm |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.Update") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
return s.updateOnApiServer(ctx, cmd) |
|
|
|
|
} |
|
|
|
@ -808,7 +839,7 @@ func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) ( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) UpdateLegacy(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.Update") |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.UpdateLegacy") |
|
|
|
|
defer span.End() |
|
|
|
|
|
|
|
|
|
if cmd.SignedInUser == nil { |
|
|
|
@ -863,6 +894,8 @@ func (s *Service) UpdateLegacy(ctx context.Context, cmd *folder.UpdateFolderComm |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) legacyUpdate(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.legacyUpdate") |
|
|
|
|
defer span.End() |
|
|
|
|
query := dashboards.GetDashboardQuery{OrgID: cmd.OrgID, UID: cmd.UID} |
|
|
|
|
queryResult, err := s.dashboardStore.GetDashboard(ctx, &query) |
|
|
|
|
if err != nil { |
|
|
|
@ -940,6 +973,8 @@ func prepareForUpdate(dashFolder *dashboards.Dashboard, orgId int64, userId int6 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) error { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.Delete") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
return s.deleteFromApiServer(ctx, cmd) |
|
|
|
|
} |
|
|
|
@ -947,6 +982,8 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) DeleteLegacy(ctx context.Context, cmd *folder.DeleteFolderCommand) error { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.DeleteLegacy") |
|
|
|
|
defer span.End() |
|
|
|
|
if cmd.SignedInUser == nil { |
|
|
|
|
return folder.ErrBadRequest.Errorf("missing signed in user") |
|
|
|
|
} |
|
|
|
@ -1015,6 +1052,8 @@ func (s *Service) DeleteLegacy(ctx context.Context, cmd *folder.DeleteFolderComm |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) deleteChildrenInFolder(ctx context.Context, orgID int64, folderUIDs []string, user identity.Requester) error { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.deleteChildrenInFolder") |
|
|
|
|
defer span.End() |
|
|
|
|
for _, v := range s.registry { |
|
|
|
|
if err := v.DeleteInFolders(ctx, orgID, folderUIDs, user); err != nil { |
|
|
|
|
return err |
|
|
|
@ -1024,6 +1063,8 @@ func (s *Service) deleteChildrenInFolder(ctx context.Context, orgID int64, folde |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) legacyDelete(ctx context.Context, cmd *folder.DeleteFolderCommand, folderUIDs []string) error { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.legacyDelete") |
|
|
|
|
defer span.End() |
|
|
|
|
// We need a list of dashboard uids inside the folder to delete related public dashboards
|
|
|
|
|
dashes, err := s.dashboardStore.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{ |
|
|
|
|
SignedInUser: cmd.SignedInUser, |
|
|
|
@ -1060,6 +1101,8 @@ func (s *Service) legacyDelete(ctx context.Context, cmd *folder.DeleteFolderComm |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.Move") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
return s.moveOnApiServer(ctx, cmd) |
|
|
|
|
} |
|
|
|
@ -1067,7 +1110,7 @@ func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*fol |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) MoveLegacy(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.Move") |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.MoveLegacy") |
|
|
|
|
defer span.End() |
|
|
|
|
|
|
|
|
|
if cmd.SignedInUser == nil { |
|
|
|
@ -1180,6 +1223,8 @@ func (s *Service) publishFolderFullPathUpdatedEvent(ctx context.Context, timesta |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) canMove(ctx context.Context, cmd *folder.MoveFolderCommand) (bool, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.canMove") |
|
|
|
|
defer span.End() |
|
|
|
|
// Check that the user is allowed to move the folder to the destination folder
|
|
|
|
|
var evaluator accesscontrol.Evaluator |
|
|
|
|
parentUID := cmd.NewParentUID |
|
|
|
@ -1228,6 +1273,8 @@ func (s *Service) canMove(ctx context.Context, cmd *folder.MoveFolderCommand) (b |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) getFolderAndParentUIDScopes(ctx context.Context, folderUID string, orgID int64) ([]string, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.getFolderAndParentUIDScopes") |
|
|
|
|
defer span.End() |
|
|
|
|
folderAndParentUIDScopes := []string{dashboards.ScopeFoldersProvider.GetResourceScopeUID(folderUID)} |
|
|
|
|
if folderUID == folder.GeneralFolderUID { |
|
|
|
|
return folderAndParentUIDScopes, nil |
|
|
|
@ -1247,6 +1294,8 @@ func (s *Service) getFolderAndParentUIDScopes(ctx context.Context, folderUID str |
|
|
|
|
// its descendant folders (folders which are nested within it either directly or indirectly) from
|
|
|
|
|
// the folder store and returns the UIDs for all its descendants.
|
|
|
|
|
func (s *Service) nestedFolderDelete(ctx context.Context, cmd *folder.DeleteFolderCommand) ([]string, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.nestedFolderDelete") |
|
|
|
|
defer span.End() |
|
|
|
|
descendantUIDs := []string{} |
|
|
|
|
if cmd.SignedInUser == nil { |
|
|
|
|
return descendantUIDs, folder.ErrBadRequest.Errorf("missing signed in user") |
|
|
|
@ -1281,6 +1330,8 @@ func (s *Service) nestedFolderDelete(ctx context.Context, cmd *folder.DeleteFold |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) GetDescendantCounts(ctx context.Context, q *folder.GetDescendantCountsQuery) (folder.DescendantCounts, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetDescendantCounts") |
|
|
|
|
defer span.End() |
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { |
|
|
|
|
return s.getDescendantCountsFromApiServer(ctx, q) |
|
|
|
|
} |
|
|
|
@ -1289,6 +1340,8 @@ func (s *Service) GetDescendantCounts(ctx context.Context, q *folder.GetDescenda |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) GetDescendantCountsLegacy(ctx context.Context, q *folder.GetDescendantCountsQuery) (folder.DescendantCounts, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.GetDescendantCountsLegacy") |
|
|
|
|
defer span.End() |
|
|
|
|
if q.SignedInUser == nil { |
|
|
|
|
return nil, folder.ErrBadRequest.Errorf("missing signed-in user") |
|
|
|
|
} |
|
|
|
@ -1327,6 +1380,9 @@ func (s *Service) GetDescendantCountsLegacy(ctx context.Context, q *folder.GetDe |
|
|
|
|
// buildSaveDashboardCommand is a simplified version on DashboardServiceImpl.buildSaveDashboardCommand
|
|
|
|
|
// keeping only the meaningful functionality for folders
|
|
|
|
|
func (s *Service) buildSaveDashboardCommand(ctx context.Context, dto *dashboards.SaveDashboardDTO) (*dashboards.SaveDashboardCommand, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.buildSaveDashboardCommand") |
|
|
|
|
defer span.End() |
|
|
|
|
|
|
|
|
|
dash := dto.Dashboard |
|
|
|
|
|
|
|
|
|
dash.OrgID = dto.OrgID |
|
|
|
@ -1468,6 +1524,8 @@ func getGuardianForSavePermissionCheck(ctx context.Context, d *dashboards.Dashbo |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) nestedFolderCreate(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.nestedFolderCreate") |
|
|
|
|
defer span.End() |
|
|
|
|
if cmd.ParentUID != "" { |
|
|
|
|
if err := s.validateParent(ctx, cmd.OrgID, cmd.ParentUID, cmd.UID); err != nil { |
|
|
|
|
return nil, err |
|
|
|
@ -1477,6 +1535,8 @@ func (s *Service) nestedFolderCreate(ctx context.Context, cmd *folder.CreateFold |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Service) validateParent(ctx context.Context, orgID int64, parentUID string, UID string) error { |
|
|
|
|
ctx, span := s.tracer.Start(ctx, "folder.validateParent") |
|
|
|
|
defer span.End() |
|
|
|
|
ancestors, err := s.store.GetParents(ctx, folder.GetParentsQuery{UID: parentUID, OrgID: orgID}) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("failed to get parents: %w", err) |
|
|
|
|