mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Nested Folders: Support getting of nested folder in folder service wh… (#58597)
* Nested Folders: Support getting of nested folder in folder service when feature flag is set * Fix lint * Fix some tests * Fix ngalert test * ngalert fix * Fix API tests * Fix some tests and lint * Fix lint 2 * Fix library elements and panels * Add access control to get folder * Cleanup and minor test change
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
@@ -394,14 +395,17 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
||||
cmd.OrgId = c.OrgID
|
||||
cmd.UserId = c.UserID
|
||||
if cmd.FolderUid != "" {
|
||||
folder, err := hs.folderService.GetFolderByUID(ctx, c.SignedInUser, c.OrgID, cmd.FolderUid)
|
||||
folder, err := hs.folderService.Get(ctx, &folder.GetFolderQuery{
|
||||
OrgID: c.OrgID,
|
||||
UID: &cmd.FolderUid,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||
return response.Error(400, "Folder not found", err)
|
||||
}
|
||||
return response.Error(500, "Error while checking folder ID", err)
|
||||
}
|
||||
cmd.FolderId = folder.Id
|
||||
cmd.FolderId = folder.ID
|
||||
}
|
||||
|
||||
dash := cmd.GetDashboardModel()
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest"
|
||||
"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/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/services/live"
|
||||
@@ -642,8 +643,8 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")).
|
||||
Return(&models.Dashboard{Id: dashID, Uid: "uid", Title: "Dash", Slug: "dash", Version: 2}, nil)
|
||||
|
||||
mockFolder := &fakeFolderService{
|
||||
GetFolderByUIDResult: &models.Folder{Id: 1, Uid: "folderUID", Title: "Folder"},
|
||||
mockFolder := &foldertest.FakeService{
|
||||
ExpectedFolder: &folder.Folder{ID: 1, UID: "folderUID", Title: "Folder"},
|
||||
}
|
||||
|
||||
postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, mockFolder, func(sc *scenarioContext) {
|
||||
@@ -673,8 +674,8 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
|
||||
mockFolder := &fakeFolderService{
|
||||
GetFolderByUIDError: errors.New("Error while searching Folder ID"),
|
||||
mockFolder := &foldertest.FakeService{
|
||||
ExpectedError: errors.New("Error while searching Folder ID"),
|
||||
}
|
||||
|
||||
postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, mockFolder, func(sc *scenarioContext) {
|
||||
|
||||
@@ -67,13 +67,14 @@ func (hs *HTTPServer) GetFolders(c *models.ReqContext) response.Response {
|
||||
// 404: notFoundError
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) GetFolderByUID(c *models.ReqContext) response.Response {
|
||||
folder, err := hs.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, web.Params(c.Req)[":uid"])
|
||||
uid := web.Params(c.Req)[":uid"]
|
||||
folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: &uid})
|
||||
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)
|
||||
return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, folder))
|
||||
}
|
||||
|
||||
// swagger:route GET /folders/id/{folder_id} folders getFolderByID
|
||||
@@ -93,13 +94,13 @@ func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response {
|
||||
if err != nil {
|
||||
return response.Error(http.StatusBadRequest, "id is invalid", err)
|
||||
}
|
||||
folder, err := hs.folderService.GetFolderByID(c.Req.Context(), c.SignedInUser, id, c.OrgID)
|
||||
folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{ID: &id, OrgID: c.OrgID})
|
||||
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)
|
||||
return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, folder))
|
||||
}
|
||||
|
||||
// swagger:route POST /folders folders createFolder
|
||||
@@ -182,8 +183,8 @@ func (hs *HTTPServer) UpdateFolder(c *models.ReqContext) response.Response {
|
||||
if err != nil {
|
||||
return apierrors.ToFolderErrorResponse(err)
|
||||
}
|
||||
g := guardian.New(c.Req.Context(), result.Id, c.OrgID, c.SignedInUser)
|
||||
return response.JSON(http.StatusOK, hs.toFolderDto(c, g, result))
|
||||
g := guardian.New(c.Req.Context(), result.ID, c.OrgID, c.SignedInUser)
|
||||
return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, result))
|
||||
}
|
||||
|
||||
// swagger:route DELETE /folders/{folder_uid} folders deleteFolder
|
||||
@@ -218,40 +219,6 @@ func (hs *HTTPServer) DeleteFolder(c *models.ReqContext) response.Response { //
|
||||
return response.JSON(http.StatusOK, "")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) toFolderDto(c *models.ReqContext, g guardian.DashboardGuardian, folder *models.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),
|
||||
}
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) newToFolderDto(c *models.ReqContext, g guardian.DashboardGuardian, folder *folder.Folder) dtos.Folder {
|
||||
canEdit, _ := g.CanEdit()
|
||||
canSave, _ := g.CanSave()
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
@@ -26,13 +27,14 @@ import (
|
||||
// 404: notFoundError
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Response {
|
||||
folder, err := hs.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, web.Params(c.Req)[":uid"])
|
||||
uid := web.Params(c.Req)[":uid"]
|
||||
folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: &uid})
|
||||
|
||||
if err != nil {
|
||||
return apierrors.ToFolderErrorResponse(err)
|
||||
}
|
||||
|
||||
g := guardian.New(c.Req.Context(), folder.Id, c.OrgID, c.SignedInUser)
|
||||
g := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser)
|
||||
|
||||
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
|
||||
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
|
||||
@@ -49,7 +51,7 @@ func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Res
|
||||
continue
|
||||
}
|
||||
|
||||
perm.FolderId = folder.Id
|
||||
perm.FolderId = folder.ID
|
||||
perm.DashboardId = 0
|
||||
|
||||
perm.UserAvatarUrl = dtos.GetGravatarUrl(perm.UserEmail)
|
||||
@@ -87,12 +89,13 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
|
||||
return response.Error(400, err.Error(), err)
|
||||
}
|
||||
|
||||
folder, err := hs.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, web.Params(c.Req)[":uid"])
|
||||
uid := web.Params(c.Req)[":uid"]
|
||||
folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: &uid})
|
||||
if err != nil {
|
||||
return apierrors.ToFolderErrorResponse(err)
|
||||
}
|
||||
|
||||
g := guardian.New(c.Req.Context(), folder.Id, c.OrgID, c.SignedInUser)
|
||||
g := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser)
|
||||
canAdmin, err := g.CanAdmin()
|
||||
if err != nil {
|
||||
return apierrors.ToFolderErrorResponse(err)
|
||||
@@ -106,7 +109,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
|
||||
for _, item := range apiCmd.Items {
|
||||
items = append(items, &models.DashboardACL{
|
||||
OrgID: c.OrgID,
|
||||
DashboardID: folder.Id,
|
||||
DashboardID: folder.ID,
|
||||
UserID: item.UserID,
|
||||
TeamID: item.TeamID,
|
||||
Role: item.Role,
|
||||
@@ -140,13 +143,13 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while checking dashboard permissions", err)
|
||||
}
|
||||
if err := hs.updateDashboardAccessControl(c.Req.Context(), c.OrgID, folder.Uid, true, items, old); err != nil {
|
||||
if err := hs.updateDashboardAccessControl(c.Req.Context(), c.OrgID, folder.UID, true, items, old); err != nil {
|
||||
return response.Error(500, "Failed to create permission", err)
|
||||
}
|
||||
return response.Success("Dashboard permissions updated")
|
||||
}
|
||||
|
||||
if err := hs.DashboardService.UpdateDashboardACL(c.Req.Context(), folder.Id, items); err != nil {
|
||||
if err := hs.DashboardService.UpdateDashboardACL(c.Req.Context(), folder.ID, items); err != nil {
|
||||
if errors.Is(err, models.ErrDashboardACLInfoMissing) {
|
||||
err = models.ErrFolderACLInfoMissing
|
||||
}
|
||||
@@ -163,7 +166,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
|
||||
|
||||
return response.JSON(http.StatusOK, util.DynMap{
|
||||
"message": "Folder permissions updated",
|
||||
"id": folder.Id,
|
||||
"id": folder.ID,
|
||||
"title": folder.Title,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -294,47 +293,3 @@ func updateFolderScenario(t *testing.T, desc string, url string, routePattern st
|
||||
fn(sc)
|
||||
})
|
||||
}
|
||||
|
||||
type fakeFolderService struct {
|
||||
folder.Service
|
||||
|
||||
GetFoldersResult []*models.Folder
|
||||
GetFoldersError error
|
||||
GetFolderByUIDResult *models.Folder
|
||||
GetFolderByUIDError error
|
||||
GetFolderByIDResult *models.Folder
|
||||
GetFolderByIDError error
|
||||
CreateFolderResult *models.Folder
|
||||
CreateFolderError error
|
||||
UpdateFolderResult *models.Folder
|
||||
UpdateFolderError error
|
||||
DeleteFolderResult *folder.Folder
|
||||
DeleteFolderError error
|
||||
DeletedFolderUids []string
|
||||
}
|
||||
|
||||
func (s *fakeFolderService) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) {
|
||||
return s.GetFoldersResult, s.GetFoldersError
|
||||
}
|
||||
|
||||
func (s *fakeFolderService) GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error) {
|
||||
return s.GetFolderByIDResult, s.GetFolderByIDError
|
||||
}
|
||||
|
||||
func (s *fakeFolderService) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) {
|
||||
return s.GetFolderByUIDResult, s.GetFolderByUIDError
|
||||
}
|
||||
|
||||
func (s *fakeFolderService) CreateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, title, uid string) (*models.Folder, error) {
|
||||
return s.CreateFolderResult, s.CreateFolderError
|
||||
}
|
||||
|
||||
func (s *fakeFolderService) UpdateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) error {
|
||||
cmd.Result = s.UpdateFolderResult
|
||||
return s.UpdateFolderError
|
||||
}
|
||||
|
||||
func (s *fakeFolderService) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
|
||||
s.DeletedFolderUids = append(s.DeletedFolderUids, cmd.UID)
|
||||
return s.DeleteFolderError
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user