Commit c1204154 by Marcus Efraimsson Committed by GitHub

Merge pull request #11398 from bergquist/readonly_dashboards

improved workflow for provisioned dashboards
parents d9b5ef8f d7688241
...@@ -102,6 +102,16 @@ func GetDashboard(c *m.ReqContext) Response { ...@@ -102,6 +102,16 @@ func GetDashboard(c *m.ReqContext) Response {
meta.FolderUrl = query.Result.GetUrl() meta.FolderUrl = query.Result.GetUrl()
} }
isDashboardProvisioned := &m.IsDashboardProvisionedQuery{DashboardId: dash.Id}
err = bus.Dispatch(isDashboardProvisioned)
if err != nil {
return Error(500, "Error while checking if dashboard is provisioned", err)
}
if isDashboardProvisioned.Result {
meta.Provisioned = true
}
// make sure db version is in sync with json model version // make sure db version is in sync with json model version
dash.Data.Set("version", dash.Version) dash.Data.Set("version", dash.Version)
...@@ -228,7 +238,8 @@ func PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response { ...@@ -228,7 +238,8 @@ func PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response {
err == m.ErrDashboardWithSameUIDExists || err == m.ErrDashboardWithSameUIDExists ||
err == m.ErrFolderNotFound || err == m.ErrFolderNotFound ||
err == m.ErrDashboardFolderCannotHaveParent || err == m.ErrDashboardFolderCannotHaveParent ||
err == m.ErrDashboardFolderNameExists { err == m.ErrDashboardFolderNameExists ||
err == m.ErrDashboardCannotSaveProvisionedDashboard {
return Error(400, err.Error(), nil) return Error(400, err.Error(), nil)
} }
......
...@@ -42,6 +42,11 @@ func TestDashboardApiEndpoint(t *testing.T) { ...@@ -42,6 +42,11 @@ func TestDashboardApiEndpoint(t *testing.T) {
return nil return nil
}) })
bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
query.Result = false
return nil
})
viewerRole := m.ROLE_VIEWER viewerRole := m.ROLE_VIEWER
editorRole := m.ROLE_EDITOR editorRole := m.ROLE_EDITOR
...@@ -192,6 +197,11 @@ func TestDashboardApiEndpoint(t *testing.T) { ...@@ -192,6 +197,11 @@ func TestDashboardApiEndpoint(t *testing.T) {
fakeDash.HasAcl = true fakeDash.HasAcl = true
setting.ViewersCanEdit = false setting.ViewersCanEdit = false
bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
query.Result = false
return nil
})
bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error { bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error {
dashboards := []*m.Dashboard{fakeDash} dashboards := []*m.Dashboard{fakeDash}
query.Result = dashboards query.Result = dashboards
...@@ -625,6 +635,11 @@ func TestDashboardApiEndpoint(t *testing.T) { ...@@ -625,6 +635,11 @@ func TestDashboardApiEndpoint(t *testing.T) {
dashTwo.FolderId = 3 dashTwo.FolderId = 3
dashTwo.HasAcl = false dashTwo.HasAcl = false
bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
query.Result = false
return nil
})
bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error { bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error {
dashboards := []*m.Dashboard{dashOne, dashTwo} dashboards := []*m.Dashboard{dashOne, dashTwo}
query.Result = dashboards query.Result = dashboards
...@@ -720,6 +735,7 @@ func TestDashboardApiEndpoint(t *testing.T) { ...@@ -720,6 +735,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
{SaveError: m.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403}, {SaveError: m.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403},
{SaveError: m.ErrDashboardInvalidUid, ExpectedStatusCode: 400}, {SaveError: m.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{SaveError: m.ErrDashboardUidToLong, ExpectedStatusCode: 400}, {SaveError: m.ErrDashboardUidToLong, ExpectedStatusCode: 400},
{SaveError: m.ErrDashboardCannotSaveProvisionedDashboard, ExpectedStatusCode: 400},
{SaveError: m.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412}, {SaveError: m.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412},
} }
...@@ -750,6 +766,11 @@ func TestDashboardApiEndpoint(t *testing.T) { ...@@ -750,6 +766,11 @@ func TestDashboardApiEndpoint(t *testing.T) {
return nil return nil
}) })
bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
query.Result = false
return nil
})
bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error { bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
query.Result = &m.DashboardVersion{ query.Result = &m.DashboardVersion{
Data: simplejson.NewFromAny(map[string]interface{}{ Data: simplejson.NewFromAny(map[string]interface{}{
......
...@@ -28,6 +28,7 @@ type DashboardMeta struct { ...@@ -28,6 +28,7 @@ type DashboardMeta struct {
FolderId int64 `json:"folderId"` FolderId int64 `json:"folderId"`
FolderTitle string `json:"folderTitle"` FolderTitle string `json:"folderTitle"`
FolderUrl string `json:"folderUrl"` FolderUrl string `json:"folderUrl"`
Provisioned bool `json:"provisioned"`
} }
type DashboardFullWithMeta struct { type DashboardFullWithMeta struct {
......
...@@ -32,6 +32,7 @@ var ( ...@@ -32,6 +32,7 @@ var (
ErrDashboardUpdateAccessDenied = errors.New("Access denied to save dashboard") ErrDashboardUpdateAccessDenied = errors.New("Access denied to save dashboard")
ErrDashboardInvalidUid = errors.New("uid contains illegal characters") ErrDashboardInvalidUid = errors.New("uid contains illegal characters")
ErrDashboardUidToLong = errors.New("uid to long. max 40 characters") ErrDashboardUidToLong = errors.New("uid to long. max 40 characters")
ErrDashboardCannotSaveProvisionedDashboard = errors.New("Cannot save provisioned dashboard")
RootFolderName = "General" RootFolderName = "General"
) )
...@@ -322,6 +323,12 @@ type GetDashboardSlugByIdQuery struct { ...@@ -322,6 +323,12 @@ type GetDashboardSlugByIdQuery struct {
Result string Result string
} }
type IsDashboardProvisionedQuery struct {
DashboardId int64
Result bool
}
type GetProvisionedDashboardDataQuery struct { type GetProvisionedDashboardDataQuery struct {
Name string Name string
......
...@@ -57,7 +57,7 @@ func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*mod ...@@ -57,7 +57,7 @@ func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*mod
return cmd.Result, nil return cmd.Result, nil
} }
func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, validateAlerts bool) (*models.SaveDashboardCommand, error) { func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, validateAlerts bool, validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
dash := dto.Dashboard dash := dto.Dashboard
dash.Title = strings.TrimSpace(dash.Title) dash.Title = strings.TrimSpace(dash.Title)
...@@ -113,6 +113,19 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, ...@@ -113,6 +113,19 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
} }
} }
if validateProvisionedDashboard {
isDashboardProvisioned := &models.IsDashboardProvisionedQuery{DashboardId: dash.Id}
err := bus.Dispatch(isDashboardProvisioned)
if err != nil {
return nil, err
}
if isDashboardProvisioned.Result {
return nil, models.ErrDashboardCannotSaveProvisionedDashboard
}
}
guard := guardian.New(dash.GetDashboardIdForSavePermissionCheck(), dto.OrgId, dto.User) guard := guardian.New(dash.GetDashboardIdForSavePermissionCheck(), dto.OrgId, dto.User)
if canSave, err := guard.CanSave(); err != nil || !canSave { if canSave, err := guard.CanSave(); err != nil || !canSave {
if err != nil { if err != nil {
...@@ -158,7 +171,7 @@ func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO, ...@@ -158,7 +171,7 @@ func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO,
UserId: 0, UserId: 0,
OrgRole: models.ROLE_ADMIN, OrgRole: models.ROLE_ADMIN,
} }
cmd, err := dr.buildSaveDashboardCommand(dto, true) cmd, err := dr.buildSaveDashboardCommand(dto, true, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -188,7 +201,7 @@ func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDash ...@@ -188,7 +201,7 @@ func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDash
UserId: 0, UserId: 0,
OrgRole: models.ROLE_ADMIN, OrgRole: models.ROLE_ADMIN,
} }
cmd, err := dr.buildSaveDashboardCommand(dto, false) cmd, err := dr.buildSaveDashboardCommand(dto, false, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -207,7 +220,7 @@ func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDash ...@@ -207,7 +220,7 @@ func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDash
} }
func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) { func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
cmd, err := dr.buildSaveDashboardCommand(dto, true) cmd, err := dr.buildSaveDashboardCommand(dto, true, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -226,7 +239,7 @@ func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Da ...@@ -226,7 +239,7 @@ func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Da
} }
func (dr *dashboardServiceImpl) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) { func (dr *dashboardServiceImpl) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
cmd, err := dr.buildSaveDashboardCommand(dto, false) cmd, err := dr.buildSaveDashboardCommand(dto, false, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -14,7 +14,9 @@ import ( ...@@ -14,7 +14,9 @@ import (
func TestDashboardService(t *testing.T) { func TestDashboardService(t *testing.T) {
Convey("Dashboard service tests", t, func() { Convey("Dashboard service tests", t, func() {
service := dashboardServiceImpl{} bus.ClearBusHandlers()
service := &dashboardServiceImpl{}
origNewDashboardGuardian := guardian.New origNewDashboardGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true}) guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
...@@ -55,6 +57,11 @@ func TestDashboardService(t *testing.T) { ...@@ -55,6 +57,11 @@ func TestDashboardService(t *testing.T) {
return nil return nil
}) })
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
cmd.Result = false
return nil
})
testCases := []struct { testCases := []struct {
Uid string Uid string
Error error Error error
...@@ -73,12 +80,42 @@ func TestDashboardService(t *testing.T) { ...@@ -73,12 +80,42 @@ func TestDashboardService(t *testing.T) {
dto.Dashboard.SetUid(tc.Uid) dto.Dashboard.SetUid(tc.Uid)
dto.User = &models.SignedInUser{} dto.User = &models.SignedInUser{}
_, err := service.buildSaveDashboardCommand(dto, true) _, err := service.buildSaveDashboardCommand(dto, true, false)
So(err, ShouldEqual, tc.Error) So(err, ShouldEqual, tc.Error)
} }
}) })
Convey("Should return validation error if dashboard is provisioned", func() {
provisioningValidated := false
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
provisioningValidated = true
cmd.Result = true
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 nil
})
dto.Dashboard = models.NewDashboard("Dash")
dto.Dashboard.SetId(3)
dto.User = &models.SignedInUser{UserId: 1}
_, err := service.SaveDashboard(dto)
So(provisioningValidated, ShouldBeTrue)
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
})
Convey("Should return validation error if alert data is invalid", func() { Convey("Should return validation error if alert data is invalid", func() {
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
cmd.Result = false
return nil
})
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error { bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
return errors.New("error") return errors.New("error")
}) })
...@@ -89,6 +126,80 @@ func TestDashboardService(t *testing.T) { ...@@ -89,6 +126,80 @@ func TestDashboardService(t *testing.T) {
}) })
}) })
Convey("Save provisioned dashboard validation", func() {
dto := &SaveDashboardDTO{}
Convey("Should not return validation error if dashboard is provisioned", func() {
provisioningValidated := false
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
provisioningValidated = true
cmd.Result = true
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 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}
_, err := service.SaveProvisionedDashboard(dto, nil)
So(err, ShouldBeNil)
So(provisioningValidated, ShouldBeFalse)
})
})
Convey("Import dashboard validation", func() {
dto := &SaveDashboardDTO{}
Convey("Should return validation error if dashboard is provisioned", func() {
provisioningValidated := false
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
provisioningValidated = true
cmd.Result = true
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 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}
_, err := service.ImportDashboard(dto)
So(provisioningValidated, ShouldBeTrue)
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
})
})
Reset(func() { Reset(func() {
guardian.New = origNewDashboardGuardian guardian.New = origNewDashboardGuardian
}) })
......
...@@ -104,7 +104,7 @@ func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) er ...@@ -104,7 +104,7 @@ func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) er
User: dr.user, User: dr.user,
} }
saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false) saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false, false)
if err != nil { if err != nil {
return toFolderError(err) return toFolderError(err)
} }
...@@ -141,7 +141,7 @@ func (dr *dashboardServiceImpl) UpdateFolder(existingUid string, cmd *models.Upd ...@@ -141,7 +141,7 @@ func (dr *dashboardServiceImpl) UpdateFolder(existingUid string, cmd *models.Upd
Overwrite: cmd.Overwrite, Overwrite: cmd.Overwrite,
} }
saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false) saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false, false)
if err != nil { if err != nil {
return toFolderError(err) return toFolderError(err)
} }
......
...@@ -110,11 +110,19 @@ func TestFolderService(t *testing.T) { ...@@ -110,11 +110,19 @@ func TestFolderService(t *testing.T) {
return nil return nil
}) })
provisioningValidated := false
bus.AddHandler("test", func(query *models.IsDashboardProvisionedQuery) error {
provisioningValidated = true
return nil
})
Convey("When creating folder should not return access denied error", func() { Convey("When creating folder should not return access denied error", func() {
err := service.CreateFolder(&models.CreateFolderCommand{ err := service.CreateFolder(&models.CreateFolderCommand{
Title: "Folder", Title: "Folder",
}) })
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(provisioningValidated, ShouldBeFalse)
}) })
Convey("When updating folder should not return access denied error", func() { Convey("When updating folder should not return access denied error", func() {
...@@ -123,6 +131,7 @@ func TestFolderService(t *testing.T) { ...@@ -123,6 +131,7 @@ func TestFolderService(t *testing.T) {
Title: "Folder", Title: "Folder",
}) })
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(provisioningValidated, ShouldBeFalse)
}) })
Convey("When deleting folder by uid should not return access denied error", func() { Convey("When deleting folder by uid should not return access denied error", func() {
......
...@@ -55,9 +55,6 @@ func createDashboardJson(data *simplejson.Json, lastModified time.Time, cfg *Das ...@@ -55,9 +55,6 @@ func createDashboardJson(data *simplejson.Json, lastModified time.Time, cfg *Das
dash.OrgId = cfg.OrgId dash.OrgId = cfg.OrgId
dash.Dashboard.OrgId = cfg.OrgId dash.Dashboard.OrgId = cfg.OrgId
dash.Dashboard.FolderId = folderId dash.Dashboard.FolderId = folderId
if !cfg.Editable {
dash.Dashboard.Data.Set("editable", cfg.Editable)
}
if dash.Dashboard.Title == "" { if dash.Dashboard.Title == "" {
return nil, models.ErrDashboardTitleEmpty return nil, models.ErrDashboardTitleEmpty
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
func init() { func init() {
bus.AddHandler("sql", GetProvisionedDashboardDataQuery) bus.AddHandler("sql", GetProvisionedDashboardDataQuery)
bus.AddHandler("sql", SaveProvisionedDashboard) bus.AddHandler("sql", SaveProvisionedDashboard)
bus.AddHandler("sql", GetProvisionedDataByDashboardId)
} }
type DashboardExtras struct { type DashboardExtras struct {
...@@ -17,6 +18,19 @@ type DashboardExtras struct { ...@@ -17,6 +18,19 @@ type DashboardExtras struct {
Value string Value string
} }
func GetProvisionedDataByDashboardId(cmd *models.IsDashboardProvisionedQuery) error {
result := &models.DashboardProvisioning{}
exist, err := x.Where("dashboard_id = ?", cmd.DashboardId).Get(result)
if err != nil {
return err
}
cmd.Result = exist
return nil
}
func SaveProvisionedDashboard(cmd *models.SaveProvisionedDashboardCommand) error { func SaveProvisionedDashboard(cmd *models.SaveProvisionedDashboardCommand) error {
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
err := saveDashboard(sess, cmd.DashboardCmd) err := saveDashboard(sess, cmd.DashboardCmd)
......
...@@ -50,6 +50,23 @@ func TestDashboardProvisioningTest(t *testing.T) { ...@@ -50,6 +50,23 @@ func TestDashboardProvisioningTest(t *testing.T) {
So(query.Result[0].DashboardId, ShouldEqual, dashId) So(query.Result[0].DashboardId, ShouldEqual, dashId)
So(query.Result[0].Updated, ShouldEqual, now.Unix()) So(query.Result[0].Updated, ShouldEqual, now.Unix())
}) })
Convey("Can query for one provisioned dashboard", func() {
query := &models.IsDashboardProvisionedQuery{DashboardId: cmd.Result.Id}
err := GetProvisionedDataByDashboardId(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue)
})
Convey("Can query for none provisioned dashboard", func() {
query := &models.IsDashboardProvisionedQuery{DashboardId: 3000}
err := GetProvisionedDataByDashboardId(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeFalse)
})
}) })
}) })
} }
...@@ -19,7 +19,6 @@ func TestIntegratedDashboardService(t *testing.T) { ...@@ -19,7 +19,6 @@ func TestIntegratedDashboardService(t *testing.T) {
var testOrgId int64 = 1 var testOrgId int64 = 1
Convey("Given saved folders and dashboards in organization A", func() { Convey("Given saved folders and dashboards in organization A", func() {
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error { bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
return nil return nil
}) })
...@@ -28,6 +27,11 @@ func TestIntegratedDashboardService(t *testing.T) { ...@@ -28,6 +27,11 @@ func TestIntegratedDashboardService(t *testing.T) {
return nil return nil
}) })
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
cmd.Result = false
return nil
})
savedFolder := saveTestFolder("Saved folder", testOrgId) savedFolder := saveTestFolder("Saved folder", testOrgId)
savedDashInFolder := saveTestDashboard("Saved dash in folder", testOrgId, savedFolder.Id) savedDashInFolder := saveTestDashboard("Saved dash in folder", testOrgId, savedFolder.Id)
saveTestDashboard("Other saved dash in folder", testOrgId, savedFolder.Id) saveTestDashboard("Other saved dash in folder", testOrgId, savedFolder.Id)
......
...@@ -6,6 +6,7 @@ import './dashnav/dashnav'; ...@@ -6,6 +6,7 @@ import './dashnav/dashnav';
import './submenu/submenu'; import './submenu/submenu';
import './save_as_modal'; import './save_as_modal';
import './save_modal'; import './save_modal';
import './save_provisioned_modal';
import './shareModalCtrl'; import './shareModalCtrl';
import './share_snapshot_ctrl'; import './share_snapshot_ctrl';
import './dashboard_srv'; import './dashboard_srv';
......
...@@ -105,6 +105,10 @@ export class DashboardSrv { ...@@ -105,6 +105,10 @@ export class DashboardSrv {
this.setCurrent(this.create(clone, this.dash.meta)); this.setCurrent(this.create(clone, this.dash.meta));
} }
if (this.dash.meta.provisioned) {
return this.showDashboardProvisionedModal();
}
if (!this.dash.meta.canSave && options.makeEditable !== true) { if (!this.dash.meta.canSave && options.makeEditable !== true) {
return Promise.resolve(); return Promise.resolve();
} }
...@@ -120,6 +124,12 @@ export class DashboardSrv { ...@@ -120,6 +124,12 @@ export class DashboardSrv {
return this.save(this.dash.getSaveModelClone(), options); return this.save(this.dash.getSaveModelClone(), options);
} }
showDashboardProvisionedModal() {
this.$rootScope.appEvent('show-modal', {
templateHtml: '<save-provisioned-dashboard-modal dismiss="dismiss()"></save-provisioned-dashboard-modal>',
});
}
showSaveAsModal() { showSaveAsModal() {
this.$rootScope.appEvent('show-modal', { this.$rootScope.appEvent('show-modal', {
templateHtml: '<save-dashboard-as-modal dismiss="dismiss()"></save-dashboard-as-modal>', templateHtml: '<save-dashboard-as-modal dismiss="dismiss()"></save-dashboard-as-modal>',
......
import angular from 'angular';
import { saveAs } from 'file-saver';
import coreModule from 'app/core/core_module';
const template = `
<div class="modal-body">
<div class="modal-header">
<h2 class="modal-header-title">
<i class="fa fa-save"></i><span class="p-l-1">Cannot save provisioned dashboard</span>
</h2>
<a class="modal-header-close" ng-click="ctrl.dismiss();">
<i class="fa fa-remove"></i>
</a>
</div>
<div class="modal-content">
<small>
This dashboard cannot be saved from Grafana's UI since it has been provisioned from another source.
Copy the JSON or save it to a file below. Then you can update your dashboard in corresponding provisioning source.<br/>
<i>See <a class="external-link" href="http://docs.grafana.org/administration/provisioning/#dashboards" target="_blank">
documentation</a> for more information about provisioning.</i>
</small>
<div class="p-t-2">
<div class="gf-form">
<code-editor content="ctrl.dashboardJson" data-mode="json" data-max-lines=15></code-editor>
</div>
<div class="gf-form-button-row">
<button class="btn btn-success" clipboard-button="ctrl.getJsonForClipboard()">
<i class="fa fa-clipboard"></i>&nbsp;Copy JSON to Clipboard
</button>
<button class="btn btn-secondary" clipboard-button="ctrl.save()">
<i class="fa fa-save"></i>&nbsp;Save JSON to file
</button>
<a class="btn btn-link" ng-click="ctrl.dismiss();">Cancel</a>
</div>
</div>
</div>
</div>
`;
export class SaveProvisionedDashboardModalCtrl {
dash: any;
dashboardJson: string;
dismiss: () => void;
/** @ngInject */
constructor(dashboardSrv) {
this.dash = dashboardSrv.getCurrent().getSaveModelClone();
delete this.dash.id;
this.dashboardJson = JSON.stringify(this.dash, null, 2);
}
save() {
var blob = new Blob([angular.toJson(this.dash, true)], {
type: 'application/json;charset=utf-8',
});
saveAs(blob, this.dash.title + '-' + new Date().getTime() + '.json');
}
getJsonForClipboard() {
return this.dashboardJson;
}
}
export function saveProvisionedDashboardModalDirective() {
return {
restrict: 'E',
template: template,
controller: SaveProvisionedDashboardModalCtrl,
bindToController: true,
controllerAs: 'ctrl',
scope: { dismiss: '&' },
};
}
coreModule.directive('saveProvisionedDashboardModal', saveProvisionedDashboardModalDirective);
import { SaveProvisionedDashboardModalCtrl } from '../save_provisioned_modal';
describe('SaveProvisionedDashboardModalCtrl', () => {
var json = {
title: 'name',
id: 5,
};
var mockDashboardSrv = {
getCurrent: function() {
return {
id: 5,
meta: {},
getSaveModelClone: function() {
return json;
},
};
},
};
var ctrl = new SaveProvisionedDashboardModalCtrl(mockDashboardSrv);
it('should remove id from dashboard model', () => {
expect(ctrl.dash.id).toBeUndefined();
});
it('should remove id from dashboard model in clipboard json', () => {
expect(ctrl.getJsonForClipboard()).toBe(JSON.stringify({ title: 'name' }, null, 2));
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment