Commit d6faa3d0 by bergquist

provisioning: improve UX when saving provisioned dashboards

parent a20f3d19
...@@ -102,6 +102,13 @@ func GetDashboard(c *m.ReqContext) Response { ...@@ -102,6 +102,13 @@ func GetDashboard(c *m.ReqContext) Response {
meta.FolderUrl = query.Result.GetUrl() meta.FolderUrl = query.Result.GetUrl()
} }
dpQuery := &m.GetProvisionedDashboardByDashboardId{DashboardId: dash.Id}
err = bus.Dispatch(dpQuery)
if dpQuery.Result != nil {
meta.CanEdit = true
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)
......
...@@ -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 {
......
...@@ -317,6 +317,12 @@ type GetDashboardSlugByIdQuery struct { ...@@ -317,6 +317,12 @@ type GetDashboardSlugByIdQuery struct {
Result string Result string
} }
type GetProvisionedDashboardByDashboardId struct {
DashboardId int64
Result *DashboardProvisioning
}
type GetProvisionedDashboardDataQuery struct { type GetProvisionedDashboardDataQuery struct {
Name string Name string
......
...@@ -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,18 @@ type DashboardExtras struct { ...@@ -17,6 +18,18 @@ type DashboardExtras struct {
Value string Value string
} }
func GetProvisionedDataByDashboardId(cmd *models.GetProvisionedDashboardByDashboardId) error {
result := &models.DashboardProvisioning{}
_, err := x.Where("dashboard_id = ?", cmd.DashboardId).Get(result)
if err != nil {
return err
}
cmd.Result = result
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,16 @@ func TestDashboardProvisioningTest(t *testing.T) { ...@@ -50,6 +50,16 @@ 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.GetProvisionedDashboardByDashboardId{DashboardId: cmd.Result.Id}
err := GetProvisionedDataByDashboardId(query)
So(err, ShouldBeNil)
So(query.Result.DashboardId, ShouldEqual, cmd.Result.Id)
So(query.Result.Updated, ShouldEqual, now.Unix())
})
}) })
}) })
} }
...@@ -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,13 @@ export class DashboardSrv { ...@@ -120,6 +124,13 @@ 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>',
modalClass: 'modal--narrow',
});
}
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>',
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<div class="navbar__spacer"></div> <div class="navbar__spacer"></div>
<div class="navbar-buttons navbar-buttons--actions"> <div class="navbar-buttons navbar-buttons--actions">
<button class="btn navbar-button navbar-button--add-panel" ng-show="::ctrl.dashboard.meta.canSave" bs-tooltip="'Add panel'" data-placement="bottom" ng-click="ctrl.addPanel()"> <button class="btn navbar-button navbar-button--add-panel" ng-show="::ctrl.dashboard.meta.canEdit" bs-tooltip="'Add panel'" data-placement="bottom" ng-click="ctrl.addPanel()">
<i class="gicon gicon-add-panel"></i> <i class="gicon gicon-add-panel"></i>
</button> </button>
......
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 dashboards</span>
</h2>
<a class="modal-header-close" ng-click="ctrl.dismiss();">
<i class="fa fa-remove"></i>
</a>
</div>
<form name="ctrl.saveForm" class="modal-content" novalidate>
<h6 class="text-center">
This dashboard cannot be saved from Grafana's UI since it have been
<a href="http://docs.grafana.org/administration/provisioning/#dashboards">provisioned</a> from
another source. Please ask your Administrator for more info.
</h6>
<div class="p-t-2">
<div class="gf-form">
<label class="gf-form-hint">
<textarea
type="text"
name="dashboardJson"
class="gf-form-input"
ng-model="ctrl.dashboardJson"
ng-model-options="{allowInvalid: true}"
autocomplete="off"
rows="3" /></textarea>
</label>
</div>
</div>
<div class="gf-form-button-row text-center">
<button type="submit" class="btn btn-success" clipboard-button="ctrl.getJsonForClipboard()" >
<i class="fa fa-clipboard"></i>&nbsp;Copy json
</button>
<button class="btn btn-inverse" ng-click="ctrl.dismiss();">Close</button>
</div>
</form>
</div>
`;
export class SaveProvisionedDashboardModalCtrl {
dashboardJson: string;
dismiss: () => void;
/** @ngInject */
constructor(dashboardSrv) {
var dashboard = dashboardSrv.getCurrent().getSaveModelClone();
delete dashboard.id;
this.dashboardJson = JSON.stringify(dashboard);
}
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';
import { describe, it, expect } from 'test/lib/common';
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('verify that the id have been removed', () => {
var copy = ctrl.getJsonForClipboard();
expect(copy).toBe(`{"title":"name"}`);
});
});
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