NestedFolders: Add folder service registry with dashboard service implementation (#65033)
* Delete folders, dashboards with registry service Co-authored-by: Serge Zaitsev <hello@zserge.com> * Update signature of ProvideDashboardServiceImpl * Regenerate mockery file * Add test for DeleteInFolder * Add test for DeleteDashboardsInFolder * Delete child dashboard associations via registry * Add validation of folder uid and org id --------- Co-authored-by: Serge Zaitsev <hello@zserge.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@@ -36,6 +37,9 @@ type Service struct {
|
||||
|
||||
// bus is currently used to publish event in case of title change
|
||||
bus bus.Bus
|
||||
|
||||
mutex sync.RWMutex
|
||||
registry map[string]folder.RegistryService
|
||||
}
|
||||
|
||||
func ProvideService(
|
||||
@@ -58,6 +62,7 @@ func ProvideService(
|
||||
accessControl: ac,
|
||||
bus: bus,
|
||||
db: db,
|
||||
registry: make(map[string]folder.RegistryService),
|
||||
}
|
||||
if features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
srv.DBMigration(db)
|
||||
@@ -437,6 +442,12 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e
|
||||
if cmd.SignedInUser == nil {
|
||||
return folder.ErrBadRequest.Errorf("missing signed in user")
|
||||
}
|
||||
if cmd.UID == "" {
|
||||
return folder.ErrBadRequest.Errorf("missing UID")
|
||||
}
|
||||
if cmd.OrgID < 1 {
|
||||
return folder.ErrBadRequest.Errorf("invalid orgID")
|
||||
}
|
||||
result := []string{cmd.UID}
|
||||
err := s.db.InTransaction(ctx, func(ctx context.Context) error {
|
||||
if s.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
@@ -466,6 +477,11 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e
|
||||
}
|
||||
return dashboards.ErrFolderAccessDenied
|
||||
}
|
||||
|
||||
if err := s.deleteChildrenInFolder(ctx, dashFolder.OrgID, dashFolder.UID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.legacyDelete(ctx, cmd, dashFolder)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -477,6 +493,15 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Service) deleteChildrenInFolder(ctx context.Context, orgID int64, UID string) error {
|
||||
for _, v := range s.registry {
|
||||
if err := v.DeleteInFolder(ctx, orgID, UID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) legacyDelete(ctx context.Context, cmd *folder.DeleteFolderCommand, dashFolder *folder.Folder) error {
|
||||
deleteCmd := dashboards.DeleteDashboardCommand{OrgID: cmd.OrgID, ID: dashFolder.ID, ForceDeleteFolderRules: cmd.ForceDeleteRules}
|
||||
|
||||
@@ -588,7 +613,8 @@ func (s *Service) nestedFolderDelete(ctx context.Context, cmd *folder.DeleteFold
|
||||
}
|
||||
result = append(result, subfolders...)
|
||||
}
|
||||
logger.Info("deleting folder", "org_id", cmd.OrgID, "uid", cmd.UID)
|
||||
|
||||
logger.Info("deleting folder and its contents", "org_id", cmd.OrgID, "uid", cmd.UID)
|
||||
err = s.store.Delete(ctx, cmd.UID, cmd.OrgID)
|
||||
if err != nil {
|
||||
logger.Info("failed deleting folder", "org_id", cmd.OrgID, "uid", cmd.UID, "err", err)
|
||||
@@ -794,3 +820,17 @@ func toFolderError(err error) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Service) RegisterService(r folder.RegistryService) error {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
_, ok := s.registry[r.Kind()]
|
||||
if ok {
|
||||
return folder.ErrTargetRegistrySrvConflict.Errorf("target registry service: %s already exists", r.Kind())
|
||||
}
|
||||
|
||||
s.registry[r.Kind()] = r
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -45,3 +45,7 @@ func (s *FakeService) MakeUserAdmin(ctx context.Context, orgID int64, userID, fo
|
||||
func (s *FakeService) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
|
||||
return s.ExpectedFolder, s.ExpectedError
|
||||
}
|
||||
|
||||
func (s *FakeService) RegisterService(service folder.RegistryService) error {
|
||||
return s.ExpectedError
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ var ErrDatabaseError = errutil.NewBase(errutil.StatusInternal, "folder.database-
|
||||
var ErrInternal = errutil.NewBase(errutil.StatusInternal, "folder.internal")
|
||||
var ErrFolderTooDeep = errutil.NewBase(errutil.StatusInternal, "folder.too-deep")
|
||||
var ErrCircularReference = errutil.NewBase(errutil.StatusBadRequest, "folder.circular-reference", errutil.WithPublicMessage("Circular reference detected"))
|
||||
var ErrTargetRegistrySrvConflict = errutil.NewBase(errutil.StatusInternal, "folder.target-registry-srv-conflict")
|
||||
|
||||
const (
|
||||
GeneralFolderUID = "general"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package folder
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type RegistryService interface {
|
||||
DeleteInFolder(ctx context.Context, orgID int64, UID string) error
|
||||
Kind() string
|
||||
}
|
||||
@@ -25,6 +25,7 @@ type Service interface {
|
||||
MakeUserAdmin(ctx context.Context, orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error
|
||||
// Move changes a folder's parent folder to the requested new parent.
|
||||
Move(ctx context.Context, cmd *MoveFolderCommand) (*Folder, error)
|
||||
RegisterService(service RegistryService) error
|
||||
}
|
||||
|
||||
// FolderStore is a folder store.
|
||||
|
||||
Reference in New Issue
Block a user