diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go
index 6a0883c16fa..ae360973fad 100644
--- a/pkg/services/alerting/extractor.go
+++ b/pkg/services/alerting/extractor.go
@@ -57,12 +57,9 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
for _, panelObj := range row.Get("panels").MustArray() {
panel := simplejson.NewFromAny(panelObj)
- jsonAlert := panel.Get("alert")
+ jsonAlert, hasAlert := panel.CheckGet("alert")
- // check if marked for deletion
- deleted := jsonAlert.Get("deleted").MustBool()
- if deleted {
- e.log.Info("Deleted alert rule found")
+ if !hasAlert {
continue
}
diff --git a/pkg/services/alerting/extractor_test.go b/pkg/services/alerting/extractor_test.go
index 6eda321f0dc..7cab1d94c7c 100644
--- a/pkg/services/alerting/extractor_test.go
+++ b/pkg/services/alerting/extractor_test.go
@@ -149,10 +149,7 @@ func TestAlertRuleExtraction(t *testing.T) {
],
"title": "Broken influxdb panel",
"transform": "table",
- "type": "table",
- "alert": {
- "deleted": true
- }
+ "type": "table"
}
],
"title": "New row"
@@ -185,7 +182,7 @@ func TestAlertRuleExtraction(t *testing.T) {
return nil
})
- alerts, err := extractor.GetRuleModels()
+ alerts, err := extractor.GetAlerts()
Convey("Get rules without error", func() {
So(err, ShouldBeNil)
diff --git a/public/app/plugins/panel/graph/alert_handle.ts b/public/app/plugins/panel/graph/alert_handle.ts
new file mode 100644
index 00000000000..c1914196e89
--- /dev/null
+++ b/public/app/plugins/panel/graph/alert_handle.ts
@@ -0,0 +1,135 @@
+///
+
+import 'jquery.flot';
+import $ from 'jquery';
+import _ from 'lodash';
+
+export class AlertHandleManager {
+ plot: any;
+ placeholder: any;
+ height: any;
+ alert: any;
+
+ constructor(private panelCtrl) {
+ this.alert = panelCtrl.panel.alert;
+ }
+
+ getHandleInnerHtml(type, op, value) {
+ if (op === '>') { op = '>'; }
+ if (op === '<') { op = '<'; }
+
+ return `
+
+
+
+
+ ${op} ${value}
+
`;
+ }
+
+ getFullHandleHtml(type, op, value) {
+ var innerTemplate = this.getHandleInnerHtml(type, op, value);
+ return `
+
+ ${innerTemplate}
+
+ `;
+ }
+
+ setupDragging(handleElem, levelModel) {
+ var isMoving = false;
+ var lastY = null;
+ var posTop;
+ var plot = this.plot;
+ var panelCtrl = this.panelCtrl;
+
+ function dragging(evt) {
+ if (lastY === null) {
+ lastY = evt.clientY;
+ } else {
+ var diff = evt.clientY - lastY;
+ posTop = posTop + diff;
+ lastY = evt.clientY;
+ handleElem.css({top: posTop + diff});
+ }
+ }
+
+ function stopped() {
+ isMoving = false;
+ // calculate graph level
+ var graphLevel = plot.c2p({left: 0, top: posTop}).y;
+ console.log('canvasPos:' + posTop + ' Graph level: ' + graphLevel);
+ graphLevel = parseInt(graphLevel.toFixed(0));
+ levelModel.level = graphLevel;
+ console.log(levelModel);
+
+ var levelCanvasPos = plot.p2c({x: 0, y: graphLevel});
+ console.log('canvas pos', levelCanvasPos);
+
+ console.log('stopped');
+ handleElem.off("mousemove", dragging);
+ handleElem.off("mouseup", dragging);
+
+ // trigger digest and render
+ panelCtrl.$scope.$apply(function() {
+ panelCtrl.render();
+ });
+ }
+
+ handleElem.bind('mousedown', function() {
+ isMoving = true;
+ lastY = null;
+ posTop = handleElem.position().top;
+ console.log('start pos', posTop);
+
+ handleElem.on("mousemove", dragging);
+ handleElem.on("mouseup", stopped);
+ });
+ }
+
+ cleanUp() {
+ if (this.placeholder) {
+ this.placeholder.find(".alert-handle-wrapper").remove();
+ }
+ }
+
+ renderHandle(type, model, defaultHandleTopPos) {
+ var handleElem = this.placeholder.find(`.alert-handle-wrapper--${type}`);
+ var level = model.level;
+ var levelStr = level;
+ var handleTopPos = 0;
+
+ // handle no value
+ if (!_.isNumber(level)) {
+ levelStr = '';
+ handleTopPos = defaultHandleTopPos;
+ } else {
+ var levelCanvasPos = this.plot.p2c({x: 0, y: level});
+ handleTopPos = Math.min(Math.max(levelCanvasPos.top, 0), this.height) - 6;
+ }
+
+ if (handleElem.length === 0) {
+ console.log('creating handle');
+ handleElem = $(this.getFullHandleHtml(type, model.op, levelStr));
+ this.placeholder.append(handleElem);
+ this.setupDragging(handleElem, model);
+ } else {
+ console.log('reusing handle!');
+ handleElem.html(this.getHandleInnerHtml(type, model.op, levelStr));
+ }
+
+ handleElem.toggleClass('alert-handle-wrapper--no-value', levelStr === '');
+ handleElem.css({top: handleTopPos});
+ }
+
+ draw(plot) {
+ this.plot = plot;
+ this.placeholder = plot.getPlaceholder();
+ this.height = plot.height();
+
+ this.renderHandle('critical', this.alert.critical, 10);
+ this.renderHandle('warn', this.alert.warn, this.height-30);
+ }
+
+}
+
diff --git a/public/app/plugins/panel/graph/alert_tab_ctrl.ts b/public/app/plugins/panel/graph/alert_tab_ctrl.ts
index d16274efb56..82ac33e3d47 100644
--- a/public/app/plugins/panel/graph/alert_tab_ctrl.ts
+++ b/public/app/plugins/panel/graph/alert_tab_ctrl.ts
@@ -73,9 +73,6 @@ export class AlertTabCtrl {
this.initAlertModel();
// set panel alert edit mode
- this.panelCtrl.editingAlert = true;
- this.panelCtrl.render();
-
$scope.$on("$destroy", () => {
this.panelCtrl.editingAlert = false;
this.panelCtrl.render();
@@ -83,7 +80,11 @@ export class AlertTabCtrl {
}
initAlertModel() {
- this.alert = this.panel.alert = this.panel.alert || {};
+ if (!this.panel.alert) {
+ return;
+ }
+
+ this.alert = this.panel.alert;
// set defaults
_.defaults(this.alert, this.defaultValues);
@@ -105,6 +106,9 @@ export class AlertTabCtrl {
this.query = new QueryPart(this.queryParams, alertQueryDef);
this.convertThresholdsToAlertThresholds();
this.transformDef = _.findWhere(this.transforms, {type: this.alert.transform.type});
+
+ this.panelCtrl.editingAlert = true;
+ this.panelCtrl.render();
}
queryUpdated() {
@@ -151,18 +155,14 @@ export class AlertTabCtrl {
}
delete() {
- this.alert = this.panel.alert = {};
- this.alert.deleted = true;
- this.initAlertModel();
+ delete this.panel.alert;
+ this.panelCtrl.editingAlert = false;
+ this.panelCtrl.render();
}
enable() {
- delete this.alert.deleted;
- this.alert.enabled = true;
- }
-
- disable() {
- this.alert.enabled = false;
+ this.panel.alert = {};
+ this.initAlertModel();
}
levelsUpdated() {
diff --git a/public/app/plugins/panel/graph/graph.js b/public/app/plugins/panel/graph/graph.js
index 13dcf76857d..599bbe554bf 100755
--- a/public/app/plugins/panel/graph/graph.js
+++ b/public/app/plugins/panel/graph/graph.js
@@ -5,6 +5,7 @@ define([
'lodash',
'app/core/utils/kbn',
'./graph_tooltip',
+ './alert_handle',
'jquery.flot',
'jquery.flot.selection',
'jquery.flot.time',
@@ -13,15 +14,17 @@ define([
'jquery.flot.fillbelow',
'jquery.flot.crosshair',
'./jquery.flot.events',
- './jquery.flot.alerts',
],
-function (angular, $, moment, _, kbn, GraphTooltip) {
+function (angular, $, moment, _, kbn, GraphTooltip, AlertHandle) {
'use strict';
var module = angular.module('grafana.directives');
var labelWidthCache = {};
var panelWidthCache = {};
+ // systemjs export
+ var AlertHandleManager = AlertHandle.AlertHandleManager;
+
module.directive('grafanaGraph', function($rootScope, timeSrv) {
return {
restrict: 'A',
@@ -35,6 +38,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
var legendSideLastValue = null;
var rootScope = scope.$root;
var panelWidth = 0;
+ var alertHandles;
rootScope.onAppEvent('setCrosshair', function(event, info) {
// do not need to to this if event is from this panel
@@ -162,6 +166,10 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
rightLabel[0].style.marginTop = (getLabelWidth(panel.yaxes[1].label, rightLabel) / 2) + 'px';
}
+
+ if (alertHandles) {
+ alertHandles.draw(plot);
+ }
}
function processOffsetHook(plot, gridMargin) {
@@ -178,24 +186,26 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
panelWidth = panelWidthCache[panel.span] = elem.width();
}
- if (ctrl.editingAlert) {
- elem.css('margin-right', '220px');
- } else {
- elem.css('margin-right', '');
- }
-
if (shouldAbortRender()) {
return;
}
+ // give space to alert editing
+ if (ctrl.editingAlert) {
+ if (!alertHandles) {
+ elem.css('margin-right', '220px');
+ alertHandles = new AlertHandleManager(ctrl);
+ }
+ } else if (alertHandles) {
+ elem.css('margin-right', '0');
+ alertHandles.cleanUp();
+ alertHandles = null;
+ }
+
var stack = panel.stack ? true : null;
// Populate element
var options = {
- alerting: {
- editing: ctrl.editingAlert,
- alert: panel.alert,
- },
hooks: {
draw: [drawHook],
processOffset: [processOffsetHook],
@@ -323,7 +333,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
}
function addGridThresholds(options, panel) {
- if (panel.alert && panel.alert.enabled) {
+ if (panel.alert) {
var crit = panel.alert.critical;
var warn = panel.alert.warn;
var critEdge = Infinity;
diff --git a/public/app/plugins/panel/graph/jquery.flot.alerts.ts b/public/app/plugins/panel/graph/jquery.flot.alerts.ts
deleted file mode 100644
index 630f58f85e9..00000000000
--- a/public/app/plugins/panel/graph/jquery.flot.alerts.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-///
-
-import 'jquery.flot';
-import $ from 'jquery';
-import _ from 'lodash';
-
-var options = {};
-
-function getHandleInnerHtml(type, op, value) {
- if (op === '>') { op = '>'; }
- if (op === '<') { op = '<'; }
-
- return `
-
-
-
-
- ${op} ${value}
-
`;
-}
-
-function getFullHandleHtml(type, op, value) {
- var innerTemplate = getHandleInnerHtml(type, op, value);
- return `
-
- ${innerTemplate}
-
- `;
-}
-
-var dragGhostElem = document.createElement('div');
-
-function dragStartHandler(evt) {
- evt.dataTransfer.setDragImage(dragGhostElem, -99999, -99999);
-}
-
-function dragEndHandler() {
- console.log('drag end');
-}
-
-function drawAlertHandles(plot) {
- var options = plot.getOptions();
- var $placeholder = plot.getPlaceholder();
-
- if (!options.alerting.editing) {
- $placeholder.find(".alert-handle-wrapper").remove();
- return;
- }
-
- var alert = options.alerting.alert;
- var height = plot.height();
-
- function renderHandle(type, model) {
- var $handle = $placeholder.find(`.alert-handle-wrapper--${type}`);
-
- if (!_.isNumber(model.level)) {
- $handle.remove();
- return;
- }
-
- if ($handle.length === 0) {
- console.log('creating handle');
- $handle = $(getFullHandleHtml(type, model.op, model.level));
- $handle.attr('draggable', true);
- $handle.bind('dragend', dragEndHandler);
- $handle.bind('dragstart', dragStartHandler);
- $placeholder.append($handle);
- } else {
- console.log('reusing handle!');
- $handle.html(getHandleInnerHtml(type, model.op, model.level));
- }
-
- var levelCanvasPos = plot.p2c({x: 0, y: model.level});
- var levelTopPos = Math.min(Math.max(levelCanvasPos.top, 0), height) - 6;
- $handle.css({top: levelTopPos});
- }
-
- renderHandle('critical', alert.critical);
- renderHandle('warn', alert.warn);
-}
-
-function shutdown() {
- console.log('shutdown');
-}
-
-function init(plot, classes) {
- plot.hooks.draw.push(drawAlertHandles);
- plot.hooks.shutdown.push(shutdown);
-}
-
-$.plot.plugins.push({
- init: init,
- options: options,
- name: 'navigationControl',
- version: '1.4'
-});
-
diff --git a/public/app/plugins/panel/graph/partials/tab_alerting.html b/public/app/plugins/panel/graph/partials/tab_alerting.html
index a90c3b78209..c2cce48a073 100644
--- a/public/app/plugins/panel/graph/partials/tab_alerting.html
+++ b/public/app/plugins/panel/graph/partials/tab_alerting.html
@@ -1,116 +1,120 @@
-