API: Support creating a nested folder (#58508)
* API: Support nested folder creation * Update swagger * fixup * Update pkg/api/dtos/folder.go Co-authored-by: Serge Zaitsev <serge.zaitsev@grafana.com> * Fix some tests * create legacy folder url from title and uid Co-authored-by: idafurjes <36131195+idafurjes@users.noreply.github.com> Co-authored-by: Serge Zaitsev <serge.zaitsev@grafana.com> Co-authored-by: Ida Furjesova <ida.furjesova@grafana.com>
This commit is contained in:
co-authored by
Serge Zaitsev
idafurjes
Ida Furjesova
parent
b5388bb080
commit
bf5a08e039
@@ -22,6 +22,8 @@ type Folder struct {
|
||||
Updated time.Time `json:"updated"`
|
||||
Version int `json:"version"`
|
||||
AccessControl accesscontrol.Metadata `json:"accessControl,omitempty"`
|
||||
// only used if nested folders are enabled
|
||||
ParentUID string `json:"parentUid"`
|
||||
}
|
||||
|
||||
type FolderSearchHit struct {
|
||||
|
||||
+50
-5
@@ -105,6 +105,8 @@ func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response {
|
||||
//
|
||||
// Create folder.
|
||||
//
|
||||
// If nested folders are enabled then it additionally expects the parent folder UID.
|
||||
//
|
||||
// Responses:
|
||||
// 200: folderResponse
|
||||
// 400: badRequestError
|
||||
@@ -113,23 +115,29 @@ func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response {
|
||||
// 409: conflictError
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) CreateFolder(c *models.ReqContext) response.Response {
|
||||
cmd := models.CreateFolderCommand{}
|
||||
cmd := folder.CreateFolderCommand{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
folder, err := hs.folderService.CreateFolder(c.Req.Context(), c.SignedInUser, c.OrgID, cmd.Title, cmd.Uid)
|
||||
cmd.OrgID = c.OrgID
|
||||
|
||||
folder, err := hs.folderService.Create(c.Req.Context(), &cmd)
|
||||
if err != nil {
|
||||
return apierrors.ToFolderErrorResponse(err)
|
||||
}
|
||||
|
||||
g := guardian.New(c.Req.Context(), folder.Id, c.OrgID, c.SignedInUser)
|
||||
return response.JSON(http.StatusOK, hs.toFolderDto(c, g, folder))
|
||||
g := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser)
|
||||
// TODO set ParentUID if nested folders are enabled
|
||||
return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, folder))
|
||||
}
|
||||
|
||||
// swagger:route PUT /folders/{folder_uid} folders updateFolder
|
||||
//
|
||||
// Update folder.
|
||||
//
|
||||
// If nested folders are enabled then it optionally expects a new parent folder UID that moves the folder and
|
||||
// includes it into the response.
|
||||
//
|
||||
// Responses:
|
||||
// 200: folderResponse
|
||||
// 400: badRequestError
|
||||
@@ -156,6 +164,7 @@ func (hs *HTTPServer) UpdateFolder(c *models.ReqContext) response.Response {
|
||||
// Delete folder.
|
||||
//
|
||||
// Deletes an existing folder identified by UID along with all dashboards (and their alerts) stored in the folder. This operation cannot be reverted.
|
||||
// If nested folders are enabled then it also deletes all the subfolders.
|
||||
//
|
||||
// Responses:
|
||||
// 200: deleteFolderResponse
|
||||
@@ -216,6 +225,42 @@ func (hs *HTTPServer) toFolderDto(c *models.ReqContext, g guardian.DashboardGuar
|
||||
}
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) newToFolderDto(c *models.ReqContext, g guardian.DashboardGuardian, folder *folder.Folder) dtos.Folder {
|
||||
canEdit, _ := g.CanEdit()
|
||||
canSave, _ := g.CanSave()
|
||||
canAdmin, _ := g.CanAdmin()
|
||||
canDelete, _ := g.CanDelete()
|
||||
|
||||
// Finding creator and last updater of the folder
|
||||
updater, creator := anonString, anonString
|
||||
/*
|
||||
if folder.CreatedBy > 0 {
|
||||
creator = hs.getUserLogin(c.Req.Context(), folder.CreatedBy)
|
||||
}
|
||||
if folder.UpdatedBy > 0 {
|
||||
updater = hs.getUserLogin(c.Req.Context(), folder.UpdatedBy)
|
||||
}
|
||||
*/
|
||||
|
||||
return dtos.Folder{
|
||||
Id: folder.ID,
|
||||
Uid: folder.UID,
|
||||
Title: folder.Title,
|
||||
//Url: folder.Url,
|
||||
//HasACL: folder.HasACL,
|
||||
CanSave: canSave,
|
||||
CanEdit: canEdit,
|
||||
CanAdmin: canAdmin,
|
||||
CanDelete: canDelete,
|
||||
CreatedBy: creator,
|
||||
Created: folder.Created,
|
||||
UpdatedBy: updater,
|
||||
Updated: folder.Updated,
|
||||
//Version: folder.Version,
|
||||
AccessControl: hs.getAccessControlMetadata(c, c.OrgID, dashboards.ScopeFoldersPrefix, folder.UID),
|
||||
}
|
||||
}
|
||||
|
||||
// swagger:parameters getFolders
|
||||
type GetFoldersParams struct {
|
||||
// Limit the maximum number of folders to return
|
||||
@@ -262,7 +307,7 @@ type GetFolderByIDParams struct {
|
||||
type CreateFolderParams struct {
|
||||
// in:body
|
||||
// required:true
|
||||
Body models.CreateFolderCommand `json:"body"`
|
||||
Body folder.CreateFolderCommand `json:"body"`
|
||||
}
|
||||
|
||||
// swagger:parameters deleteFolder
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
service "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"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
@@ -130,7 +131,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
|
||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
||||
dashboardStore.On("UpdateDashboardACL", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
mockSQLStore := mockstore.NewSQLStoreMock()
|
||||
|
||||
@@ -187,7 +188,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
CheckPermissionBeforeUpdateError: guardian.ErrGuardianPermissionExists,
|
||||
})
|
||||
|
||||
folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
|
||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
||||
|
||||
cmd := dtos.UpdateDashboardACLCommand{
|
||||
Items: []dtos.DashboardACLUpdateItem{
|
||||
@@ -251,7 +252,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
|
||||
)
|
||||
|
||||
folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
|
||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
||||
|
||||
cmd := dtos.UpdateDashboardACLCommand{
|
||||
Items: []dtos.DashboardACLUpdateItem{
|
||||
@@ -297,7 +298,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
|
||||
var gotItems []*models.DashboardACL
|
||||
|
||||
folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
|
||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
||||
dashboardStore.On("UpdateDashboardACL", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||
gotItems = args.Get(2).([]*models.DashboardACL)
|
||||
}).Return(nil).Once()
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestFoldersAPIEndpoint(t *testing.T) {
|
||||
Title: "Folder",
|
||||
}
|
||||
|
||||
folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
|
||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
||||
|
||||
createFolderScenario(t, "When calling POST on", "/api/folders", "/api/folders", folderService, cmd,
|
||||
func(sc *scenarioContext) {
|
||||
@@ -94,7 +94,7 @@ func TestFoldersAPIEndpoint(t *testing.T) {
|
||||
Title: "Folder upd",
|
||||
}
|
||||
|
||||
folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder upd"}
|
||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder upd"}
|
||||
|
||||
updateFolderScenario(t, "When calling PUT on", "/api/folders/uid", "/api/folders/:uid", folderService, cmd,
|
||||
func(sc *scenarioContext) {
|
||||
@@ -179,7 +179,7 @@ func TestHTTPServer_FolderMetadata(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Should attach access control metadata to folder response", func(t *testing.T) {
|
||||
folderService.ExpectedFolder = &models.Folder{Uid: "folderUid"}
|
||||
folderService.ExpectedFolder = &folder.Folder{UID: "folderUid"}
|
||||
|
||||
req := server.NewGetRequest("/api/folders/folderUid?accesscontrol=true")
|
||||
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{
|
||||
@@ -202,7 +202,7 @@ func TestHTTPServer_FolderMetadata(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Should attach access control metadata to folder response", func(t *testing.T) {
|
||||
folderService.ExpectedFolder = &models.Folder{Uid: "folderUid"}
|
||||
folderService.ExpectedFolder = &folder.Folder{UID: "folderUid"}
|
||||
|
||||
req := server.NewGetRequest("/api/folders/folderUid")
|
||||
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
@@ -55,9 +56,9 @@ func TestImportDashboardService(t *testing.T) {
|
||||
},
|
||||
}
|
||||
folderService := &foldertest.FakeService{
|
||||
ExpectedFolder: &models.Folder{
|
||||
Id: 5,
|
||||
Uid: "123",
|
||||
ExpectedFolder: &folder.Folder{
|
||||
ID: 5,
|
||||
UID: "123",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -115,9 +116,9 @@ func TestImportDashboardService(t *testing.T) {
|
||||
}
|
||||
libraryPanelService := &libraryPanelServiceMock{}
|
||||
folderService := &foldertest.FakeService{
|
||||
ExpectedFolder: &models.Folder{
|
||||
Id: 5,
|
||||
Uid: "123",
|
||||
ExpectedFolder: &folder.Folder{
|
||||
ID: 5,
|
||||
UID: "123",
|
||||
},
|
||||
}
|
||||
s := &ImportDashboardService{
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
@@ -150,16 +149,21 @@ func (s *Service) GetFolderByTitle(ctx context.Context, user *user.SignedInUser,
|
||||
return dashFolder, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, title, uid string) (*models.Folder, error) {
|
||||
dashFolder := models.NewDashboardFolder(title)
|
||||
dashFolder.OrgId = orgID
|
||||
func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
|
||||
dashFolder := models.NewDashboardFolder(cmd.Title)
|
||||
dashFolder.OrgId = cmd.OrgID
|
||||
|
||||
trimmedUID := strings.TrimSpace(uid)
|
||||
trimmedUID := strings.TrimSpace(cmd.UID)
|
||||
if trimmedUID == accesscontrol.GeneralFolderUID {
|
||||
return nil, dashboards.ErrFolderInvalidUID
|
||||
}
|
||||
|
||||
dashFolder.SetUid(trimmedUID)
|
||||
|
||||
user, err := appcontext.User(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userID := user.UserID
|
||||
if userID == 0 {
|
||||
userID = -1
|
||||
@@ -170,7 +174,7 @@ func (s *Service) CreateFolder(ctx context.Context, user *user.SignedInUser, org
|
||||
|
||||
dto := &dashboards.SaveDashboardDTO{
|
||||
Dashboard: dashFolder,
|
||||
OrgId: orgID,
|
||||
OrgId: cmd.OrgID,
|
||||
User: user,
|
||||
}
|
||||
|
||||
@@ -185,7 +189,7 @@ func (s *Service) CreateFolder(ctx context.Context, user *user.SignedInUser, org
|
||||
}
|
||||
|
||||
var createdFolder *models.Folder
|
||||
createdFolder, err = s.dashboardStore.GetFolderByID(ctx, orgID, dash.Id)
|
||||
createdFolder, err = s.dashboardStore.GetFolderByID(ctx, cmd.OrgID, dash.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -204,9 +208,9 @@ func (s *Service) CreateFolder(ctx context.Context, user *user.SignedInUser, org
|
||||
{BuiltinRole: string(org.RoleViewer), Permission: models.PERMISSION_VIEW.String()},
|
||||
}...)
|
||||
|
||||
_, permissionErr = s.permissions.SetPermissions(ctx, orgID, createdFolder.Uid, permissions...)
|
||||
_, permissionErr = s.permissions.SetPermissions(ctx, cmd.OrgID, createdFolder.Uid, permissions...)
|
||||
} else if s.cfg.EditorsCanAdmin && user.IsRealUser() && !user.IsAnonymous {
|
||||
permissionErr = s.MakeUserAdmin(ctx, orgID, userID, createdFolder.Id, true)
|
||||
permissionErr = s.MakeUserAdmin(ctx, cmd.OrgID, userID, createdFolder.Id, true)
|
||||
}
|
||||
|
||||
if permissionErr != nil {
|
||||
@@ -219,31 +223,34 @@ func (s *Service) CreateFolder(ctx context.Context, user *user.SignedInUser, org
|
||||
description = dash.Data.Get("description").MustString()
|
||||
}
|
||||
|
||||
parentUID := folder.RootFolderUID
|
||||
if cmd.ParentUID != "" {
|
||||
parentUID = cmd.ParentUID
|
||||
}
|
||||
_, err := s.store.Create(ctx, folder.CreateFolderCommand{
|
||||
// TODO: Today, if a UID isn't specified, the dashboard store
|
||||
// generates a new UID. The new folder store will need to do this as
|
||||
// well, but for now we take the UID from the newly created folder.
|
||||
UID: dash.Uid,
|
||||
OrgID: orgID,
|
||||
Title: title,
|
||||
OrgID: cmd.OrgID,
|
||||
Title: cmd.Title,
|
||||
Description: description,
|
||||
ParentUID: folder.RootFolderUID,
|
||||
ParentUID: parentUID,
|
||||
})
|
||||
if err != nil {
|
||||
// We'll log the error and also roll back the previously-created
|
||||
// (legacy) folder.
|
||||
s.log.Error("error saving folder to nested folder store", err)
|
||||
|
||||
err = s.DeleteFolder(ctx, &folder.DeleteFolderCommand{UID: createdFolder.Uid, OrgID: orgID, ForceDeleteRules: true})
|
||||
err = s.DeleteFolder(ctx, &folder.DeleteFolderCommand{UID: createdFolder.Uid, OrgID: cmd.OrgID, ForceDeleteRules: true})
|
||||
if err != nil {
|
||||
s.log.Error("error deleting folder after failed save to nested folder store", err)
|
||||
}
|
||||
return createdFolder, err
|
||||
return folder.FromDashboard(dash), err
|
||||
}
|
||||
// The folder UID is specified (or generated) during creation, so we'll
|
||||
// stop here and return the created model.Folder.
|
||||
}
|
||||
return createdFolder, nil
|
||||
return folder.FromDashboard(dash), nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) error {
|
||||
@@ -333,15 +340,6 @@ func (s *Service) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderComm
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
|
||||
// check the flag, if old - do whatever did before
|
||||
// for new only the store
|
||||
if cmd.UID == "" {
|
||||
cmd.UID = util.GenerateShortUID()
|
||||
}
|
||||
return s.store.Create(ctx, *cmd)
|
||||
}
|
||||
|
||||
func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
|
||||
// check the flag, if old - do whatever did before
|
||||
// for new only the store
|
||||
|
||||
@@ -79,12 +79,12 @@ func TestIntegrationFolderService(t *testing.T) {
|
||||
folderId := rand.Int63()
|
||||
folderUID := util.GenerateShortUID()
|
||||
|
||||
newFolder := models.NewFolder("Folder")
|
||||
newFolder.Id = folderId
|
||||
newFolder.Uid = folderUID
|
||||
f := models.NewFolder("Folder")
|
||||
f.Id = folderId
|
||||
f.Uid = folderUID
|
||||
|
||||
dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(newFolder, nil)
|
||||
dashStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(newFolder, nil)
|
||||
dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(f, nil)
|
||||
dashStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(f, nil)
|
||||
|
||||
t.Run("When get folder by id should return access denied error", func(t *testing.T) {
|
||||
_, err := service.GetFolderByID(context.Background(), usr, folderId, orgID)
|
||||
@@ -104,7 +104,12 @@ func TestIntegrationFolderService(t *testing.T) {
|
||||
|
||||
t.Run("When creating folder should return access denied error", func(t *testing.T) {
|
||||
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*models.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil).Times(2)
|
||||
_, err := service.CreateFolder(context.Background(), usr, orgID, newFolder.Title, folderUID)
|
||||
ctx := appcontext.WithUser(context.Background(), usr)
|
||||
_, err := service.Create(ctx, &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: f.Title,
|
||||
UID: folderUID,
|
||||
})
|
||||
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
@@ -158,16 +163,26 @@ func TestIntegrationFolderService(t *testing.T) {
|
||||
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(dash, nil).Once()
|
||||
dashStore.On("GetFolderByID", mock.Anything, orgID, dash.Id).Return(f, nil)
|
||||
|
||||
actualFolder, err := service.CreateFolder(context.Background(), usr, orgID, dash.Title, "someuid")
|
||||
ctx := appcontext.WithUser(context.Background(), usr)
|
||||
actualFolder, err := service.Create(ctx, &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: dash.Title,
|
||||
UID: "someuid",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, f, actualFolder)
|
||||
require.Equal(t, f, actualFolder.ToLegacyModel())
|
||||
})
|
||||
|
||||
t.Run("When creating folder should return error if uid is general", func(t *testing.T) {
|
||||
dash := models.NewDashboardFolder("Test-Folder")
|
||||
dash.Id = rand.Int63()
|
||||
|
||||
_, err := service.CreateFolder(context.Background(), usr, orgID, dash.Title, "general")
|
||||
ctx := appcontext.WithUser(context.Background(), usr)
|
||||
_, err := service.Create(ctx, &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: dash.Title,
|
||||
UID: "general",
|
||||
})
|
||||
require.ErrorIs(t, err, dashboards.ErrFolderInvalidUID)
|
||||
})
|
||||
|
||||
@@ -287,14 +302,38 @@ func TestIntegrationFolderService(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestFolderService(t *testing.T) {
|
||||
func TestNestedFolderServiceFeatureToggle(t *testing.T) {
|
||||
folderStore := NewFakeStore()
|
||||
|
||||
dashboardsvc := dashboards.FakeDashboardService{}
|
||||
dashboardsvc.On("BuildSaveDashboardCommand",
|
||||
mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"),
|
||||
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil)
|
||||
dashStore := dashboards.FakeDashboardStore{}
|
||||
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil)
|
||||
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&models.Folder{}, nil)
|
||||
cfg := setting.NewCfg()
|
||||
cfg.RBACEnabled = false
|
||||
nestedFoldersEnabled := true
|
||||
features := featuremgmt.WithFeatures()
|
||||
cfg.IsFeatureToggleEnabled = func(key string) bool {
|
||||
if key == featuremgmt.FlagNestedFolders {
|
||||
return nestedFoldersEnabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
cfg.IsFeatureToggleEnabled = features.IsEnabled
|
||||
folderService := &Service{
|
||||
store: folderStore,
|
||||
cfg: cfg,
|
||||
store: folderStore,
|
||||
dashboardStore: &dashStore,
|
||||
dashboardService: &dashboardsvc,
|
||||
features: features,
|
||||
}
|
||||
t.Run("create folder", func(t *testing.T) {
|
||||
folderStore.ExpectedFolder = &folder.Folder{}
|
||||
res, err := folderService.Create(context.Background(), &folder.CreateFolderCommand{})
|
||||
ctx := appcontext.WithUser(context.Background(), usr)
|
||||
res, err := folderService.Create(ctx, &folder.CreateFolderCommand{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.UID)
|
||||
})
|
||||
@@ -381,7 +420,12 @@ func TestNestedFolderService(t *testing.T) {
|
||||
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil)
|
||||
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&models.Folder{}, nil)
|
||||
|
||||
_, err := foldersvc.CreateFolder(ctx, usr, orgID, "myFolder", "myFolder")
|
||||
ctx = appcontext.WithUser(ctx, usr)
|
||||
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: "myFolder",
|
||||
UID: "myFolder",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// CreateFolder should not call the folder store create if the feature toggle is not enabled.
|
||||
require.False(t, store.CreateCalled)
|
||||
@@ -434,7 +478,12 @@ func TestNestedFolderService(t *testing.T) {
|
||||
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil)
|
||||
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil)
|
||||
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&models.Folder{}, nil)
|
||||
_, err := foldersvc.CreateFolder(ctx, usr, orgID, "myFolder", "myFolder")
|
||||
ctx = appcontext.WithUser(ctx, usr)
|
||||
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: "myFolder",
|
||||
UID: "myFolder",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// CreateFolder should also call the folder store's create method.
|
||||
require.True(t, store.CreateCalled)
|
||||
@@ -457,7 +506,12 @@ func TestNestedFolderService(t *testing.T) {
|
||||
store.ExpectedError = errors.New("FAILED")
|
||||
|
||||
// the service return success as long as the legacy create succeeds
|
||||
_, err := foldersvc.CreateFolder(ctx, usr, orgID, "myFolder", "myFolder")
|
||||
ctx = appcontext.WithUser(ctx, usr)
|
||||
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: "myFolder",
|
||||
UID: "myFolder",
|
||||
})
|
||||
require.Error(t, err, "FAILED")
|
||||
|
||||
// CreateFolder should also call the folder store's create method.
|
||||
|
||||
@@ -36,6 +36,15 @@ func (ss *sqlStore) Create(ctx context.Context, cmd folder.CreateFolderCommand)
|
||||
}
|
||||
|
||||
var foldr *folder.Folder
|
||||
/*
|
||||
user, err := appcontext.User(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version := 1
|
||||
updatedBy := user.UserID
|
||||
createdBy := user.UserID
|
||||
*/
|
||||
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
var sqlOrArgs []interface{}
|
||||
if cmd.ParentUID == "" {
|
||||
@@ -47,7 +56,7 @@ func (ss *sqlStore) Create(ctx context.Context, cmd folder.CreateFolderCommand)
|
||||
UID: &cmd.ParentUID,
|
||||
OrgID: cmd.OrgID,
|
||||
}); err != nil {
|
||||
return err
|
||||
return folder.ErrFolderNotFound.Errorf("parent folder does not exist")
|
||||
}
|
||||
}
|
||||
sql := "INSERT INTO folder(org_id, uid, parent_uid, title, description, created, updated) VALUES(?, ?, ?, ?, ?, ?, ?)"
|
||||
|
||||
Binary file not shown.
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
type FakeService struct {
|
||||
ExpectedFolders []*models.Folder
|
||||
ExpectedFolder *models.Folder
|
||||
ExpectedFolder *folder.Folder
|
||||
ExpectedError error
|
||||
}
|
||||
|
||||
@@ -20,19 +20,22 @@ func (s *FakeService) GetFolders(ctx context.Context, user *user.SignedInUser, o
|
||||
return s.ExpectedFolders, s.ExpectedError
|
||||
}
|
||||
func (s *FakeService) GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error) {
|
||||
return s.ExpectedFolder, s.ExpectedError
|
||||
return s.ExpectedFolder.ToLegacyModel(), s.ExpectedError
|
||||
}
|
||||
func (s *FakeService) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) {
|
||||
return s.ExpectedFolder, s.ExpectedError
|
||||
if s.ExpectedFolder == nil {
|
||||
return nil, s.ExpectedError
|
||||
}
|
||||
return s.ExpectedFolder.ToLegacyModel(), s.ExpectedError
|
||||
}
|
||||
func (s *FakeService) GetFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*models.Folder, error) {
|
||||
return s.ExpectedFolder, s.ExpectedError
|
||||
return s.ExpectedFolder.ToLegacyModel(), s.ExpectedError
|
||||
}
|
||||
func (s *FakeService) CreateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, title, uid string) (*models.Folder, error) {
|
||||
func (s *FakeService) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
|
||||
return s.ExpectedFolder, s.ExpectedError
|
||||
}
|
||||
func (s *FakeService) UpdateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) error {
|
||||
cmd.Result = s.ExpectedFolder
|
||||
cmd.Result = s.ExpectedFolder.ToLegacyModel()
|
||||
return s.ExpectedError
|
||||
}
|
||||
func (s *FakeService) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
|
||||
|
||||
@@ -3,6 +3,7 @@ package folder
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
@@ -33,7 +34,17 @@ type Folder struct {
|
||||
|
||||
// TODO: validate if this field is required/relevant to folders.
|
||||
// currently there is no such column
|
||||
// Version int
|
||||
// Url string
|
||||
// UpdatedBy int64
|
||||
// CreatedBy int64
|
||||
// HasACL bool
|
||||
}
|
||||
|
||||
type FolderDTO struct {
|
||||
Folder
|
||||
|
||||
Children []FolderDTO
|
||||
}
|
||||
|
||||
// NewFolder tales a title and returns a Folder with the Created and Updated
|
||||
@@ -51,7 +62,7 @@ func NewFolder(title string, description string) *Folder {
|
||||
// to create a folder.
|
||||
type CreateFolderCommand struct {
|
||||
UID string `json:"uid"`
|
||||
OrgID int64 `json:"orgId"`
|
||||
OrgID int64 `json:"-"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
ParentUID string `json:"parent_uid"`
|
||||
@@ -72,7 +83,7 @@ type UpdateFolderCommand struct {
|
||||
type MoveFolderCommand struct {
|
||||
UID string `json:"uid"`
|
||||
NewParentUID string `json:"new_parent_uid"`
|
||||
OrgID int64 `json:"orgId"`
|
||||
OrgID int64 `json:"-"`
|
||||
}
|
||||
|
||||
// DeleteFolderCommand captures the information required by the folder service
|
||||
@@ -113,3 +124,34 @@ type GetTreeQuery struct {
|
||||
Limit int64
|
||||
Page int64
|
||||
}
|
||||
|
||||
// ToLegacyModel is temporary until the two folder services are merged
|
||||
func (f *Folder) ToLegacyModel() *models.Folder {
|
||||
return &models.Folder{
|
||||
Id: f.ID,
|
||||
Uid: f.UID,
|
||||
Title: f.Title,
|
||||
Url: models.GetFolderUrl(f.UID, models.SlugifyTitle(f.Title)),
|
||||
Version: 0,
|
||||
Created: f.Created,
|
||||
Updated: f.Updated,
|
||||
UpdatedBy: 0,
|
||||
CreatedBy: 0,
|
||||
HasACL: false,
|
||||
}
|
||||
}
|
||||
|
||||
func FromDashboard(dash *models.Dashboard) *Folder {
|
||||
return &Folder{
|
||||
ID: dash.Id,
|
||||
UID: dash.Uid,
|
||||
Title: dash.Title,
|
||||
//HasACL: dash.HasACL,
|
||||
//Url: dash.GetUrl(),
|
||||
//Version: dash.Version,
|
||||
Created: dash.Created,
|
||||
//CreatedBy: dash.CreatedBy,
|
||||
Updated: dash.Updated,
|
||||
//UpdatedBy: dash.UpdatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ type Service interface {
|
||||
GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error)
|
||||
GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error)
|
||||
GetFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*models.Folder, error)
|
||||
CreateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, title, uid string) (*models.Folder, error)
|
||||
Create(ctx context.Context, cmd *CreateFolderCommand) (*Folder, error)
|
||||
UpdateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) error
|
||||
DeleteFolder(ctx context.Context, cmd *DeleteFolderCommand) error
|
||||
MakeUserAdmin(ctx context.Context, orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error
|
||||
|
||||
@@ -524,11 +524,11 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilter is set to existing folders, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||
command := getCreatePanelCommand(newFolder.Id, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(newFolder.ID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
folderFilter := strconv.FormatInt(newFolder.Id, 10)
|
||||
folderFilter := strconv.FormatInt(newFolder.ID, 10)
|
||||
|
||||
err := sc.reqContext.Req.ParseForm()
|
||||
require.NoError(t, err)
|
||||
@@ -548,7 +548,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
{
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: newFolder.Id,
|
||||
FolderID: newFolder.ID,
|
||||
UID: result.Result.Elements[0].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Kind: int64(models.PanelElement),
|
||||
@@ -564,7 +564,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
Version: 1,
|
||||
Meta: LibraryElementDTOMeta{
|
||||
FolderName: "NewFolder",
|
||||
FolderUID: newFolder.Uid,
|
||||
FolderUID: newFolder.UID,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
Updated: result.Result.Elements[0].Meta.Updated,
|
||||
@@ -591,7 +591,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilter is set to a nonexistent folders, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||
command := getCreatePanelCommand(newFolder.Id, "Text - Library Panel2")
|
||||
command := getCreatePanelCommand(newFolder.ID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||
cmd := PatchLibraryElementCommand{
|
||||
FolderID: newFolder.Id,
|
||||
FolderID: newFolder.ID,
|
||||
Name: "Panel - New name",
|
||||
Model: []byte(`
|
||||
{
|
||||
@@ -48,7 +48,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
Result: libraryElement{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: newFolder.Id,
|
||||
FolderID: newFolder.ID,
|
||||
UID: sc.initialResult.Result.UID,
|
||||
Name: "Panel - New name",
|
||||
Kind: int64(models.PanelElement),
|
||||
@@ -90,7 +90,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||
cmd := PatchLibraryElementCommand{
|
||||
FolderID: newFolder.Id,
|
||||
FolderID: newFolder.ID,
|
||||
Kind: int64(models.PanelElement),
|
||||
Version: 1,
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
resp := sc.service.patchHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.initialResult.Result.FolderID = newFolder.Id
|
||||
sc.initialResult.Result.FolderID = newFolder.ID
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = userInDbName
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarURL = userInDbAvatar
|
||||
sc.initialResult.Result.Meta.Updated = result.Result.Meta.Updated
|
||||
@@ -325,7 +325,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with a folder where a library panel with the same name already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||
command := getCreatePanelCommand(newFolder.Id, "Text - Library Panel")
|
||||
command := getCreatePanelCommand(newFolder.ID, "Text - Library Panel")
|
||||
sc.ctx.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
|
||||
@@ -73,7 +73,7 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
command := getCreatePanelCommand(folder.Id, "Library Panel Name")
|
||||
command := getCreatePanelCommand(folder.ID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
@@ -82,14 +82,14 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
fromFolder := createFolderWithACL(t, sc.sqlStore, "Everyone", sc.user, everyonePermissions)
|
||||
command := getCreatePanelCommand(fromFolder.Id, "Library Panel Name")
|
||||
command := getCreatePanelCommand(fromFolder.ID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
cmd := PatchLibraryElementCommand{FolderID: toFolder.Id, Version: 1, Kind: int64(models.PanelElement)}
|
||||
cmd := PatchLibraryElementCommand{FolderID: toFolder.ID, Version: 1, Kind: int64(models.PanelElement)}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp = sc.service.patchHandler(sc.reqContext)
|
||||
@@ -99,14 +99,14 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
fromFolder := createFolderWithACL(t, sc.sqlStore, "Everyone", sc.user, testCase.items)
|
||||
command := getCreatePanelCommand(fromFolder.Id, "Library Panel Name")
|
||||
command := getCreatePanelCommand(fromFolder.ID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
cmd := PatchLibraryElementCommand{FolderID: toFolder.Id, Version: 1, Kind: int64(models.PanelElement)}
|
||||
cmd := PatchLibraryElementCommand{FolderID: toFolder.ID, Version: 1, Kind: int64(models.PanelElement)}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp = sc.service.patchHandler(sc.reqContext)
|
||||
@@ -116,7 +116,7 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to delete a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||
cmd := getCreatePanelCommand(folder.Id, "Library Panel Name")
|
||||
cmd := getCreatePanelCommand(folder.ID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -151,7 +151,7 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
|
||||
command := getCreatePanelCommand(folder.Id, "Library Panel Name")
|
||||
command := getCreatePanelCommand(folder.ID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -173,7 +173,7 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
cmd := PatchLibraryElementCommand{FolderID: folder.Id, Version: 1, Kind: int64(models.PanelElement)}
|
||||
cmd := PatchLibraryElementCommand{FolderID: folder.ID, Version: 1, Kind: int64(models.PanelElement)}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
|
||||
sc.ctx.Req.Body = mockRequestBody(cmd)
|
||||
resp = sc.service.patchHandler(sc.reqContext)
|
||||
@@ -216,7 +216,7 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to a folder that doesn't exist, it should fail", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
|
||||
command := getCreatePanelCommand(folder.Id, "Library Panel Name")
|
||||
command := getCreatePanelCommand(folder.ID, "Library Panel Name")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -245,7 +245,7 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
var results []libraryElement
|
||||
for i, folderCase := range folderCases {
|
||||
folder := createFolderWithACL(t, sc.sqlStore, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||
cmd := getCreatePanelCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
|
||||
cmd := getCreatePanelCommand(folder.ID, fmt.Sprintf("Library Panel in Folder%v", i))
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -254,7 +254,7 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
result.Result.Meta.UpdatedBy.Name = userInDbName
|
||||
result.Result.Meta.UpdatedBy.AvatarURL = userInDbAvatar
|
||||
result.Result.Meta.FolderName = folder.Title
|
||||
result.Result.Meta.FolderUID = folder.Uid
|
||||
result.Result.Meta.FolderUID = folder.UID
|
||||
results = append(results, result.Result)
|
||||
}
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
@@ -308,7 +308,7 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
var results []libraryElement
|
||||
for i, folderCase := range folderCases {
|
||||
folder := createFolderWithACL(t, sc.sqlStore, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||
cmd := getCreatePanelCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
|
||||
cmd := getCreatePanelCommand(folder.ID, fmt.Sprintf("Library Panel in Folder%v", i))
|
||||
sc.reqContext.Req.Body = mockRequestBody(cmd)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -317,7 +317,7 @@ func TestLibraryElementPermissions(t *testing.T) {
|
||||
result.Result.Meta.UpdatedBy.Name = userInDbName
|
||||
result.Result.Meta.UpdatedBy.AvatarURL = userInDbAvatar
|
||||
result.Result.Meta.FolderName = folder.Title
|
||||
result.Result.Meta.FolderUID = folder.Uid
|
||||
result.Result.Meta.FolderUID = folder.UID
|
||||
results = append(results, result.Result)
|
||||
}
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"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/tracing"
|
||||
@@ -25,6 +26,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
dashboardservice "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/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
@@ -294,7 +296,7 @@ func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash
|
||||
}
|
||||
|
||||
func createFolderWithACL(t *testing.T, sqlStore db.DB, title string, user user.SignedInUser,
|
||||
items []folderACLItem) *models.Folder {
|
||||
items []folderACLItem) *folder.Folder {
|
||||
t.Helper()
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
@@ -312,10 +314,13 @@ func createFolderWithACL(t *testing.T, sqlStore db.DB, title string, user user.S
|
||||
)
|
||||
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, d, dashboardStore, nil, features, folderPermissions, nil)
|
||||
t.Logf("Creating folder with title and UID %q", title)
|
||||
folder, err := s.CreateFolder(context.Background(), &user, user.OrgID, title, title)
|
||||
ctx := appcontext.WithUser(context.Background(), &user)
|
||||
folder, err := s.Create(ctx, &folder.CreateFolderCommand{
|
||||
OrgID: user.OrgID, Title: title, UID: title,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
updateFolderACL(t, dashboardStore, folder.Id, items)
|
||||
updateFolderACL(t, dashboardStore, folder.ID, items)
|
||||
|
||||
return folder
|
||||
}
|
||||
@@ -456,7 +461,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||
},
|
||||
}
|
||||
|
||||
sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{})
|
||||
sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{}).ToLegacyModel()
|
||||
|
||||
fn(t, sc)
|
||||
})
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"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/tracing"
|
||||
@@ -22,6 +23,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
dashboardservice "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/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
@@ -705,7 +707,7 @@ func createDashboard(t *testing.T, sqlStore db.DB, user *user.SignedInUser, dash
|
||||
}
|
||||
|
||||
func createFolderWithACL(t *testing.T, sqlStore db.DB, title string, user *user.SignedInUser,
|
||||
items []folderACLItem) *models.Folder {
|
||||
items []folderACLItem) *folder.Folder {
|
||||
t.Helper()
|
||||
|
||||
ac := acmock.New()
|
||||
@@ -720,10 +722,11 @@ func createFolderWithACL(t *testing.T, sqlStore db.DB, title string, user *user.
|
||||
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, d, dashboardStore, nil, features, folderPermissions, nil)
|
||||
|
||||
t.Logf("Creating folder with title and UID %q", title)
|
||||
folder, err := s.CreateFolder(context.Background(), user, user.OrgID, title, title)
|
||||
ctx := appcontext.WithUser(context.Background(), user)
|
||||
folder, err := s.Create(ctx, &folder.CreateFolderCommand{OrgID: user.OrgID, Title: title, UID: title})
|
||||
require.NoError(t, err)
|
||||
|
||||
updateFolderACL(t, dashboardStore, folder.Id, items)
|
||||
updateFolderACL(t, dashboardStore, folder.ID, items)
|
||||
|
||||
return folder
|
||||
}
|
||||
@@ -858,7 +861,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||
sqlStore: sqlStore,
|
||||
}
|
||||
|
||||
sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{})
|
||||
sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{}).ToLegacyModel()
|
||||
|
||||
fn(t, sc)
|
||||
})
|
||||
|
||||
@@ -14,9 +14,11 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"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/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
gfmodels "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||
@@ -24,6 +26,7 @@ import (
|
||||
databasestore "github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
dashboardservice "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/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert"
|
||||
@@ -122,8 +125,13 @@ func CreateTestAlertRuleWithLabels(t testing.TB, ctx context.Context, dbstore *s
|
||||
OrgRole: org.RoleAdmin,
|
||||
IsGrafanaAdmin: true,
|
||||
}
|
||||
folder, err := dbstore.FolderService.CreateFolder(ctx, user, orgID, "FOLDER-"+util.GenerateShortUID(), folderUID)
|
||||
if errors.Is(err, dashboards.ErrFolderWithSameUIDExists) || errors.Is(err, dashboards.ErrFolderVersionMismatch) {
|
||||
|
||||
ctx = appcontext.WithUser(ctx, user)
|
||||
f, err := dbstore.FolderService.Create(ctx, &folder.CreateFolderCommand{OrgID: orgID, Title: "FOLDER-" + util.GenerateShortUID(), UID: folderUID})
|
||||
var folder *gfmodels.Folder
|
||||
if err == nil {
|
||||
folder = f.ToLegacyModel()
|
||||
} else if errors.Is(err, dashboards.ErrFolderWithSameUIDExists) || errors.Is(err, dashboards.ErrFolderVersionMismatch) {
|
||||
folder, err = dbstore.FolderService.GetFolderByUID(ctx, user, orgID, folderUID)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user