PluginManager: Make Plugins, Renderer and DataSources non-global (#31866)
* PluginManager: Make Plugins and DataSources non-global Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix integration tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Replace outdated command Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * DashboardService: Ensure it gets constructed with necessary parameters Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix build Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * DashboardService: Ensure it gets constructed with necessary parameters Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove dead code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove FocusConvey Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove dead code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Undo interface changes Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Backend: Move tsdbifaces.RequestHandler to plugins.DataRequestHandler Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Rename to DataSourceCount Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Consolidate dashboard interfaces into one Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix dashboard integration tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
@@ -3,11 +3,10 @@ package dashboards
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func MakeUserAdmin(bus bus.Bus, orgID int64, userID int64, dashboardID int64, setViewAndEditPermissions bool) error {
|
||||
func (dr *dashboardServiceImpl) MakeUserAdmin(orgID int64, userID int64, dashboardID int64, setViewAndEditPermissions bool) error {
|
||||
rtEditor := models.ROLE_EDITOR
|
||||
rtViewer := models.ROLE_VIEWER
|
||||
|
||||
@@ -43,12 +42,7 @@ func MakeUserAdmin(bus bus.Bus, orgID int64, userID int64, dashboardID int64, se
|
||||
)
|
||||
}
|
||||
|
||||
aclCmd := &models.UpdateDashboardAclCommand{
|
||||
DashboardID: dashboardID,
|
||||
Items: items,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(aclCmd); err != nil {
|
||||
if err := dr.dashboardStore.UpdateDashboardACL(dashboardID, items); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package dashboards
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/gtime"
|
||||
"github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@@ -20,6 +23,7 @@ type DashboardService interface {
|
||||
SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error)
|
||||
ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error)
|
||||
DeleteDashboard(dashboardId int64, orgId int64) error
|
||||
MakeUserAdmin(orgID int64, userID, dashboardID int64, setViewAndEditPermissions bool) error
|
||||
}
|
||||
|
||||
// DashboardProvisioningService is a service for operating on provisioned dashboards.
|
||||
@@ -27,21 +31,22 @@ type DashboardProvisioningService interface {
|
||||
SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
|
||||
SaveFolderForProvisionedDashboards(*SaveDashboardDTO) (*models.Dashboard, error)
|
||||
GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error)
|
||||
GetProvisionedDashboardDataByDashboardID(DashboardId int64) (*models.DashboardProvisioning, error)
|
||||
UnprovisionDashboard(dashboardId int64) error
|
||||
DeleteProvisionedDashboard(dashboardId int64, orgId int64) error
|
||||
GetProvisionedDashboardDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error)
|
||||
UnprovisionDashboard(dashboardID int64) error
|
||||
DeleteProvisionedDashboard(dashboardID int64, orgID int64) error
|
||||
}
|
||||
|
||||
// NewService is a factory for creating a new dashboard service.
|
||||
var NewService = func() DashboardService {
|
||||
var NewService = func(store dashboards.Store) DashboardService {
|
||||
return &dashboardServiceImpl{
|
||||
log: log.New("dashboard-service"),
|
||||
dashboardStore: store,
|
||||
log: log.New("dashboard-service"),
|
||||
}
|
||||
}
|
||||
|
||||
// NewProvisioningService is a factory for creating a new dashboard provisioning service.
|
||||
var NewProvisioningService = func() DashboardProvisioningService {
|
||||
return NewService().(*dashboardServiceImpl)
|
||||
var NewProvisioningService = func(store dashboards.Store) DashboardProvisioningService {
|
||||
return NewService(store).(*dashboardServiceImpl)
|
||||
}
|
||||
|
||||
type SaveDashboardDTO struct {
|
||||
@@ -54,35 +59,32 @@ type SaveDashboardDTO struct {
|
||||
}
|
||||
|
||||
type dashboardServiceImpl struct {
|
||||
orgId int64
|
||||
user *models.SignedInUser
|
||||
log log.Logger
|
||||
dashboardStore dashboards.Store
|
||||
orgId int64
|
||||
user *models.SignedInUser
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
|
||||
cmd := &models.GetProvisionedDashboardDataQuery{Name: name}
|
||||
err := bus.Dispatch(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
return dr.dashboardStore.GetProvisionedDashboardData(name)
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(dashboardId int64) (*models.DashboardProvisioning, error) {
|
||||
cmd := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: dashboardId}
|
||||
err := bus.Dispatch(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
// GetProvisionedData gets provisioned dashboard data.
|
||||
//
|
||||
// Stubbable by tests.
|
||||
var GetProvisionedData = func(store dashboards.Store, dashboardID int64) (*models.DashboardProvisioning, error) {
|
||||
return store.GetProvisionedDataByDashboardID(dashboardID)
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, validateAlerts bool,
|
||||
func (dr *dashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error) {
|
||||
return GetProvisionedData(dr.dashboardStore, dashboardID)
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, shouldValidateAlerts bool,
|
||||
validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
|
||||
dash := dto.Dashboard
|
||||
|
||||
dash.OrgId = dto.OrgId
|
||||
dash.Title = strings.TrimSpace(dash.Title)
|
||||
dash.Data.Set("title", dash.Title)
|
||||
dash.SetUid(strings.TrimSpace(dash.Uid))
|
||||
@@ -109,29 +111,18 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if validateAlerts {
|
||||
validateAlertsCmd := models.ValidateDashboardAlertsCommand{
|
||||
OrgId: dto.OrgId,
|
||||
Dashboard: dash,
|
||||
User: dto.User,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&validateAlertsCmd); err != nil {
|
||||
if shouldValidateAlerts {
|
||||
if err := validateAlerts(dash, dto.User); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
validateBeforeSaveCmd := models.ValidateDashboardBeforeSaveCommand{
|
||||
OrgId: dto.OrgId,
|
||||
Dashboard: dash,
|
||||
Overwrite: dto.Overwrite,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&validateBeforeSaveCmd); err != nil {
|
||||
isParentFolderChanged, err := dr.dashboardStore.ValidateDashboardBeforeSave(dash, dto.Overwrite)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if validateBeforeSaveCmd.Result.IsParentFolderChanged {
|
||||
if isParentFolderChanged {
|
||||
folderGuardian := guardian.New(dash.FolderId, dto.OrgId, dto.User)
|
||||
if canSave, err := folderGuardian.CanSave(); err != nil || !canSave {
|
||||
if err != nil {
|
||||
@@ -178,6 +169,11 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
var validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
extractor := alerting.NewDashAlertExtractor(dash, dash.OrgId, user)
|
||||
return extractor.ValidateAlerts()
|
||||
}
|
||||
|
||||
func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
||||
if setting.MinRefreshInterval == "" {
|
||||
return nil
|
||||
@@ -191,11 +187,11 @@ func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
||||
|
||||
minRefreshInterval, err := gtime.ParseDuration(setting.MinRefreshInterval)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("parsing min refresh interval %q failed: %w", setting.MinRefreshInterval, err)
|
||||
}
|
||||
d, err := gtime.ParseDuration(refresh)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("parsing refresh duration %q failed: %w", refresh, err)
|
||||
}
|
||||
|
||||
if d < minRefreshInterval {
|
||||
@@ -205,14 +201,17 @@ func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) updateAlerting(cmd *models.SaveDashboardCommand, dto *SaveDashboardDTO) error {
|
||||
alertCmd := models.UpdateDashboardAlertsCommand{
|
||||
OrgId: dto.OrgId,
|
||||
Dashboard: cmd.Result,
|
||||
User: dto.User,
|
||||
// UpdateAlerting updates alerting.
|
||||
//
|
||||
// Stubbable by tests.
|
||||
var UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard, user *models.SignedInUser) error {
|
||||
extractor := alerting.NewDashAlertExtractor(dashboard, orgID, user)
|
||||
alerts, err := extractor.GetAlerts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bus.Dispatch(&alertCmd)
|
||||
return store.SaveAlerts(dashboard.Id, alerts)
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO,
|
||||
@@ -234,24 +233,18 @@ func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
saveCmd := &models.SaveProvisionedDashboardCommand{
|
||||
DashboardCmd: cmd,
|
||||
DashboardProvisioning: provisioning,
|
||||
}
|
||||
|
||||
// dashboard
|
||||
err = bus.Dispatch(saveCmd)
|
||||
dash, err := dr.dashboardStore.SaveProvisionedDashboard(*cmd, provisioning)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// alerts
|
||||
err = dr.updateAlerting(cmd, dto)
|
||||
if err != nil {
|
||||
if err := UpdateAlerting(dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
return dash, nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDashboardDTO) (*models.Dashboard, error) {
|
||||
@@ -264,22 +257,23 @@ func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDash
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bus.Dispatch(cmd)
|
||||
dash, err := dr.dashboardStore.SaveDashboard(*cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = dr.updateAlerting(cmd, dto)
|
||||
if err != nil {
|
||||
if err := UpdateAlerting(dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
return dash, nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error) {
|
||||
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
|
||||
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval", "dashboardUid", dto.Dashboard.Uid, "dashboardTitle", dto.Dashboard.Title, "minRefreshInterval", setting.MinRefreshInterval)
|
||||
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval",
|
||||
"dashboardUid", dto.Dashboard.Uid, "dashboardTitle", dto.Dashboard.Title, "minRefreshInterval",
|
||||
setting.MinRefreshInterval)
|
||||
dto.Dashboard.Data.Set("refresh", setting.MinRefreshInterval)
|
||||
}
|
||||
|
||||
@@ -288,17 +282,16 @@ func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO, allowUiUpda
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bus.Dispatch(cmd)
|
||||
dash, err := dr.dashboardStore.SaveDashboard(*cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("saving dashboard failed: %w", err)
|
||||
}
|
||||
|
||||
if err := UpdateAlerting(dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = dr.updateAlerting(cmd, dto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
return dash, nil
|
||||
}
|
||||
|
||||
// DeleteDashboard removes dashboard from the DB. Errors out if the dashboard was provisioned. Should be used for
|
||||
@@ -327,9 +320,12 @@ func (dr *dashboardServiceImpl) deleteDashboard(dashboardId int64, orgId int64,
|
||||
return bus.Dispatch(cmd)
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
|
||||
func (dr *dashboardServiceImpl) ImportDashboard(dto *SaveDashboardDTO) (
|
||||
*models.Dashboard, error) {
|
||||
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
|
||||
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval", "dashboardUid", dto.Dashboard.Uid, "dashboardTitle", dto.Dashboard.Title, "minRefreshInterval", setting.MinRefreshInterval)
|
||||
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval",
|
||||
"dashboardUid", dto.Dashboard.Uid, "dashboardTitle", dto.Dashboard.Title,
|
||||
"minRefreshInterval", setting.MinRefreshInterval)
|
||||
dto.Dashboard.Data.Set("refresh", setting.MinRefreshInterval)
|
||||
}
|
||||
|
||||
@@ -359,6 +355,7 @@ type FakeDashboardService struct {
|
||||
SaveDashboardResult *models.Dashboard
|
||||
SaveDashboardError error
|
||||
SavedDashboards []*SaveDashboardDTO
|
||||
ProvisionedDashData *models.DashboardProvisioning
|
||||
}
|
||||
|
||||
func (s *FakeDashboardService) SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error) {
|
||||
@@ -385,8 +382,12 @@ func (s *FakeDashboardService) DeleteDashboard(dashboardId int64, orgId int64) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FakeDashboardService) GetProvisionedDashboardDataByDashboardID(id int64) (*models.DashboardProvisioning, error) {
|
||||
return s.ProvisionedDashData, nil
|
||||
}
|
||||
|
||||
func MockDashboardService(mock *FakeDashboardService) {
|
||||
NewService = func() DashboardService {
|
||||
NewService = func(dashboards.Store) DashboardService {
|
||||
return mock
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,928 @@
|
||||
// +build integration
|
||||
|
||||
package dashboards
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
const testOrgID int64 = 1
|
||||
|
||||
func TestIntegratedDashboardService(t *testing.T) {
|
||||
t.Run("Given saved folders and dashboards in organization A", func(t *testing.T) {
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
t.Cleanup(func() {
|
||||
UpdateAlerting = origUpdateAlerting
|
||||
})
|
||||
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Basic validation tests
|
||||
|
||||
permissionScenario(t, "When saving a dashboard with non-existing id", true,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": float64(123412321),
|
||||
"title": "Expect error",
|
||||
}),
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardNotFound, err)
|
||||
})
|
||||
|
||||
// Given other organization
|
||||
|
||||
t.Run("Given organization B", func(t *testing.T) {
|
||||
const otherOrgId int64 = 2
|
||||
|
||||
permissionScenario(t, "When creating a dashboard with same id as dashboard in organization A",
|
||||
true, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: otherOrgId,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInFolder.Id,
|
||||
"title": "Expect error",
|
||||
}),
|
||||
Overwrite: false,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardNotFound, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a dashboard with same uid as dashboard in organization A, it should create a new dashboard in org B",
|
||||
true, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
const otherOrgId int64 = 2
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: otherOrgId,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"uid": sc.savedDashInFolder.Uid,
|
||||
"title": "Dash with existing uid in other org",
|
||||
}),
|
||||
Overwrite: false,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(0, otherOrgId, sc.savedDashInFolder.Uid, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotEqual(t, sc.savedDashInFolder.Id, dash.Id)
|
||||
assert.Equal(t, res.Id, dash.Id)
|
||||
assert.Equal(t, otherOrgId, dash.OrgId)
|
||||
assert.Equal(t, sc.savedDashInFolder.Uid, dash.Uid)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Given user has no permission to save", func(t *testing.T) {
|
||||
const canSave = false
|
||||
|
||||
permissionScenario(t, "When creating a new dashboard in the General folder", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"title": "Dash",
|
||||
}),
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a new dashboard in other folder, it should create dashboard guardian for other folder with correct arguments and rsult in access denied error",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"title": "Dash",
|
||||
}),
|
||||
FolderId: sc.otherSavedFolder.Id,
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a new dashboard by existing title in folder, it should create dashboard guardian for folder with correct arguments and result in access denied error",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"title": sc.savedDashInFolder.Title,
|
||||
}),
|
||||
FolderId: sc.savedFolder.Id,
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, sc.savedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a new dashboard by existing UID in folder, it should create dashboard guardian for folder with correct arguments and result in access denied error",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"uid": sc.savedDashInFolder.Uid,
|
||||
"title": "New dash",
|
||||
}),
|
||||
FolderId: sc.savedFolder.Id,
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, sc.savedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating a dashboard by existing id in the General folder, it should create dashboard guardian for dashboard with correct arguments and result in access denied error",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInGeneralFolder.Id,
|
||||
"title": "Dash",
|
||||
}),
|
||||
FolderId: sc.savedDashInGeneralFolder.FolderId,
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating a dashboard by existing id in other folder, it should create dashboard guardian for dashboard with correct arguments and result in access denied error",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInFolder.Id,
|
||||
"title": "Dash",
|
||||
}),
|
||||
FolderId: sc.savedDashInFolder.FolderId,
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When moving a dashboard by existing ID to other folder from General folder, it should create dashboard guardian for other folder with correct arguments and result in access denied error",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInGeneralFolder.Id,
|
||||
"title": "Dash",
|
||||
}),
|
||||
FolderId: sc.otherSavedFolder.Id,
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When moving a dashboard by existing id to the General folder from other folder, it should create dashboard guardian for General folder with correct arguments and result in access denied error",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInFolder.Id,
|
||||
"title": "Dash",
|
||||
}),
|
||||
FolderId: 0,
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When moving a dashboard by existing uid to other folder from General folder, it should create dashboard guardian for other folder with correct arguments and result in access denied error",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"uid": sc.savedDashInGeneralFolder.Uid,
|
||||
"title": "Dash",
|
||||
}),
|
||||
FolderId: sc.otherSavedFolder.Id,
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When moving a dashboard by existing UID to the General folder from other folder, it should create dashboard guardian for General folder with correct arguments and result in access denied error",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"uid": sc.savedDashInFolder.Uid,
|
||||
"title": "Dash",
|
||||
}),
|
||||
FolderId: 0,
|
||||
UserId: 10000,
|
||||
Overwrite: true,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||
|
||||
assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId)
|
||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Given user has permission to save", func(t *testing.T) {
|
||||
const canSave = true
|
||||
|
||||
t.Run("and overwrite flag is set to false", func(t *testing.T) {
|
||||
const shouldOverwrite = false
|
||||
|
||||
permissionScenario(t, "When creating a dashboard in General folder with same name as dashboard in other folder",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": sc.savedDashInFolder.Title,
|
||||
}),
|
||||
FolderId: 0,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.Id, dash.Id)
|
||||
assert.Equal(t, int64(0), dash.FolderId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a dashboard in other folder with same name as dashboard in General folder",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": sc.savedDashInGeneralFolder.Title,
|
||||
}),
|
||||
FolderId: sc.savedFolder.Id,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
assert.NotEqual(t, sc.savedDashInGeneralFolder.Id, res.Id)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, sc.savedFolder.Id, dash.FolderId)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a folder with same name as dashboard in other folder",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": sc.savedDashInFolder.Title,
|
||||
}),
|
||||
IsFolder: true,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
assert.NotEqual(t, sc.savedDashInGeneralFolder.Id, res.Id)
|
||||
assert.True(t, res.IsFolder)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(0), dash.FolderId)
|
||||
assert.True(t, dash.IsFolder)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When saving a dashboard without id and uid and unique title in folder",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"title": "Dash without id and uid",
|
||||
}),
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
assert.Greater(t, res.Id, int64(0))
|
||||
assert.NotEmpty(t, res.Uid)
|
||||
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.Id, dash.Id)
|
||||
assert.Equal(t, res.Uid, dash.Uid)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When saving a dashboard when dashboard id is zero ", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": 0,
|
||||
"title": "Dash with zero id",
|
||||
}),
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.Id, dash.Id)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When saving a dashboard in non-existing folder", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"title": "Expect error",
|
||||
}),
|
||||
FolderId: 123412321,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardFolderNotFound, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating an existing dashboard by id without current version", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInGeneralFolder.Id,
|
||||
"title": "test dash 23",
|
||||
}),
|
||||
FolderId: sc.savedFolder.Id,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardVersionMismatch, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating an existing dashboard by id with current version", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInGeneralFolder.Id,
|
||||
"title": "Updated title",
|
||||
"version": sc.savedDashInGeneralFolder.Version,
|
||||
}),
|
||||
FolderId: sc.savedFolder.Id,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInGeneralFolder.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated title", dash.Title)
|
||||
assert.Equal(t, sc.savedFolder.Id, dash.FolderId)
|
||||
assert.Greater(t, dash.Version, sc.savedDashInGeneralFolder.Version)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating an existing dashboard by uid without current version", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"uid": sc.savedDashInFolder.Uid,
|
||||
"title": "test dash 23",
|
||||
}),
|
||||
FolderId: 0,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardVersionMismatch, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating an existing dashboard by uid with current version", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"uid": sc.savedDashInFolder.Uid,
|
||||
"title": "Updated title",
|
||||
"version": sc.savedDashInFolder.Version,
|
||||
}),
|
||||
FolderId: 0,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInFolder.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated title", dash.Title)
|
||||
assert.Equal(t, int64(0), dash.FolderId)
|
||||
assert.Greater(t, dash.Version, sc.savedDashInFolder.Version)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a dashboard with same name as dashboard in other folder",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": sc.savedDashInFolder.Title,
|
||||
}),
|
||||
FolderId: sc.savedDashInFolder.FolderId,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardWithSameNameInFolderExists, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a dashboard with same name as dashboard in General folder",
|
||||
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": sc.savedDashInGeneralFolder.Title,
|
||||
}),
|
||||
FolderId: sc.savedDashInGeneralFolder.FolderId,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardWithSameNameInFolderExists, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a folder with same name as existing folder", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": sc.savedFolder.Title,
|
||||
}),
|
||||
IsFolder: true,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardWithSameNameInFolderExists, err)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("and overwrite flag is set to true", func(t *testing.T) {
|
||||
const shouldOverwrite = true
|
||||
|
||||
permissionScenario(t, "When updating an existing dashboard by id without current version", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInGeneralFolder.Id,
|
||||
"title": "Updated title",
|
||||
}),
|
||||
FolderId: sc.savedFolder.Id,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInGeneralFolder.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated title", dash.Title)
|
||||
assert.Equal(t, sc.savedFolder.Id, dash.FolderId)
|
||||
assert.Greater(t, dash.Version, sc.savedDashInGeneralFolder.Version)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating an existing dashboard by uid without current version", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"uid": sc.savedDashInFolder.Uid,
|
||||
"title": "Updated title",
|
||||
}),
|
||||
FolderId: 0,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInFolder.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated title", dash.Title)
|
||||
assert.Equal(t, int64(0), dash.FolderId)
|
||||
assert.Greater(t, dash.Version, sc.savedDashInFolder.Version)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating uid for existing dashboard using id", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInFolder.Id,
|
||||
"uid": "new-uid",
|
||||
"title": sc.savedDashInFolder.Title,
|
||||
}),
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
assert.Equal(t, sc.savedDashInFolder.Id, res.Id)
|
||||
assert.Equal(t, "new-uid", res.Uid)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInFolder.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "new-uid", dash.Uid)
|
||||
assert.Greater(t, dash.Version, sc.savedDashInFolder.Version)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating uid to an existing uid for existing dashboard using id", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInFolder.Id,
|
||||
"uid": sc.savedDashInGeneralFolder.Uid,
|
||||
"title": sc.savedDashInFolder.Title,
|
||||
}),
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardWithSameUIDExists, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a dashboard with same name as dashboard in other folder", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": sc.savedDashInFolder.Title,
|
||||
}),
|
||||
FolderId: sc.savedDashInFolder.FolderId,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
assert.Equal(t, sc.savedDashInFolder.Id, res.Id)
|
||||
assert.Equal(t, sc.savedDashInFolder.Uid, res.Uid)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.Id, dash.Id)
|
||||
assert.Equal(t, res.Uid, dash.Uid)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When creating a dashboard with same name as dashboard in General folder", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: testOrgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": sc.savedDashInGeneralFolder.Title,
|
||||
}),
|
||||
FolderId: sc.savedDashInGeneralFolder.FolderId,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||
require.NotNil(t, res)
|
||||
assert.Equal(t, sc.savedDashInGeneralFolder.Id, res.Id)
|
||||
assert.Equal(t, sc.savedDashInGeneralFolder.Uid, res.Uid)
|
||||
|
||||
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.Id, dash.Id)
|
||||
assert.Equal(t, res.Uid, dash.Uid)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating existing folder to a dashboard using id", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedFolder.Id,
|
||||
"title": "new title",
|
||||
}),
|
||||
IsFolder: false,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating existing dashboard to a folder using id", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": sc.savedDashInFolder.Id,
|
||||
"title": "new folder title",
|
||||
}),
|
||||
IsFolder: true,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating existing folder to a dashboard using uid", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"uid": sc.savedFolder.Uid,
|
||||
"title": "new title",
|
||||
}),
|
||||
IsFolder: false,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating existing dashboard to a folder using uid", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"uid": sc.savedDashInFolder.Uid,
|
||||
"title": "new folder title",
|
||||
}),
|
||||
IsFolder: true,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating existing folder to a dashboard using title", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"title": sc.savedFolder.Title,
|
||||
}),
|
||||
IsFolder: false,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardWithSameNameAsFolder, err)
|
||||
})
|
||||
|
||||
permissionScenario(t, "When updating existing dashboard to a folder using title", canSave,
|
||||
func(t *testing.T, sc *permissionScenarioContext) {
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"title": sc.savedDashInGeneralFolder.Title,
|
||||
}),
|
||||
IsFolder: true,
|
||||
Overwrite: shouldOverwrite,
|
||||
}
|
||||
|
||||
err := callSaveWithError(cmd, sc.sqlStore)
|
||||
assert.Equal(t, models.ErrDashboardFolderWithSameNameAsDashboard, err)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type permissionScenarioContext struct {
|
||||
dashboardGuardianMock *guardian.FakeDashboardGuardian
|
||||
sqlStore *sqlstore.SQLStore
|
||||
savedFolder *models.Dashboard
|
||||
savedDashInFolder *models.Dashboard
|
||||
otherSavedFolder *models.Dashboard
|
||||
savedDashInGeneralFolder *models.Dashboard
|
||||
}
|
||||
|
||||
type permissionScenarioFunc func(t *testing.T, sc *permissionScenarioContext)
|
||||
|
||||
func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionScenarioFunc) {
|
||||
t.Helper()
|
||||
|
||||
mock := &guardian.FakeDashboardGuardian{
|
||||
CanSaveValue: canSave,
|
||||
}
|
||||
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
|
||||
savedFolder := saveTestFolder(t, "Saved folder", testOrgID, sqlStore)
|
||||
savedDashInFolder := saveTestDashboard(t, "Saved dash in folder", testOrgID, savedFolder.Id, sqlStore)
|
||||
saveTestDashboard(t, "Other saved dash in folder", testOrgID, savedFolder.Id, sqlStore)
|
||||
savedDashInGeneralFolder := saveTestDashboard(t, "Saved dashboard in general folder", testOrgID, 0, sqlStore)
|
||||
otherSavedFolder := saveTestFolder(t, "Other saved folder", testOrgID, sqlStore)
|
||||
|
||||
require.Equal(t, "Saved folder", savedFolder.Title)
|
||||
require.Equal(t, "saved-folder", savedFolder.Slug)
|
||||
require.NotEqual(t, int64(0), savedFolder.Id)
|
||||
require.True(t, savedFolder.IsFolder)
|
||||
require.Equal(t, int64(0), savedFolder.FolderId)
|
||||
require.NotEmpty(t, savedFolder.Uid)
|
||||
|
||||
require.Equal(t, "Saved dash in folder", savedDashInFolder.Title)
|
||||
require.Equal(t, "saved-dash-in-folder", savedDashInFolder.Slug)
|
||||
require.NotEqual(t, int64(0), savedDashInFolder.Id)
|
||||
require.False(t, savedDashInFolder.IsFolder)
|
||||
require.Equal(t, savedFolder.Id, savedDashInFolder.FolderId)
|
||||
require.NotEmpty(t, savedDashInFolder.Uid)
|
||||
|
||||
origNewDashboardGuardian := guardian.New
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewDashboardGuardian
|
||||
})
|
||||
guardian.MockDashboardGuardian(mock)
|
||||
|
||||
sc := &permissionScenarioContext{
|
||||
dashboardGuardianMock: mock,
|
||||
sqlStore: sqlStore,
|
||||
savedDashInFolder: savedDashInFolder,
|
||||
otherSavedFolder: otherSavedFolder,
|
||||
savedDashInGeneralFolder: savedDashInGeneralFolder,
|
||||
savedFolder: savedFolder,
|
||||
}
|
||||
|
||||
fn(t, sc)
|
||||
})
|
||||
}
|
||||
|
||||
func callSaveWithResult(t *testing.T, cmd models.SaveDashboardCommand, sqlStore *sqlstore.SQLStore) *models.Dashboard {
|
||||
t.Helper()
|
||||
|
||||
dto := toSaveDashboardDto(cmd)
|
||||
res, err := NewService(sqlStore).SaveDashboard(&dto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func callSaveWithError(cmd models.SaveDashboardCommand, sqlStore *sqlstore.SQLStore) error {
|
||||
dto := toSaveDashboardDto(cmd)
|
||||
_, err := NewService(sqlStore).SaveDashboard(&dto, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlStore *sqlstore.SQLStore) *models.Dashboard {
|
||||
t.Helper()
|
||||
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: orgID,
|
||||
FolderId: folderID,
|
||||
IsFolder: false,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": title,
|
||||
}),
|
||||
}
|
||||
|
||||
dto := SaveDashboardDTO{
|
||||
OrgId: orgID,
|
||||
Dashboard: cmd.GetDashboardModel(),
|
||||
User: &models.SignedInUser{
|
||||
UserId: 1,
|
||||
OrgRole: models.ROLE_ADMIN,
|
||||
},
|
||||
}
|
||||
|
||||
res, err := NewService(sqlStore).SaveDashboard(&dto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore *sqlstore.SQLStore) *models.Dashboard {
|
||||
t.Helper()
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: orgID,
|
||||
FolderId: 0,
|
||||
IsFolder: true,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": title,
|
||||
}),
|
||||
}
|
||||
|
||||
dto := SaveDashboardDTO{
|
||||
OrgId: orgID,
|
||||
Dashboard: cmd.GetDashboardModel(),
|
||||
User: &models.SignedInUser{
|
||||
UserId: 1,
|
||||
OrgRole: models.ROLE_ADMIN,
|
||||
},
|
||||
}
|
||||
|
||||
res, err := NewService(sqlStore).SaveDashboard(&dto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func toSaveDashboardDto(cmd models.SaveDashboardCommand) SaveDashboardDTO {
|
||||
dash := (&cmd).GetDashboardModel()
|
||||
|
||||
return SaveDashboardDTO{
|
||||
Dashboard: dash,
|
||||
Message: cmd.Message,
|
||||
OrgId: cmd.OrgId,
|
||||
User: &models.SignedInUser{UserId: cmd.UserId},
|
||||
Overwrite: cmd.Overwrite,
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
@@ -17,8 +18,10 @@ func TestDashboardService(t *testing.T) {
|
||||
Convey("Dashboard service tests", t, func() {
|
||||
bus.ClearBusHandlers()
|
||||
|
||||
fakeStore := fakeDashboardStore{}
|
||||
service := &dashboardServiceImpl{
|
||||
log: log.New("test.logger"),
|
||||
log: log.New("test.logger"),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
origNewDashboardGuardian := guardian.New
|
||||
@@ -51,19 +54,13 @@ func TestDashboardService(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("When saving a dashboard should validate uid", func() {
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
cmd.Result = nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
Uid string
|
||||
@@ -89,27 +86,23 @@ func TestDashboardService(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Should return validation error if dashboard is provisioned", func() {
|
||||
provisioningValidated := false
|
||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
provisioningValidated = true
|
||||
cmd.Result = &models.DashboardProvisioning{}
|
||||
return nil
|
||||
t.Cleanup(func() {
|
||||
fakeStore.provisionedData = nil
|
||||
})
|
||||
fakeStore.provisionedData = &models.DashboardProvisioning{}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
dto.Dashboard = models.NewDashboard("Dash")
|
||||
dto.Dashboard.SetId(3)
|
||||
dto.User = &models.SignedInUser{UserId: 1}
|
||||
_, err := service.SaveDashboard(dto, false)
|
||||
So(provisioningValidated, ShouldBeTrue)
|
||||
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
|
||||
})
|
||||
|
||||
@@ -121,21 +114,20 @@ func TestDashboardService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
dto.Dashboard = models.NewDashboard("Dash")
|
||||
dto.Dashboard.SetId(3)
|
||||
dto.User = &models.SignedInUser{UserId: 1}
|
||||
_, err := service.SaveDashboard(dto, true)
|
||||
So(provisioningValidated, ShouldBeFalse)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Should return validation error if alert data is invalid", func() {
|
||||
@@ -144,9 +136,13 @@ func TestDashboardService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return fmt.Errorf("alert validation error")
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return fmt.Errorf("alert validation error")
|
||||
}
|
||||
|
||||
dto.Dashboard = models.NewDashboard("Dash")
|
||||
_, err := service.SaveDashboard(dto, false)
|
||||
@@ -165,23 +161,27 @@ func TestDashboardService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
t.Cleanup(func() {
|
||||
UpdateAlerting = origUpdateAlerting
|
||||
})
|
||||
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||
user *models.SignedInUser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
dto.Dashboard = models.NewDashboard("Dash")
|
||||
dto.Dashboard.SetId(3)
|
||||
dto.User = &models.SignedInUser{UserId: 1}
|
||||
@@ -200,22 +200,26 @@ func TestDashboardService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
t.Cleanup(func() {
|
||||
UpdateAlerting = origUpdateAlerting
|
||||
})
|
||||
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||
user *models.SignedInUser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
dto.Dashboard = models.NewDashboard("Dash")
|
||||
dto.Dashboard.SetId(3)
|
||||
@@ -231,41 +235,42 @@ func TestDashboardService(t *testing.T) {
|
||||
dto := &SaveDashboardDTO{}
|
||||
|
||||
Convey("Should return validation error if dashboard is provisioned", func() {
|
||||
provisioningValidated := false
|
||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
provisioningValidated = true
|
||||
cmd.Result = &models.DashboardProvisioning{}
|
||||
return nil
|
||||
t.Cleanup(func() {
|
||||
fakeStore.provisionedData = nil
|
||||
})
|
||||
fakeStore.provisionedData = &models.DashboardProvisioning{}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
t.Cleanup(func() {
|
||||
UpdateAlerting = origUpdateAlerting
|
||||
})
|
||||
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||
user *models.SignedInUser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
dto.Dashboard = models.NewDashboard("Dash")
|
||||
dto.Dashboard.SetId(3)
|
||||
dto.User = &models.SignedInUser{UserId: 1}
|
||||
_, err := service.ImportDashboard(dto)
|
||||
So(provisioningValidated, ShouldBeTrue)
|
||||
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given provisioned dashboard", func() {
|
||||
result := setupDeleteHandlers(true)
|
||||
result := setupDeleteHandlers(t, &fakeStore, true)
|
||||
|
||||
Convey("DeleteProvisionedDashboard should delete it", func() {
|
||||
err := service.DeleteProvisionedDashboard(1, 1)
|
||||
@@ -281,7 +286,7 @@ func TestDashboardService(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Given non provisioned dashboard", func() {
|
||||
result := setupDeleteHandlers(false)
|
||||
result := setupDeleteHandlers(t, &fakeStore, false)
|
||||
|
||||
Convey("DeleteProvisionedDashboard should delete it", func() {
|
||||
err := service.DeleteProvisionedDashboard(1, 1)
|
||||
@@ -306,15 +311,15 @@ type Result struct {
|
||||
deleteWasCalled bool
|
||||
}
|
||||
|
||||
func setupDeleteHandlers(provisioned bool) *Result {
|
||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
if provisioned {
|
||||
cmd.Result = &models.DashboardProvisioning{}
|
||||
} else {
|
||||
cmd.Result = nil
|
||||
}
|
||||
return nil
|
||||
func setupDeleteHandlers(t *testing.T, fakeStore *fakeDashboardStore, provisioned bool) *Result {
|
||||
t.Helper()
|
||||
|
||||
t.Cleanup(func() {
|
||||
fakeStore.provisionedData = nil
|
||||
})
|
||||
if provisioned {
|
||||
fakeStore.provisionedData = &models.DashboardProvisioning{}
|
||||
}
|
||||
|
||||
result := &Result{}
|
||||
bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
|
||||
@@ -326,3 +331,32 @@ func setupDeleteHandlers(provisioned bool) *Result {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
type fakeDashboardStore struct {
|
||||
dashboards.Store
|
||||
|
||||
validationError error
|
||||
provisionedData *models.DashboardProvisioning
|
||||
}
|
||||
|
||||
func (s *fakeDashboardStore) ValidateDashboardBeforeSave(dashboard *models.Dashboard, overwrite bool) (
|
||||
bool, error) {
|
||||
return false, s.validationError
|
||||
}
|
||||
|
||||
func (s *fakeDashboardStore) GetProvisionedDataByDashboardID(int64) (*models.DashboardProvisioning, error) {
|
||||
return s.provisionedData, nil
|
||||
}
|
||||
|
||||
func (s *fakeDashboardStore) SaveProvisionedDashboard(models.SaveDashboardCommand,
|
||||
*models.DashboardProvisioning) (*models.Dashboard, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *fakeDashboardStore) SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error) {
|
||||
return cmd.GetDashboardModel(), nil
|
||||
}
|
||||
|
||||
func (s *fakeDashboardStore) SaveAlerts(dashID int64, alerts []*models.Alert) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,28 +2,32 @@ package dashboards
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/dashboards"
|
||||
"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
|
||||
// FolderService is a service for operating on folders.
|
||||
type FolderService interface {
|
||||
GetFolders(limit int64) ([]*models.Folder, error)
|
||||
GetFolderByID(id int64) (*models.Folder, error)
|
||||
GetFolderByUID(uid string) (*models.Folder, error)
|
||||
CreateFolder(cmd *models.CreateFolderCommand) error
|
||||
CreateFolder(title, uid string) (*models.Folder, error)
|
||||
UpdateFolder(uid string, cmd *models.UpdateFolderCommand) error
|
||||
DeleteFolder(uid string) (*models.Folder, error)
|
||||
MakeUserAdmin(orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error
|
||||
}
|
||||
|
||||
// NewFolderService factory for creating a new folder service
|
||||
var NewFolderService = func(orgId int64, user *models.SignedInUser) FolderService {
|
||||
// NewFolderService is a factory for creating a new folder service.
|
||||
var NewFolderService = func(orgID int64, user *models.SignedInUser, store dashboards.Store) FolderService {
|
||||
return &dashboardServiceImpl{
|
||||
orgId: orgId,
|
||||
user: user,
|
||||
orgId: orgID,
|
||||
user: user,
|
||||
dashboardStore: store,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +62,6 @@ func (dr *dashboardServiceImpl) GetFolders(limit int64) ([]*models.Folder, error
|
||||
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)
|
||||
}
|
||||
@@ -93,8 +96,17 @@ func (dr *dashboardServiceImpl) GetFolderByUID(uid string) (*models.Folder, erro
|
||||
return dashToFolder(dashFolder), nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) error {
|
||||
dashFolder := cmd.GetDashboardModel(dr.orgId, dr.user.UserId)
|
||||
func (dr *dashboardServiceImpl) CreateFolder(title, uid string) (*models.Folder, error) {
|
||||
dashFolder := models.NewDashboardFolder(title)
|
||||
dashFolder.OrgId = dr.orgId
|
||||
dashFolder.SetUid(strings.TrimSpace(uid))
|
||||
userID := dr.user.UserId
|
||||
if userID == 0 {
|
||||
userID = -1
|
||||
}
|
||||
dashFolder.CreatedBy = userID
|
||||
dashFolder.UpdatedBy = userID
|
||||
dashFolder.UpdateSlug()
|
||||
|
||||
dto := &SaveDashboardDTO{
|
||||
Dashboard: dashFolder,
|
||||
@@ -104,23 +116,21 @@ func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) er
|
||||
|
||||
saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false, false)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
err = bus.Dispatch(saveDashboardCmd)
|
||||
dash, err := dr.dashboardStore.SaveDashboard(*saveDashboardCmd)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Id: saveDashboardCmd.Result.Id}
|
||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Id: dash.Id}
|
||||
dashFolder, err = getFolder(query)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
cmd.Result = dashToFolder(dashFolder)
|
||||
|
||||
return nil
|
||||
return dashToFolder(dashFolder), nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) UpdateFolder(existingUid string, cmd *models.UpdateFolderCommand) error {
|
||||
@@ -144,12 +154,12 @@ func (dr *dashboardServiceImpl) UpdateFolder(existingUid string, cmd *models.Upd
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
err = bus.Dispatch(saveDashboardCmd)
|
||||
dash, err := dr.dashboardStore.SaveDashboard(*saveDashboardCmd)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
query = models.GetDashboardQuery{OrgId: dr.orgId, Id: saveDashboardCmd.Result.Id}
|
||||
query = models.GetDashboardQuery{OrgId: dr.orgId, Id: dash.Id}
|
||||
dashFolder, err = getFolder(query)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -15,8 +16,9 @@ import (
|
||||
func TestFolderService(t *testing.T) {
|
||||
Convey("Folder service tests", t, func() {
|
||||
service := dashboardServiceImpl{
|
||||
orgId: 1,
|
||||
user: &models.SignedInUser{UserId: 1},
|
||||
orgId: 1,
|
||||
user: &models.SignedInUser{UserId: 1},
|
||||
dashboardStore: &fakeDashboardStore{},
|
||||
}
|
||||
|
||||
Convey("Given user has no permissions", func() {
|
||||
@@ -28,32 +30,26 @@ func TestFolderService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
return models.ErrDashboardUpdateAccessDenied
|
||||
origStore := service.dashboardStore
|
||||
t.Cleanup(func() {
|
||||
service.dashboardStore = origStore
|
||||
})
|
||||
service.dashboardStore = &fakeDashboardStore{
|
||||
validationError: 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)
|
||||
_, err := service.CreateFolder("Folder", "")
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
@@ -62,7 +58,6 @@ func TestFolderService(t *testing.T) {
|
||||
Uid: "uid",
|
||||
Title: "Folder",
|
||||
})
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
@@ -89,18 +84,14 @@ func TestFolderService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
t.Cleanup(func() {
|
||||
UpdateAlerting = origUpdateAlerting
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||
user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.SaveDashboardCommand) error {
|
||||
cmd.Result = dash
|
||||
@@ -120,9 +111,7 @@ func TestFolderService(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("When creating folder should not return access denied error", func() {
|
||||
err := service.CreateFolder(&models.CreateFolderCommand{
|
||||
Title: "Folder",
|
||||
})
|
||||
_, err := service.CreateFolder("Folder", "")
|
||||
So(err, ShouldBeNil)
|
||||
So(provisioningValidated, ShouldBeFalse)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user