mirror of
https://github.com/grafana/grafana.git
synced 2025-07-28 16:22:18 +08: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"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"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/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
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.OrgId = c.OrgID
|
||||||
cmd.UserId = c.UserID
|
cmd.UserId = c.UserID
|
||||||
if cmd.FolderUid != "" {
|
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 err != nil {
|
||||||
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||||
return response.Error(400, "Folder not found", err)
|
return response.Error(400, "Folder not found", err)
|
||||||
}
|
}
|
||||||
return response.Error(500, "Error while checking folder ID", err)
|
return response.Error(500, "Error while checking folder ID", err)
|
||||||
}
|
}
|
||||||
cmd.FolderId = folder.Id
|
cmd.FolderId = folder.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
dash := cmd.GetDashboardModel()
|
dash := cmd.GetDashboardModel()
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest"
|
"github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"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/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||||
"github.com/grafana/grafana/pkg/services/live"
|
"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")).
|
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)
|
Return(&models.Dashboard{Id: dashID, Uid: "uid", Title: "Dash", Slug: "dash", Version: 2}, nil)
|
||||||
|
|
||||||
mockFolder := &fakeFolderService{
|
mockFolder := &foldertest.FakeService{
|
||||||
GetFolderByUIDResult: &models.Folder{Id: 1, Uid: "folderUID", Title: "Folder"},
|
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) {
|
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)
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||||
|
|
||||||
mockFolder := &fakeFolderService{
|
mockFolder := &foldertest.FakeService{
|
||||||
GetFolderByUIDError: errors.New("Error while searching Folder ID"),
|
ExpectedError: errors.New("Error while searching Folder ID"),
|
||||||
}
|
}
|
||||||
|
|
||||||
postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, mockFolder, func(sc *scenarioContext) {
|
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
|
// 404: notFoundError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
func (hs *HTTPServer) GetFolderByUID(c *models.ReqContext) response.Response {
|
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 {
|
if err != nil {
|
||||||
return apierrors.ToFolderErrorResponse(err)
|
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)
|
||||||
return response.JSON(http.StatusOK, hs.toFolderDto(c, g, folder))
|
return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, folder))
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:route GET /folders/id/{folder_id} folders getFolderByID
|
// 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 {
|
if err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "id is invalid", err)
|
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 {
|
if err != nil {
|
||||||
return apierrors.ToFolderErrorResponse(err)
|
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)
|
||||||
return response.JSON(http.StatusOK, hs.toFolderDto(c, g, folder))
|
return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, folder))
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:route POST /folders folders createFolder
|
// swagger:route POST /folders folders createFolder
|
||||||
@ -182,8 +183,8 @@ func (hs *HTTPServer) UpdateFolder(c *models.ReqContext) response.Response {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return apierrors.ToFolderErrorResponse(err)
|
return apierrors.ToFolderErrorResponse(err)
|
||||||
}
|
}
|
||||||
g := guardian.New(c.Req.Context(), result.Id, c.OrgID, c.SignedInUser)
|
g := guardian.New(c.Req.Context(), result.ID, c.OrgID, c.SignedInUser)
|
||||||
return response.JSON(http.StatusOK, hs.toFolderDto(c, g, result))
|
return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, result))
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:route DELETE /folders/{folder_uid} folders deleteFolder
|
// 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, "")
|
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 {
|
func (hs *HTTPServer) newToFolderDto(c *models.ReqContext, g guardian.DashboardGuardian, folder *folder.Folder) dtos.Folder {
|
||||||
canEdit, _ := g.CanEdit()
|
canEdit, _ := g.CanEdit()
|
||||||
canSave, _ := g.CanSave()
|
canSave, _ := g.CanSave()
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"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/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
@ -26,13 +27,14 @@ import (
|
|||||||
// 404: notFoundError
|
// 404: notFoundError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Response {
|
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 {
|
if err != nil {
|
||||||
return apierrors.ToFolderErrorResponse(err)
|
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 {
|
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
|
||||||
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
|
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
|
||||||
@ -49,7 +51,7 @@ func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Res
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
perm.FolderId = folder.Id
|
perm.FolderId = folder.ID
|
||||||
perm.DashboardId = 0
|
perm.DashboardId = 0
|
||||||
|
|
||||||
perm.UserAvatarUrl = dtos.GetGravatarUrl(perm.UserEmail)
|
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)
|
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 {
|
if err != nil {
|
||||||
return apierrors.ToFolderErrorResponse(err)
|
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()
|
canAdmin, err := g.CanAdmin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return apierrors.ToFolderErrorResponse(err)
|
return apierrors.ToFolderErrorResponse(err)
|
||||||
@ -106,7 +109,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
|
|||||||
for _, item := range apiCmd.Items {
|
for _, item := range apiCmd.Items {
|
||||||
items = append(items, &models.DashboardACL{
|
items = append(items, &models.DashboardACL{
|
||||||
OrgID: c.OrgID,
|
OrgID: c.OrgID,
|
||||||
DashboardID: folder.Id,
|
DashboardID: folder.ID,
|
||||||
UserID: item.UserID,
|
UserID: item.UserID,
|
||||||
TeamID: item.TeamID,
|
TeamID: item.TeamID,
|
||||||
Role: item.Role,
|
Role: item.Role,
|
||||||
@ -140,13 +143,13 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Error while checking dashboard permissions", err)
|
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.Error(500, "Failed to create permission", err)
|
||||||
}
|
}
|
||||||
return response.Success("Dashboard permissions updated")
|
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) {
|
if errors.Is(err, models.ErrDashboardACLInfoMissing) {
|
||||||
err = models.ErrFolderACLInfoMissing
|
err = models.ErrFolderACLInfoMissing
|
||||||
}
|
}
|
||||||
@ -163,7 +166,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
|
|||||||
|
|
||||||
return response.JSON(http.StatusOK, util.DynMap{
|
return response.JSON(http.StatusOK, util.DynMap{
|
||||||
"message": "Folder permissions updated",
|
"message": "Folder permissions updated",
|
||||||
"id": folder.Id,
|
"id": folder.ID,
|
||||||
"title": folder.Title,
|
"title": folder.Title,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -294,47 +293,3 @@ func updateFolderScenario(t *testing.T, desc string, url string, routePattern st
|
|||||||
fn(sc)
|
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
|
|
||||||
}
|
|
||||||
|
@ -85,17 +85,20 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
|
|||||||
|
|
||||||
// here we need to get FolderId from FolderUID if it present in the request, if both exist, FolderUID would overwrite FolderID
|
// here we need to get FolderId from FolderUID if it present in the request, if both exist, FolderUID would overwrite FolderID
|
||||||
if req.FolderUid != "" {
|
if req.FolderUid != "" {
|
||||||
folder, err := s.folderService.GetFolderByUID(ctx, req.User, req.User.OrgID, req.FolderUid)
|
folder, err := s.folderService.Get(ctx, &folder.GetFolderQuery{
|
||||||
|
OrgID: req.User.OrgID,
|
||||||
|
UID: &req.FolderUid,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.FolderId = folder.Id
|
req.FolderId = folder.ID
|
||||||
} else {
|
} else {
|
||||||
folder, err := s.folderService.GetFolderByID(ctx, req.User, req.FolderId, req.User.OrgID)
|
folder, err := s.folderService.Get(ctx, &folder.GetFolderQuery{ID: &req.FolderId, OrgID: req.User.OrgID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.FolderUid = folder.Uid
|
req.FolderUid = folder.UID
|
||||||
}
|
}
|
||||||
|
|
||||||
saveCmd := models.SaveDashboardCommand{
|
saveCmd := models.SaveDashboardCommand{
|
||||||
|
@ -53,7 +53,7 @@ func NewFolderNameScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.Uid)}, nil
|
return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.UID)}, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ func NewFolderIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.Uid)}, nil
|
return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.UID)}, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ func resolveDashboardScope(ctx context.Context, db Store, orgID int64, dashboard
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
folderUID = folder.Uid
|
folderUID = folder.UID
|
||||||
}
|
}
|
||||||
|
|
||||||
return []string{
|
return []string{
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,9 +30,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
|
|||||||
orgId := rand.Int63()
|
orgId := rand.Int63()
|
||||||
title := "Very complex :title with: and /" + util.GenerateShortUID()
|
title := "Very complex :title with: and /" + util.GenerateShortUID()
|
||||||
|
|
||||||
db := models.NewFolder(title)
|
db := &folder.Folder{Title: title, ID: rand.Int63(), UID: util.GenerateShortUID()}
|
||||||
db.Id = rand.Int63()
|
|
||||||
db.Uid = util.GenerateShortUID()
|
|
||||||
dashboardStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
|
dashboardStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
|
||||||
|
|
||||||
scope := "folders:name:" + title
|
scope := "folders:name:" + title
|
||||||
@ -40,7 +39,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, resolvedScopes, 1)
|
require.Len(t, resolvedScopes, 1)
|
||||||
|
|
||||||
require.Equal(t, fmt.Sprintf("folders:uid:%v", db.Uid), resolvedScopes[0])
|
require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0])
|
||||||
|
|
||||||
dashboardStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title)
|
dashboardStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title)
|
||||||
})
|
})
|
||||||
@ -88,17 +87,17 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
|
|||||||
orgId := rand.Int63()
|
orgId := rand.Int63()
|
||||||
uid := util.GenerateShortUID()
|
uid := util.GenerateShortUID()
|
||||||
|
|
||||||
db := &models.Folder{Id: rand.Int63(), Uid: uid}
|
db := &folder.Folder{ID: rand.Int63(), UID: uid}
|
||||||
dashboardStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
|
dashboardStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once()
|
||||||
|
|
||||||
scope := "folders:id:" + strconv.FormatInt(db.Id, 10)
|
scope := "folders:id:" + strconv.FormatInt(db.ID, 10)
|
||||||
|
|
||||||
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
|
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, resolvedScopes, 1)
|
require.Len(t, resolvedScopes, 1)
|
||||||
require.Equal(t, fmt.Sprintf("folders:uid:%v", db.Uid), resolvedScopes[0])
|
require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0])
|
||||||
|
|
||||||
dashboardStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.Id)
|
dashboardStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.ID)
|
||||||
})
|
})
|
||||||
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
|
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
|
||||||
dashboardStore := &FakeDashboardStore{}
|
dashboardStore := &FakeDashboardStore{}
|
||||||
@ -157,18 +156,18 @@ func TestNewDashboardIDScopeResolver(t *testing.T) {
|
|||||||
_, resolver := NewDashboardIDScopeResolver(store)
|
_, resolver := NewDashboardIDScopeResolver(store)
|
||||||
|
|
||||||
orgID := rand.Int63()
|
orgID := rand.Int63()
|
||||||
folder := &models.Folder{Id: 2, Uid: "2"}
|
folder := &folder.Folder{ID: 2, UID: "2"}
|
||||||
dashboard := &models.Dashboard{Id: 1, FolderId: folder.Id, Uid: "1"}
|
dashboard := &models.Dashboard{Id: 1, FolderId: folder.ID, Uid: "1"}
|
||||||
|
|
||||||
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once()
|
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once()
|
||||||
store.On("GetFolderByID", mock.Anything, orgID, folder.Id).Return(folder, nil).Once()
|
store.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once()
|
||||||
|
|
||||||
scope := ac.Scope("dashboards", "id", strconv.FormatInt(dashboard.Id, 10))
|
scope := ac.Scope("dashboards", "id", strconv.FormatInt(dashboard.Id, 10))
|
||||||
resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope)
|
resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, resolvedScopes, 2)
|
require.Len(t, resolvedScopes, 2)
|
||||||
require.Equal(t, fmt.Sprintf("dashboards:uid:%s", dashboard.Uid), resolvedScopes[0])
|
require.Equal(t, fmt.Sprintf("dashboards:uid:%s", dashboard.Uid), resolvedScopes[0])
|
||||||
require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.Uid), resolvedScopes[1])
|
require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.UID), resolvedScopes[1])
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
|
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
|
||||||
@ -203,18 +202,18 @@ func TestNewDashboardUIDScopeResolver(t *testing.T) {
|
|||||||
_, resolver := NewDashboardUIDScopeResolver(store)
|
_, resolver := NewDashboardUIDScopeResolver(store)
|
||||||
|
|
||||||
orgID := rand.Int63()
|
orgID := rand.Int63()
|
||||||
folder := &models.Folder{Id: 2, Uid: "2"}
|
folder := &folder.Folder{ID: 2, UID: "2"}
|
||||||
dashboard := &models.Dashboard{Id: 1, FolderId: folder.Id, Uid: "1"}
|
dashboard := &models.Dashboard{Id: 1, FolderId: folder.ID, Uid: "1"}
|
||||||
|
|
||||||
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once()
|
store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once()
|
||||||
store.On("GetFolderByID", mock.Anything, orgID, folder.Id).Return(folder, nil).Once()
|
store.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once()
|
||||||
|
|
||||||
scope := ac.Scope("dashboards", "uid", dashboard.Uid)
|
scope := ac.Scope("dashboards", "uid", dashboard.Uid)
|
||||||
resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope)
|
resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, resolvedScopes, 2)
|
require.Len(t, resolvedScopes, 2)
|
||||||
require.Equal(t, fmt.Sprintf("dashboards:uid:%s", dashboard.Uid), resolvedScopes[0])
|
require.Equal(t, fmt.Sprintf("dashboards:uid:%s", dashboard.Uid), resolvedScopes[0])
|
||||||
require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.Uid), resolvedScopes[1])
|
require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.UID), resolvedScopes[1])
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
|
t.Run("resolver should fail if input scope is not expected", func(t *testing.T) {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DashboardService is a service for operating on dashboards.
|
// DashboardService is a service for operating on dashboards.
|
||||||
@ -89,9 +90,9 @@ type Store interface {
|
|||||||
//go:generate mockery --name FolderStore --structname FakeFolderStore --inpackage --filename folder_store_mock.go
|
//go:generate mockery --name FolderStore --structname FakeFolderStore --inpackage --filename folder_store_mock.go
|
||||||
type FolderStore interface {
|
type FolderStore interface {
|
||||||
// GetFolderByTitle retrieves a folder by its title
|
// GetFolderByTitle retrieves a folder by its title
|
||||||
GetFolderByTitle(ctx context.Context, orgID int64, title string) (*models.Folder, error)
|
GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error)
|
||||||
// GetFolderByUID retrieves a folder by its UID
|
// GetFolderByUID retrieves a folder by its UID
|
||||||
GetFolderByUID(ctx context.Context, orgID int64, uid string) (*models.Folder, error)
|
GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error)
|
||||||
// GetFolderByID retrieves a folder by its ID
|
// GetFolderByID retrieves a folder by its ID
|
||||||
GetFolderByID(ctx context.Context, orgID int64, id int64) (*models.Folder, error)
|
GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||||
@ -74,7 +75,7 @@ func (d *DashboardStore) ValidateDashboardBeforeSave(ctx context.Context, dashbo
|
|||||||
return isParentFolderChanged, nil
|
return isParentFolderChanged, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*models.Folder, error) {
|
func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error) {
|
||||||
if title == "" {
|
if title == "" {
|
||||||
return nil, dashboards.ErrFolderTitleEmpty
|
return nil, dashboards.ErrFolderTitleEmpty
|
||||||
}
|
}
|
||||||
@ -94,10 +95,10 @@ func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, titl
|
|||||||
dashboard.SetUid(dashboard.Uid)
|
dashboard.SetUid(dashboard.Uid)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return models.DashboardToFolder(&dashboard), err
|
return folder.FromDashboard(&dashboard), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*models.Folder, error) {
|
func (d *DashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error) {
|
||||||
dashboard := models.Dashboard{OrgId: orgID, FolderId: 0, Id: id}
|
dashboard := models.Dashboard{OrgId: orgID, FolderId: 0, Id: id}
|
||||||
err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||||
has, err := sess.Table(&models.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)).Where("folder_id=0").Get(&dashboard)
|
has, err := sess.Table(&models.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)).Where("folder_id=0").Get(&dashboard)
|
||||||
@ -114,10 +115,10 @@ func (d *DashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int6
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return models.DashboardToFolder(&dashboard), nil
|
return folder.FromDashboard(&dashboard), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*models.Folder, error) {
|
func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error) {
|
||||||
if uid == "" {
|
if uid == "" {
|
||||||
return nil, dashboards.ErrDashboardIdentifierNotSet
|
return nil, dashboards.ErrDashboardIdentifierNotSet
|
||||||
}
|
}
|
||||||
@ -138,7 +139,7 @@ func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid st
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return models.DashboardToFolder(&dashboard), nil
|
return folder.FromDashboard(&dashboard), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DashboardStore) GetProvisionedDataByDashboardID(ctx context.Context, dashboardID int64) (*models.DashboardProvisioning, error) {
|
func (d *DashboardStore) GetProvisionedDataByDashboardID(ctx context.Context, dashboardID int64) (*models.DashboardProvisioning, error) {
|
||||||
|
@ -481,7 +481,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
|||||||
t.Run("GetFolderByTitle should find the folder", func(t *testing.T) {
|
t.Run("GetFolderByTitle should find the folder", func(t *testing.T) {
|
||||||
result, err := dashboardStore.GetFolderByTitle(context.Background(), orgId, title)
|
result, err := dashboardStore.GetFolderByTitle(context.Background(), orgId, title)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, folder1.Id, result.Id)
|
require.Equal(t, folder1.Id, result.ID)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -494,7 +494,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("should return folder by UID", func(t *testing.T) {
|
t.Run("should return folder by UID", func(t *testing.T) {
|
||||||
d, err := dashboardStore.GetFolderByUID(context.Background(), orgId, folder.Uid)
|
d, err := dashboardStore.GetFolderByUID(context.Background(), orgId, folder.Uid)
|
||||||
require.Equal(t, folder.Id, d.Id)
|
require.Equal(t, folder.Id, d.ID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
t.Run("should not find dashboard", func(t *testing.T) {
|
t.Run("should not find dashboard", func(t *testing.T) {
|
||||||
@ -518,7 +518,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("should return folder by ID", func(t *testing.T) {
|
t.Run("should return folder by ID", func(t *testing.T) {
|
||||||
d, err := dashboardStore.GetFolderByID(context.Background(), orgId, folder.Id)
|
d, err := dashboardStore.GetFolderByID(context.Background(), orgId, folder.Id)
|
||||||
require.Equal(t, folder.Id, d.Id)
|
require.Equal(t, folder.Id, d.ID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
t.Run("should not find dashboard", func(t *testing.T) {
|
t.Run("should not find dashboard", func(t *testing.T) {
|
||||||
|
@ -600,5 +600,5 @@ func (dr DashboardServiceImpl) CountDashboardsInFolder(ctx context.Context, quer
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.CountDashboardsInFolder(ctx, &dashboards.CountDashboardsInFolderRequest{FolderID: folder.Id, OrgID: u.OrgID})
|
return dr.dashboardStore.CountDashboardsInFolder(ctx, &dashboards.CountDashboardsInFolderRequest{FolderID: folder.ID, OrgID: u.OrgID})
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"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/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@ -226,7 +227,7 @@ func TestDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Count dashboards in folder", func(t *testing.T) {
|
t.Run("Count dashboards in folder", func(t *testing.T) {
|
||||||
fakeStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&models.Folder{}, nil)
|
fakeStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
|
||||||
fakeStore.On("CountDashboardsInFolder", mock.Anything, mock.AnythingOfType("*dashboards.CountDashboardsInFolderRequest")).Return(int64(3), nil)
|
fakeStore.On("CountDashboardsInFolder", mock.Anything, mock.AnythingOfType("*dashboards.CountDashboardsInFolderRequest")).Return(int64(3), nil)
|
||||||
|
|
||||||
// set up a ctx with signed in user
|
// set up a ctx with signed in user
|
||||||
|
@ -5,6 +5,7 @@ package dashboards
|
|||||||
import (
|
import (
|
||||||
context "context"
|
context "context"
|
||||||
|
|
||||||
|
folder "github.com/grafana/grafana/pkg/services/folder"
|
||||||
models "github.com/grafana/grafana/pkg/models"
|
models "github.com/grafana/grafana/pkg/models"
|
||||||
mock "github.com/stretchr/testify/mock"
|
mock "github.com/stretchr/testify/mock"
|
||||||
)
|
)
|
||||||
@ -194,15 +195,15 @@ func (_m *FakeDashboardStore) GetDashboardsByPluginID(ctx context.Context, query
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetFolderByID provides a mock function with given fields: ctx, orgID, id
|
// GetFolderByID provides a mock function with given fields: ctx, orgID, id
|
||||||
func (_m *FakeDashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*models.Folder, error) {
|
func (_m *FakeDashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error) {
|
||||||
ret := _m.Called(ctx, orgID, id)
|
ret := _m.Called(ctx, orgID, id)
|
||||||
|
|
||||||
var r0 *models.Folder
|
var r0 *folder.Folder
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, int64, int64) *models.Folder); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, int64, int64) *folder.Folder); ok {
|
||||||
r0 = rf(ctx, orgID, id)
|
r0 = rf(ctx, orgID, id)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).(*models.Folder)
|
r0 = ret.Get(0).(*folder.Folder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,15 +218,15 @@ func (_m *FakeDashboardStore) GetFolderByID(ctx context.Context, orgID int64, id
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetFolderByTitle provides a mock function with given fields: ctx, orgID, title
|
// GetFolderByTitle provides a mock function with given fields: ctx, orgID, title
|
||||||
func (_m *FakeDashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*models.Folder, error) {
|
func (_m *FakeDashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error) {
|
||||||
ret := _m.Called(ctx, orgID, title)
|
ret := _m.Called(ctx, orgID, title)
|
||||||
|
|
||||||
var r0 *models.Folder
|
var r0 *folder.Folder
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, int64, string) *models.Folder); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, int64, string) *folder.Folder); ok {
|
||||||
r0 = rf(ctx, orgID, title)
|
r0 = rf(ctx, orgID, title)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).(*models.Folder)
|
r0 = ret.Get(0).(*folder.Folder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,15 +241,15 @@ func (_m *FakeDashboardStore) GetFolderByTitle(ctx context.Context, orgID int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetFolderByUID provides a mock function with given fields: ctx, orgID, uid
|
// GetFolderByUID provides a mock function with given fields: ctx, orgID, uid
|
||||||
func (_m *FakeDashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*models.Folder, error) {
|
func (_m *FakeDashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error) {
|
||||||
ret := _m.Called(ctx, orgID, uid)
|
ret := _m.Called(ctx, orgID, uid)
|
||||||
|
|
||||||
var r0 *models.Folder
|
var r0 *folder.Folder
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, int64, string) *models.Folder); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, int64, string) *folder.Folder); ok {
|
||||||
r0 = rf(ctx, orgID, uid)
|
r0 = rf(ctx, orgID, uid)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).(*models.Folder)
|
r0 = ret.Get(0).(*folder.Folder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ type Service struct {
|
|||||||
searchService *search.SearchService
|
searchService *search.SearchService
|
||||||
features *featuremgmt.FeatureManager
|
features *featuremgmt.FeatureManager
|
||||||
permissions accesscontrol.FolderPermissionsService
|
permissions accesscontrol.FolderPermissionsService
|
||||||
|
accessControl accesscontrol.AccessControl
|
||||||
|
|
||||||
// bus is currently used to publish events that cause scheduler to update rules.
|
// bus is currently used to publish events that cause scheduler to update rules.
|
||||||
bus bus.Bus
|
bus bus.Bus
|
||||||
@ -62,10 +63,41 @@ func ProvideService(
|
|||||||
searchService: searchService,
|
searchService: searchService,
|
||||||
features: features,
|
features: features,
|
||||||
permissions: folderPermissionsService,
|
permissions: folderPermissionsService,
|
||||||
|
accessControl: ac,
|
||||||
bus: bus,
|
bus: bus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.Folder, error) {
|
||||||
|
user, err := appcontext.User(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.cfg.IsFeatureToggleEnabled(featuremgmt.FlagNestedFolders) {
|
||||||
|
if ok, err := s.accessControl.Evaluate(ctx, user, accesscontrol.EvalPermission(
|
||||||
|
dashboards.ActionFoldersRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(*cmd.UID),
|
||||||
|
)); !ok {
|
||||||
|
if err != nil {
|
||||||
|
return nil, toFolderError(err)
|
||||||
|
}
|
||||||
|
return nil, dashboards.ErrFolderAccessDenied
|
||||||
|
}
|
||||||
|
return s.store.Get(ctx, *cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case cmd.UID != nil:
|
||||||
|
return s.getFolderByUID(ctx, user, cmd.OrgID, *cmd.UID)
|
||||||
|
case cmd.ID != nil:
|
||||||
|
return s.getFolderByID(ctx, user, *cmd.ID, cmd.OrgID)
|
||||||
|
case cmd.Title != nil:
|
||||||
|
return s.getFolderByTitle(ctx, user, cmd.OrgID, *cmd.Title)
|
||||||
|
default:
|
||||||
|
return nil, folder.ErrBadRequest.Errorf("either on of UID, ID, Title fields must be present")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) {
|
func (s *Service) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) {
|
||||||
searchQuery := search.Query{
|
searchQuery := search.Query{
|
||||||
SignedInUser: user,
|
SignedInUser: user,
|
||||||
@ -95,9 +127,9 @@ func (s *Service) GetFolders(ctx context.Context, user *user.SignedInUser, orgID
|
|||||||
return folders, nil
|
return folders, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error) {
|
func (s *Service) getFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*folder.Folder, error) {
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
return &models.Folder{Id: id, Title: "General"}, nil
|
return &folder.Folder{ID: id, Title: "General"}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dashFolder, err := s.dashboardStore.GetFolderByID(ctx, orgID, id)
|
dashFolder, err := s.dashboardStore.GetFolderByID(ctx, orgID, id)
|
||||||
@ -105,7 +137,7 @@ func (s *Service) GetFolderByID(ctx context.Context, user *user.SignedInUser, id
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
g := guardian.New(ctx, dashFolder.Id, orgID, user)
|
g := guardian.New(ctx, dashFolder.ID, orgID, user)
|
||||||
if canView, err := g.CanView(); err != nil || !canView {
|
if canView, err := g.CanView(); err != nil || !canView {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
@ -116,13 +148,13 @@ func (s *Service) GetFolderByID(ctx context.Context, user *user.SignedInUser, id
|
|||||||
return dashFolder, nil
|
return dashFolder, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) {
|
func (s *Service) getFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*folder.Folder, error) {
|
||||||
dashFolder, err := s.dashboardStore.GetFolderByUID(ctx, orgID, uid)
|
dashFolder, err := s.dashboardStore.GetFolderByUID(ctx, orgID, uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
g := guardian.New(ctx, dashFolder.Id, orgID, user)
|
g := guardian.New(ctx, dashFolder.ID, orgID, user)
|
||||||
if canView, err := g.CanView(); err != nil || !canView {
|
if canView, err := g.CanView(); err != nil || !canView {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
@ -133,13 +165,13 @@ func (s *Service) GetFolderByUID(ctx context.Context, user *user.SignedInUser, o
|
|||||||
return dashFolder, nil
|
return dashFolder, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) GetFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*models.Folder, error) {
|
func (s *Service) getFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*folder.Folder, error) {
|
||||||
dashFolder, err := s.dashboardStore.GetFolderByTitle(ctx, orgID, title)
|
dashFolder, err := s.dashboardStore.GetFolderByTitle(ctx, orgID, title)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
g := guardian.New(ctx, dashFolder.Id, orgID, user)
|
g := guardian.New(ctx, dashFolder.ID, orgID, user)
|
||||||
if canView, err := g.CanView(); err != nil || !canView {
|
if canView, err := g.CanView(); err != nil || !canView {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
@ -189,7 +221,7 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
|
|||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var createdFolder *models.Folder
|
var createdFolder *folder.Folder
|
||||||
createdFolder, err = s.dashboardStore.GetFolderByID(ctx, cmd.OrgID, dash.Id)
|
createdFolder, err = s.dashboardStore.GetFolderByID(ctx, cmd.OrgID, dash.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -209,9 +241,9 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
|
|||||||
{BuiltinRole: string(org.RoleViewer), Permission: models.PERMISSION_VIEW.String()},
|
{BuiltinRole: string(org.RoleViewer), Permission: models.PERMISSION_VIEW.String()},
|
||||||
}...)
|
}...)
|
||||||
|
|
||||||
_, permissionErr = s.permissions.SetPermissions(ctx, cmd.OrgID, createdFolder.Uid, permissions...)
|
_, permissionErr = s.permissions.SetPermissions(ctx, cmd.OrgID, createdFolder.UID, permissions...)
|
||||||
} else if s.cfg.EditorsCanAdmin && user.IsRealUser() && !user.IsAnonymous {
|
} else if s.cfg.EditorsCanAdmin && user.IsRealUser() && !user.IsAnonymous {
|
||||||
permissionErr = s.MakeUserAdmin(ctx, cmd.OrgID, userID, createdFolder.Id, true)
|
permissionErr = s.MakeUserAdmin(ctx, cmd.OrgID, userID, createdFolder.ID, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if permissionErr != nil {
|
if permissionErr != nil {
|
||||||
@ -242,7 +274,7 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
|
|||||||
// We'll log the error and also roll back the previously-created
|
// We'll log the error and also roll back the previously-created
|
||||||
// (legacy) folder.
|
// (legacy) folder.
|
||||||
s.log.Error("error saving folder to nested folder store", err)
|
s.log.Error("error saving folder to nested folder store", err)
|
||||||
err = s.DeleteFolder(ctx, &folder.DeleteFolderCommand{UID: createdFolder.Uid, OrgID: cmd.OrgID, ForceDeleteRules: true})
|
err = s.DeleteFolder(ctx, &folder.DeleteFolderCommand{UID: createdFolder.UID, OrgID: cmd.OrgID, ForceDeleteRules: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Error("error deleting folder after failed save to nested folder store", err)
|
s.log.Error("error deleting folder after failed save to nested folder store", err)
|
||||||
}
|
}
|
||||||
@ -254,7 +286,7 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
|
|||||||
return folder.FromDashboard(dash), nil
|
return folder.FromDashboard(dash), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*models.Folder, error) {
|
func (s *Service) Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*folder.Folder, error) {
|
||||||
foldr, err := s.legacyUpdate(ctx, user, orgID, existingUid, cmd)
|
foldr, err := s.legacyUpdate(ctx, user, orgID, existingUid, cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -276,7 +308,7 @@ func (s *Service) Update(ctx context.Context, user *user.SignedInUser, orgID int
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = s.store.Update(ctx, folder.UpdateFolderCommand{
|
foldr, err := s.store.Update(ctx, folder.UpdateFolderCommand{
|
||||||
Folder: getFolder,
|
Folder: getFolder,
|
||||||
NewUID: &cmd.Uid,
|
NewUID: &cmd.Uid,
|
||||||
NewTitle: &cmd.Title,
|
NewTitle: &cmd.Title,
|
||||||
@ -285,11 +317,12 @@ func (s *Service) Update(ctx context.Context, user *user.SignedInUser, orgID int
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return foldr, nil
|
||||||
}
|
}
|
||||||
return foldr, nil
|
return foldr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) legacyUpdate(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*models.Folder, error) {
|
func (s *Service) legacyUpdate(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*folder.Folder, error) {
|
||||||
query := models.GetDashboardQuery{OrgId: orgID, Uid: existingUid}
|
query := models.GetDashboardQuery{OrgId: orgID, Uid: existingUid}
|
||||||
_, err := s.dashboardStore.GetDashboard(ctx, &query)
|
_, err := s.dashboardStore.GetDashboard(ctx, &query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -322,7 +355,7 @@ func (s *Service) legacyUpdate(ctx context.Context, user *user.SignedInUser, org
|
|||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var foldr *models.Folder
|
var foldr *folder.Folder
|
||||||
foldr, err = s.dashboardStore.GetFolderByID(ctx, orgID, dash.Id)
|
foldr, err = s.dashboardStore.GetFolderByID(ctx, orgID, dash.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -359,7 +392,7 @@ func (s *Service) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderComm
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
guard := guardian.New(ctx, dashFolder.Id, cmd.OrgID, user)
|
guard := guardian.New(ctx, dashFolder.ID, cmd.OrgID, user)
|
||||||
if canSave, err := guard.CanDelete(); err != nil || !canSave {
|
if canSave, err := guard.CanDelete(); err != nil || !canSave {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
@ -367,7 +400,7 @@ func (s *Service) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderComm
|
|||||||
return dashboards.ErrFolderAccessDenied
|
return dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteCmd := models.DeleteDashboardCommand{OrgId: cmd.OrgID, Id: dashFolder.Id, ForceDeleteFolderRules: cmd.ForceDeleteRules}
|
deleteCmd := models.DeleteDashboardCommand{OrgId: cmd.OrgID, Id: dashFolder.ID, ForceDeleteFolderRules: cmd.ForceDeleteRules}
|
||||||
|
|
||||||
if err := s.dashboardStore.DeleteDashboard(ctx, &deleteCmd); err != nil {
|
if err := s.dashboardStore.DeleteDashboard(ctx, &deleteCmd); err != nil {
|
||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
@ -416,12 +449,6 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.Folder, error) {
|
|
||||||
// check the flag, if old - do whatever did before
|
|
||||||
// for new only the store
|
|
||||||
return s.store.Get(ctx, *cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetParents(ctx context.Context, cmd *folder.GetParentsQuery) ([]*folder.Folder, error) {
|
func (s *Service) GetParents(ctx context.Context, cmd *folder.GetParentsQuery) ([]*folder.Folder, error) {
|
||||||
// check the flag, if old - do whatever did before
|
// check the flag, if old - do whatever did before
|
||||||
// for new only the store
|
// for new only the store
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
dashboardsvc "github.com/grafana/grafana/pkg/services/dashboards/service"
|
dashboardsvc "github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||||
@ -79,26 +80,26 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
folderId := rand.Int63()
|
folderId := rand.Int63()
|
||||||
folderUID := util.GenerateShortUID()
|
folderUID := util.GenerateShortUID()
|
||||||
|
|
||||||
f := models.NewFolder("Folder")
|
f := folder.NewFolder("Folder", "")
|
||||||
f.Id = folderId
|
f.ID = folderId
|
||||||
f.Uid = folderUID
|
f.UID = folderUID
|
||||||
|
|
||||||
dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(f, nil)
|
dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(f, nil)
|
||||||
dashStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).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) {
|
t.Run("When get folder by id should return access denied error", func(t *testing.T) {
|
||||||
_, err := service.GetFolderByID(context.Background(), usr, folderId, orgID)
|
_, err := service.getFolderByID(context.Background(), usr, folderId, orgID)
|
||||||
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When get folder by id, with id = 0 should return default folder", func(t *testing.T) {
|
t.Run("When get folder by id, with id = 0 should return default folder", func(t *testing.T) {
|
||||||
folder, err := service.GetFolderByID(context.Background(), usr, 0, orgID)
|
foldr, err := service.getFolderByID(context.Background(), usr, 0, orgID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, folder, &models.Folder{Id: 0, Title: "General"})
|
require.Equal(t, foldr, &folder.Folder{ID: 0, Title: "General"})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When get folder by uid should return access denied error", func(t *testing.T) {
|
t.Run("When get folder by uid should return access denied error", func(t *testing.T) {
|
||||||
_, err := service.GetFolderByUID(context.Background(), usr, orgID, folderUID)
|
_, err := service.getFolderByUID(context.Background(), usr, orgID, folderUID)
|
||||||
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
t.Run("When creating folder should not return access denied error", func(t *testing.T) {
|
t.Run("When creating folder should not return access denied error", func(t *testing.T) {
|
||||||
dash := models.NewDashboardFolder("Test-Folder")
|
dash := models.NewDashboardFolder("Test-Folder")
|
||||||
dash.Id = rand.Int63()
|
dash.Id = rand.Int63()
|
||||||
f := models.DashboardToFolder(dash)
|
f := folder.FromDashboard(dash)
|
||||||
|
|
||||||
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*models.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
|
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*models.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
|
||||||
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(dash, nil).Once()
|
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(dash, nil).Once()
|
||||||
@ -170,7 +171,7 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
UID: "someuid",
|
UID: "someuid",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, f, actualFolder.ToLegacyModel())
|
require.Equal(t, f, actualFolder)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When creating folder should return error if uid is general", func(t *testing.T) {
|
t.Run("When creating folder should return error if uid is general", func(t *testing.T) {
|
||||||
@ -190,7 +191,7 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
dashboardFolder := models.NewDashboardFolder("Folder")
|
dashboardFolder := models.NewDashboardFolder("Folder")
|
||||||
dashboardFolder.Id = rand.Int63()
|
dashboardFolder.Id = rand.Int63()
|
||||||
dashboardFolder.Uid = util.GenerateShortUID()
|
dashboardFolder.Uid = util.GenerateShortUID()
|
||||||
f := models.DashboardToFolder(dashboardFolder)
|
f := folder.FromDashboard(dashboardFolder)
|
||||||
|
|
||||||
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*models.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
|
dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*models.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil)
|
||||||
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(dashboardFolder, nil)
|
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(dashboardFolder, nil)
|
||||||
@ -207,10 +208,10 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When deleting folder by uid should not return access denied error", func(t *testing.T) {
|
t.Run("When deleting folder by uid should not return access denied error", func(t *testing.T) {
|
||||||
f := models.NewFolder(util.GenerateShortUID())
|
f := folder.NewFolder(util.GenerateShortUID(), "")
|
||||||
f.Id = rand.Int63()
|
f.ID = rand.Int63()
|
||||||
f.Uid = util.GenerateShortUID()
|
f.UID = util.GenerateShortUID()
|
||||||
dashStore.On("GetFolderByUID", mock.Anything, orgID, f.Uid).Return(f, nil)
|
dashStore.On("GetFolderByUID", mock.Anything, orgID, f.UID).Return(f, nil)
|
||||||
|
|
||||||
var actualCmd *models.DeleteDashboardCommand
|
var actualCmd *models.DeleteDashboardCommand
|
||||||
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||||
@ -221,13 +222,13 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = appcontext.WithUser(ctx, usr)
|
ctx = appcontext.WithUser(ctx, usr)
|
||||||
err := service.DeleteFolder(ctx, &folder.DeleteFolderCommand{
|
err := service.DeleteFolder(ctx, &folder.DeleteFolderCommand{
|
||||||
UID: f.Uid,
|
UID: f.UID,
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
ForceDeleteRules: expectedForceDeleteRules,
|
ForceDeleteRules: expectedForceDeleteRules,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, actualCmd)
|
require.NotNil(t, actualCmd)
|
||||||
require.Equal(t, f.Id, actualCmd.Id)
|
require.Equal(t, f.ID, actualCmd.Id)
|
||||||
require.Equal(t, orgID, actualCmd.OrgId)
|
require.Equal(t, orgID, actualCmd.OrgId)
|
||||||
require.Equal(t, expectedForceDeleteRules, actualCmd.ForceDeleteFolderRules)
|
require.Equal(t, expectedForceDeleteRules, actualCmd.ForceDeleteFolderRules)
|
||||||
})
|
})
|
||||||
@ -242,33 +243,33 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true})
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true})
|
||||||
|
|
||||||
t.Run("When get folder by id should return folder", func(t *testing.T) {
|
t.Run("When get folder by id should return folder", func(t *testing.T) {
|
||||||
expected := models.NewFolder(util.GenerateShortUID())
|
expected := folder.NewFolder(util.GenerateShortUID(), "")
|
||||||
expected.Id = rand.Int63()
|
expected.ID = rand.Int63()
|
||||||
|
|
||||||
dashStore.On("GetFolderByID", mock.Anything, orgID, expected.Id).Return(expected, nil)
|
dashStore.On("GetFolderByID", mock.Anything, orgID, expected.ID).Return(expected, nil)
|
||||||
|
|
||||||
actual, err := service.GetFolderByID(context.Background(), usr, expected.Id, orgID)
|
actual, err := service.getFolderByID(context.Background(), usr, expected.ID, orgID)
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When get folder by uid should return folder", func(t *testing.T) {
|
t.Run("When get folder by uid should return folder", func(t *testing.T) {
|
||||||
expected := models.NewFolder(util.GenerateShortUID())
|
expected := folder.NewFolder(util.GenerateShortUID(), "")
|
||||||
expected.Uid = util.GenerateShortUID()
|
expected.UID = util.GenerateShortUID()
|
||||||
|
|
||||||
dashStore.On("GetFolderByUID", mock.Anything, orgID, expected.Uid).Return(expected, nil)
|
dashStore.On("GetFolderByUID", mock.Anything, orgID, expected.UID).Return(expected, nil)
|
||||||
|
|
||||||
actual, err := service.GetFolderByUID(context.Background(), usr, orgID, expected.Uid)
|
actual, err := service.getFolderByUID(context.Background(), usr, orgID, expected.UID)
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When get folder by title should return folder", func(t *testing.T) {
|
t.Run("When get folder by title should return folder", func(t *testing.T) {
|
||||||
expected := models.NewFolder("TEST-" + util.GenerateShortUID())
|
expected := folder.NewFolder("TEST-"+util.GenerateShortUID(), "")
|
||||||
|
|
||||||
dashStore.On("GetFolderByTitle", mock.Anything, orgID, expected.Title).Return(expected, nil)
|
dashStore.On("GetFolderByTitle", mock.Anything, orgID, expected.Title).Return(expected, nil)
|
||||||
|
|
||||||
actual, err := service.GetFolderByTitle(context.Background(), usr, orgID, expected.Title)
|
actual, err := service.getFolderByTitle(context.Background(), usr, orgID, expected.Title)
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
@ -311,7 +312,7 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
|
|||||||
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil)
|
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil)
|
||||||
dashStore := dashboards.FakeDashboardStore{}
|
dashStore := dashboards.FakeDashboardStore{}
|
||||||
dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, 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)
|
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
cfg.RBACEnabled = false
|
cfg.RBACEnabled = false
|
||||||
nestedFoldersEnabled := true
|
nestedFoldersEnabled := true
|
||||||
@ -338,18 +339,6 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
|
|||||||
require.NotNil(t, res.UID)
|
require.NotNil(t, res.UID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("delete folder", func(t *testing.T) {
|
|
||||||
folderStore.ExpectedFolder = &folder.Folder{}
|
|
||||||
err := folderService.Delete(context.Background(), &folder.DeleteFolderCommand{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("get folder", func(t *testing.T) {
|
|
||||||
folderStore.ExpectedFolder = &folder.Folder{}
|
|
||||||
_, err := folderService.Get(context.Background(), &folder.GetFolderQuery{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("get parents folder", func(t *testing.T) {
|
t.Run("get parents folder", func(t *testing.T) {
|
||||||
folderStore.ExpectedFolder = &folder.Folder{}
|
folderStore.ExpectedFolder = &folder.Folder{}
|
||||||
_, err := folderService.GetParents(context.Background(), &folder.GetParentsQuery{})
|
_, err := folderService.GetParents(context.Background(), &folder.GetParentsQuery{})
|
||||||
@ -378,12 +367,6 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 4, len(res))
|
require.Equal(t, 4, len(res))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("move folder", func(t *testing.T) {
|
|
||||||
folderStore.ExpectedFolder = &folder.Folder{}
|
|
||||||
_, err := folderService.Move(context.Background(), &folder.MoveFolderCommand{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNestedFolderService(t *testing.T) {
|
func TestNestedFolderService(t *testing.T) {
|
||||||
@ -412,7 +395,7 @@ func TestNestedFolderService(t *testing.T) {
|
|||||||
mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"),
|
mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"),
|
||||||
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil)
|
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("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)
|
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
|
||||||
|
|
||||||
ctx = appcontext.WithUser(ctx, usr)
|
ctx = appcontext.WithUser(ctx, usr)
|
||||||
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
|
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
|
||||||
@ -430,7 +413,7 @@ func TestNestedFolderService(t *testing.T) {
|
|||||||
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||||
actualCmd = args.Get(1).(*models.DeleteDashboardCommand)
|
actualCmd = args.Get(1).(*models.DeleteDashboardCommand)
|
||||||
}).Return(nil).Once()
|
}).Return(nil).Once()
|
||||||
dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&models.Folder{}, nil)
|
dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
|
||||||
|
|
||||||
g := guardian.New
|
g := guardian.New
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
||||||
@ -463,6 +446,9 @@ func TestNestedFolderService(t *testing.T) {
|
|||||||
dashboardStore: dashStore,
|
dashboardStore: dashStore,
|
||||||
store: store,
|
store: store,
|
||||||
features: features,
|
features: features,
|
||||||
|
accessControl: actest.FakeAccessControl{
|
||||||
|
ExpectedEvaluate: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("create, no error", func(t *testing.T) {
|
t.Run("create, no error", func(t *testing.T) {
|
||||||
@ -471,7 +457,7 @@ func TestNestedFolderService(t *testing.T) {
|
|||||||
mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"),
|
mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"),
|
||||||
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil)
|
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("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)
|
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
|
||||||
ctx = appcontext.WithUser(ctx, usr)
|
ctx = appcontext.WithUser(ctx, usr)
|
||||||
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
|
_, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
@ -493,8 +479,8 @@ func TestNestedFolderService(t *testing.T) {
|
|||||||
mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"),
|
mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"),
|
||||||
mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil)
|
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("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)
|
dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
|
||||||
dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&models.Folder{}, nil)
|
dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
|
||||||
|
|
||||||
// return an error from the folder store
|
// return an error from the folder store
|
||||||
store.ExpectedError = errors.New("FAILED")
|
store.ExpectedError = errors.New("FAILED")
|
||||||
|
@ -14,29 +14,25 @@ type FakeService struct {
|
|||||||
ExpectedError error
|
ExpectedError error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewFakeService() *FakeService {
|
||||||
|
return &FakeService{}
|
||||||
|
}
|
||||||
|
|
||||||
var _ folder.Service = (*FakeService)(nil)
|
var _ folder.Service = (*FakeService)(nil)
|
||||||
|
|
||||||
func (s *FakeService) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) {
|
func (s *FakeService) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) {
|
||||||
return s.ExpectedFolders, s.ExpectedError
|
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.ToLegacyModel(), s.ExpectedError
|
|
||||||
}
|
|
||||||
func (s *FakeService) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) {
|
|
||||||
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.ToLegacyModel(), s.ExpectedError
|
|
||||||
}
|
|
||||||
func (s *FakeService) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
|
func (s *FakeService) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
|
||||||
return s.ExpectedFolder, s.ExpectedError
|
return s.ExpectedFolder, s.ExpectedError
|
||||||
}
|
}
|
||||||
func (s *FakeService) Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*models.Folder, error) {
|
func (s *FakeService) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.Folder, error) {
|
||||||
|
return s.ExpectedFolder, s.ExpectedError
|
||||||
|
}
|
||||||
|
func (s *FakeService) Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*folder.Folder, error) {
|
||||||
cmd.Result = s.ExpectedFolder.ToLegacyModel()
|
cmd.Result = s.ExpectedFolder.ToLegacyModel()
|
||||||
return s.ExpectedFolder.ToLegacyModel(), s.ExpectedError
|
return s.ExpectedFolder, s.ExpectedError
|
||||||
}
|
}
|
||||||
func (s *FakeService) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
|
func (s *FakeService) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
|
||||||
return s.ExpectedError
|
return s.ExpectedError
|
||||||
|
@ -9,13 +9,18 @@ import (
|
|||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error)
|
GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error)
|
||||||
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)
|
|
||||||
Create(ctx context.Context, cmd *CreateFolderCommand) (*Folder, error)
|
Create(ctx context.Context, cmd *CreateFolderCommand) (*Folder, error)
|
||||||
|
|
||||||
|
// GetFolder takes a GetFolderCommand and returns a folder matching the
|
||||||
|
// request. One of ID, UID, or Title must be included. If multiple values
|
||||||
|
// are included in the request, Grafana will select one in order of
|
||||||
|
// specificity (ID, UID, Title).
|
||||||
|
Get(ctx context.Context, cmd *GetFolderQuery) (*Folder, error)
|
||||||
|
|
||||||
// Update is used to update a folder's UID, Title and Description. To change
|
// Update is used to update a folder's UID, Title and Description. To change
|
||||||
// a folder's parent folder, use Move.
|
// a folder's parent folder, use Move.
|
||||||
Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*models.Folder, error)
|
Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*Folder, error)
|
||||||
DeleteFolder(ctx context.Context, cmd *DeleteFolderCommand) error
|
DeleteFolder(ctx context.Context, cmd *DeleteFolderCommand) error
|
||||||
MakeUserAdmin(ctx context.Context, orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error
|
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 changes a folder's parent folder to the requested new parent.
|
||||||
@ -35,12 +40,6 @@ type NestedFolderService interface {
|
|||||||
// dashboards in the folder.
|
// dashboards in the folder.
|
||||||
Delete(ctx context.Context, cmd *DeleteFolderCommand) (*Folder, error)
|
Delete(ctx context.Context, cmd *DeleteFolderCommand) (*Folder, error)
|
||||||
|
|
||||||
// GetFolder takes a GetFolderCommand and returns a folder matching the
|
|
||||||
// request. One of ID, UID, or Title must be included. If multiple values
|
|
||||||
// are included in the request, Grafana will select one in order of
|
|
||||||
// specificity (ID, UID, Title).
|
|
||||||
Get(ctx context.Context, cmd *GetFolderQuery) (*Folder, error)
|
|
||||||
|
|
||||||
// GetParents returns an ordered list of parent folders for the given
|
// GetParents returns an ordered list of parent folders for the given
|
||||||
// folder, starting with the root node and ending with the requested child
|
// folder, starting with the root node and ending with the requested child
|
||||||
// node.
|
// node.
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/middleware"
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,11 +48,11 @@ func (l *LibraryElementService) createHandler(c *models.ReqContext) response.Res
|
|||||||
if *cmd.FolderUID == "" {
|
if *cmd.FolderUID == "" {
|
||||||
cmd.FolderID = 0
|
cmd.FolderID = 0
|
||||||
} else {
|
} else {
|
||||||
folder, err := l.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, *cmd.FolderUID)
|
folder, err := l.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: cmd.FolderUID})
|
||||||
if err != nil || folder == nil {
|
if err != nil || folder == nil {
|
||||||
return response.Error(http.StatusBadRequest, "failed to get folder", err)
|
return response.Error(http.StatusBadRequest, "failed to get folder", err)
|
||||||
}
|
}
|
||||||
cmd.FolderID = folder.Id
|
cmd.FolderID = folder.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,12 +62,12 @@ func (l *LibraryElementService) createHandler(c *models.ReqContext) response.Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
if element.FolderID != 0 {
|
if element.FolderID != 0 {
|
||||||
folder, err := l.folderService.GetFolderByID(c.Req.Context(), c.SignedInUser, element.FolderID, c.OrgID)
|
folder, err := l.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, ID: &element.FolderID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "failed to get folder", err)
|
return response.Error(http.StatusInternalServerError, "failed to get folder", err)
|
||||||
}
|
}
|
||||||
element.FolderUID = folder.Uid
|
element.FolderUID = folder.UID
|
||||||
element.Meta.FolderUID = folder.Uid
|
element.Meta.FolderUID = folder.UID
|
||||||
element.Meta.FolderName = folder.Title
|
element.Meta.FolderName = folder.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,11 +176,11 @@ func (l *LibraryElementService) patchHandler(c *models.ReqContext) response.Resp
|
|||||||
if *cmd.FolderUID == "" {
|
if *cmd.FolderUID == "" {
|
||||||
cmd.FolderID = 0
|
cmd.FolderID = 0
|
||||||
} else {
|
} else {
|
||||||
folder, err := l.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, *cmd.FolderUID)
|
folder, err := l.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: cmd.FolderUID})
|
||||||
if err != nil || folder == nil {
|
if err != nil || folder == nil {
|
||||||
return response.Error(http.StatusBadRequest, "failed to get folder", err)
|
return response.Error(http.StatusBadRequest, "failed to get folder", err)
|
||||||
}
|
}
|
||||||
cmd.FolderID = folder.Id
|
cmd.FolderID = folder.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,12 +190,12 @@ func (l *LibraryElementService) patchHandler(c *models.ReqContext) response.Resp
|
|||||||
}
|
}
|
||||||
|
|
||||||
if element.FolderID != 0 {
|
if element.FolderID != 0 {
|
||||||
folder, err := l.folderService.GetFolderByID(c.Req.Context(), c.SignedInUser, element.FolderID, c.OrgID)
|
folder, err := l.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, ID: &element.FolderID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "failed to get folder", err)
|
return response.Error(http.StatusInternalServerError, "failed to get folder", err)
|
||||||
}
|
}
|
||||||
element.FolderUID = folder.Uid
|
element.FolderUID = folder.UID
|
||||||
element.Meta.FolderUID = folder.Uid
|
element.Meta.FolderUID = folder.UID
|
||||||
element.Meta.FolderName = folder.Title
|
element.Meta.FolderName = folder.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"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/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
@ -39,12 +40,12 @@ func (l *LibraryElementService) requireEditPermissionsOnFolder(ctx context.Conte
|
|||||||
if isGeneralFolder(folderID) && user.HasRole(org.RoleViewer) {
|
if isGeneralFolder(folderID) && user.HasRole(org.RoleViewer) {
|
||||||
return dashboards.ErrFolderAccessDenied
|
return dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
folder, err := l.folderService.GetFolderByID(ctx, user, folderID, user.OrgID)
|
folder, err := l.folderService.Get(ctx, &folder.GetFolderQuery{ID: &folderID, OrgID: user.OrgID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
g := guardian.New(ctx, folder.Id, user.OrgID, user)
|
g := guardian.New(ctx, folder.ID, user.OrgID, user)
|
||||||
|
|
||||||
canEdit, err := g.CanEdit()
|
canEdit, err := g.CanEdit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -62,12 +63,12 @@ func (l *LibraryElementService) requireViewPermissionsOnFolder(ctx context.Conte
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
folder, err := l.folderService.GetFolderByID(ctx, user, folderID, user.OrgID)
|
folder, err := l.folderService.Get(ctx, &folder.GetFolderQuery{ID: &folderID, OrgID: user.OrgID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
g := guardian.New(ctx, folder.Id, user.OrgID, user)
|
g := guardian.New(ctx, folder.ID, user.OrgID, user)
|
||||||
|
|
||||||
canView, err := g.CanView()
|
canView, err := g.CanView()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
func TestCreateLibraryElement(t *testing.T) {
|
func TestCreateLibraryElement(t *testing.T) {
|
||||||
scenarioWithPanel(t, "When an admin tries to create a library panel that already exists, it should fail",
|
scenarioWithPanel(t, "When an admin tries to create a library panel that already exists, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 400, resp.Status())
|
require.Equal(t, 400, resp.Status())
|
||||||
@ -65,7 +65,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, "When an admin tries to create a library panel that does not exists using an nonexistent UID, it should succeed",
|
testScenario(t, "When an admin tries to create a library panel that does not exists using an nonexistent UID, it should succeed",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Nonexistent UID")
|
command := getCreatePanelCommand(sc.folder.ID, "Nonexistent UID")
|
||||||
command.UID = util.GenerateShortUID()
|
command.UID = util.GenerateShortUID()
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
@ -114,7 +114,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an existent UID, it should fail",
|
scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an existent UID, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Existing UID")
|
command := getCreatePanelCommand(sc.folder.ID, "Existing UID")
|
||||||
command.UID = sc.initialResult.Result.UID
|
command.UID = sc.initialResult.Result.UID
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
@ -123,7 +123,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an invalid UID, it should fail",
|
scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an invalid UID, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Invalid UID")
|
command := getCreatePanelCommand(sc.folder.ID, "Invalid UID")
|
||||||
command.UID = "Testing an invalid UID"
|
command.UID = "Testing an invalid UID"
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
@ -132,7 +132,7 @@ func TestCreateLibraryElement(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an UID that is too long, it should fail",
|
scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an UID that is too long, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Invalid UID")
|
command := getCreatePanelCommand(sc.folder.ID, "Invalid UID")
|
||||||
command.UID = "j6T00KRZzj6T00KRZzj6T00KRZzj6T00KRZzj6T00K"
|
command.UID = "j6T00KRZzj6T00KRZzj6T00KRZzj6T00KRZzj6T00K"
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
|
@ -73,7 +73,7 @@ func TestDeleteLibraryElement(t *testing.T) {
|
|||||||
Title: "Testing deleteHandler ",
|
Title: "Testing deleteHandler ",
|
||||||
Data: simplejson.NewFromAny(dashJSON),
|
Data: simplejson.NewFromAny(dashJSON),
|
||||||
}
|
}
|
||||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.Id)
|
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id)
|
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all panel elements and both panels and variables exist, it should only return panels",
|
scenarioWithPanel(t, "When an admin tries to get all panel elements and both panels and variables exist, it should only return panels",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreateVariableCommand(sc.folder.Id, "query0")
|
command := getCreateVariableCommand(sc.folder.ID, "query0")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -77,7 +77,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -103,7 +103,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all variable elements and both panels and variables exist, it should only return panels",
|
scenarioWithPanel(t, "When an admin tries to get all variable elements and both panels and variables exist, it should only return panels",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreateVariableCommand(sc.folder.Id, "query0")
|
command := getCreateVariableCommand(sc.folder.ID, "query0")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -142,7 +142,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -168,7 +168,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist, it should succeed",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist, it should succeed",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -204,7 +204,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -239,7 +239,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[1].Meta.Created,
|
Created: result.Result.Elements[1].Meta.Created,
|
||||||
Updated: result.Result.Elements[1].Meta.Updated,
|
Updated: result.Result.Elements[1].Meta.Updated,
|
||||||
@ -265,7 +265,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and sort desc is set, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and sort desc is set, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -304,7 +304,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -339,7 +339,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[1].Meta.Created,
|
Created: result.Result.Elements[1].Meta.Created,
|
||||||
Updated: result.Result.Elements[1].Meta.Updated,
|
Updated: result.Result.Elements[1].Meta.Updated,
|
||||||
@ -365,7 +365,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to existing types, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to existing types, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreateCommandWithModel(sc.folder.Id, "Gauge - Library Panel", models.PanelElement, []byte(`
|
command := getCreateCommandWithModel(sc.folder.ID, "Gauge - Library Panel", models.PanelElement, []byte(`
|
||||||
{
|
{
|
||||||
"datasource": "${DS_GDEV-TESTDATA}",
|
"datasource": "${DS_GDEV-TESTDATA}",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@ -378,7 +378,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
|
|
||||||
command = getCreateCommandWithModel(sc.folder.Id, "BarGauge - Library Panel", models.PanelElement, []byte(`
|
command = getCreateCommandWithModel(sc.folder.ID, "BarGauge - Library Panel", models.PanelElement, []byte(`
|
||||||
{
|
{
|
||||||
"datasource": "${DS_GDEV-TESTDATA}",
|
"datasource": "${DS_GDEV-TESTDATA}",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@ -425,7 +425,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -460,7 +460,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[1].Meta.Created,
|
Created: result.Result.Elements[1].Meta.Created,
|
||||||
Updated: result.Result.Elements[1].Meta.Updated,
|
Updated: result.Result.Elements[1].Meta.Updated,
|
||||||
@ -486,7 +486,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to a nonexistent type, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to a nonexistent type, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreateCommandWithModel(sc.folder.Id, "Gauge - Library Panel", models.PanelElement, []byte(`
|
command := getCreateCommandWithModel(sc.folder.ID, "Gauge - Library Panel", models.PanelElement, []byte(`
|
||||||
{
|
{
|
||||||
"datasource": "${DS_GDEV-TESTDATA}",
|
"datasource": "${DS_GDEV-TESTDATA}",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@ -621,7 +621,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 General folder, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilter is set to General folder, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -661,7 +661,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -696,7 +696,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[1].Meta.Created,
|
Created: result.Result.Elements[1].Meta.Created,
|
||||||
Updated: result.Result.Elements[1].Meta.Updated,
|
Updated: result.Result.Elements[1].Meta.Updated,
|
||||||
@ -722,7 +722,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and excludeUID is set, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and excludeUID is set, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -761,7 +761,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -787,7 +787,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -826,7 +826,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -852,7 +852,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 2, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 2, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -892,7 +892,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -918,7 +918,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in the description, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in the description, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreateCommandWithModel(sc.folder.Id, "Text - Library Panel2", models.PanelElement, []byte(`
|
command := getCreateCommandWithModel(sc.folder.ID, "Text - Library Panel2", models.PanelElement, []byte(`
|
||||||
{
|
{
|
||||||
"datasource": "${DS_GDEV-TESTDATA}",
|
"datasource": "${DS_GDEV-TESTDATA}",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@ -967,7 +967,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -993,7 +993,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in both name and description, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in both name and description, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreateCommandWithModel(sc.folder.Id, "Some Other", models.PanelElement, []byte(`
|
command := getCreateCommandWithModel(sc.folder.ID, "Some Other", models.PanelElement, []byte(`
|
||||||
{
|
{
|
||||||
"datasource": "${DS_GDEV-TESTDATA}",
|
"datasource": "${DS_GDEV-TESTDATA}",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@ -1040,7 +1040,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -1075,7 +1075,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[1].Meta.Created,
|
Created: result.Result.Elements[1].Meta.Created,
|
||||||
Updated: result.Result.Elements[1].Meta.Updated,
|
Updated: result.Result.Elements[1].Meta.Updated,
|
||||||
@ -1101,7 +1101,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 1 and searchString is panel2, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 1 and searchString is panel2, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -1142,7 +1142,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
Updated: result.Result.Elements[0].Meta.Updated,
|
Updated: result.Result.Elements[0].Meta.Updated,
|
||||||
@ -1168,7 +1168,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString is panel, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString is panel, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -1199,7 +1199,7 @@ func TestGetAllLibraryElements(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString does not exist, it should succeed and the result should be correct",
|
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString does not exist, it should succeed and the result should be correct",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
|
@ -49,7 +49,7 @@ func TestGetLibraryElement(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: res.Result.Meta.Created,
|
Created: res.Result.Meta.Created,
|
||||||
Updated: res.Result.Meta.Updated,
|
Updated: res.Result.Meta.Updated,
|
||||||
@ -119,7 +119,7 @@ func TestGetLibraryElement(t *testing.T) {
|
|||||||
Title: "Testing getHandler",
|
Title: "Testing getHandler",
|
||||||
Data: simplejson.NewFromAny(dashJSON),
|
Data: simplejson.NewFromAny(dashJSON),
|
||||||
}
|
}
|
||||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.Id)
|
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id)
|
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ func TestGetLibraryElement(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: LibraryElementDTOMeta{
|
Meta: LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: "ScenarioFolder",
|
||||||
FolderUID: sc.folder.Uid,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 1,
|
ConnectedDashboards: 1,
|
||||||
Created: res.Result.Meta.Created,
|
Created: res.Result.Meta.Created,
|
||||||
Updated: res.Result.Meta.Updated,
|
Updated: res.Result.Meta.Updated,
|
||||||
|
@ -187,7 +187,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with an existing UID, it should fail",
|
scenarioWithPanel(t, "When an admin tries to patch a library panel with an existing UID, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Existing UID")
|
command := getCreatePanelCommand(sc.folder.ID, "Existing UID")
|
||||||
command.UID = util.GenerateShortUID()
|
command.UID = util.GenerateShortUID()
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
@ -307,7 +307,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with a name that already exists, it should fail",
|
scenarioWithPanel(t, "When an admin tries to patch a library panel with a name that already exists, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Another Panel")
|
command := getCreatePanelCommand(sc.folder.ID, "Another Panel")
|
||||||
sc.ctx.Req.Body = mockRequestBody(command)
|
sc.ctx.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
var result = validateAndUnMarshalResponse(t, resp)
|
var result = validateAndUnMarshalResponse(t, resp)
|
||||||
@ -343,7 +343,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
|||||||
scenarioWithPanel(t, "When an admin tries to patch a library panel in another org, it should fail",
|
scenarioWithPanel(t, "When an admin tries to patch a library panel in another org, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
cmd := PatchLibraryElementCommand{
|
cmd := PatchLibraryElementCommand{
|
||||||
FolderID: sc.folder.Id,
|
FolderID: sc.folder.ID,
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Kind: int64(models.PanelElement),
|
Kind: int64(models.PanelElement),
|
||||||
}
|
}
|
||||||
@ -357,7 +357,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
|||||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with an old version number, it should fail",
|
scenarioWithPanel(t, "When an admin tries to patch a library panel with an old version number, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
cmd := PatchLibraryElementCommand{
|
cmd := PatchLibraryElementCommand{
|
||||||
FolderID: sc.folder.Id,
|
FolderID: sc.folder.ID,
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Kind: int64(models.PanelElement),
|
Kind: int64(models.PanelElement),
|
||||||
}
|
}
|
||||||
@ -373,7 +373,7 @@ func TestPatchLibraryElement(t *testing.T) {
|
|||||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with an other kind, it should succeed but panel should not change",
|
scenarioWithPanel(t, "When an admin tries to patch a library panel with an other kind, it should succeed but panel should not change",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
cmd := PatchLibraryElementCommand{
|
cmd := PatchLibraryElementCommand{
|
||||||
FolderID: sc.folder.Id,
|
FolderID: sc.folder.ID,
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Kind: int64(models.VariableElement),
|
Kind: int64(models.VariableElement),
|
||||||
}
|
}
|
||||||
|
@ -73,23 +73,23 @@ func TestDeleteLibraryPanelsInFolder(t *testing.T) {
|
|||||||
Title: "Testing DeleteLibraryElementsInFolder",
|
Title: "Testing DeleteLibraryElementsInFolder",
|
||||||
Data: simplejson.NewFromAny(dashJSON),
|
Data: simplejson.NewFromAny(dashJSON),
|
||||||
}
|
}
|
||||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.Id)
|
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id)
|
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.Uid)
|
err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID)
|
||||||
require.EqualError(t, err, ErrFolderHasConnectedLibraryElements.Error())
|
require.EqualError(t, err, ErrFolderHasConnectedLibraryElements.Error())
|
||||||
})
|
})
|
||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to delete a folder uid that doesn't exist, it should fail",
|
scenarioWithPanel(t, "When an admin tries to delete a folder uid that doesn't exist, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
err := sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.Uid+"xxxx")
|
err := sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID+"xxxx")
|
||||||
require.EqualError(t, err, dashboards.ErrFolderNotFound.Error())
|
require.EqualError(t, err, dashboards.ErrFolderNotFound.Error())
|
||||||
})
|
})
|
||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too",
|
scenarioWithPanel(t, "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreateVariableCommand(sc.folder.Id, "query0")
|
command := getCreateVariableCommand(sc.folder.ID, "query0")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -102,7 +102,7 @@ func TestDeleteLibraryPanelsInFolder(t *testing.T) {
|
|||||||
require.NotNil(t, result.Result)
|
require.NotNil(t, result.Result)
|
||||||
require.Equal(t, 2, len(result.Result.Elements))
|
require.Equal(t, 2, len(result.Result.Elements))
|
||||||
|
|
||||||
err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.Uid)
|
err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
resp = sc.service.getAllHandler(sc.reqContext)
|
resp = sc.service.getAllHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@ -146,7 +146,7 @@ func TestGetLibraryPanelConnections(t *testing.T) {
|
|||||||
Title: "Testing GetLibraryPanelConnections",
|
Title: "Testing GetLibraryPanelConnections",
|
||||||
Data: simplejson.NewFromAny(dashJSON),
|
Data: simplejson.NewFromAny(dashJSON),
|
||||||
}
|
}
|
||||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.Id)
|
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID)
|
||||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id)
|
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ type scenarioContext struct {
|
|||||||
service *LibraryElementService
|
service *LibraryElementService
|
||||||
reqContext *models.ReqContext
|
reqContext *models.ReqContext
|
||||||
user user.SignedInUser
|
user user.SignedInUser
|
||||||
folder *models.Folder
|
folder *folder.Folder
|
||||||
initialResult libraryElementResult
|
initialResult libraryElementResult
|
||||||
sqlStore db.DB
|
sqlStore db.DB
|
||||||
}
|
}
|
||||||
@ -387,7 +387,7 @@ func scenarioWithPanel(t *testing.T, desc string, fn func(t *testing.T, sc scena
|
|||||||
guardian.InitLegacyGuardian(store, &dashboards.FakeDashboardService{}, &teamtest.FakeService{})
|
guardian.InitLegacyGuardian(store, &dashboards.FakeDashboardService{}, &teamtest.FakeService{})
|
||||||
|
|
||||||
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel")
|
command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
resp := sc.service.createHandler(sc.reqContext)
|
||||||
sc.initialResult = validateAndUnMarshalResponse(t, resp)
|
sc.initialResult = validateAndUnMarshalResponse(t, resp)
|
||||||
@ -402,13 +402,26 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
ctx := web.Context{Req: &http.Request{
|
orgID := int64(1)
|
||||||
|
role := org.RoleAdmin
|
||||||
|
usr := user.SignedInUser{
|
||||||
|
UserID: 1,
|
||||||
|
Name: "Signed In User",
|
||||||
|
Login: "signed_in_user",
|
||||||
|
Email: "signed.in.user@test.com",
|
||||||
|
OrgID: orgID,
|
||||||
|
OrgRole: role,
|
||||||
|
LastSeenAt: time.Now(),
|
||||||
|
}
|
||||||
|
req := &http.Request{
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
"Content-Type": []string{"application/json"},
|
"Content-Type": []string{"application/json"},
|
||||||
},
|
},
|
||||||
}}
|
}
|
||||||
orgID := int64(1)
|
ctx := appcontext.WithUser(context.Background(), &usr)
|
||||||
role := org.RoleAdmin
|
req = req.WithContext(ctx)
|
||||||
|
webCtx := web.Context{Req: req}
|
||||||
|
|
||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
dashboardStore := database.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
|
dashboardStore := database.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
|
||||||
features := featuremgmt.WithFeatures()
|
features := featuremgmt.WithFeatures()
|
||||||
@ -428,16 +441,6 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||||||
folderService: folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), sqlStore.Cfg, dashboardService, dashboardStore, nil, features, folderPermissions, nil),
|
folderService: folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), sqlStore.Cfg, dashboardService, dashboardStore, nil, features, folderPermissions, nil),
|
||||||
}
|
}
|
||||||
|
|
||||||
usr := user.SignedInUser{
|
|
||||||
UserID: 1,
|
|
||||||
Name: "Signed In User",
|
|
||||||
Login: "signed_in_user",
|
|
||||||
Email: "signed.in.user@test.com",
|
|
||||||
OrgID: orgID,
|
|
||||||
OrgRole: role,
|
|
||||||
LastSeenAt: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// deliberate difference between signed in user and user in db to make it crystal clear
|
// deliberate difference between signed in user and user in db to make it crystal clear
|
||||||
// what to expect in the tests
|
// what to expect in the tests
|
||||||
// In the real world these are identical
|
// In the real world these are identical
|
||||||
@ -452,16 +455,16 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||||||
|
|
||||||
sc := scenarioContext{
|
sc := scenarioContext{
|
||||||
user: usr,
|
user: usr,
|
||||||
ctx: &ctx,
|
ctx: &webCtx,
|
||||||
service: &service,
|
service: &service,
|
||||||
sqlStore: sqlStore,
|
sqlStore: sqlStore,
|
||||||
reqContext: &models.ReqContext{
|
reqContext: &models.ReqContext{
|
||||||
Context: &ctx,
|
Context: &webCtx,
|
||||||
SignedInUser: &usr,
|
SignedInUser: &usr,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{}).ToLegacyModel()
|
sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{})
|
||||||
|
|
||||||
fn(t, sc)
|
fn(t, sc)
|
||||||
})
|
})
|
||||||
|
@ -850,12 +850,14 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||||||
Login: userInDbName,
|
Login: userInDbName,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := sqlStore.CreateUser(context.Background(), cmd)
|
ctx := appcontext.WithUser(context.Background(), usr)
|
||||||
|
|
||||||
|
_, err := sqlStore.CreateUser(ctx, cmd)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sc := scenarioContext{
|
sc := scenarioContext{
|
||||||
user: usr,
|
user: usr,
|
||||||
ctx: context.Background(),
|
ctx: ctx,
|
||||||
service: &service,
|
service: &service,
|
||||||
elementService: elementService,
|
elementService: elementService,
|
||||||
sqlStore: sqlStore,
|
sqlStore: sqlStore,
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
@ -181,7 +182,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res
|
|||||||
return response.JSON(http.StatusOK, ruleResponse)
|
return response.JSON(http.StatusOK, ruleResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv PrometheusSrv) toRuleGroup(groupName string, folder *models.Folder, rules []*ngmodels.AlertRule, labelOptions []ngmodels.LabelOption) *apimodels.RuleGroup {
|
func (srv PrometheusSrv) toRuleGroup(groupName string, folder *folder.Folder, rules []*ngmodels.AlertRule, labelOptions []ngmodels.LabelOption) *apimodels.RuleGroup {
|
||||||
newGroup := &apimodels.RuleGroup{
|
newGroup := &apimodels.RuleGroup{
|
||||||
Name: groupName,
|
Name: groupName,
|
||||||
File: folder.Title, // file is what Prometheus uses for provisioning, we replace it with namespace.
|
File: folder.Title, // file is what Prometheus uses for provisioning, we replace it with namespace.
|
||||||
|
@ -83,7 +83,7 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *models.ReqContext, namespaceTitle s
|
|||||||
unauthz, provisioned := false, false
|
unauthz, provisioned := false, false
|
||||||
q := ngmodels.ListAlertRulesQuery{
|
q := ngmodels.ListAlertRulesQuery{
|
||||||
OrgID: c.SignedInUser.OrgID,
|
OrgID: c.SignedInUser.OrgID,
|
||||||
NamespaceUIDs: []string{namespace.Uid},
|
NamespaceUIDs: []string{namespace.UID},
|
||||||
RuleGroup: ruleGroup,
|
RuleGroup: ruleGroup,
|
||||||
}
|
}
|
||||||
if err = srv.store.ListAlertRules(ctx, &q); err != nil {
|
if err = srv.store.ListAlertRules(ctx, &q); err != nil {
|
||||||
@ -163,7 +163,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext, namespace
|
|||||||
|
|
||||||
q := ngmodels.ListAlertRulesQuery{
|
q := ngmodels.ListAlertRulesQuery{
|
||||||
OrgID: c.SignedInUser.OrgID,
|
OrgID: c.SignedInUser.OrgID,
|
||||||
NamespaceUIDs: []string{namespace.Uid},
|
NamespaceUIDs: []string{namespace.UID},
|
||||||
}
|
}
|
||||||
if err := srv.store.ListAlertRules(c.Req.Context(), &q); err != nil {
|
if err := srv.store.ListAlertRules(c.Req.Context(), &q); err != nil {
|
||||||
return ErrResp(http.StatusInternalServerError, err, "failed to update rule group")
|
return ErrResp(http.StatusInternalServerError, err, "failed to update rule group")
|
||||||
@ -189,7 +189,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext, namespace
|
|||||||
if !authorizeAccessToRuleGroup(rules, hasAccess) {
|
if !authorizeAccessToRuleGroup(rules, hasAccess) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result[namespaceTitle] = append(result[namespaceTitle], toGettableRuleGroupConfig(groupName, rules, namespace.Id, provenanceRecords))
|
result[namespaceTitle] = append(result[namespaceTitle], toGettableRuleGroupConfig(groupName, rules, namespace.ID, provenanceRecords))
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.JSON(http.StatusAccepted, result)
|
return response.JSON(http.StatusAccepted, result)
|
||||||
@ -205,7 +205,7 @@ func (srv RulerSrv) RouteGetRulesGroupConfig(c *models.ReqContext, namespaceTitl
|
|||||||
|
|
||||||
q := ngmodels.ListAlertRulesQuery{
|
q := ngmodels.ListAlertRulesQuery{
|
||||||
OrgID: c.SignedInUser.OrgID,
|
OrgID: c.SignedInUser.OrgID,
|
||||||
NamespaceUIDs: []string{namespace.Uid},
|
NamespaceUIDs: []string{namespace.UID},
|
||||||
RuleGroup: ruleGroup,
|
RuleGroup: ruleGroup,
|
||||||
}
|
}
|
||||||
if err := srv.store.ListAlertRules(c.Req.Context(), &q); err != nil {
|
if err := srv.store.ListAlertRules(c.Req.Context(), &q); err != nil {
|
||||||
@ -226,7 +226,7 @@ func (srv RulerSrv) RouteGetRulesGroupConfig(c *models.ReqContext, namespaceTitl
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := apimodels.RuleGroupConfigResponse{
|
result := apimodels.RuleGroupConfigResponse{
|
||||||
GettableRuleGroupConfig: toGettableRuleGroupConfig(ruleGroup, q.Result, namespace.Id, provenanceRecords),
|
GettableRuleGroupConfig: toGettableRuleGroupConfig(ruleGroup, q.Result, namespace.ID, provenanceRecords),
|
||||||
}
|
}
|
||||||
return response.JSON(http.StatusAccepted, result)
|
return response.JSON(http.StatusAccepted, result)
|
||||||
}
|
}
|
||||||
@ -296,7 +296,7 @@ func (srv RulerSrv) RouteGetRulesConfig(c *models.ReqContext) response.Response
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
namespace := folder.Title
|
namespace := folder.Title
|
||||||
result[namespace] = append(result[namespace], toGettableRuleGroupConfig(groupKey.RuleGroup, rules, folder.Id, provenanceRecords))
|
result[namespace] = append(result[namespace], toGettableRuleGroupConfig(groupKey.RuleGroup, rules, folder.ID, provenanceRecords))
|
||||||
}
|
}
|
||||||
return response.JSON(http.StatusOK, result)
|
return response.JSON(http.StatusOK, result)
|
||||||
}
|
}
|
||||||
@ -316,7 +316,7 @@ func (srv RulerSrv) RoutePostNameRulesConfig(c *models.ReqContext, ruleGroupConf
|
|||||||
|
|
||||||
groupKey := ngmodels.AlertRuleGroupKey{
|
groupKey := ngmodels.AlertRuleGroupKey{
|
||||||
OrgID: c.SignedInUser.OrgID,
|
OrgID: c.SignedInUser.OrgID,
|
||||||
NamespaceUID: namespace.Uid,
|
NamespaceUID: namespace.UID,
|
||||||
RuleGroup: ruleGroupConfig.Name,
|
RuleGroup: ruleGroupConfig.Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
acMock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
acMock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||||
@ -382,7 +383,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
|
|||||||
ruleStore := fakes.NewRuleStore(t)
|
ruleStore := fakes.NewRuleStore(t)
|
||||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||||
groupKey := models.GenerateGroupKey(orgID)
|
groupKey := models.GenerateGroupKey(orgID)
|
||||||
groupKey.NamespaceUID = folder.Uid
|
groupKey.NamespaceUID = folder.UID
|
||||||
|
|
||||||
expectedRules := models.GenerateAlertRules(rand.Intn(5)+5, models.AlertRuleGen(withGroupKey(groupKey), models.WithUniqueGroupIndex()))
|
expectedRules := models.GenerateAlertRules(rand.Intn(5)+5, models.AlertRuleGen(withGroupKey(groupKey), models.WithUniqueGroupIndex()))
|
||||||
ruleStore.PutRule(context.Background(), expectedRules...)
|
ruleStore.PutRule(context.Background(), expectedRules...)
|
||||||
@ -426,12 +427,12 @@ func TestRouteGetRulesConfig(t *testing.T) {
|
|||||||
ruleStore := fakes.NewRuleStore(t)
|
ruleStore := fakes.NewRuleStore(t)
|
||||||
folder1 := randFolder()
|
folder1 := randFolder()
|
||||||
folder2 := randFolder()
|
folder2 := randFolder()
|
||||||
ruleStore.Folders[orgID] = []*models2.Folder{folder1, folder2}
|
ruleStore.Folders[orgID] = []*folder.Folder{folder1, folder2}
|
||||||
|
|
||||||
group1Key := models.GenerateGroupKey(orgID)
|
group1Key := models.GenerateGroupKey(orgID)
|
||||||
group1Key.NamespaceUID = folder1.Uid
|
group1Key.NamespaceUID = folder1.UID
|
||||||
group2Key := models.GenerateGroupKey(orgID)
|
group2Key := models.GenerateGroupKey(orgID)
|
||||||
group2Key.NamespaceUID = folder2.Uid
|
group2Key.NamespaceUID = folder2.UID
|
||||||
|
|
||||||
group1 := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(group1Key)))
|
group1 := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(group1Key)))
|
||||||
group2 := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(group2Key)))
|
group2 := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(group2Key)))
|
||||||
@ -464,7 +465,7 @@ func TestRouteGetRulesConfig(t *testing.T) {
|
|||||||
ruleStore := fakes.NewRuleStore(t)
|
ruleStore := fakes.NewRuleStore(t)
|
||||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||||
groupKey := models.GenerateGroupKey(orgID)
|
groupKey := models.GenerateGroupKey(orgID)
|
||||||
groupKey.NamespaceUID = folder.Uid
|
groupKey.NamespaceUID = folder.UID
|
||||||
|
|
||||||
expectedRules := models.GenerateAlertRules(rand.Intn(5)+5, models.AlertRuleGen(withGroupKey(groupKey), models.WithUniqueGroupIndex()))
|
expectedRules := models.GenerateAlertRules(rand.Intn(5)+5, models.AlertRuleGen(withGroupKey(groupKey), models.WithUniqueGroupIndex()))
|
||||||
ruleStore.PutRule(context.Background(), expectedRules...)
|
ruleStore.PutRule(context.Background(), expectedRules...)
|
||||||
@ -509,7 +510,7 @@ func TestRouteGetRulesGroupConfig(t *testing.T) {
|
|||||||
ruleStore := fakes.NewRuleStore(t)
|
ruleStore := fakes.NewRuleStore(t)
|
||||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||||
groupKey := models.GenerateGroupKey(orgID)
|
groupKey := models.GenerateGroupKey(orgID)
|
||||||
groupKey.NamespaceUID = folder.Uid
|
groupKey.NamespaceUID = folder.UID
|
||||||
|
|
||||||
expectedRules := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(groupKey)))
|
expectedRules := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(groupKey)))
|
||||||
ruleStore.PutRule(context.Background(), expectedRules...)
|
ruleStore.PutRule(context.Background(), expectedRules...)
|
||||||
@ -544,7 +545,7 @@ func TestRouteGetRulesGroupConfig(t *testing.T) {
|
|||||||
ruleStore := fakes.NewRuleStore(t)
|
ruleStore := fakes.NewRuleStore(t)
|
||||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||||
groupKey := models.GenerateGroupKey(orgID)
|
groupKey := models.GenerateGroupKey(orgID)
|
||||||
groupKey.NamespaceUID = folder.Uid
|
groupKey.NamespaceUID = folder.UID
|
||||||
|
|
||||||
expectedRules := models.GenerateAlertRules(rand.Intn(5)+5, models.AlertRuleGen(withGroupKey(groupKey), models.WithUniqueGroupIndex()))
|
expectedRules := models.GenerateAlertRules(rand.Intn(5)+5, models.AlertRuleGen(withGroupKey(groupKey), models.WithUniqueGroupIndex()))
|
||||||
ruleStore.PutRule(context.Background(), expectedRules...)
|
ruleStore.PutRule(context.Background(), expectedRules...)
|
||||||
@ -699,9 +700,9 @@ func withGroup(groupName string) func(rule *models.AlertRule) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func withNamespace(namespace *models2.Folder) func(rule *models.AlertRule) {
|
func withNamespace(namespace *folder.Folder) func(rule *models.AlertRule) {
|
||||||
return func(rule *models.AlertRule) {
|
return func(rule *models.AlertRule) {
|
||||||
rule.NamespaceUID = namespace.Uid
|
rule.NamespaceUID = namespace.UID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
@ -18,7 +18,7 @@ func validateRuleNode(
|
|||||||
groupName string,
|
groupName string,
|
||||||
interval time.Duration,
|
interval time.Duration,
|
||||||
orgId int64,
|
orgId int64,
|
||||||
namespace *models.Folder,
|
namespace *folder.Folder,
|
||||||
conditionValidator func(ngmodels.Condition) error,
|
conditionValidator func(ngmodels.Condition) error,
|
||||||
cfg *setting.UnifiedAlertingSettings) (*ngmodels.AlertRule, error) {
|
cfg *setting.UnifiedAlertingSettings) (*ngmodels.AlertRule, error) {
|
||||||
intervalSeconds, err := validateInterval(cfg, interval)
|
intervalSeconds, err := validateInterval(cfg, interval)
|
||||||
@ -93,7 +93,7 @@ func validateRuleNode(
|
|||||||
Data: ruleNode.GrafanaManagedAlert.Data,
|
Data: ruleNode.GrafanaManagedAlert.Data,
|
||||||
UID: ruleNode.GrafanaManagedAlert.UID,
|
UID: ruleNode.GrafanaManagedAlert.UID,
|
||||||
IntervalSeconds: intervalSeconds,
|
IntervalSeconds: intervalSeconds,
|
||||||
NamespaceUID: namespace.Uid,
|
NamespaceUID: namespace.UID,
|
||||||
RuleGroup: groupName,
|
RuleGroup: groupName,
|
||||||
NoDataState: noDataState,
|
NoDataState: noDataState,
|
||||||
ExecErrState: errorState,
|
ExecErrState: errorState,
|
||||||
@ -152,7 +152,7 @@ func validateForInterval(ruleNode *apimodels.PostableExtendedRuleNode) (time.Dur
|
|||||||
func validateRuleGroup(
|
func validateRuleGroup(
|
||||||
ruleGroupConfig *apimodels.PostableRuleGroupConfig,
|
ruleGroupConfig *apimodels.PostableRuleGroupConfig,
|
||||||
orgId int64,
|
orgId int64,
|
||||||
namespace *models.Folder,
|
namespace *folder.Folder,
|
||||||
conditionValidator func(ngmodels.Condition) error,
|
conditionValidator func(ngmodels.Condition) error,
|
||||||
cfg *setting.UnifiedAlertingSettings) ([]*ngmodels.AlertRule, error) {
|
cfg *setting.UnifiedAlertingSettings) ([]*ngmodels.AlertRule, error) {
|
||||||
if ruleGroupConfig.Name == "" {
|
if ruleGroupConfig.Name == "" {
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/exp/rand"
|
"golang.org/x/exp/rand"
|
||||||
|
|
||||||
models2 "github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
@ -83,18 +83,18 @@ func validGroup(cfg *setting.UnifiedAlertingSettings, rules ...apimodels.Postabl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func randFolder() *models2.Folder {
|
func randFolder() *folder.Folder {
|
||||||
return &models2.Folder{
|
return &folder.Folder{
|
||||||
Id: rand.Int63(),
|
ID: rand.Int63(),
|
||||||
Uid: util.GenerateShortUID(),
|
UID: util.GenerateShortUID(),
|
||||||
Title: "TEST-FOLDER-" + util.GenerateShortUID(),
|
Title: "TEST-FOLDER-" + util.GenerateShortUID(),
|
||||||
Url: "",
|
// URL: "",
|
||||||
Version: 0,
|
// Version: 0,
|
||||||
Created: time.Time{},
|
Created: time.Time{},
|
||||||
Updated: time.Time{},
|
Updated: time.Time{},
|
||||||
UpdatedBy: 0,
|
// UpdatedBy: 0,
|
||||||
CreatedBy: 0,
|
// CreatedBy: 0,
|
||||||
HasACL: false,
|
// HasACL: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ func TestValidateRuleNode_NoUID(t *testing.T) {
|
|||||||
require.Equal(t, int64(interval.Seconds()), alert.IntervalSeconds)
|
require.Equal(t, int64(interval.Seconds()), alert.IntervalSeconds)
|
||||||
require.Equal(t, int64(0), alert.Version)
|
require.Equal(t, int64(0), alert.Version)
|
||||||
require.Equal(t, api.GrafanaManagedAlert.UID, alert.UID)
|
require.Equal(t, api.GrafanaManagedAlert.UID, alert.UID)
|
||||||
require.Equal(t, folder.Uid, alert.NamespaceUID)
|
require.Equal(t, folder.UID, alert.NamespaceUID)
|
||||||
require.Nil(t, alert.DashboardUID)
|
require.Nil(t, alert.DashboardUID)
|
||||||
require.Nil(t, alert.PanelID)
|
require.Nil(t, alert.PanelID)
|
||||||
require.Equal(t, name, alert.RuleGroup)
|
require.Equal(t, name, alert.RuleGroup)
|
||||||
|
@ -3,15 +3,15 @@ package api
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RuleStore is the interface for persisting alert rules and instances
|
// RuleStore is the interface for persisting alert rules and instances
|
||||||
type RuleStore interface {
|
type RuleStore interface {
|
||||||
GetUserVisibleNamespaces(context.Context, int64, *user.SignedInUser) (map[string]*models.Folder, error)
|
GetUserVisibleNamespaces(context.Context, int64, *user.SignedInUser) (map[string]*folder.Folder, error)
|
||||||
GetNamespaceByTitle(context.Context, string, int64, *user.SignedInUser, bool) (*models.Folder, error)
|
GetNamespaceByTitle(context.Context, string, int64, *user.SignedInUser, bool) (*folder.Folder, error)
|
||||||
GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmodels.GetAlertRulesGroupByRuleUIDQuery) error
|
GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmodels.GetAlertRulesGroupByRuleUIDQuery) error
|
||||||
ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error
|
ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/expr"
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
models2 "github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -140,9 +140,9 @@ func WithOrgID(orgId int64) AlertRuleMutator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithNamespace(namespace *models2.Folder) AlertRuleMutator {
|
func WithNamespace(namespace *folder.Folder) AlertRuleMutator {
|
||||||
return func(rule *AlertRule) {
|
return func(rule *AlertRule) {
|
||||||
rule.NamespaceUID = namespace.Uid
|
rule.NamespaceUID = namespace.UID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/events"
|
"github.com/grafana/grafana/pkg/events"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
models2 "github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/schedule"
|
"github.com/grafana/grafana/pkg/services/ngalert/schedule"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
||||||
@ -22,9 +22,9 @@ import (
|
|||||||
|
|
||||||
func Test_subscribeToFolderChanges(t *testing.T) {
|
func Test_subscribeToFolderChanges(t *testing.T) {
|
||||||
orgID := rand.Int63()
|
orgID := rand.Int63()
|
||||||
folder := &models2.Folder{
|
folder := &folder.Folder{
|
||||||
Id: 0,
|
ID: 0,
|
||||||
Uid: util.GenerateShortUID(),
|
UID: util.GenerateShortUID(),
|
||||||
Title: "Folder" + util.GenerateShortUID(),
|
Title: "Folder" + util.GenerateShortUID(),
|
||||||
}
|
}
|
||||||
rules := models.GenerateAlertRules(5, models.AlertRuleGen(models.WithOrgID(orgID), models.WithNamespace(folder)))
|
rules := models.GenerateAlertRules(5, models.AlertRuleGen(models.WithOrgID(orgID), models.WithNamespace(folder)))
|
||||||
@ -42,8 +42,8 @@ func Test_subscribeToFolderChanges(t *testing.T) {
|
|||||||
err := bus.Publish(context.Background(), &events.FolderTitleUpdated{
|
err := bus.Publish(context.Background(), &events.FolderTitleUpdated{
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
Title: "Folder" + util.GenerateShortUID(),
|
Title: "Folder" + util.GenerateShortUID(),
|
||||||
ID: folder.Id,
|
ID: folder.ID,
|
||||||
UID: folder.Uid,
|
UID: folder.UID,
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||||
@ -298,8 +299,8 @@ func (st DBstore) GetRuleGroupInterval(ctx context.Context, orgID int64, namespa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetUserVisibleNamespaces returns the folders that are visible to the user and have at least one alert in it
|
// GetUserVisibleNamespaces returns the folders that are visible to the user and have at least one alert in it
|
||||||
func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, user *user.SignedInUser) (map[string]*models.Folder, error) {
|
func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, user *user.SignedInUser) (map[string]*folder.Folder, error) {
|
||||||
namespaceMap := make(map[string]*models.Folder)
|
namespaceMap := make(map[string]*folder.Folder)
|
||||||
|
|
||||||
searchQuery := models.FindPersistedDashboardsQuery{
|
searchQuery := models.FindPersistedDashboardsQuery{
|
||||||
OrgId: orgID,
|
OrgId: orgID,
|
||||||
@ -330,9 +331,9 @@ func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, use
|
|||||||
if !hit.IsFolder {
|
if !hit.IsFolder {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
namespaceMap[hit.UID] = &models.Folder{
|
namespaceMap[hit.UID] = &folder.Folder{
|
||||||
Id: hit.ID,
|
ID: hit.ID,
|
||||||
Uid: hit.UID,
|
UID: hit.UID,
|
||||||
Title: hit.Title,
|
Title: hit.Title,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,15 +343,15 @@ func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, use
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetNamespaceByTitle is a handler for retrieving a namespace by its title. Alerting rules follow a Grafana folder-like structure which we call namespaces.
|
// GetNamespaceByTitle is a handler for retrieving a namespace by its title. Alerting rules follow a Grafana folder-like structure which we call namespaces.
|
||||||
func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, orgID int64, user *user.SignedInUser, withCanSave bool) (*models.Folder, error) {
|
func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, orgID int64, user *user.SignedInUser, withCanSave bool) (*folder.Folder, error) {
|
||||||
folder, err := st.FolderService.GetFolderByTitle(ctx, user, orgID, namespace)
|
folder, err := st.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, Title: &namespace})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if access control is disabled, check that the user is allowed to save in the folder.
|
// if access control is disabled, check that the user is allowed to save in the folder.
|
||||||
if withCanSave && st.AccessControl.IsDisabled() {
|
if withCanSave && st.AccessControl.IsDisabled() {
|
||||||
g := guardian.New(ctx, folder.Id, orgID, user)
|
g := guardian.New(ctx, folder.ID, orgID, user)
|
||||||
if canSave, err := g.CanSave(); err != nil || !canSave {
|
if canSave, err := g.CanSave(); err != nil || !canSave {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st.Logger.Error("checking can save permission has failed", "userId", user.UserID, "username", user.Login, "namespace", namespace, "orgId", orgID, "error", err)
|
st.Logger.Error("checking can save permission has failed", "userId", user.UserID, "username", user.Login, "namespace", namespace, "orgId", orgID, "error", err)
|
||||||
@ -363,8 +364,8 @@ func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, org
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetNamespaceByUID is a handler for retrieving a namespace by its UID. Alerting rules follow a Grafana folder-like structure which we call namespaces.
|
// GetNamespaceByUID is a handler for retrieving a namespace by its UID. Alerting rules follow a Grafana folder-like structure which we call namespaces.
|
||||||
func (st DBstore) GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user *user.SignedInUser) (*models.Folder, error) {
|
func (st DBstore) GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user *user.SignedInUser) (*folder.Folder, error) {
|
||||||
folder, err := st.FolderService.GetFolderByUID(ctx, user, orgID, uid)
|
folder, err := st.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, Title: &uid})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ func TestIntegrationUpdateAlertRules(t *testing.T) {
|
|||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
}
|
}
|
||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
store := DBstore{
|
store := &DBstore{
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
Cfg: setting.UnifiedAlertingSettings{
|
Cfg: setting.UnifiedAlertingSettings{
|
||||||
BaseInterval: time.Duration(rand.Int63n(100)) * time.Second,
|
BaseInterval: time.Duration(rand.Int63n(100)) * time.Second,
|
||||||
@ -136,7 +136,7 @@ func TestIntegration_CountAlertRules(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
store := DBstore{SQLStore: sqlStore}
|
store := &DBstore{SQLStore: sqlStore}
|
||||||
rule := createRule(t, store)
|
rule := createRule(t, store)
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
@ -175,7 +175,7 @@ func TestIntegration_CountAlertRules(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRule(t *testing.T, store DBstore) *models.AlertRule {
|
func createRule(t *testing.T, store *DBstore) *models.AlertRule {
|
||||||
rule := models.AlertRuleGen(withIntervalMatching(store.Cfg.BaseInterval))()
|
rule := models.AlertRuleGen(withIntervalMatching(store.Cfg.BaseInterval))()
|
||||||
err := store.SQLStore.WithDbSession(context.Background(), func(sess *db.Session) error {
|
err := store.SQLStore.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||||
_, err := sess.Table(models.AlertRule{}).InsertOne(rule)
|
_, err := sess.Table(models.AlertRule{}).InsertOne(rule)
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
models2 "github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@ -23,7 +23,7 @@ type RuleStore struct {
|
|||||||
Rules map[int64][]*models.AlertRule
|
Rules map[int64][]*models.AlertRule
|
||||||
Hook func(cmd interface{}) error // use Hook if you need to intercept some query and return an error
|
Hook func(cmd interface{}) error // use Hook if you need to intercept some query and return an error
|
||||||
RecordedOps []interface{}
|
RecordedOps []interface{}
|
||||||
Folders map[int64][]*models2.Folder
|
Folders map[int64][]*folder.Folder
|
||||||
}
|
}
|
||||||
|
|
||||||
type GenericRecordedQuery struct {
|
type GenericRecordedQuery struct {
|
||||||
@ -38,7 +38,7 @@ func NewRuleStore(t *testing.T) *RuleStore {
|
|||||||
Hook: func(interface{}) error {
|
Hook: func(interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Folders: map[int64][]*models2.Folder{},
|
Folders: map[int64][]*folder.Folder{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,18 +58,18 @@ mainloop:
|
|||||||
rgs = append(rgs, r)
|
rgs = append(rgs, r)
|
||||||
f.Rules[r.OrgID] = rgs
|
f.Rules[r.OrgID] = rgs
|
||||||
|
|
||||||
var existing *models2.Folder
|
var existing *folder.Folder
|
||||||
folders := f.Folders[r.OrgID]
|
folders := f.Folders[r.OrgID]
|
||||||
for _, folder := range folders {
|
for _, folder := range folders {
|
||||||
if folder.Uid == r.NamespaceUID {
|
if folder.UID == r.NamespaceUID {
|
||||||
existing = folder
|
existing = folder
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if existing == nil {
|
if existing == nil {
|
||||||
folders = append(folders, &models2.Folder{
|
folders = append(folders, &folder.Folder{
|
||||||
Id: rand.Int63(),
|
ID: rand.Int63(),
|
||||||
Uid: r.NamespaceUID,
|
UID: r.NamespaceUID,
|
||||||
Title: "TEST-FOLDER-" + util.GenerateShortUID(),
|
Title: "TEST-FOLDER-" + util.GenerateShortUID(),
|
||||||
})
|
})
|
||||||
f.Folders[r.OrgID] = folders
|
f.Folders[r.OrgID] = folders
|
||||||
@ -227,11 +227,11 @@ func (f *RuleStore) ListAlertRules(_ context.Context, q *models.ListAlertRulesQu
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ *user.SignedInUser) (map[string]*models2.Folder, error) {
|
func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ *user.SignedInUser) (map[string]*folder.Folder, error) {
|
||||||
f.mtx.Lock()
|
f.mtx.Lock()
|
||||||
defer f.mtx.Unlock()
|
defer f.mtx.Unlock()
|
||||||
|
|
||||||
namespacesMap := map[string]*models2.Folder{}
|
namespacesMap := map[string]*folder.Folder{}
|
||||||
|
|
||||||
_, ok := f.Rules[orgID]
|
_, ok := f.Rules[orgID]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -239,12 +239,12 @@ func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ *
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, folder := range f.Folders[orgID] {
|
for _, folder := range f.Folders[orgID] {
|
||||||
namespacesMap[folder.Uid] = folder
|
namespacesMap[folder.UID] = folder
|
||||||
}
|
}
|
||||||
return namespacesMap, nil
|
return namespacesMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID int64, _ *user.SignedInUser, _ bool) (*models2.Folder, error) {
|
func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID int64, _ *user.SignedInUser, _ bool) (*folder.Folder, error) {
|
||||||
folders := f.Folders[orgID]
|
folders := f.Folders[orgID]
|
||||||
for _, folder := range folders {
|
for _, folder := range folders {
|
||||||
if folder.Title == title {
|
if folder.Title == title {
|
||||||
@ -254,7 +254,7 @@ func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID i
|
|||||||
return nil, fmt.Errorf("not found")
|
return nil, fmt.Errorf("not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64, _ *user.SignedInUser) (*models2.Folder, error) {
|
func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64, _ *user.SignedInUser) (*folder.Folder, error) {
|
||||||
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
||||||
Name: "GetNamespaceByUID",
|
Name: "GetNamespaceByUID",
|
||||||
Params: []interface{}{orgID, uid},
|
Params: []interface{}{orgID, uid},
|
||||||
@ -262,7 +262,7 @@ func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64
|
|||||||
|
|
||||||
folders := f.Folders[orgID]
|
folders := f.Folders[orgID]
|
||||||
for _, folder := range folders {
|
for _, folder := range folders {
|
||||||
if folder.Uid == uid {
|
if folder.UID == uid {
|
||||||
return folder, nil
|
return folder, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
gfmodels "github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
@ -68,6 +67,7 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG,
|
|||||||
})
|
})
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
|
cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled
|
||||||
cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{
|
cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{
|
||||||
BaseInterval: setting.SchedulerBaseInterval,
|
BaseInterval: setting.SchedulerBaseInterval,
|
||||||
}
|
}
|
||||||
@ -127,12 +127,10 @@ func CreateTestAlertRuleWithLabels(t testing.TB, ctx context.Context, dbstore *s
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx = appcontext.WithUser(ctx, user)
|
ctx = appcontext.WithUser(ctx, user)
|
||||||
f, err := dbstore.FolderService.Create(ctx, &folder.CreateFolderCommand{OrgID: orgID, Title: "FOLDER-" + util.GenerateShortUID(), UID: folderUID})
|
_, err := dbstore.FolderService.Create(ctx, &folder.CreateFolderCommand{OrgID: orgID, Title: "FOLDER-" + util.GenerateShortUID(), UID: folderUID})
|
||||||
var folder *gfmodels.Folder
|
// var foldr *folder.Folder
|
||||||
if err == nil {
|
if errors.Is(err, dashboards.ErrFolderWithSameUIDExists) || errors.Is(err, dashboards.ErrFolderVersionMismatch) {
|
||||||
folder = f.ToLegacyModel()
|
_, err = dbstore.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, UID: &folderUID})
|
||||||
} 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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -160,7 +158,7 @@ func CreateTestAlertRuleWithLabels(t testing.TB, ctx context.Context, dbstore *s
|
|||||||
Labels: labels,
|
Labels: labels,
|
||||||
Annotations: map[string]string{"testAnnoKey": "testAnnoValue"},
|
Annotations: map[string]string{"testAnnoKey": "testAnnoValue"},
|
||||||
IntervalSeconds: intervalSeconds,
|
IntervalSeconds: intervalSeconds,
|
||||||
NamespaceUID: folder.Uid,
|
NamespaceUID: folderUID,
|
||||||
RuleGroup: ruleGroup,
|
RuleGroup: ruleGroup,
|
||||||
NoDataState: models.NoData,
|
NoDataState: models.NoData,
|
||||||
ExecErrState: models.AlertingErrState,
|
ExecErrState: models.AlertingErrState,
|
||||||
@ -170,7 +168,7 @@ func CreateTestAlertRuleWithLabels(t testing.TB, ctx context.Context, dbstore *s
|
|||||||
|
|
||||||
q := models.ListAlertRulesQuery{
|
q := models.ListAlertRulesQuery{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
NamespaceUIDs: []string{folder.Uid},
|
NamespaceUIDs: []string{folderUID},
|
||||||
RuleGroup: ruleGroup,
|
RuleGroup: ruleGroup,
|
||||||
}
|
}
|
||||||
err = dbstore.ListAlertRules(ctx, &q)
|
err = dbstore.ListAlertRules(ctx, &q)
|
||||||
@ -178,6 +176,6 @@ func CreateTestAlertRuleWithLabels(t testing.TB, ctx context.Context, dbstore *s
|
|||||||
require.NotEmpty(t, q.Result)
|
require.NotEmpty(t, q.Result)
|
||||||
|
|
||||||
rule := q.Result[0]
|
rule := q.Result[0]
|
||||||
t.Logf("alert definition: %v with title: %q interval: %d folder: %s created", rule.GetKey(), rule.Title, rule.IntervalSeconds, folder.Uid)
|
t.Logf("alert definition: %v with title: %q interval: %d folder: %s created", rule.GetKey(), rule.Title, rule.IntervalSeconds, folderUID)
|
||||||
return rule
|
return rule
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user