NestedFolders: Add API endpoint for descendant count in a folder (#66550)

* Add CountInFolder to RegistryService interface
* Add folder children counts api route
* Update fake GetFolderChildrenCounts
* Add test for getting folder children counts
* Add validation to folder children counts handler
* Update openapi specs
* Update pkg/services/folder/folderimpl/folder.go
Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>

---------

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
This commit is contained in:
Arati R
2023-04-24 15:57:28 +02:00
committed by GitHub
parent 990b3c07ab
commit fd434cab58
14 changed files with 821 additions and 36 deletions

View File

@@ -623,6 +623,28 @@ func (s *Service) nestedFolderDelete(ctx context.Context, cmd *folder.DeleteFold
return result, nil
}
func (s *Service) GetChildrenCounts(ctx context.Context, cmd *folder.GetChildrenCountsQuery) (folder.ChildrenCounts, error) {
if cmd.SignedInUser == nil {
return nil, folder.ErrBadRequest.Errorf("missing signed-in user")
}
if *cmd.UID == "" {
return nil, folder.ErrBadRequest.Errorf("missing UID")
}
if cmd.OrgID < 1 {
return nil, folder.ErrBadRequest.Errorf("invalid orgID")
}
countsMap := make(folder.ChildrenCounts, len(s.registry))
for _, v := range s.registry {
c, err := v.CountInFolder(ctx, cmd.OrgID, *cmd.UID, cmd.SignedInUser)
if err != nil {
return nil, err
}
countsMap[v.Kind()] += c
}
return countsMap, nil
}
// MakeUserAdmin is copy of DashboardServiceImpl.MakeUserAdmin
func (s *Service) MakeUserAdmin(ctx context.Context, orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error {
rtEditor := org.RoleEditor

View File

@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/appcontext"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/db/dbtest"
"github.com/grafana/grafana/pkg/infra/log"
@@ -22,6 +23,7 @@ import (
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/dashboards/service"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
@@ -930,6 +932,65 @@ func TestNestedFolderService(t *testing.T) {
})
}
func TestGetChildrenCounts(t *testing.T) {
g := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true})
t.Cleanup(func() {
guardian.New = g
})
folderId := rand.Int63()
folderUID := util.GenerateShortUID()
f := folder.NewFolder("Folder", "")
f.ID = folderId
f.UID = folderUID
f.OrgID = orgID
nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedFolder = f
dashStore := dashboards.FakeDashboardStore{}
dashboardCount := int64(2)
countCmd := dashboards.CountDashboardsInFolderRequest{
FolderID: f.ID,
OrgID: f.OrgID,
}
dashStore.On("CountDashboardsInFolder", mock.Anything, &countCmd).Return(dashboardCount, nil)
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(f, nil)
cfg := setting.NewCfg()
cfg.RBACEnabled = false
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
folderService := &Service{
cfg: cfg,
store: nestedFolderStore,
dashboardStore: &dashStore,
dashboardFolderStore: dashboardFolderStore,
features: features,
log: log.New("test-folder-service"),
accessControl: acimpl.ProvideAccessControl(cfg),
registry: make(map[string]folder.RegistryService),
}
ac := acmock.New()
folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService()
_, err := service.ProvideDashboardServiceImpl(cfg, &dashStore, dashboardFolderStore, nil, features, folderPermissions, dashboardPermissions, ac, folderService)
require.NoError(t, err)
signedInUser := user.SignedInUser{UserID: 1, OrgID: orgID}
ctx := appcontext.WithUser(context.Background(), &signedInUser)
res, err := folderService.GetChildrenCounts(ctx, &folder.GetChildrenCountsQuery{
SignedInUser: usr,
UID: &folderUID,
OrgID: orgID,
})
require.NoError(t, err)
require.Equal(t, res["dashboard"], dashboardCount)
}
func CreateSubtreeInStore(t *testing.T, store *sqlStore, service *Service, depth int, prefix string, cmd folder.CreateFolderCommand) []string {
t.Helper()

View File

@@ -7,9 +7,10 @@ import (
)
type FakeService struct {
ExpectedFolders []*folder.Folder
ExpectedFolder *folder.Folder
ExpectedError error
ExpectedFolders []*folder.Folder
ExpectedFolder *folder.Folder
ExpectedError error
ExpectedChildrenCounts map[string]int64
}
func NewFakeService() *FakeService {
@@ -49,3 +50,7 @@ func (s *FakeService) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (
func (s *FakeService) RegisterService(service folder.RegistryService) error {
return s.ExpectedError
}
func (s *FakeService) GetChildrenCounts(ctx context.Context, cmd *folder.GetChildrenCountsQuery) (folder.ChildrenCounts, error) {
return s.ExpectedChildrenCounts, s.ExpectedError
}

View File

@@ -160,3 +160,14 @@ type HasEditPermissionInFoldersQuery struct {
type HasAdminPermissionInDashboardsOrFoldersQuery struct {
SignedInUser *user.SignedInUser
}
// GetChildrenCountsQuery captures the information required by the folder service
// to return the count of children in a folder.
type GetChildrenCountsQuery struct {
UID *string
OrgID int64
SignedInUser *user.SignedInUser `json:"-"`
}
type ChildrenCounts map[string]int64

View File

@@ -2,9 +2,12 @@ package folder
import (
"context"
"github.com/grafana/grafana/pkg/services/user"
)
type RegistryService interface {
DeleteInFolder(ctx context.Context, orgID int64, UID string) error
DeleteInFolder(ctx context.Context, orgID int64, uid string) error
CountInFolder(ctx context.Context, orgID int64, uid string, user *user.SignedInUser) (int64, error)
Kind() string
}

View File

@@ -26,6 +26,7 @@ type Service interface {
// Move changes a folder's parent folder to the requested new parent.
Move(ctx context.Context, cmd *MoveFolderCommand) (*Folder, error)
RegisterService(service RegistryService) error
GetChildrenCounts(ctx context.Context, cmd *GetChildrenCountsQuery) (ChildrenCounts, error)
}
// FolderStore is a folder store.