K8s: Folders: Modify GetChildren to return only Folder References (#103072)
* Return FolderReference instead of Folder on GetChildren Signed-off-by: Maicon Costa <maiconscosta@gmail.com> --------- Signed-off-by: Maicon Costa <maiconscosta@gmail.com>
This commit is contained in:
@@ -581,6 +581,8 @@ require (
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
require github.com/grafana/grafana/pkg/build v0.0.0-20250401142320-991b016d6319 // @grafana/grafana-developer-enablement-squad
|
||||
|
||||
// Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream
|
||||
replace github.com/crewjam/saml => github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56
|
||||
|
||||
|
||||
@@ -1628,6 +1628,8 @@ github.com/grafana/grafana/pkg/apis/secret v0.0.0-20250319110241-5a004939da2a h1
|
||||
github.com/grafana/grafana/pkg/apis/secret v0.0.0-20250319110241-5a004939da2a/go.mod h1:K/fP4kODJmABug5b90PhACUZD6Xh/veEz2b1VRKNyuA=
|
||||
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250325075903-77fa2271be7a h1:NN0j9zdqYpfliR0P+au/PAJ5lqP7IZPNe8tAX5eaQNE=
|
||||
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250325075903-77fa2271be7a/go.mod h1:3Z958XEs20R6Wf5y4TFD07PGuGld6grB+wZ1qP/iyqg=
|
||||
github.com/grafana/grafana/pkg/build v0.0.0-20250401142320-991b016d6319 h1:Yh/pW6dY9fb9473zeL706kuqZlNG4jkMkiwljM665zI=
|
||||
github.com/grafana/grafana/pkg/build v0.0.0-20250401142320-991b016d6319/go.mod h1:xgwzIkkrPbAIY6PXa2fuCBzad5cFM4hdHKW7/V32szE=
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0=
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao=
|
||||
github.com/grafana/grafana/pkg/semconv v0.0.0-20250220164708-c8d4ff28a450 h1:wSqgLKFwI7fyeqf3djRXGClBLb/UPjZ4XPm/UsKFDB0=
|
||||
|
||||
@@ -84,7 +84,6 @@ func (hs *HTTPServer) GetFolders(c *contextmodel.ReqContext) response.Response {
|
||||
UID: c.Query("parentUid"),
|
||||
Permission: permission,
|
||||
SignedInUser: c.SignedInUser,
|
||||
RefOnly: true, // nolint:staticcheck
|
||||
}
|
||||
|
||||
folders, err := hs.folderService.GetChildren(c.Req.Context(), q)
|
||||
|
||||
@@ -385,14 +385,14 @@ func (s *Service) setFullpath(ctx context.Context, f *folder.Folder, user identi
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
func (s *Service) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.getChildrenFromApiServer(ctx, q)
|
||||
}
|
||||
return s.GetChildrenLegacy(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
defer func(t time.Time) {
|
||||
parent := q.UID
|
||||
if q.UID != folder.SharedWithMeFolderUID {
|
||||
@@ -465,7 +465,7 @@ func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQu
|
||||
return children, nil
|
||||
}
|
||||
|
||||
func (s *Service) getRootFolders(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
func (s *Service) getRootFolders(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
permissions := q.SignedInUser.GetPermissions()
|
||||
var folderPermissions []string
|
||||
if q.Permission == dashboardaccess.PERMISSION_EDIT {
|
||||
@@ -513,7 +513,7 @@ func (s *Service) getRootFolders(ctx context.Context, q *folder.GetChildrenQuery
|
||||
// fetch folder from dashboard store
|
||||
dashFolder, ok := dashFolders[f.UID]
|
||||
if !ok {
|
||||
s.log.Error("failed to fetch folder by UID from dashboard store", "orgID", f.OrgID, "uid", f.UID)
|
||||
s.log.Error("failed to fetch folder by UID from dashboard store", "orgID", q.OrgID, "uid", f.UID)
|
||||
}
|
||||
// always expose the dashboard store sequential ID
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc()
|
||||
@@ -527,21 +527,21 @@ func (s *Service) getRootFolders(ctx context.Context, q *folder.GetChildrenQuery
|
||||
|
||||
// add "shared with me" folder on the 1st page
|
||||
if (q.Page == 0 || q.Page == 1) && len(q.FolderUIDs) != 0 {
|
||||
children = append([]*folder.Folder{&folder.SharedWithMeFolder}, children...)
|
||||
children = append([]*folder.FolderReference{folder.SharedWithMeFolder.ToFolderReference()}, children...)
|
||||
}
|
||||
|
||||
return children, nil
|
||||
}
|
||||
|
||||
// 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.Folder, error) {
|
||||
func (s *Service) GetSharedWithMe(ctx context.Context, q *folder.GetChildrenQuery, forceLegacy bool) ([]*folder.FolderReference, error) {
|
||||
start := time.Now()
|
||||
availableNonRootFolders, err := s.getAvailableNonRootFolders(ctx, q, forceLegacy)
|
||||
if err != nil {
|
||||
s.metrics.sharedWithMeFetchFoldersRequestsDuration.WithLabelValues("failure").Observe(time.Since(start).Seconds())
|
||||
return nil, folder.ErrInternal.Errorf("failed to fetch subfolders to which the user has explicit access: %w", err)
|
||||
}
|
||||
var rootFolders []*folder.Folder
|
||||
var rootFolders []*folder.FolderReference
|
||||
if forceLegacy {
|
||||
rootFolders, err = s.GetChildrenLegacy(ctx, &folder.GetChildrenQuery{UID: "", OrgID: q.OrgID, SignedInUser: q.SignedInUser, Permission: q.Permission})
|
||||
} else {
|
||||
@@ -552,9 +552,9 @@ func (s *Service) GetSharedWithMe(ctx context.Context, q *folder.GetChildrenQuer
|
||||
return nil, folder.ErrInternal.Errorf("failed to fetch root folders to which the user has access: %w", err)
|
||||
}
|
||||
|
||||
availableNonRootFolders = s.deduplicateAvailableFolders(ctx, availableNonRootFolders, rootFolders, q.OrgID)
|
||||
dedupAvailableNonRootFolders := s.deduplicateAvailableFolders(ctx, availableNonRootFolders, rootFolders, q.OrgID)
|
||||
s.metrics.sharedWithMeFetchFoldersRequestsDuration.WithLabelValues("success").Observe(time.Since(start).Seconds())
|
||||
return availableNonRootFolders, nil
|
||||
return dedupAvailableNonRootFolders, nil
|
||||
}
|
||||
|
||||
func (s *Service) getAvailableNonRootFolders(ctx context.Context, q *folder.GetChildrenQuery, forceLegacy bool) ([]*folder.Folder, error) {
|
||||
@@ -618,12 +618,17 @@ func (s *Service) getAvailableNonRootFolders(ctx context.Context, q *folder.GetC
|
||||
return nonRootFolders, nil
|
||||
}
|
||||
|
||||
func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*folder.Folder, rootFolders []*folder.Folder, orgID int64) []*folder.Folder {
|
||||
allFolders := append(folders, rootFolders...)
|
||||
foldersDedup := make([]*folder.Folder, 0)
|
||||
func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*folder.Folder, rootFolders []*folder.FolderReference, orgID int64) []*folder.FolderReference {
|
||||
foldersRef := make([]*folder.FolderReference, len(folders))
|
||||
for i, f := range folders {
|
||||
foldersRef[i] = f.ToFolderReference()
|
||||
}
|
||||
|
||||
allFolders := append(foldersRef, rootFolders...)
|
||||
foldersDedup := make([]*folder.FolderReference, 0)
|
||||
|
||||
for _, f := range folders {
|
||||
isSubfolder := slices.ContainsFunc(allFolders, func(folder *folder.Folder) bool {
|
||||
isSubfolder := slices.ContainsFunc(allFolders, func(folder *folder.FolderReference) bool {
|
||||
return f.ParentUID == folder.UID
|
||||
})
|
||||
|
||||
@@ -638,7 +643,7 @@ func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*fo
|
||||
}
|
||||
|
||||
for _, parentUID := range parentUIDs {
|
||||
contains := slices.ContainsFunc(allFolders, func(f *folder.Folder) bool {
|
||||
contains := slices.ContainsFunc(allFolders, func(f *folder.FolderReference) bool {
|
||||
return f.UID == parentUID
|
||||
})
|
||||
if contains {
|
||||
@@ -649,7 +654,7 @@ func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*fo
|
||||
}
|
||||
|
||||
if !isSubfolder {
|
||||
foldersDedup = append(foldersDedup, f)
|
||||
foldersDedup = append(foldersDedup, f.ToFolderReference())
|
||||
}
|
||||
}
|
||||
return foldersDedup
|
||||
|
||||
@@ -343,7 +343,7 @@ func (s *Service) getFolderByTitleFromApiServer(ctx context.Context, orgID int64
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (s *Service) getChildrenFromApiServer(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
func (s *Service) getChildrenFromApiServer(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
defer func(t time.Time) {
|
||||
parent := q.UID
|
||||
if q.UID != folder.SharedWithMeFolderUID {
|
||||
@@ -397,7 +397,7 @@ func (s *Service) getChildrenFromApiServer(ctx context.Context, q *folder.GetChi
|
||||
return children, nil
|
||||
}
|
||||
|
||||
func (s *Service) getRootFoldersFromApiServer(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
func (s *Service) getRootFoldersFromApiServer(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
permissions := q.SignedInUser.GetPermissions()
|
||||
var folderPermissions []string
|
||||
if q.Permission == dashboardaccess.PERMISSION_EDIT {
|
||||
@@ -432,7 +432,7 @@ func (s *Service) getRootFoldersFromApiServer(ctx context.Context, q *folder.Get
|
||||
|
||||
// add "shared with me" folder on the 1st page
|
||||
if (q.Page == 0 || q.Page == 1) && len(q.FolderUIDs) != 0 {
|
||||
children = append([]*folder.Folder{&folder.SharedWithMeFolder}, children...)
|
||||
children = append([]*folder.FolderReference{folder.SharedWithMeFolder.ToFolderReference()}, children...)
|
||||
}
|
||||
|
||||
return children, nil
|
||||
|
||||
@@ -321,8 +321,8 @@ func (ss *FolderStoreImpl) GetParents(ctx context.Context, q folder.GetParentsQu
|
||||
return util.Reverse(folders[1:]), nil
|
||||
}
|
||||
|
||||
func (ss *FolderStoreImpl) GetChildren(ctx context.Context, q folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
var folders []*folder.Folder
|
||||
func (ss *FolderStoreImpl) GetChildren(ctx context.Context, q folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
var folders []*folder.FolderReference
|
||||
|
||||
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
sql := strings.Builder{}
|
||||
@@ -368,12 +368,6 @@ func (ss *FolderStoreImpl) GetChildren(ctx context.Context, q folder.GetChildren
|
||||
return folder.ErrDatabaseError.Errorf("failed to get folder children: %w", err)
|
||||
}
|
||||
|
||||
if err := concurrency.ForEachJob(ctx, len(folders), runtime.NumCPU(), func(ctx context.Context, idx int) error {
|
||||
folders[idx].WithURL()
|
||||
return nil
|
||||
}); err != nil {
|
||||
ss.log.Debug("failed to set URL to folders", "err", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return folders, err
|
||||
|
||||
@@ -633,7 +633,6 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
|
||||
childrenUIDs := make([]string, 0, len(children))
|
||||
for _, c := range children {
|
||||
assert.NotEmpty(t, c.URL)
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
@@ -650,7 +649,6 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
|
||||
childrenUIDs := make([]string, 0, len(children))
|
||||
for _, c := range children {
|
||||
assert.NotEmpty(t, c.URL)
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
assert.Equal(t, []string{parent.UID}, childrenUIDs)
|
||||
@@ -683,7 +681,6 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
|
||||
childrenUIDs = make([]string, 0, len(children))
|
||||
for _, c := range children {
|
||||
assert.NotEmpty(t, c.URL)
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
@@ -701,7 +698,6 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
|
||||
childrenUIDs = make([]string, 0, len(children))
|
||||
for _, c := range children {
|
||||
assert.NotEmpty(t, c.URL)
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
@@ -721,7 +717,6 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
|
||||
childrenUIDs = make([]string, 0, len(children))
|
||||
for _, c := range children {
|
||||
assert.NotEmpty(t, c.URL)
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
@@ -739,7 +734,6 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
|
||||
childrenUIDs = make([]string, 0, len(children))
|
||||
for _, c := range children {
|
||||
assert.NotEmpty(t, c.URL)
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
@@ -757,7 +751,6 @@ func TestIntegrationGetChildren(t *testing.T) {
|
||||
|
||||
childrenUIDs = make([]string, 0, len(children))
|
||||
for _, c := range children {
|
||||
assert.NotEmpty(t, c.URL)
|
||||
childrenUIDs = append(childrenUIDs, c.UID)
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ func (ss *FolderUnifiedStoreImpl) GetParents(ctx context.Context, q folder.GetPa
|
||||
return hits, nil
|
||||
}
|
||||
|
||||
func (ss *FolderUnifiedStoreImpl) GetChildren(ctx context.Context, q folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
func (ss *FolderUnifiedStoreImpl) GetChildren(ctx context.Context, q folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
// the general folder is saved as an empty string in the database
|
||||
if q.UID == folder.GeneralFolderUID {
|
||||
q.UID = ""
|
||||
@@ -230,36 +230,22 @@ func (ss *FolderUnifiedStoreImpl) GetChildren(ctx context.Context, q folder.GetC
|
||||
}
|
||||
|
||||
allowK6Folder := (q.SignedInUser != nil && q.SignedInUser.IsIdentityType(claims.TypeServiceAccount))
|
||||
hits := make([]*folder.Folder, 0)
|
||||
hits := make([]*folder.FolderReference, 0)
|
||||
for _, item := range res.Hits {
|
||||
// filter out k6 folders if request is not from a service account
|
||||
if item.Name == accesscontrol.K6FolderUID && !allowK6Folder {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: Remove this once we migrate the alerting use case.
|
||||
// This is a temporary flag, and will be removed once we migrate the alerting use case to
|
||||
// expect a folder ref too for children folders.
|
||||
if q.RefOnly { // nolint:staticcheck
|
||||
f := &folder.Folder{
|
||||
ID: item.Field.GetNestedInt64(search.DASHBOARD_LEGACY_ID),
|
||||
UID: item.Name,
|
||||
Title: item.Title,
|
||||
ParentUID: item.Folder,
|
||||
}
|
||||
|
||||
if item.Field.GetNestedString(resource.SEARCH_FIELD_MANAGER_KIND) != "" {
|
||||
f.ManagedBy = utils.ParseManagerKindString(item.Field.GetNestedString(resource.SEARCH_FIELD_MANAGER_KIND))
|
||||
}
|
||||
|
||||
hits = append(hits, f)
|
||||
continue
|
||||
f := &folder.FolderReference{
|
||||
ID: item.Field.GetNestedInt64(search.DASHBOARD_LEGACY_ID),
|
||||
UID: item.Name,
|
||||
Title: item.Title,
|
||||
ParentUID: item.Folder,
|
||||
}
|
||||
|
||||
// search only returns a subset of info, get all info of the folder
|
||||
f, err := ss.Get(ctx, folder.GetFolderQuery{UID: &item.Name, OrgID: q.OrgID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if item.Field.GetNestedString(resource.SEARCH_FIELD_MANAGER_KIND) != "" {
|
||||
f.ManagedBy = utils.ParseManagerKindString(item.Field.GetNestedString(resource.SEARCH_FIELD_MANAGER_KIND))
|
||||
}
|
||||
|
||||
hits = append(hits, f)
|
||||
|
||||
@@ -430,9 +430,8 @@ func TestGetChildren(t *testing.T) {
|
||||
|
||||
// don't set page or limit - should be automatically added
|
||||
result, err := store.GetChildren(ctx, folder.GetChildrenQuery{
|
||||
UID: "folder1",
|
||||
OrgID: orgID,
|
||||
RefOnly: true, // nolint:staticcheck
|
||||
UID: "folder1",
|
||||
OrgID: orgID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result, 2)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
type FakeService struct {
|
||||
ExpectedFoldersRef []*folder.FolderReference
|
||||
ExpectedFolders []*folder.Folder
|
||||
ExpectedFolder *folder.Folder
|
||||
ExpectedHitList model.HitList
|
||||
@@ -22,11 +23,11 @@ func NewFakeService() *FakeService {
|
||||
|
||||
var _ folder.Service = (*FakeService)(nil)
|
||||
|
||||
func (s *FakeService) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
return s.ExpectedFolders, s.ExpectedError
|
||||
func (s *FakeService) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
return s.ExpectedFoldersRef, s.ExpectedError
|
||||
}
|
||||
func (s *FakeService) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
return s.ExpectedFolders, s.ExpectedError
|
||||
func (s *FakeService) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
return s.ExpectedFoldersRef, s.ExpectedError
|
||||
}
|
||||
|
||||
func (s *FakeService) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
||||
|
||||
@@ -63,6 +63,18 @@ type Folder struct {
|
||||
ManagedBy utils.ManagerKind `json:"managedBy,omitempty"`
|
||||
}
|
||||
|
||||
type FolderReference struct {
|
||||
// Deprecated: use UID instead
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
UID string `xorm:"uid"`
|
||||
Title string
|
||||
ParentUID string `xorm:"parent_uid"`
|
||||
|
||||
// When the folder belongs to a repository
|
||||
// NOTE: this is only populated when folders are managed by unified storage
|
||||
ManagedBy utils.ManagerKind `json:"managedBy,omitempty"`
|
||||
}
|
||||
|
||||
var GeneralFolder = Folder{ID: 0, Title: "General"}
|
||||
var RootFolder = &Folder{ID: 0, Title: "Dashboards", UID: GeneralFolderUID, ParentUID: ""}
|
||||
var SharedWithMeFolder = Folder{
|
||||
@@ -89,6 +101,16 @@ func (f *Folder) WithURL() *Folder {
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Folder) ToFolderReference() *FolderReference {
|
||||
return &FolderReference{
|
||||
ID: f.ID,
|
||||
UID: f.UID,
|
||||
Title: f.Title,
|
||||
ParentUID: f.ParentUID,
|
||||
ManagedBy: f.ManagedBy,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFolder tales a title and returns a Folder with the Created and Updated
|
||||
// fields set to the current time.
|
||||
func NewFolder(title string, description string) *Folder {
|
||||
@@ -221,9 +243,6 @@ type GetChildrenQuery struct {
|
||||
|
||||
// array of folder uids to filter by
|
||||
FolderUIDs []string `json:"-"`
|
||||
|
||||
// Deprecated: this is a temporary flag, and will be removed once we migrate the alerting use case
|
||||
RefOnly bool
|
||||
}
|
||||
|
||||
type HasEditPermissionInFoldersQuery struct {
|
||||
|
||||
@@ -46,8 +46,8 @@ type Service interface {
|
||||
SearchFolders(ctx context.Context, q SearchFoldersQuery) (model.HitList, error)
|
||||
|
||||
// GetChildren returns an array containing all child folders.
|
||||
GetChildren(ctx context.Context, q *GetChildrenQuery) ([]*Folder, error)
|
||||
GetChildrenLegacy(ctx context.Context, q *GetChildrenQuery) ([]*Folder, error)
|
||||
GetChildren(ctx context.Context, q *GetChildrenQuery) ([]*FolderReference, error)
|
||||
GetChildrenLegacy(ctx context.Context, q *GetChildrenQuery) ([]*FolderReference, error)
|
||||
|
||||
// GetParents returns an array containing add parent folders if nested folders are enabled
|
||||
// otherwise it returns an empty array
|
||||
|
||||
@@ -38,7 +38,7 @@ type Store interface {
|
||||
|
||||
// GetChildren returns the set of immediate children folders (depth=1) of the
|
||||
// given folder.
|
||||
GetChildren(ctx context.Context, q GetChildrenQuery) ([]*Folder, error)
|
||||
GetChildren(ctx context.Context, q GetChildrenQuery) ([]*FolderReference, 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.
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
type fakeStore struct {
|
||||
ExpectedChildFolders []*Folder
|
||||
ExpectedChildFolders []*FolderReference
|
||||
ExpectedParentFolders []*Folder
|
||||
ExpectedFolders []*Folder
|
||||
ExpectedFolder *Folder
|
||||
@@ -47,7 +47,7 @@ func (f *fakeStore) GetParents(ctx context.Context, q GetParentsQuery) ([]*Folde
|
||||
return f.ExpectedParentFolders, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *fakeStore) GetChildren(ctx context.Context, cmd GetChildrenQuery) ([]*Folder, error) {
|
||||
func (f *fakeStore) GetChildren(ctx context.Context, cmd GetChildrenQuery) ([]*FolderReference, error) {
|
||||
return f.ExpectedChildFolders, f.ExpectedError
|
||||
}
|
||||
|
||||
|
||||
@@ -515,7 +515,7 @@ func Test_authorizeAccessToRuleGroup(t *testing.T) {
|
||||
t.Run("should fail if user does not have access to namespace", func(t *testing.T) {
|
||||
f := &folder.Folder{UID: "test-folder"}
|
||||
gen := models.RuleGen
|
||||
genWithFolder := gen.With(gen.WithNamespace(f))
|
||||
genWithFolder := gen.With(gen.WithNamespace(f.ToFolderReference()))
|
||||
rules := genWithFolder.GenerateManyRef(1, 5)
|
||||
|
||||
ac := &recordingAccessControlFake{}
|
||||
|
||||
@@ -413,7 +413,7 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusPostRuleGroups(c *context
|
||||
return successfulResponse()
|
||||
}
|
||||
|
||||
func (srv *ConvertPrometheusSrv) getOrCreateNamespace(c *contextmodel.ReqContext, title string, logger log.Logger, workingFolderUID string) (*folder.Folder, response.Response) {
|
||||
func (srv *ConvertPrometheusSrv) getOrCreateNamespace(c *contextmodel.ReqContext, title string, logger log.Logger, workingFolderUID string) (*folder.FolderReference, response.Response) {
|
||||
logger.Debug("Getting or creating a new folder")
|
||||
|
||||
ns, err := srv.ruleStore.GetOrCreateNamespaceByTitle(
|
||||
|
||||
@@ -83,7 +83,7 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
t.Run("and group argument is empty", func(t *testing.T) {
|
||||
t.Run("return Forbidden if user is not authorized to access any group in the folder", func(t *testing.T) {
|
||||
ruleStore := initFakeRuleStore(t)
|
||||
ruleStore.PutRule(context.Background(), gen.With(gen.WithNamespace(folder)).GenerateManyRef(1, 5)...)
|
||||
ruleStore.PutRule(context.Background(), gen.With(gen.WithNamespace(folder.ToFolderReference())).GenerateManyRef(1, 5)...)
|
||||
|
||||
request := createRequestContextWithPerms(orgID, map[int64]map[string][]string{}, nil)
|
||||
|
||||
@@ -96,7 +96,7 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
ruleStore := initFakeRuleStore(t)
|
||||
provisioningStore := fakes.NewFakeProvisioningStore()
|
||||
|
||||
folderGen := gen.With(gen.WithNamespace(folder))
|
||||
folderGen := gen.With(gen.WithNamespace(folder.ToFolderReference()))
|
||||
|
||||
authorizedRulesInFolder := folderGen.With(gen.WithGroupPrefix("authz-")).GenerateManyRef(1, 5)
|
||||
|
||||
@@ -123,7 +123,7 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
ruleStore := initFakeRuleStore(t)
|
||||
provisioningStore := fakes.NewFakeProvisioningStore()
|
||||
|
||||
folderGen := gen.With(gen.WithNamespace(folder))
|
||||
folderGen := gen.With(gen.WithNamespace(folder.ToFolderReference()))
|
||||
|
||||
provisionedRulesInFolder := folderGen.With(gen.WithSameGroup()).GenerateManyRef(1, 5)
|
||||
err := provisioningStore.SetProvenance(context.Background(), provisionedRulesInFolder[0], orgID, models.ProvenanceAPI)
|
||||
@@ -155,7 +155,7 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
t.Run("return Forbidden if user is not authorized to access the group", func(t *testing.T) {
|
||||
ruleStore := initFakeRuleStore(t)
|
||||
|
||||
groupGen := gen.With(gen.WithNamespace(folder), gen.WithSameGroup())
|
||||
groupGen := gen.With(gen.WithNamespace(folder.ToFolderReference()), gen.WithSameGroup())
|
||||
|
||||
authorizedRulesInGroup := groupGen.GenerateManyRef(1, 5)
|
||||
ruleStore.PutRule(context.Background(), authorizedRulesInGroup...)
|
||||
@@ -175,7 +175,7 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
ruleStore := initFakeRuleStore(t)
|
||||
provisioningStore := fakes.NewFakeProvisioningStore()
|
||||
|
||||
groupGen := gen.With(gen.WithNamespace(folder), gen.WithSameGroup())
|
||||
groupGen := gen.With(gen.WithNamespace(folder.ToFolderReference()), gen.WithSameGroup())
|
||||
|
||||
provisionedRulesInFolder := groupGen.GenerateManyRef(1, 5)
|
||||
err := provisioningStore.SetProvenance(context.Background(), provisionedRulesInFolder[0], orgID, models.ProvenanceAPI)
|
||||
@@ -204,7 +204,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
|
||||
folder := randFolder()
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
folderGen := gen.With(gen.WithOrgID(orgID), gen.WithNamespace(folder))
|
||||
folderGen := gen.With(gen.WithOrgID(orgID), gen.WithNamespace(folder.ToFolderReference()))
|
||||
queryAccessRules := folderGen.GenerateManyRef(2, 6)
|
||||
ruleStore.PutRule(context.Background(), queryAccessRules...)
|
||||
noQueryAccessRules := folderGen.GenerateManyRef(2, 6)
|
||||
@@ -246,7 +246,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
|
||||
folder := randFolder()
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
expectedRules := gen.With(gen.WithOrgID(orgID), gen.WithNamespace(folder)).GenerateManyRef(2, 6)
|
||||
expectedRules := gen.With(gen.WithOrgID(orgID), gen.WithNamespace(folder.ToFolderReference())).GenerateManyRef(2, 6)
|
||||
ruleStore.PutRule(context.Background(), expectedRules...)
|
||||
|
||||
svc := createService(ruleStore)
|
||||
|
||||
@@ -12,13 +12,13 @@ import (
|
||||
// RuleStore is the interface for persisting alert rules and instances
|
||||
type RuleStore interface {
|
||||
// TODO after deprecating namespace_id field in GettableGrafanaRule we can simplify this interface
|
||||
// by returning map[string]struct{} instead of map[string]*folder.Folder
|
||||
// by returning map[string]struct{} instead of map[string]*folder.FolderReference
|
||||
GetUserVisibleNamespaces(context.Context, int64, identity.Requester) (map[string]*folder.Folder, error)
|
||||
GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user identity.Requester) (*folder.Folder, error)
|
||||
GetNamespaceByTitle(ctx context.Context, fullpath string, orgID int64, user identity.Requester, parentUID string) (*folder.Folder, error)
|
||||
GetOrCreateNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.Folder, error)
|
||||
GetNamespaceByTitle(ctx context.Context, fullpath string, orgID int64, user identity.Requester, parentUID string) (*folder.FolderReference, error)
|
||||
GetOrCreateNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.FolderReference, error)
|
||||
// GetNamespaceChildren returns all children (first level) of the namespace with the given id.
|
||||
GetNamespaceChildren(ctx context.Context, uid string, orgID int64, user identity.Requester) ([]*folder.Folder, error)
|
||||
GetNamespaceChildren(ctx context.Context, uid string, orgID int64, user identity.Requester) ([]*folder.FolderReference, error)
|
||||
|
||||
GetAlertRuleByUID(ctx context.Context, query *ngmodels.GetAlertRuleByUIDQuery) (*ngmodels.AlertRule, error)
|
||||
GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmodels.GetAlertRulesGroupByRuleUIDQuery) ([]*ngmodels.AlertRule, error)
|
||||
|
||||
@@ -320,7 +320,7 @@ type Namespaced interface {
|
||||
GetNamespaceUID() string
|
||||
}
|
||||
|
||||
type Namespace folder.Folder
|
||||
type Namespace folder.FolderReference
|
||||
|
||||
func (n Namespace) GetNamespaceUID() string {
|
||||
return n.UID
|
||||
|
||||
@@ -298,7 +298,7 @@ func (a *AlertRuleMutators) WithNamespaceUID(namespaceUID string) AlertRuleMutat
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlertRuleMutators) WithNamespace(namespace *folder.Folder) AlertRuleMutator {
|
||||
func (a *AlertRuleMutators) WithNamespace(namespace *folder.FolderReference) AlertRuleMutator {
|
||||
return a.WithNamespaceUID(namespace.UID)
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ func Test_subscribeToFolderChanges(t *testing.T) {
|
||||
Title: "Folder" + util.GenerateShortUID(),
|
||||
}
|
||||
gen := models.RuleGen
|
||||
rules := gen.With(gen.WithOrgID(orgID), gen.WithNamespace(folder1)).GenerateManyRef(5)
|
||||
rules := gen.With(gen.WithOrgID(orgID), gen.WithNamespace(folder1.ToFolderReference())).GenerateManyRef(5)
|
||||
|
||||
bus := bus.ProvideBus(tracing.InitializeTracerForTest())
|
||||
db := fakes.NewRuleStore(t)
|
||||
|
||||
@@ -522,7 +522,7 @@ func (h *RemoteLokiBackend) getFolderUIDsForFilter(ctx context.Context, query mo
|
||||
uids := make([]string, 0, len(folders))
|
||||
// now keep only UIDs of folder in which user can read rules.
|
||||
for _, f := range folders {
|
||||
hasAccess, err := h.ac.HasAccessInFolder(ctx, query.SignedInUser, models.Namespace(*f))
|
||||
hasAccess, err := h.ac.HasAccessInFolder(ctx, query.SignedInUser, models.Namespace(*f.ToFolderReference()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (st DBstore) GetNamespaceByUID(ctx context.Context, uid string, orgID int64
|
||||
}
|
||||
|
||||
// GetNamespaceChildren gets namespace (folder) children (first level) by its UID.
|
||||
func (st DBstore) GetNamespaceChildren(ctx context.Context, uid string, orgID int64, user identity.Requester) ([]*folder.Folder, error) {
|
||||
func (st DBstore) GetNamespaceChildren(ctx context.Context, uid string, orgID int64, user identity.Requester) ([]*folder.FolderReference, error) {
|
||||
q := &folder.GetChildrenQuery{
|
||||
UID: uid,
|
||||
OrgID: orgID,
|
||||
@@ -55,7 +55,7 @@ func (st DBstore) GetNamespaceChildren(ctx context.Context, uid string, orgID in
|
||||
return nil, err
|
||||
}
|
||||
|
||||
found := make([]*folder.Folder, 0, len(folders))
|
||||
found := make([]*folder.FolderReference, 0, len(folders))
|
||||
for _, f := range folders {
|
||||
if f.ParentUID == uid {
|
||||
found = append(found, f)
|
||||
@@ -66,13 +66,13 @@ func (st DBstore) GetNamespaceChildren(ctx context.Context, uid string, orgID in
|
||||
}
|
||||
|
||||
// GetNamespaceByTitle gets namespace by its title in the specified folder.
|
||||
func (st DBstore) GetNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.Folder, error) {
|
||||
func (st DBstore) GetNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.FolderReference, error) {
|
||||
folders, err := st.GetNamespaceChildren(ctx, parentUID, orgID, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
foundByTitle := []*folder.Folder{}
|
||||
foundByTitle := []*folder.FolderReference{}
|
||||
for _, f := range folders {
|
||||
if f.Title == title {
|
||||
foundByTitle = append(foundByTitle, f)
|
||||
@@ -95,12 +95,12 @@ func (st DBstore) GetNamespaceByTitle(ctx context.Context, title string, orgID i
|
||||
//
|
||||
// To avoid race conditions when two concurrent requests try to create the same folder,
|
||||
// we create folders with a deterministic UID based on the parent UID, title, and organization ID.
|
||||
func (st DBstore) GetOrCreateNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.Folder, error) {
|
||||
func (st DBstore) GetOrCreateNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.FolderReference, error) {
|
||||
if len(title) == 0 {
|
||||
return nil, fmt.Errorf("title is empty")
|
||||
}
|
||||
|
||||
var f *folder.Folder
|
||||
var f *folder.FolderReference
|
||||
var err error
|
||||
|
||||
f, err = st.GetNamespaceByTitle(ctx, title, orgID, user, parentUID)
|
||||
@@ -122,7 +122,8 @@ func (st DBstore) GetOrCreateNamespaceByTitle(ctx context.Context, title string,
|
||||
SignedInUser: user,
|
||||
ParentUID: parentUID,
|
||||
}
|
||||
f, err = st.FolderService.Create(ctx, cmd)
|
||||
var newFolder *folder.Folder
|
||||
newFolder, err = st.FolderService.Create(ctx, cmd)
|
||||
if err != nil {
|
||||
// Handle potential race condition where another request might have created
|
||||
// the folder between our check and creation attempt
|
||||
@@ -137,6 +138,8 @@ func (st DBstore) GetOrCreateNamespaceByTitle(ctx context.Context, title string,
|
||||
fmt.Errorf("lookup folder: %w", lookupErr),
|
||||
))
|
||||
}
|
||||
|
||||
f = newFolder.ToFolderReference()
|
||||
}
|
||||
|
||||
return f, nil
|
||||
|
||||
@@ -273,13 +273,13 @@ func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
func (f *RuleStore) GetOrCreateNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.Folder, error) {
|
||||
func (f *RuleStore) GetOrCreateNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.FolderReference, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
for _, folder := range f.Folders[orgID] {
|
||||
if folder.Title == title && folder.ParentUID == parentUID {
|
||||
return folder, nil
|
||||
return folder.ToFolderReference(), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,31 +292,31 @@ func (f *RuleStore) GetOrCreateNamespaceByTitle(ctx context.Context, title strin
|
||||
}
|
||||
|
||||
f.Folders[orgID] = append(f.Folders[orgID], newFolder)
|
||||
return newFolder, nil
|
||||
return newFolder.ToFolderReference(), nil
|
||||
}
|
||||
|
||||
func (f *RuleStore) GetNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.Folder, error) {
|
||||
func (f *RuleStore) GetNamespaceByTitle(ctx context.Context, title string, orgID int64, user identity.Requester, parentUID string) (*folder.FolderReference, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
for _, folder := range f.Folders[orgID] {
|
||||
if folder.Title == title && folder.ParentUID == parentUID {
|
||||
return folder, nil
|
||||
return folder.ToFolderReference(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, dashboards.ErrFolderNotFound
|
||||
}
|
||||
|
||||
func (f *RuleStore) GetNamespaceChildren(ctx context.Context, uid string, orgID int64, user identity.Requester) ([]*folder.Folder, error) {
|
||||
func (f *RuleStore) GetNamespaceChildren(ctx context.Context, uid string, orgID int64, user identity.Requester) ([]*folder.FolderReference, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
result := []*folder.Folder{}
|
||||
result := []*folder.FolderReference{}
|
||||
|
||||
for _, folder := range f.Folders[orgID] {
|
||||
if folder.ParentUID == uid {
|
||||
result = append(result, folder)
|
||||
result = append(result, folder.ToFolderReference())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user