mirror of
https://github.com/grafana/grafana.git
synced 2025-08-03 05:08:36 +08:00
provisioning: improve UX when saving provisioned dashboards
This commit is contained in:
@ -102,6 +102,13 @@ func GetDashboard(c *m.ReqContext) Response {
|
||||
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
|
||||
dash.Data.Set("version", dash.Version)
|
||||
|
||||
|
@ -28,6 +28,7 @@ type DashboardMeta struct {
|
||||
FolderId int64 `json:"folderId"`
|
||||
FolderTitle string `json:"folderTitle"`
|
||||
FolderUrl string `json:"folderUrl"`
|
||||
Provisioned bool `json:"provisioned"`
|
||||
}
|
||||
|
||||
type DashboardFullWithMeta struct {
|
||||
|
@ -317,6 +317,12 @@ type GetDashboardSlugByIdQuery struct {
|
||||
Result string
|
||||
}
|
||||
|
||||
type GetProvisionedDashboardByDashboardId struct {
|
||||
DashboardId int64
|
||||
|
||||
Result *DashboardProvisioning
|
||||
}
|
||||
|
||||
type GetProvisionedDashboardDataQuery struct {
|
||||
Name string
|
||||
|
||||
|
@ -55,9 +55,6 @@ func createDashboardJson(data *simplejson.Json, lastModified time.Time, cfg *Das
|
||||
dash.OrgId = cfg.OrgId
|
||||
dash.Dashboard.OrgId = cfg.OrgId
|
||||
dash.Dashboard.FolderId = folderId
|
||||
if !cfg.Editable {
|
||||
dash.Dashboard.Data.Set("editable", cfg.Editable)
|
||||
}
|
||||
|
||||
if dash.Dashboard.Title == "" {
|
||||
return nil, models.ErrDashboardTitleEmpty
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
func init() {
|
||||
bus.AddHandler("sql", GetProvisionedDashboardDataQuery)
|
||||
bus.AddHandler("sql", SaveProvisionedDashboard)
|
||||
bus.AddHandler("sql", GetProvisionedDataByDashboardId)
|
||||
}
|
||||
|
||||
type DashboardExtras struct {
|
||||
@ -17,6 +18,18 @@ type DashboardExtras struct {
|
||||
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 {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
err := saveDashboard(sess, cmd.DashboardCmd)
|
||||
|
@ -50,6 +50,16 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
||||
So(query.Result[0].DashboardId, ShouldEqual, dashId)
|
||||
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';
|
||||
import './submenu/submenu';
|
||||
import './save_as_modal';
|
||||
import './save_modal';
|
||||
import './save_provisioned_modal';
|
||||
import './shareModalCtrl';
|
||||
import './share_snapshot_ctrl';
|
||||
import './dashboard_srv';
|
||||
|
@ -105,6 +105,10 @@ export class DashboardSrv {
|
||||
this.setCurrent(this.create(clone, this.dash.meta));
|
||||
}
|
||||
|
||||
if (this.dash.meta.provisioned) {
|
||||
return this.showDashboardProvisionedModal();
|
||||
}
|
||||
|
||||
if (!this.dash.meta.canSave && options.makeEditable !== true) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -120,6 +124,13 @@ export class DashboardSrv {
|
||||
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() {
|
||||
this.$rootScope.appEvent('show-modal', {
|
||||
templateHtml: '<save-dashboard-as-modal dismiss="dismiss()"></save-dashboard-as-modal>',
|
||||
|
@ -17,7 +17,7 @@
|
||||
<div class="navbar__spacer"></div>
|
||||
|
||||
<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>
|
||||
</button>
|
||||
|
||||
|
74
public/app/features/dashboard/save_provisioned_modal.ts
Normal file
74
public/app/features/dashboard/save_provisioned_modal.ts
Normal file
@ -0,0 +1,74 @@
|
||||
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> 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);
|
@ -0,0 +1,28 @@
|
||||
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"}`);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user