Merge branch 'master' into docs_v5.0
This commit is contained in:
@@ -41,7 +41,10 @@ type SaveDashboardDTO struct {
|
||||
Dashboard *models.Dashboard
|
||||
}
|
||||
|
||||
type dashboardServiceImpl struct{}
|
||||
type dashboardServiceImpl struct {
|
||||
orgId int64
|
||||
user *models.SignedInUser
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
|
||||
cmd := &models.GetProvisionedDashboardDataQuery{Name: name}
|
||||
@@ -53,7 +56,7 @@ func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*mod
|
||||
return cmd.Result, nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO) (*models.SaveDashboardCommand, error) {
|
||||
func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, validateAlerts bool) (*models.SaveDashboardCommand, error) {
|
||||
dash := dto.Dashboard
|
||||
|
||||
dash.Title = strings.TrimSpace(dash.Title)
|
||||
@@ -78,13 +81,15 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO)
|
||||
return nil, models.ErrDashboardUidToLong
|
||||
}
|
||||
|
||||
validateAlertsCmd := models.ValidateDashboardAlertsCommand{
|
||||
OrgId: dto.OrgId,
|
||||
Dashboard: dash,
|
||||
}
|
||||
if validateAlerts {
|
||||
validateAlertsCmd := models.ValidateDashboardAlertsCommand{
|
||||
OrgId: dto.OrgId,
|
||||
Dashboard: dash,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&validateAlertsCmd); err != nil {
|
||||
return nil, models.ErrDashboardContainsInvalidAlertData
|
||||
if err := bus.Dispatch(&validateAlertsCmd); err != nil {
|
||||
return nil, models.ErrDashboardContainsInvalidAlertData
|
||||
}
|
||||
}
|
||||
|
||||
validateBeforeSaveCmd := models.ValidateDashboardBeforeSaveCommand{
|
||||
@@ -142,7 +147,7 @@ func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO,
|
||||
UserId: 0,
|
||||
OrgRole: models.ROLE_ADMIN,
|
||||
}
|
||||
cmd, err := dr.buildSaveDashboardCommand(dto)
|
||||
cmd, err := dr.buildSaveDashboardCommand(dto, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -172,7 +177,7 @@ func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDash
|
||||
UserId: 0,
|
||||
OrgRole: models.ROLE_ADMIN,
|
||||
}
|
||||
cmd, err := dr.buildSaveDashboardCommand(dto)
|
||||
cmd, err := dr.buildSaveDashboardCommand(dto, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -191,7 +196,7 @@ func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDash
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
|
||||
cmd, err := dr.buildSaveDashboardCommand(dto)
|
||||
cmd, err := dr.buildSaveDashboardCommand(dto, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestDashboardService(t *testing.T) {
|
||||
service := dashboardServiceImpl{}
|
||||
|
||||
origNewDashboardGuardian := guardian.New
|
||||
mockDashboardGuardian(&fakeDashboardGuardian{canSave: true})
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
||||
|
||||
Convey("Save dashboard validation", func() {
|
||||
dto := &SaveDashboardDTO{}
|
||||
@@ -72,7 +72,7 @@ func TestDashboardService(t *testing.T) {
|
||||
dto.Dashboard.SetUid(tc.Uid)
|
||||
dto.User = &models.SignedInUser{}
|
||||
|
||||
_, err := service.buildSaveDashboardCommand(dto)
|
||||
_, err := service.buildSaveDashboardCommand(dto, true)
|
||||
So(err, ShouldEqual, tc.Error)
|
||||
}
|
||||
})
|
||||
@@ -93,52 +93,3 @@ func TestDashboardService(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func mockDashboardGuardian(mock *fakeDashboardGuardian) {
|
||||
guardian.New = func(dashId int64, orgId int64, user *models.SignedInUser) guardian.DashboardGuardian {
|
||||
mock.orgId = orgId
|
||||
mock.dashId = dashId
|
||||
mock.user = user
|
||||
return mock
|
||||
}
|
||||
}
|
||||
|
||||
type fakeDashboardGuardian struct {
|
||||
dashId int64
|
||||
orgId int64
|
||||
user *models.SignedInUser
|
||||
canSave bool
|
||||
canEdit bool
|
||||
canView bool
|
||||
canAdmin bool
|
||||
hasPermission bool
|
||||
checkPermissionBeforeUpdate bool
|
||||
}
|
||||
|
||||
func (g *fakeDashboardGuardian) CanSave() (bool, error) {
|
||||
return g.canSave, nil
|
||||
}
|
||||
|
||||
func (g *fakeDashboardGuardian) CanEdit() (bool, error) {
|
||||
return g.canEdit, nil
|
||||
}
|
||||
|
||||
func (g *fakeDashboardGuardian) CanView() (bool, error) {
|
||||
return g.canView, nil
|
||||
}
|
||||
|
||||
func (g *fakeDashboardGuardian) CanAdmin() (bool, error) {
|
||||
return g.canAdmin, nil
|
||||
}
|
||||
|
||||
func (g *fakeDashboardGuardian) HasPermission(permission models.PermissionType) (bool, error) {
|
||||
return g.hasPermission, nil
|
||||
}
|
||||
|
||||
func (g *fakeDashboardGuardian) CheckPermissionBeforeUpdate(permission models.PermissionType, updatePermissions []*models.DashboardAcl) (bool, error) {
|
||||
return g.checkPermissionBeforeUpdate, nil
|
||||
}
|
||||
|
||||
func (g *fakeDashboardGuardian) GetAcl() ([]*models.DashboardAclInfoDTO, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
package dashboards
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
)
|
||||
|
||||
// FolderService service for operating on folders
|
||||
type FolderService interface {
|
||||
GetFolders(limit int) ([]*models.Folder, error)
|
||||
GetFolderById(id int64) (*models.Folder, error)
|
||||
GetFolderByUid(uid string) (*models.Folder, error)
|
||||
CreateFolder(cmd *models.CreateFolderCommand) error
|
||||
UpdateFolder(uid string, cmd *models.UpdateFolderCommand) error
|
||||
DeleteFolder(uid string) (*models.Folder, error)
|
||||
}
|
||||
|
||||
// NewFolderService factory for creating a new folder service
|
||||
var NewFolderService = func(orgId int64, user *models.SignedInUser) FolderService {
|
||||
return &dashboardServiceImpl{
|
||||
orgId: orgId,
|
||||
user: user,
|
||||
}
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) GetFolders(limit int) ([]*models.Folder, error) {
|
||||
if limit == 0 {
|
||||
limit = 1000
|
||||
}
|
||||
|
||||
searchQuery := search.Query{
|
||||
SignedInUser: dr.user,
|
||||
DashboardIds: make([]int64, 0),
|
||||
FolderIds: make([]int64, 0),
|
||||
Limit: limit,
|
||||
OrgId: dr.orgId,
|
||||
Type: "dash-folder",
|
||||
Permission: models.PERMISSION_VIEW,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&searchQuery); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folders := make([]*models.Folder, 0)
|
||||
|
||||
for _, hit := range searchQuery.Result {
|
||||
folders = append(folders, &models.Folder{
|
||||
Id: hit.Id,
|
||||
Uid: hit.Uid,
|
||||
Title: hit.Title,
|
||||
})
|
||||
}
|
||||
|
||||
return folders, nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) GetFolderById(id int64) (*models.Folder, error) {
|
||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Id: id}
|
||||
dashFolder, err := getFolder(query)
|
||||
|
||||
if err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
g := guardian.New(dashFolder.Id, dr.orgId, dr.user)
|
||||
if canView, err := g.CanView(); err != nil || !canView {
|
||||
if err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
return nil, models.ErrFolderAccessDenied
|
||||
}
|
||||
|
||||
return dashToFolder(dashFolder), nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) GetFolderByUid(uid string) (*models.Folder, error) {
|
||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Uid: uid}
|
||||
dashFolder, err := getFolder(query)
|
||||
|
||||
if err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
g := guardian.New(dashFolder.Id, dr.orgId, dr.user)
|
||||
if canView, err := g.CanView(); err != nil || !canView {
|
||||
if err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
return nil, models.ErrFolderAccessDenied
|
||||
}
|
||||
|
||||
return dashToFolder(dashFolder), nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) error {
|
||||
dashFolder := cmd.GetDashboardModel(dr.orgId, dr.user.UserId)
|
||||
|
||||
dto := &SaveDashboardDTO{
|
||||
Dashboard: dashFolder,
|
||||
OrgId: dr.orgId,
|
||||
User: dr.user,
|
||||
}
|
||||
|
||||
saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
err = bus.Dispatch(saveDashboardCmd)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Id: saveDashboardCmd.Result.Id}
|
||||
dashFolder, err = getFolder(query)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
cmd.Result = dashToFolder(dashFolder)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) UpdateFolder(existingUid string, cmd *models.UpdateFolderCommand) error {
|
||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Uid: existingUid}
|
||||
dashFolder, err := getFolder(query)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
cmd.UpdateDashboardModel(dashFolder, dr.orgId, dr.user.UserId)
|
||||
|
||||
dto := &SaveDashboardDTO{
|
||||
Dashboard: dashFolder,
|
||||
OrgId: dr.orgId,
|
||||
User: dr.user,
|
||||
Overwrite: cmd.Overwrite,
|
||||
}
|
||||
|
||||
saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
err = bus.Dispatch(saveDashboardCmd)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
query = models.GetDashboardQuery{OrgId: dr.orgId, Id: saveDashboardCmd.Result.Id}
|
||||
dashFolder, err = getFolder(query)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
cmd.Result = dashToFolder(dashFolder)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) DeleteFolder(uid string) (*models.Folder, error) {
|
||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Uid: uid}
|
||||
dashFolder, err := getFolder(query)
|
||||
if err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
guardian := guardian.New(dashFolder.Id, dr.orgId, dr.user)
|
||||
if canSave, err := guardian.CanSave(); err != nil || !canSave {
|
||||
if err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
return nil, models.ErrFolderAccessDenied
|
||||
}
|
||||
|
||||
deleteCmd := models.DeleteDashboardCommand{OrgId: dr.orgId, Id: dashFolder.Id}
|
||||
if err := bus.Dispatch(&deleteCmd); err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
return dashToFolder(dashFolder), nil
|
||||
}
|
||||
|
||||
func getFolder(query models.GetDashboardQuery) (*models.Dashboard, error) {
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
if !query.Result.IsFolder {
|
||||
return nil, models.ErrFolderNotFound
|
||||
}
|
||||
|
||||
return query.Result, nil
|
||||
}
|
||||
|
||||
func dashToFolder(dash *models.Dashboard) *models.Folder {
|
||||
return &models.Folder{
|
||||
Id: dash.Id,
|
||||
Uid: dash.Uid,
|
||||
Title: dash.Title,
|
||||
HasAcl: dash.HasAcl,
|
||||
Url: dash.GetUrl(),
|
||||
Version: dash.Version,
|
||||
Created: dash.Created,
|
||||
CreatedBy: dash.CreatedBy,
|
||||
Updated: dash.Updated,
|
||||
UpdatedBy: dash.UpdatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
func toFolderError(err error) error {
|
||||
if err == models.ErrDashboardTitleEmpty {
|
||||
return models.ErrFolderTitleEmpty
|
||||
}
|
||||
|
||||
if err == models.ErrDashboardUpdateAccessDenied {
|
||||
return models.ErrFolderAccessDenied
|
||||
}
|
||||
|
||||
if err == models.ErrDashboardWithSameNameInFolderExists {
|
||||
return models.ErrFolderSameNameExists
|
||||
}
|
||||
|
||||
if err == models.ErrDashboardWithSameUIDExists {
|
||||
return models.ErrFolderWithSameUIDExists
|
||||
}
|
||||
|
||||
if err == models.ErrDashboardVersionMismatch {
|
||||
return models.ErrFolderVersionMismatch
|
||||
}
|
||||
|
||||
if err == models.ErrDashboardNotFound {
|
||||
return models.ErrFolderNotFound
|
||||
}
|
||||
|
||||
if err == models.ErrDashboardFailedGenerateUniqueUid {
|
||||
err = models.ErrFolderFailedGenerateUniqueUid
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package dashboards
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestFolderService(t *testing.T) {
|
||||
Convey("Folder service tests", t, func() {
|
||||
service := dashboardServiceImpl{
|
||||
orgId: 1,
|
||||
user: &models.SignedInUser{UserId: 1},
|
||||
}
|
||||
|
||||
Convey("Given user has no permissions", func() {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{})
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
|
||||
query.Result = models.NewDashboardFolder("Folder")
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
return models.ErrDashboardUpdateAccessDenied
|
||||
})
|
||||
|
||||
Convey("When get folder by id should return access denied error", func() {
|
||||
_, err := service.GetFolderById(1)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
Convey("When get folder by uid should return access denied error", func() {
|
||||
_, err := service.GetFolderByUid("uid")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
Convey("When creating folder should return access denied error", func() {
|
||||
err := service.CreateFolder(&models.CreateFolderCommand{
|
||||
Title: "Folder",
|
||||
})
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
Convey("When updating folder should return access denied error", func() {
|
||||
err := service.UpdateFolder("uid", &models.UpdateFolderCommand{
|
||||
Uid: "uid",
|
||||
Title: "Folder",
|
||||
})
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
Convey("When deleting folder by uid should return access denied error", func() {
|
||||
_, err := service.DeleteFolder("uid")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
Reset(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given user has permission to save", func() {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
||||
|
||||
dash := models.NewDashboardFolder("Folder")
|
||||
dash.Id = 1
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
|
||||
query.Result = dash
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.SaveDashboardCommand) error {
|
||||
cmd.Result = dash
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
Convey("When creating folder should not return access denied error", func() {
|
||||
err := service.CreateFolder(&models.CreateFolderCommand{
|
||||
Title: "Folder",
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("When updating folder should not return access denied error", func() {
|
||||
err := service.UpdateFolder("uid", &models.UpdateFolderCommand{
|
||||
Uid: "uid",
|
||||
Title: "Folder",
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("When deleting folder by uid should not return access denied error", func() {
|
||||
_, err := service.DeleteFolder("uid")
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Reset(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given user has permission to view", func() {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true})
|
||||
|
||||
dashFolder := models.NewDashboardFolder("Folder")
|
||||
dashFolder.Id = 1
|
||||
dashFolder.Uid = "uid-abc"
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
|
||||
query.Result = dashFolder
|
||||
return nil
|
||||
})
|
||||
|
||||
Convey("When get folder by id should return folder", func() {
|
||||
f, _ := service.GetFolderById(1)
|
||||
So(f.Id, ShouldEqual, dashFolder.Id)
|
||||
So(f.Uid, ShouldEqual, dashFolder.Uid)
|
||||
So(f.Title, ShouldEqual, dashFolder.Title)
|
||||
})
|
||||
|
||||
Convey("When get folder by uid should return folder", func() {
|
||||
f, _ := service.GetFolderByUid("uid")
|
||||
So(f.Id, ShouldEqual, dashFolder.Id)
|
||||
So(f.Uid, ShouldEqual, dashFolder.Uid)
|
||||
So(f.Title, ShouldEqual, dashFolder.Title)
|
||||
})
|
||||
|
||||
Reset(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Should map errors correct", func() {
|
||||
testCases := []struct {
|
||||
ActualError error
|
||||
ExpectedError error
|
||||
}{
|
||||
{ActualError: models.ErrDashboardTitleEmpty, ExpectedError: models.ErrFolderTitleEmpty},
|
||||
{ActualError: models.ErrDashboardUpdateAccessDenied, ExpectedError: models.ErrFolderAccessDenied},
|
||||
{ActualError: models.ErrDashboardWithSameNameInFolderExists, ExpectedError: models.ErrFolderSameNameExists},
|
||||
{ActualError: models.ErrDashboardWithSameUIDExists, ExpectedError: models.ErrFolderWithSameUIDExists},
|
||||
{ActualError: models.ErrDashboardVersionMismatch, ExpectedError: models.ErrFolderVersionMismatch},
|
||||
{ActualError: models.ErrDashboardNotFound, ExpectedError: models.ErrFolderNotFound},
|
||||
{ActualError: models.ErrDashboardFailedGenerateUniqueUid, ExpectedError: models.ErrFolderFailedGenerateUniqueUid},
|
||||
{ActualError: models.ErrDashboardInvalidUid, ExpectedError: models.ErrDashboardInvalidUid},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
actualError := toFolderError(tc.ActualError)
|
||||
if actualError != tc.ExpectedError {
|
||||
t.Errorf("For error '%s' expected error '%s', actual '%s'", tc.ActualError, tc.ExpectedError, actualError)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -158,3 +158,52 @@ func (g *dashboardGuardianImpl) getTeams() ([]*m.Team, error) {
|
||||
g.groups = query.Result
|
||||
return query.Result, err
|
||||
}
|
||||
|
||||
type FakeDashboardGuardian struct {
|
||||
DashId int64
|
||||
OrgId int64
|
||||
User *m.SignedInUser
|
||||
CanSaveValue bool
|
||||
CanEditValue bool
|
||||
CanViewValue bool
|
||||
CanAdminValue bool
|
||||
HasPermissionValue bool
|
||||
CheckPermissionBeforeUpdateValue bool
|
||||
}
|
||||
|
||||
func (g *FakeDashboardGuardian) CanSave() (bool, error) {
|
||||
return g.CanSaveValue, nil
|
||||
}
|
||||
|
||||
func (g *FakeDashboardGuardian) CanEdit() (bool, error) {
|
||||
return g.CanEditValue, nil
|
||||
}
|
||||
|
||||
func (g *FakeDashboardGuardian) CanView() (bool, error) {
|
||||
return g.CanViewValue, nil
|
||||
}
|
||||
|
||||
func (g *FakeDashboardGuardian) CanAdmin() (bool, error) {
|
||||
return g.CanAdminValue, nil
|
||||
}
|
||||
|
||||
func (g *FakeDashboardGuardian) HasPermission(permission m.PermissionType) (bool, error) {
|
||||
return g.HasPermissionValue, nil
|
||||
}
|
||||
|
||||
func (g *FakeDashboardGuardian) CheckPermissionBeforeUpdate(permission m.PermissionType, updatePermissions []*m.DashboardAcl) (bool, error) {
|
||||
return g.CheckPermissionBeforeUpdateValue, nil
|
||||
}
|
||||
|
||||
func (g *FakeDashboardGuardian) GetAcl() ([]*m.DashboardAclInfoDTO, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func MockDashboardGuardian(mock *FakeDashboardGuardian) {
|
||||
New = func(dashId int64, orgId int64, user *m.SignedInUser) DashboardGuardian {
|
||||
mock.OrgId = orgId
|
||||
mock.DashId = dashId
|
||||
mock.User = user
|
||||
return mock
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,12 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
||||
func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
|
||||
dash := cmd.GetDashboardModel()
|
||||
|
||||
userId := cmd.UserId
|
||||
|
||||
if userId == 0 {
|
||||
userId = -1
|
||||
}
|
||||
|
||||
if dash.Id > 0 {
|
||||
var existing m.Dashboard
|
||||
dashWithIdExists, err := sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existing)
|
||||
@@ -76,17 +82,23 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
|
||||
|
||||
if dash.Id == 0 {
|
||||
dash.SetVersion(1)
|
||||
dash.Created = time.Now()
|
||||
dash.CreatedBy = userId
|
||||
dash.Updated = time.Now()
|
||||
dash.UpdatedBy = userId
|
||||
metrics.M_Api_Dashboard_Insert.Inc()
|
||||
affectedRows, err = sess.Insert(dash)
|
||||
} else {
|
||||
v := dash.Version
|
||||
v++
|
||||
dash.SetVersion(v)
|
||||
dash.SetVersion(dash.Version + 1)
|
||||
|
||||
if !cmd.UpdatedAt.IsZero() {
|
||||
dash.Updated = cmd.UpdatedAt
|
||||
} else {
|
||||
dash.Updated = time.Now()
|
||||
}
|
||||
|
||||
dash.UpdatedBy = userId
|
||||
|
||||
affectedRows, err = sess.MustCols("folder_id").ID(dash.Id).Update(dash)
|
||||
}
|
||||
|
||||
@@ -514,7 +526,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
|
||||
}
|
||||
|
||||
if !folderExists {
|
||||
return m.ErrFolderNotFound
|
||||
return m.ErrDashboardFolderNotFound
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,9 +142,9 @@ func TestIntegratedDashboardService(t *testing.T) {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||
|
||||
So(sc.dashboardGuardianMock.dashId, ShouldEqual, 0)
|
||||
So(sc.dashboardGuardianMock.orgId, ShouldEqual, cmd.OrgId)
|
||||
So(sc.dashboardGuardianMock.user.UserId, ShouldEqual, cmd.UserId)
|
||||
So(sc.dashboardGuardianMock.DashId, ShouldEqual, 0)
|
||||
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -165,9 +165,9 @@ func TestIntegratedDashboardService(t *testing.T) {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||
|
||||
So(sc.dashboardGuardianMock.dashId, ShouldEqual, otherSavedFolder.Id)
|
||||
So(sc.dashboardGuardianMock.orgId, ShouldEqual, cmd.OrgId)
|
||||
So(sc.dashboardGuardianMock.user.UserId, ShouldEqual, cmd.UserId)
|
||||
So(sc.dashboardGuardianMock.DashId, ShouldEqual, otherSavedFolder.Id)
|
||||
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -189,9 +189,9 @@ func TestIntegratedDashboardService(t *testing.T) {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||
|
||||
So(sc.dashboardGuardianMock.dashId, ShouldEqual, savedDashInGeneralFolder.Id)
|
||||
So(sc.dashboardGuardianMock.orgId, ShouldEqual, cmd.OrgId)
|
||||
So(sc.dashboardGuardianMock.user.UserId, ShouldEqual, cmd.UserId)
|
||||
So(sc.dashboardGuardianMock.DashId, ShouldEqual, savedDashInGeneralFolder.Id)
|
||||
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -213,9 +213,9 @@ func TestIntegratedDashboardService(t *testing.T) {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||
|
||||
So(sc.dashboardGuardianMock.dashId, ShouldEqual, savedDashInFolder.Id)
|
||||
So(sc.dashboardGuardianMock.orgId, ShouldEqual, cmd.OrgId)
|
||||
So(sc.dashboardGuardianMock.user.UserId, ShouldEqual, cmd.UserId)
|
||||
So(sc.dashboardGuardianMock.DashId, ShouldEqual, savedDashInFolder.Id)
|
||||
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -363,7 +363,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
||||
|
||||
Convey("It should result in folder not found error", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderNotFound)
|
||||
So(err, ShouldEqual, models.ErrDashboardFolderNotFound)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -785,68 +785,16 @@ func TestIntegratedDashboardService(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func mockDashboardGuardian(mock *mockDashboardGuarder) {
|
||||
guardian.New = func(dashId int64, orgId int64, user *models.SignedInUser) guardian.DashboardGuardian {
|
||||
mock.orgId = orgId
|
||||
mock.dashId = dashId
|
||||
mock.user = user
|
||||
return mock
|
||||
}
|
||||
}
|
||||
|
||||
type mockDashboardGuarder struct {
|
||||
dashId int64
|
||||
orgId int64
|
||||
user *models.SignedInUser
|
||||
canSave bool
|
||||
canSaveCallCounter int
|
||||
canEdit bool
|
||||
canView bool
|
||||
canAdmin bool
|
||||
hasPermission bool
|
||||
checkPermissionBeforeRemove bool
|
||||
checkPermissionBeforeUpdate bool
|
||||
}
|
||||
|
||||
func (g *mockDashboardGuarder) CanSave() (bool, error) {
|
||||
g.canSaveCallCounter++
|
||||
return g.canSave, nil
|
||||
}
|
||||
|
||||
func (g *mockDashboardGuarder) CanEdit() (bool, error) {
|
||||
return g.canEdit, nil
|
||||
}
|
||||
|
||||
func (g *mockDashboardGuarder) CanView() (bool, error) {
|
||||
return g.canView, nil
|
||||
}
|
||||
|
||||
func (g *mockDashboardGuarder) CanAdmin() (bool, error) {
|
||||
return g.canAdmin, nil
|
||||
}
|
||||
|
||||
func (g *mockDashboardGuarder) HasPermission(permission models.PermissionType) (bool, error) {
|
||||
return g.hasPermission, nil
|
||||
}
|
||||
|
||||
func (g *mockDashboardGuarder) CheckPermissionBeforeUpdate(permission models.PermissionType, updatePermissions []*models.DashboardAcl) (bool, error) {
|
||||
return g.checkPermissionBeforeUpdate, nil
|
||||
}
|
||||
|
||||
func (g *mockDashboardGuarder) GetAcl() ([]*models.DashboardAclInfoDTO, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type scenarioContext struct {
|
||||
dashboardGuardianMock *mockDashboardGuarder
|
||||
dashboardGuardianMock *guardian.FakeDashboardGuardian
|
||||
}
|
||||
|
||||
type scenarioFunc func(c *scenarioContext)
|
||||
|
||||
func dashboardGuardianScenario(desc string, mock *mockDashboardGuarder, fn scenarioFunc) {
|
||||
func dashboardGuardianScenario(desc string, mock *guardian.FakeDashboardGuardian, fn scenarioFunc) {
|
||||
Convey(desc, func() {
|
||||
origNewDashboardGuardian := guardian.New
|
||||
mockDashboardGuardian(mock)
|
||||
guardian.MockDashboardGuardian(mock)
|
||||
|
||||
sc := &scenarioContext{
|
||||
dashboardGuardianMock: mock,
|
||||
@@ -861,15 +809,15 @@ func dashboardGuardianScenario(desc string, mock *mockDashboardGuarder, fn scena
|
||||
}
|
||||
|
||||
type dashboardPermissionScenarioContext struct {
|
||||
dashboardGuardianMock *mockDashboardGuarder
|
||||
dashboardGuardianMock *guardian.FakeDashboardGuardian
|
||||
}
|
||||
|
||||
type dashboardPermissionScenarioFunc func(sc *dashboardPermissionScenarioContext)
|
||||
|
||||
func dashboardPermissionScenario(desc string, mock *mockDashboardGuarder, fn dashboardPermissionScenarioFunc) {
|
||||
func dashboardPermissionScenario(desc string, mock *guardian.FakeDashboardGuardian, fn dashboardPermissionScenarioFunc) {
|
||||
Convey(desc, func() {
|
||||
origNewDashboardGuardian := guardian.New
|
||||
mockDashboardGuardian(mock)
|
||||
guardian.MockDashboardGuardian(mock)
|
||||
|
||||
sc := &dashboardPermissionScenarioContext{
|
||||
dashboardGuardianMock: mock,
|
||||
@@ -884,8 +832,8 @@ func dashboardPermissionScenario(desc string, mock *mockDashboardGuarder, fn das
|
||||
}
|
||||
|
||||
func permissionScenario(desc string, canSave bool, fn dashboardPermissionScenarioFunc) {
|
||||
mock := &mockDashboardGuarder{
|
||||
canSave: canSave,
|
||||
mock := &guardian.FakeDashboardGuardian{
|
||||
CanSaveValue: canSave,
|
||||
}
|
||||
dashboardPermissionScenario(desc, mock, fn)
|
||||
}
|
||||
@@ -902,10 +850,10 @@ func callSaveWithError(cmd models.SaveDashboardCommand) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func dashboardServiceScenario(desc string, mock *mockDashboardGuarder, fn scenarioFunc) {
|
||||
func dashboardServiceScenario(desc string, mock *guardian.FakeDashboardGuardian, fn scenarioFunc) {
|
||||
Convey(desc, func() {
|
||||
origNewDashboardGuardian := guardian.New
|
||||
mockDashboardGuardian(mock)
|
||||
guardian.MockDashboardGuardian(mock)
|
||||
|
||||
sc := &scenarioContext{
|
||||
dashboardGuardianMock: mock,
|
||||
|
||||
@@ -3,6 +3,7 @@ package sqlstore
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
@@ -124,6 +125,24 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
generateNewUid = util.GenerateShortUid
|
||||
})
|
||||
|
||||
Convey("Should be able to create dashboard", func() {
|
||||
cmd := m.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"title": "folderId",
|
||||
"tags": []interface{}{},
|
||||
}),
|
||||
UserId: 100,
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
So(err, ShouldBeNil)
|
||||
So(cmd.Result.CreatedBy, ShouldEqual, 100)
|
||||
So(cmd.Result.Created.IsZero(), ShouldBeFalse)
|
||||
So(cmd.Result.UpdatedBy, ShouldEqual, 100)
|
||||
So(cmd.Result.Updated.IsZero(), ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("Should be able to update dashboard by id and remove folderId", func() {
|
||||
cmd := m.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
@@ -134,6 +153,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
}),
|
||||
Overwrite: true,
|
||||
FolderId: 2,
|
||||
UserId: 100,
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
@@ -149,6 +169,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
}),
|
||||
FolderId: 0,
|
||||
Overwrite: true,
|
||||
UserId: 100,
|
||||
}
|
||||
|
||||
err = SaveDashboard(&cmd)
|
||||
@@ -162,6 +183,10 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
err = GetDashboard(&query)
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result.FolderId, ShouldEqual, 0)
|
||||
So(query.Result.CreatedBy, ShouldEqual, savedDash.CreatedBy)
|
||||
So(query.Result.Created, ShouldEqual, savedDash.Created.Truncate(time.Second))
|
||||
So(query.Result.UpdatedBy, ShouldEqual, 100)
|
||||
So(query.Result.Updated.IsZero(), ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("Should be able to delete a dashboard folder and its children", func() {
|
||||
|
||||
Reference in New Issue
Block a user