From 453f8b312a983a09e4ee21f8ecf7ae951bee411e Mon Sep 17 00:00:00 2001 From: Carl Bergquist Date: Fri, 22 Apr 2016 14:55:35 +0200 Subject: [PATCH 01/13] Revert "Revert "Gauges"" --- .../app/plugins/panel/singlestat/editor.html | 37 + public/app/plugins/panel/singlestat/module.ts | 95 +- public/app/system.conf.js | 3 +- public/test/test-main.js | 3 +- public/vendor/flot/jquery.flot.gauge.js | 960 ++++++++++++++++++ 5 files changed, 1094 insertions(+), 4 deletions(-) create mode 100644 public/vendor/flot/jquery.flot.gauge.js diff --git a/public/app/plugins/panel/singlestat/editor.html b/public/app/plugins/panel/singlestat/editor.html index 6b0806133b8..437445a2400 100644 --- a/public/app/plugins/panel/singlestat/editor.html +++ b/public/app/plugins/panel/singlestat/editor.html @@ -156,6 +156,43 @@ +
+
+
+
    +
  • + Gauge +
  • +
  • + Show  + + +
  • +
  • + Threshold labels  + + +
  • +
  • + Min +
  • +
  • + +
  • +
  • + Max +
  • +
  • + +
  • +
+
+
+
+
+
diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index 8af684c9d68..6d60a70269e 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -4,8 +4,10 @@ import angular from 'angular'; import _ from 'lodash'; import $ from 'jquery'; import 'jquery.flot'; +import 'jquery.flot.gauge'; import kbn from 'app/core/utils/kbn'; +import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; import {MetricsPanelCtrl} from 'app/plugins/sdk'; @@ -38,6 +40,12 @@ var panelDefaults = { full: false, lineColor: 'rgb(31, 120, 193)', fillColor: 'rgba(31, 118, 189, 0.18)', + }, + gauge: { + show: false, + minValue: 0, + maxValue: 100, + thresholdLabels: true } }; @@ -270,6 +278,86 @@ class SingleStatCtrl extends MetricsPanelCtrl { return body; } + function addGauge() { + var plotCanvas = $('
'); + var plotCss = { + top: '10px', + margin: 'auto', + position: 'relative', + height: (elem.height() * 0.9) + 'px', + width: elem.width() + 'px' + }; + + plotCanvas.css(plotCss); + + var thresholds = []; + for (var i = 0; i < data.thresholds.length; i++) { + thresholds.push({ + value: data.thresholds[i], + color: data.colorMap[i] + }); + } + thresholds.push({ + value: panel.gauge.maxValue, + color: data.colorMap[data.colorMap.length - 1] + }); + + var bgColor = config.bootData.user.lightTheme + ? 'rgb(230,230,230)' + : 'rgb(38,38,38)'; + + var options = { + series: { + gauges: { + gauge: { + min: panel.gauge.minValue, + max: panel.gauge.maxValue, + background: { color: bgColor }, + border: { color: null }, + shadow: { show: false }, + width: 38 + }, + frame: { show: false }, + label: { show: false }, + layout: { margin: 0 }, + cell: { border: { width: 0 } }, + threshold: { + values: thresholds, + label: { + show: panel.gauge.thresholdLabels, + margin: 8, + font: { size: 18 } + }, + width: 8 + }, + value: { + color: panel.colorValue ? getColorForValue(data, data.valueRounded) : null, + formatter: function () { return data.valueFormated; }, + font: { size: getGaugeFontSize() } + }, + show: true + } + } + }; + + elem.append(plotCanvas); + + var plotSeries = { + data: [[0, data.valueRounded]] + }; + + $.plot(plotCanvas, [plotSeries], options); + } + + function getGaugeFontSize() { + if (panel.valueFontSize) { + var num = parseInt(panel.valueFontSize.substring(0, panel.valueFontSize.length - 1)); + return 30 * (num / 100); + } else { + return 30; + } + } + function addSparkline() { var width = elem.width() + 20; if (width < 30) { @@ -331,11 +419,10 @@ class SingleStatCtrl extends MetricsPanelCtrl { function render() { if (!ctrl.data) { return; } - data = ctrl.data; setElementHeight(); - var body = getBigValueHtml(); + var body = panel.gauge.show ? '' : getBigValueHtml(); if (panel.colorBackground && !isNaN(data.valueRounded)) { var color = getColorForValue(data, data.valueRounded); @@ -358,6 +445,10 @@ class SingleStatCtrl extends MetricsPanelCtrl { addSparkline(); } + if (panel.gauge.show) { + addGauge(); + } + elem.toggleClass('pointer', panel.links.length > 0); if (panel.links.length > 0) { diff --git a/public/app/system.conf.js b/public/app/system.conf.js index 276988e5c34..40b70c0e30e 100644 --- a/public/app/system.conf.js +++ b/public/app/system.conf.js @@ -27,7 +27,8 @@ System.config({ "jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent", "jquery.flot.time": "vendor/flot/jquery.flot.time", "jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair", - "jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow" + "jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow", + "jquery.flot.gauge": "vendor/flot/jquery.flot.gauge" }, packages: { diff --git a/public/test/test-main.js b/public/test/test-main.js index d40955022fe..dbee086c250 100644 --- a/public/test/test-main.js +++ b/public/test/test-main.js @@ -35,7 +35,8 @@ "jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent", "jquery.flot.time": "vendor/flot/jquery.flot.time", "jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair", - "jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow" + "jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow", + "jquery.flot.gauge": "vendor/flot/jquery.flot.gauge" }, packages: { diff --git a/public/vendor/flot/jquery.flot.gauge.js b/public/vendor/flot/jquery.flot.gauge.js new file mode 100644 index 00000000000..d8ed958e990 --- /dev/null +++ b/public/vendor/flot/jquery.flot.gauge.js @@ -0,0 +1,960 @@ +/*! + * jquery.flot.gauge v1.1.0 * + * + * Flot plugin for rendering gauge charts. + * + * Copyright (c) 2015 @toyoty99. + * Licensed under the MIT license. + */ + +/** + * @module flot.gauge + */ +(function($) { + + + /** + * Gauge class + * + * @class Gauge + */ + var Gauge = (function() { + /** + * context of canvas + * + * @property context + * @type Object + */ + var context; + /** + * placeholder of canvas + * + * @property placeholder + * @type Object + */ + var placeholder; + /** + * options of plot + * + * @property options + * @type Object + */ + var options; + /** + * options of gauge + * + * @property gaugeOptions + * @type Object + */ + var gaugeOptions; + /** + * data series + * + * @property series + * @type Array + */ + var series; + /** + * logger + * + * @property logger + * @type Object + */ + var logger; + + /** + * constructor + * + * @class Gauge + * @constructor + * @param {Object} gaugeOptions gauge options + */ + var Gauge = function(plot, ctx) { + context = ctx; + placeholder = plot.getPlaceholder(); + options = plot.getOptions(); + gaugeOptions = options.series.gauges; + series = plot.getData(); + logger = getLogger(gaugeOptions.debug); + } + + /** + * calculate layout + * + * @method calculateLayout + * @return the calculated layout properties + */ + Gauge.prototype.calculateLayout = function() { + + var canvasWidth = placeholder.width(); + var canvasHeight = placeholder.height(); + + + + // calculate cell size + var columns = Math.min(series.length, gaugeOptions.layout.columns); + var rows = Math.ceil(series.length / columns); + + + + var margin = gaugeOptions.layout.margin; + var hMargin = gaugeOptions.layout.hMargin; + var vMargin = gaugeOptions.layout.vMargin; + var cellWidth = (canvasWidth - (margin * 2) - (hMargin * (columns - 1))) / columns; + var cellHeight = (canvasHeight - (margin * 2) - (vMargin * (rows - 1))) / rows; + if (gaugeOptions.layout.square) { + var cell = Math.min(cellWidth, cellHeight); + cellWidth = cell; + cellHeight = cell; + } + + + + // calculate 'auto' values + calculateAutoValues(gaugeOptions, cellWidth); + + // calculate maximum radius + var cellMargin = gaugeOptions.cell.margin; + var labelMargin = 0; + var labelFontSize = 0; + if (gaugeOptions.label.show) { + labelMargin = gaugeOptions.label.margin; + labelFontSize = gaugeOptions.label.font.size; + } + var valueMargin = 0; + var valueFontSize = 0; + if (gaugeOptions.value.show) { + valueMargin = gaugeOptions.value.margin; + valueFontSize = gaugeOptions.value.font.size; + } + var thresholdWidth = 0; + if (gaugeOptions.threshold.show) { + thresholdWidth = gaugeOptions.threshold.width; + } + var thresholdLabelMargin = 0; + var thresholdLabelFontSize = 0; + if (gaugeOptions.threshold.label.show) { + thresholdLabelMargin = gaugeOptions.threshold.label.margin; + thresholdLabelFontSize = gaugeOptions.threshold.label.font.size; + } + + var maxRadiusH = (cellWidth / 2) - cellMargin - thresholdWidth - (thresholdLabelMargin * 2) - thresholdLabelFontSize; + + var startAngle = gaugeOptions.gauge.startAngle; + var endAngle = gaugeOptions.gauge.endAngle; + var dAngle = (endAngle - startAngle) / 100; + var heightRatioV = -1; + for (var a = startAngle; a < endAngle; a += dAngle) { + heightRatioV = Math.max(heightRatioV, Math.sin(toRad(a))); + } + heightRatioV = Math.max(heightRatioV, Math.sin(toRad(endAngle))); + var outerRadiusV = (cellHeight - (cellMargin * 2) - (labelMargin * 2) - labelFontSize) / (1 + heightRatioV); + if (outerRadiusV * heightRatioV < valueMargin + (valueFontSize / 2)) { + outerRadiusV = cellHeight - (cellMargin * 2) - (labelMargin * 2) - labelFontSize - valueMargin - (valueFontSize / 2); + } + var maxRadiusV = outerRadiusV - (thresholdLabelMargin * 2) - thresholdLabelFontSize - thresholdWidth; + + var radius = Math.min(maxRadiusH, maxRadiusV); + + + var width = gaugeOptions.gauge.width; + if (width >= radius) { + width = Math.max(3, radius / 3); + } + + + var outerRadius = (thresholdLabelMargin * 2) + thresholdLabelFontSize + thresholdWidth + radius; + var gaugeOuterHeight = Math.max(outerRadius * (1 + heightRatioV), outerRadius + valueMargin + (valueFontSize / 2)); + + return { + canvasWidth: canvasWidth, + canvasHeight: canvasHeight, + margin: margin, + hMargin: hMargin, + vMargin: vMargin, + columns: columns, + rows: rows, + cellWidth: cellWidth, + cellHeight: cellHeight, + cellMargin: cellMargin, + labelMargin: labelMargin, + labelFontSize: labelFontSize, + valueMargin: valueMargin, + valueFontSize: valueFontSize, + width: width, + radius: radius, + thresholdWidth: thresholdWidth, + thresholdLabelMargin: thresholdLabelMargin, + thresholdLabelFontSize: thresholdLabelFontSize, + gaugeOuterHeight: gaugeOuterHeight + }; + } + + /** + * calculate the values which are set as 'auto' + * + * @method calculateAutoValues + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Number} cellWidth the width of cell + */ + function calculateAutoValues(gaugeOptionsi, cellWidth) { + + if (gaugeOptionsi.gauge.width === "auto") { + gaugeOptionsi.gauge.width = Math.max(5, cellWidth / 8); + } + if (gaugeOptionsi.label.margin === "auto") { + gaugeOptionsi.label.margin = Math.max(1, cellWidth / 20); + } + if (gaugeOptionsi.label.font.size === "auto") { + gaugeOptionsi.label.font.size = Math.max(5, cellWidth / 8); + } + if (gaugeOptionsi.value.margin === "auto") { + gaugeOptionsi.value.margin = Math.max(1, cellWidth / 30); + } + if (gaugeOptionsi.value.font.size === "auto") { + gaugeOptionsi.value.font.size = Math.max(5, cellWidth / 9); + } + if (gaugeOptionsi.threshold.width === "auto") { + gaugeOptionsi.threshold.width = Math.max(3, cellWidth / 100); + } + if (gaugeOptionsi.threshold.label.margin === "auto") { + gaugeOptionsi.threshold.label.margin = Math.max(3, cellWidth / 40); + } + if (gaugeOptionsi.threshold.label.font.size === "auto") { + gaugeOptionsi.threshold.label.font.size = Math.max(5, cellWidth / 15); + } + + } + Gauge.prototype.calculateAutoValues = calculateAutoValues; + + /** + * calculate the layout of the cell inside + * + * @method calculateCellLayout + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Number} cellWidth the width of cell + * @param {Number} i the index of the series + * @return the calculated cell layout properties + */ + Gauge.prototype.calculateCellLayout = function(gaugeOptionsi, layout, i) { + + // calculate top, left and center + var c = col(layout.columns, i); + var r = row(layout.columns, i); + var x = layout.margin + (layout.cellWidth + layout.hMargin) * c; + var y = layout.margin + (layout.cellHeight + layout.vMargin) * r; + var cx = x + (layout.cellWidth / 2); + var cy = y + layout.cellMargin + (layout.labelMargin * 2) + layout.labelFontSize + layout.thresholdWidth + + layout.thresholdLabelFontSize + (layout.thresholdLabelMargin * 2) + layout.radius; + var blank = layout.cellHeight - (layout.cellMargin * 2) - (layout.labelMargin * 2) - layout.labelFontSize - layout.gaugeOuterHeight; + var offsetY = 0; + if (gaugeOptionsi.cell.vAlign === "middle") { + offsetY = (blank / 2); + } else if (gaugeOptionsi.cell.vAlign === "bottom") { + offsetY = blank; + } + cy += offsetY; + + return { + col: c, + row: r, + x: x, + y: y, + offsetY: offsetY, + cellWidth: layout.cellWidth, + cellHeight: layout.cellHeight, + cellMargin: layout.cellMargin, + cx: cx, + cy: cy + } + } + + /** + * draw the background of chart + * + * @method drawBackground + * @param {Object} layout the layout properties + */ + Gauge.prototype.drawBackground = function(layout) { + + if (!gaugeOptions.frame.show) { + return; + } + context.save(); + context.strokeStyle = options.grid.borderColor; + context.lineWidth = options.grid.borderWidth; + context.strokeRect(0, 0, layout.canvasWidth, layout.canvasHeight); + if (options.grid.backgroundColor) { + context.fillStyle = options.grid.backgroundColor; + context.fillRect(0, 0, layout.canvasWidth, layout.canvasHeight); + } + context.restore(); + } + + /** + * draw the background of cell + * + * @method drawCellBackground + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Object} cellLayout the cell layout properties + */ + Gauge.prototype.drawCellBackground = function(gaugeOptionsi, cellLayout) { + + context.save(); + if (gaugeOptionsi.cell.border && gaugeOptionsi.cell.border.show && gaugeOptionsi.cell.border.color && gaugeOptionsi.cell.border.width) { + context.strokeStyle = gaugeOptionsi.cell.border.color; + context.lineWidth = gaugeOptionsi.cell.border.width; + context.strokeRect(cellLayout.x, cellLayout.y, cellLayout.cellWidth, cellLayout.cellHeight); + } + if (gaugeOptionsi.cell.background && gaugeOptionsi.cell.background.color) { + context.fillStyle = gaugeOptionsi.cell.background.color; + context.fillRect(cellLayout.x, cellLayout.y, cellLayout.cellWidth, cellLayout.cellHeight); + } + context.restore(); + } + + /** + * draw the gauge + * + * @method drawGauge + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Object} layout the layout properties + * @param {Object} cellLayout the cell layout properties + * @param {String} label the label of data + * @param {Number} data the value of the gauge + */ + Gauge.prototype.drawGauge = function(gaugeOptionsi, layout, cellLayout, label, data) { + + + var blur = gaugeOptionsi.gauge.shadow.show ? gaugeOptionsi.gauge.shadow.blur : 0; + + + // draw gauge frame + drawArcWithShadow( + cellLayout.cx, // center x + cellLayout.cy, // center y + layout.radius, + layout.width, + toRad(gaugeOptionsi.gauge.startAngle), + toRad(gaugeOptionsi.gauge.endAngle), + gaugeOptionsi.gauge.border.color, // line color + gaugeOptionsi.gauge.border.width, // line width + gaugeOptionsi.gauge.background.color, // fill color + blur); + + // draw gauge + var c1 = getColor(gaugeOptionsi, data); + var a2 = calculateAngle(gaugeOptionsi, layout, data); + drawArcWithShadow( + cellLayout.cx, // center x + cellLayout.cy, // center y + layout.radius - 1, + layout.width - 2, + toRad(gaugeOptionsi.gauge.startAngle), + toRad(a2), + c1, // line color + 1, // line width + c1, // fill color + blur); + } + + /** + * decide the color of the data from the threshold options + * + * @method getColor + * @private + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Number} data the value of the gauge + */ + function getColor(gaugeOptionsi, data) { + var color; + for (var i = 0; i < gaugeOptionsi.threshold.values.length; i++) { + var threshold = gaugeOptionsi.threshold.values[i]; + color = threshold.color; + if (data <= threshold.value) { + break; + } + } + return color; + } + + /** + * calculate the angle of the data + * + * @method calculateAngle + * @private + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Object} layout the layout properties + * @param {Number} data the value of the gauge + */ + function calculateAngle(gaugeOptionsi, layout, data) { + var a = + gaugeOptionsi.gauge.startAngle + + (gaugeOptionsi.gauge.endAngle - gaugeOptionsi.gauge.startAngle) + * ((data - gaugeOptionsi.gauge.min) / (gaugeOptionsi.gauge.max - gaugeOptionsi.gauge.min)); + + if (a < gaugeOptionsi.gauge.startAngle) { + a = gaugeOptionsi.gauge.startAngle; + } else if (a > gaugeOptionsi.gauge.endAngle) { + a = gaugeOptionsi.gauge.endAngle; + } + return a; + } + + /** + * draw the arc of the threshold + * + * @method drawThreshold + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Object} layout the layout properties + * @param {Object} cellLayout the cell layout properties + */ + Gauge.prototype.drawThreshold = function(gaugeOptionsi, layout, cellLayout) { + + var a1 = gaugeOptionsi.gauge.startAngle; + for (var i = 0; i < gaugeOptionsi.threshold.values.length; i++) { + var threshold = gaugeOptionsi.threshold.values[i]; + c1 = threshold.color; + a2 = calculateAngle(gaugeOptionsi, layout, threshold.value); + drawArc( + context, + cellLayout.cx, // center x + cellLayout.cy, // center y + layout.radius + layout.thresholdWidth, + layout.thresholdWidth - 2, + toRad(a1), + toRad(a2), + c1, // line color + 1, // line width + c1); // fill color + a1 = a2; + } + } + + /** + * draw an arc with a shadow + * + * @method drawArcWithShadow + * @private + * @param {Number} cx the x position of the center + * @param {Number} cy the y position of the center + * @param {Number} r the radius of an arc + * @param {Number} w the width of an arc + * @param {Number} rd1 the start angle of an arc in radians + * @param {Number} rd2 the end angle of an arc in radians + * @param {String} lc the color of a line + * @param {Number} lw the widht of a line + * @param {String} fc the fill color of an arc + * @param {Number} blur the shdow blur + */ + function drawArcWithShadow(cx, cy, r, w, rd1, rd2, lc, lw, fc, blur) { + if (rd1 === rd2) { + return; + } + context.save(); + + drawArc(context, cx, cy, r, w, rd1, rd2, lc, lw, fc); + + if (blur) { + drawArc(context, cx, cy, r, w, rd1, rd2); + context.clip(); + context.shadowOffsetX = 0; + context.shadowOffsetY = 0; + context.shadowBlur = 10; + context.shadowColor = "gray"; + drawArc(context, cx, cy, r + 1, w + 2, rd1, rd2, lc, 1); + } + context.restore(); + } + + /** + * draw the label of the gauge + * + * @method drawLable + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Object} layout the layout properties + * @param {Object} cellLayout the cell layout properties + * @param {Number} i the index of the series + * @param {Object} item the item of the series + */ + Gauge.prototype.drawLable = function(gaugeOptionsi, layout, cellLayout, i, item) { + + drawText( + cellLayout.cx, + cellLayout.y + cellLayout.cellMargin + layout.labelMargin + cellLayout.offsetY, + "flotGagueLabel" + i, + gaugeOptionsi.label.formatter ? gaugeOptionsi.label.formatter(item.label, item.data[0][1]) : text, + gaugeOptionsi.label); + } + + /** + * draw the value of the gauge + * + * @method drawValue + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Object} layout the layout properties + * @param {Object} cellLayout the cell layout properties + * @param {Number} i the index of the series + * @param {Object} item the item of the series + */ + Gauge.prototype.drawValue = function(gaugeOptionsi, layout, cellLayout, i, item) { + + drawText( + cellLayout.cx, + cellLayout.cy - (gaugeOptionsi.value.font.size / 2), + "flotGagueValue" + i, + gaugeOptionsi.value.formatter ? gaugeOptionsi.value.formatter(item.label, item.data[0][1]) : text, + gaugeOptionsi.value); + } + + /** + * draw the values of the threshold + * + * @method drawThresholdValues + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Object} layout the layout properties + * @param {Object} cellLayout the cell layout properties + * @param {Number} i the index of the series + */ + Gauge.prototype.drawThresholdValues = function(gaugeOptionsi, layout, cellLayout, i) { + + // min, max + drawThresholdValue(gaugeOptionsi, layout, cellLayout, "Min" + i, gaugeOptionsi.gauge.min, gaugeOptionsi.gauge.startAngle); + drawThresholdValue(gaugeOptionsi, layout, cellLayout, "Max" + i, gaugeOptionsi.gauge.max, gaugeOptionsi.gauge.endAngle); + // threshold values + for (var j = 0; j < gaugeOptionsi.threshold.values.length; j++) { + var threshold = gaugeOptionsi.threshold.values[j]; + if (threshold.value > gaugeOptionsi.gauge.min && threshold.value < gaugeOptionsi.gauge.max) { + var a = calculateAngle(gaugeOptionsi, layout, threshold.value); + drawThresholdValue(gaugeOptionsi, layout, cellLayout, i + "_" + j, threshold.value, a); + } + } + } + + /** + * draw the value of the threshold + * + * @method drawThresholdValue + * @param {Object} gaugeOptionsi the options of the gauge + * @param {Object} layout the layout properties + * @param {Object} cellLayout the cell layout properties + * @param {Number} i the index of the series + * @param {Number} value the value of the threshold + * @param {Number} a the angle of the value drawn + */ + function drawThresholdValue(gaugeOptionsi, layout, cellLayout, i, value, a) { + drawText( + cellLayout.cx + + ((layout.thresholdLabelMargin + (layout.thresholdLabelFontSize / 2) + layout.radius) + * Math.cos(toRad(a))), + cellLayout.cy + + ((layout.thresholdLabelMargin + (layout.thresholdLabelFontSize / 2) + layout.radius) + * Math.sin(toRad(a))), + "flotGagueThresholdValue" + i, + gaugeOptionsi.threshold.label.formatter ? gaugeOptionsi.threshold.label.formatter(value) : value, + gaugeOptionsi.threshold.label, + a); + } + + /** + * draw a text + * + * the textOptions is assumed as follows: + * + * textOptions: { + * background: { + * color: null, + * opacity: 0 + * }, + * font: { + * size: "auto" + * family: "\"MS ゴシック\",sans-serif" + * }, + * color: null + * } + * + * @method drawText + * @private + * @param {Number} x the x position of the text drawn (left top) + * @param {Number} y the y position of the text drawn (left top) + * @param {String} id the id of the dom element + * @param {String} text the text drawn + * @param {Object} textOptions the option of the text + * @param {Number} [a] the angle of the value drawn + */ + function drawText(x, y, id, text, textOptions, a) { + var span = $("." + id, placeholder); + var exists = span.length; + if (!exists) { + span = $("") + span.attr("id", id); + span.css("position", "absolute"); + span.css("top", y + "px"); + if (textOptions.font.size) { + span.css("font-size", textOptions.font.size + "px"); + } + if (textOptions.font.family) { + span.css("font-family", textOptions.font.family); + } + if (textOptions.color) { + span.css("color", textOptions.color); + } + if (textOptions.background.color) { + span.css("background-color", textOptions.background.color); + } + if (textOptions.background.opacity) { + span.css("opacity", textOptions.background.opacity); + } + placeholder.append(span); + } + span.text(text); + // after append, readjust the left position + span.css("left", x + "px"); // for redraw, resetting the left position is needed here + span.css("left", (parseInt(span.css("left")) - (span.width()/ 2)) + "px"); + + // at last, set angle + if (!exists && a) { + span.css("top", (parseInt(span.css("top")) - (span.height()/ 2)) + "px"); + span.css("transform", "rotate(" + ((180 * a) + 90) + "deg)"); // not supported for ie8 + } + } + + return Gauge; + })(); + /** + * get a instance of Logger + * + * @method getLogger + * @for flot.gauge + * @private + * @param {Object} debugOptions the options of debug + */ + function getLogger(debugOptions) { + return typeof Logger !== "undefined" ? new Logger(debugOptions) : null; + } + + /** + * calculate the index of columns for the specified data + * + * @method col + * @for flot.gauge + * @param {Number} columns the number of columns + * @param {Number} i the index of the series + * @return the index of columns + */ + function col(columns, i) { + return i % columns; + } + + /** + * calculate the index of rows for the specified data + * + * @method row + * @for flot.gauge + * @param {Number} columns the number of rows + * @param {Number} i the index of the series + * @return the index of rows + */ + function row(columns, i) { + return Math.floor(i / columns); + } + + /** + * calculate the angle in radians + * + * internally, use a number without PI (0 - 2). + * so, in this function, multiply PI + * + * @method toRad + * @for flot.gauge + * @param {Number} a the number of angle without PI + * @return the angle in radians + */ + function toRad(a) { + return a * Math.PI; + } + + /** + * draw an arc + * + * @method drawArc + * @for flot.gauge + * @param {Object} context the context of canvas + * @param {Number} cx the x position of the center + * @param {Number} cy the y position of the center + * @param {Number} r the radius of an arc + * @param {Number} w the width of an arc + * @param {Number} rd1 the start angle of an arc in radians + * @param {Number} rd2 the end angle of an arc in radians + * @param {String} lc the color of a line + * @param {Number} lw the widht of a line + * @param {String} fc the fill color of an arc + */ + function drawArc(context, cx, cy, r, w, rd1, rd2, lc, lw, fc) { + if (rd1 === rd2) { + return; + } + var counterClockwise = false; + context.save(); + context.beginPath(); + context.arc(cx, cy, r, rd1, rd2, counterClockwise); + context.lineTo(cx + (r - w) * Math.cos(rd2), + cy + (r - w) * Math.sin(rd2)); + context.arc(cx, cy, r - w, rd2, rd1, !counterClockwise); + context.closePath(); + if (lw) { + context.lineWidth = lw; + } + if (lc) { + context.strokeStyle = lc; + context.stroke(); + } + if (fc) { + context.fillStyle = fc; + context.fill(); + } + context.restore(); + } + + /** + * initialize plugin + * + * @method init + * @for flot.gauge + * @private + * @param {Object} plot a instance of plot + */ + function init (plot) { + // add processOptions hook + plot.hooks.processOptions.push(function(plot, options) { + var logger = getLogger(options.series.gauges.debug); + + + + + // turn 'grid' and 'legend' off + if (options.series.gauges.show) { + options.grid.show = false; + options.legend.show = false; + } + + // sort threshold + var thresholds = options.series.gauges.threshold.values; + + thresholds.sort(function(a, b) { + if (a.value < b.value) { + return -1; + } else if (a.value > b.value) { + return 1; + } else { + return 0; + } + }); + + + + }); + + // add draw hook + plot.hooks.draw.push(function(plot, context) { + var options = plot.getOptions(); + var gaugeOptions = options.series.gauges; + + var logger = getLogger(gaugeOptions.debug); + + + if (!gaugeOptions.show) { + return; + } + + var series = plot.getData(); + + if (!series || !series.length) { + return; // if no series were passed + } + + var gauge = new Gauge(plot, context); + + // calculate layout + var layout = gauge.calculateLayout(); + + // debug layout + if (gaugeOptions.debug.layout) { + + } + + // draw background + gauge.drawBackground(layout) + + // draw cells (label, gauge, value, threshold) + for (var i = 0; i < series.length; i++) { + var item = series[i]; + + var gaugeOptionsi = $.extend({}, gaugeOptions, item.gauges); + if (item.gauges) { + // re-calculate 'auto' values + gauge.calculateAutoValues(gaugeOptionsi, layout.cellWidth); + } + + // calculate cell layout + var cellLayout = gauge.calculateCellLayout(gaugeOptionsi, layout, i); + + // draw cell background + gauge.drawCellBackground(gaugeOptionsi, cellLayout) + // debug layout + if (gaugeOptionsi.debug.layout) { + + } + // draw label + if (gaugeOptionsi.label.show) { + gauge.drawLable(gaugeOptionsi, layout, cellLayout, i, item); + } + // draw gauge + gauge.drawGauge(gaugeOptionsi, layout, cellLayout, item.label, item.data[0][1]); + // draw threshold + if (gaugeOptionsi.threshold.show) { + gauge.drawThreshold(gaugeOptionsi, layout, cellLayout); + } + if (gaugeOptionsi.threshold.label.show) { + gauge.drawThresholdValues(gaugeOptionsi, layout, cellLayout, i) + } + // draw value + if (gaugeOptionsi.value.show) { + gauge.drawValue(gaugeOptionsi, layout, cellLayout, i, item); + } + } + }); + } + + /** + * [defaults description] + * + * @property defaults + * @type {Object} + */ + var defaults = { + series: { + gauges: { + debug: { + log: false, + layout: false, + alert: false + }, + show: false, + layout: { + margin: 5, + columns: 3, + hMargin: 5, + vMargin: 5, + square: false + }, + frame: { + show: true + }, + cell: { + background: { + color: null + }, + border: { + show: true, + color: "black", + width: 1 + }, + margin: 5, + vAlign: "middle" // 'top' or 'middle' or 'bottom' + }, + gauge: { + width: "auto", // a specified number, or 'auto' + startAngle: 0.9, // 0 - 2 factor of the radians + endAngle: 2.1, // 0 - 2 factor of the radians + min: 0, + max: 100, + background: { + color: "white" + }, + border: { + color: "lightgray", + width: 2 + }, + shadow: { + show: true, + blur: 5 + } + }, + label: { + show: true, + margin: "auto", // a specified number, or 'auto' + background: { + color: null, + opacity: 0 + }, + font: { + size: "auto", // a specified number, or 'auto' + family: "sans-serif" + }, + color: null, + formatter: function(label, value) { + return label; + } + }, + value: { + show: true, + margin: "auto", // a specified number, or 'auto' + background: { + color: null, + opacity: 0 + }, + font: { + size: "auto", // a specified number, or 'auto' + family: "sans-serif" + }, + color: null, + formatter: function(label, value) { + return parseInt(value); + } + }, + threshold: { + show: true, + width: "auto", // a specified number, or 'auto' + label: { + show: true, + margin: "auto", // a specified number, or 'auto' + background: { + color: null, + opacity: 0 + }, + font: { + size: "auto", // a specified number, or 'auto' + family: ",sans-serif" + }, + color: null, + formatter: function(value) { + return value; + } + }, + values: [ + { + value: 50, + color: "lightgreen" + }, { + value: 80, + color: "yellow" + }, { + value: 100, + color: "red" + } + ] + } + } + } + }; + + // register the gauge plugin + $.plot.plugins.push({ + init: init, + options: defaults, + name: "gauge", + version: "1.1.0" + }); + +})(jQuery); From 16dbf3166d2246904a8766d5d403dd23706f4892 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 22 Apr 2016 15:29:33 +0200 Subject: [PATCH 02/13] fix(panels): avoid deep copy problem for panel defaults Moves the defaults to be an instance property. This solves the problem of lodash defaults function not doing a deep copy. The symptom of this is that new deep properties for a panel are assigned to every panel of the same type by the defaults function in the constructor. --- public/app/plugins/panel/graph/module.ts | 166 +++++++++--------- public/app/plugins/panel/pluginlist/module.ts | 10 +- public/app/plugins/panel/singlestat/module.ts | 78 ++++---- public/app/plugins/panel/table/module.ts | 56 +++--- public/app/plugins/panel/text/module.ts | 14 +- 5 files changed, 161 insertions(+), 163 deletions(-) diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 1fcd44204f7..4608de804aa 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -13,85 +13,6 @@ import TimeSeries from 'app/core/time_series2'; import * as fileExport from 'app/core/utils/file_export'; import {MetricsPanelCtrl} from 'app/plugins/sdk'; -var panelDefaults = { - // datasource name, null = default datasource - datasource: null, - // sets client side (flot) or native graphite png renderer (png) - renderer: 'flot', - yaxes: [ - { - label: null, - show: true, - logBase: 1, - min: null, - max: null, - format: 'short' - }, - { - label: null, - show: true, - logBase: 1, - min: null, - max: null, - format: 'short' - } - ], - xaxis: { - show: true - }, - grid : { - threshold1: null, - threshold2: null, - threshold1Color: 'rgba(216, 200, 27, 0.27)', - threshold2Color: 'rgba(234, 112, 112, 0.22)' - }, - // show/hide lines - lines : true, - // fill factor - fill : 1, - // line width in pixels - linewidth : 2, - // show hide points - points : false, - // point radius in pixels - pointradius : 5, - // show hide bars - bars : false, - // enable/disable stacking - stack : false, - // stack percentage mode - percentage : false, - // legend options - legend: { - show: true, // disable/enable legend - values: false, // disable/enable legend values - min: false, - max: false, - current: false, - total: false, - avg: false - }, - // how null points should be handled - nullPointMode : 'connected', - // staircase line mode - steppedLine: false, - // tooltip options - tooltip : { - value_type: 'cumulative', - shared: true, - msResolution: false, - }, - // time overrides - timeFrom: null, - timeShift: null, - // metric queries - targets: [{}], - // series color overrides - aliasColors: {}, - // other style overrides - seriesOverrides: [], -}; - class GraphCtrl extends MetricsPanelCtrl { static template = template; @@ -105,14 +26,93 @@ class GraphCtrl extends MetricsPanelCtrl { datapointsWarning: boolean; colors: any = []; + panelDefaults = { + // datasource name, null = default datasource + datasource: null, + // sets client side (flot) or native graphite png renderer (png) + renderer: 'flot', + yaxes: [ + { + label: null, + show: true, + logBase: 1, + min: null, + max: null, + format: 'short' + }, + { + label: null, + show: true, + logBase: 1, + min: null, + max: null, + format: 'short' + } + ], + xaxis: { + show: true + }, + grid : { + threshold1: null, + threshold2: null, + threshold1Color: 'rgba(216, 200, 27, 0.27)', + threshold2Color: 'rgba(234, 112, 112, 0.22)' + }, + // show/hide lines + lines : true, + // fill factor + fill : 1, + // line width in pixels + linewidth : 2, + // show hide points + points : false, + // point radius in pixels + pointradius : 5, + // show hide bars + bars : false, + // enable/disable stacking + stack : false, + // stack percentage mode + percentage : false, + // legend options + legend: { + show: true, // disable/enable legend + values: false, // disable/enable legend values + min: false, + max: false, + current: false, + total: false, + avg: false + }, + // how null points should be handled + nullPointMode : 'connected', + // staircase line mode + steppedLine: false, + // tooltip options + tooltip : { + value_type: 'cumulative', + shared: true, + msResolution: false, + }, + // time overrides + timeFrom: null, + timeShift: null, + // metric queries + targets: [{}], + // series color overrides + aliasColors: {}, + // other style overrides + seriesOverrides: [], + }; + /** @ngInject */ constructor($scope, $injector, private annotationsSrv) { super($scope, $injector); - _.defaults(this.panel, angular.copy(panelDefaults)); - _.defaults(this.panel.tooltip, panelDefaults.tooltip); - _.defaults(this.panel.grid, panelDefaults.grid); - _.defaults(this.panel.legend, panelDefaults.legend); + _.defaults(this.panel, angular.copy(this.panelDefaults)); + _.defaults(this.panel.tooltip, this.panelDefaults.tooltip); + _.defaults(this.panel.grid, this.panelDefaults.grid); + _.defaults(this.panel.legend, this.panelDefaults.legend); this.colors = $scope.$root.colors; diff --git a/public/app/plugins/panel/pluginlist/module.ts b/public/app/plugins/panel/pluginlist/module.ts index 8ec86efd9c2..5ef1fb6fb0c 100644 --- a/public/app/plugins/panel/pluginlist/module.ts +++ b/public/app/plugins/panel/pluginlist/module.ts @@ -4,20 +4,20 @@ import _ from 'lodash'; import config from 'app/core/config'; import {PanelCtrl} from '../../../features/panel/panel_ctrl'; -// Set and populate defaults -var panelDefaults = { -}; - class PluginListCtrl extends PanelCtrl { static templateUrl = 'module.html'; pluginList: any[]; viewModel: any; + // Set and populate defaults + panelDefaults = { + }; + /** @ngInject */ constructor($scope, $injector, private backendSrv, private $location) { super($scope, $injector); - _.defaults(this.panel, panelDefaults); + _.defaults(this.panel, this.panelDefaults); this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); this.pluginList = []; diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index 6d60a70269e..0e28aa8baba 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -11,44 +11,6 @@ import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; import {MetricsPanelCtrl} from 'app/plugins/sdk'; -// Set and populate defaults -var panelDefaults = { - links: [], - datasource: null, - maxDataPoints: 100, - interval: null, - targets: [{}], - cacheTimeout: null, - format: 'none', - prefix: '', - postfix: '', - nullText: null, - valueMaps: [ - { value: 'null', op: '=', text: 'N/A' } - ], - nullPointMode: 'connected', - valueName: 'avg', - prefixFontSize: '50%', - valueFontSize: '80%', - postfixFontSize: '50%', - thresholds: '', - colorBackground: false, - colorValue: false, - colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], - sparkline: { - show: false, - full: false, - lineColor: 'rgb(31, 120, 193)', - fillColor: 'rgba(31, 118, 189, 0.18)', - }, - gauge: { - show: false, - minValue: 0, - maxValue: 100, - thresholdLabels: true - } -}; - class SingleStatCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; @@ -57,10 +19,48 @@ class SingleStatCtrl extends MetricsPanelCtrl { fontSizes: any[]; unitFormats: any[]; + // Set and populate defaults + panelDefaults = { + links: [], + datasource: null, + maxDataPoints: 100, + interval: null, + targets: [{}], + cacheTimeout: null, + format: 'none', + prefix: '', + postfix: '', + nullText: null, + valueMaps: [ + { value: 'null', op: '=', text: 'N/A' } + ], + nullPointMode: 'connected', + valueName: 'avg', + prefixFontSize: '50%', + valueFontSize: '80%', + postfixFontSize: '50%', + thresholds: '', + colorBackground: false, + colorValue: false, + colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], + sparkline: { + show: false, + full: false, + lineColor: 'rgb(31, 120, 193)', + fillColor: 'rgba(31, 118, 189, 0.18)', + }, + gauge: { + show: false, + minValue: 0, + maxValue: 100, + thresholdLabels: true + } + }; + /** @ngInject */ constructor($scope, $injector, private $location, private linkSrv) { super($scope, $injector); - _.defaults(this.panel, panelDefaults); + _.defaults(this.panel, this.panelDefaults); this.events.on('data-received', this.onDataReceived.bind(this)); this.events.on('data-error', this.onDataError.bind(this)); diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index 7a2973cc390..837cf0df916 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -10,33 +10,6 @@ import {transformDataToTable} from './transformers'; import {tablePanelEditor} from './editor'; import {TableRenderer} from './renderer'; -var panelDefaults = { - targets: [{}], - transform: 'timeseries_to_columns', - pageSize: null, - showHeader: true, - styles: [ - { - type: 'date', - pattern: 'Time', - dateFormat: 'YYYY-MM-DD HH:mm:ss', - }, - { - unit: 'short', - type: 'number', - decimals: 2, - colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], - colorMode: null, - pattern: '/.*/', - thresholds: [], - } - ], - columns: [], - scroll: true, - fontSize: '100%', - sort: {col: 0, desc: true}, -}; - class TablePanelCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; @@ -44,6 +17,33 @@ class TablePanelCtrl extends MetricsPanelCtrl { dataRaw: any; table: any; + panelDefaults = { + targets: [{}], + transform: 'timeseries_to_columns', + pageSize: null, + showHeader: true, + styles: [ + { + type: 'date', + pattern: 'Time', + dateFormat: 'YYYY-MM-DD HH:mm:ss', + }, + { + unit: 'short', + type: 'number', + decimals: 2, + colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], + colorMode: null, + pattern: '/.*/', + thresholds: [], + } + ], + columns: [], + scroll: true, + fontSize: '100%', + sort: {col: 0, desc: true}, + }; + /** @ngInject */ constructor($scope, $injector, private annotationsSrv) { super($scope, $injector); @@ -56,7 +56,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { delete this.panel.fields; } - _.defaults(this.panel, panelDefaults); + _.defaults(this.panel, this.panelDefaults); this.events.on('data-received', this.onDataReceived.bind(this)); this.events.on('data-error', this.onDataError.bind(this)); diff --git a/public/app/plugins/panel/text/module.ts b/public/app/plugins/panel/text/module.ts index 42657788c0a..7228f220d46 100644 --- a/public/app/plugins/panel/text/module.ts +++ b/public/app/plugins/panel/text/module.ts @@ -3,23 +3,21 @@ import _ from 'lodash'; import {PanelCtrl} from 'app/plugins/sdk'; - // Set and populate defaults -var panelDefaults = { - mode : "markdown", // 'html', 'markdown', 'text' - content : "# title", -}; - export class TextPanelCtrl extends PanelCtrl { static templateUrl = `public/app/plugins/panel/text/module.html`; remarkable: any; content: string; - + // Set and populate defaults + panelDefaults = { + mode : "markdown", // 'html', 'markdown', 'text' + content : "# title", + }; /** @ngInject */ constructor($scope, $injector, private templateSrv, private $sce) { super($scope, $injector); - _.defaults(this.panel, panelDefaults); + _.defaults(this.panel, this.panelDefaults); this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); this.events.on('refresh', this.onRender.bind(this)); From 9917de09afe78d77b4641c67d272c43f2f4a0ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 22 Apr 2016 15:49:51 +0200 Subject: [PATCH 03/13] fix(graph): removed angular copy that was no longer needed --- public/app/plugins/panel/graph/module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 4608de804aa..a11c537b178 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -109,7 +109,7 @@ class GraphCtrl extends MetricsPanelCtrl { constructor($scope, $injector, private annotationsSrv) { super($scope, $injector); - _.defaults(this.panel, angular.copy(this.panelDefaults)); + _.defaults(this.panel, this.panelDefaults); _.defaults(this.panel.tooltip, this.panelDefaults.tooltip); _.defaults(this.panel.grid, this.panelDefaults.grid); _.defaults(this.panel.legend, this.panelDefaults.legend); From 349f6b51a5d5417e3179af0b590df83a85a60a27 Mon Sep 17 00:00:00 2001 From: Matt Toback Date: Fri, 22 Apr 2016 11:32:19 -0400 Subject: [PATCH 04/13] Updated link color in text panel to be -link-color --- public/sass/components/_panel_text.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/public/sass/components/_panel_text.scss b/public/sass/components/_panel_text.scss index 9de31bab4c8..6e0301cecc5 100644 --- a/public/sass/components/_panel_text.scss +++ b/public/sass/components/_panel_text.scss @@ -4,4 +4,5 @@ margin: 0 0 $spacer $spacer * 1.5; } li {line-height: 2;} + a { color: $external-link-color; } } From 25b0773a947939708b2d7219cde684745e4b1abb Mon Sep 17 00:00:00 2001 From: Dan Cech Date: Fri, 22 Apr 2016 12:12:36 -0400 Subject: [PATCH 05/13] fix broken link to grafana.net, standardize on https://grafana.net for links --- docs/sources/plugins/index.md | 2 +- public/app/features/plugins/partials/update_instructions.html | 2 +- public/app/plugins/datasource/influxdb/README.md | 4 ++-- public/app/plugins/panel/pluginlist/module.html | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sources/plugins/index.md b/docs/sources/plugins/index.md index 08654bfb946..bb5c8062223 100644 --- a/docs/sources/plugins/index.md +++ b/docs/sources/plugins/index.md @@ -15,7 +15,7 @@ Grafana already have a strong community of contributors and plugin developers. By making it easier to develop and install plugins we hope that the community can grow even stronger and develop new plugins that we would never think about. -You can discover available plugins on [Grafana.net](http://grafana.net) +You can discover available plugins on [Grafana.net](https://grafana.net) diff --git a/public/app/features/plugins/partials/update_instructions.html b/public/app/features/plugins/partials/update_instructions.html index 55455e24af7..9648be53c86 100644 --- a/public/app/features/plugins/partials/update_instructions.html +++ b/public/app/features/plugins/partials/update_instructions.html @@ -14,7 +14,7 @@

Type the following on the command line to update {{plugin.name}}.

grafana-cli plugins update {{plugin.id}}
- Check out {{plugin.name}} on Grafana.net for README and changelog. If you do not have access to the command line, ask your Grafana administator. + Check out {{plugin.name}} on Grafana.net for README and changelog. If you do not have access to the command line, ask your Grafana administator.

Pro tip: To update all plugins at once, type grafana-cli plugins update-all on the command line.

diff --git a/public/app/plugins/datasource/influxdb/README.md b/public/app/plugins/datasource/influxdb/README.md index 45eaa51eb0f..d8d98cf5c6b 100644 --- a/public/app/plugins/datasource/influxdb/README.md +++ b/public/app/plugins/datasource/influxdb/README.md @@ -6,8 +6,8 @@ There are currently two separate datasources for InfluxDB in Grafana: InfluxDB 0 This is the plugin for InfluxDB 0.9. It is rapidly evolving and we continue to track its API. -InfluxDB 0.8 is no longer maintained by InfluxDB Inc, but we provide support as a convenience to existing users. You can find it [here](https://www.grafana.net/plugins/grafana-influxdb-08-datasource). +InfluxDB 0.8 is no longer maintained by InfluxDB Inc, but we provide support as a convenience to existing users. You can find it [here](https://grafana.net/plugins/grafana-influxdb-08-datasource). Read more about InfluxDB here: -[http://docs.grafana.org/datasources/influxdb/](http://docs.grafana.org/datasources/influxdb/) \ No newline at end of file +[http://docs.grafana.org/datasources/influxdb/](http://docs.grafana.org/datasources/influxdb/) diff --git a/public/app/plugins/panel/pluginlist/module.html b/public/app/plugins/panel/pluginlist/module.html index c73da35b391..8e74f173a07 100644 --- a/public/app/plugins/panel/pluginlist/module.html +++ b/public/app/plugins/panel/pluginlist/module.html @@ -22,7 +22,7 @@
- + No additional panels installed. Browse Grafana.net
From 70acfb2cfd942d75eb0fc75ef610d909a8d26c3e Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 21 Apr 2016 11:15:51 +0200 Subject: [PATCH 06/13] fix(cli): adds better help text. The zip lib is throwing panics sometimes when the response is malformed. The cli will now try to download the zip file up to three times before aborting. The cli gives a better error message and informes the user about retrying. closes #4651 --- pkg/cmd/grafana-cli/commands/install_command.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index addcf9a8b7e..1ed3a8a3fe8 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -127,9 +127,14 @@ func downloadFile(pluginName, filePath, url string) (err error) { if r := recover(); r != nil { retryCount++ if retryCount < 3 { - fmt.Printf("\nFailed downloading. Will retry once.\n%v\n", r) + + fmt.Println("Failed downloading. Will retry once.") downloadFile(pluginName, filePath, url) } else { + failure := fmt.Sprintf("%v", r) + if failure == "runtime error: makeslice: len out of range" { + log.Errorf("Failed to extract zipped HTTP response. Please try again.\n") + } panic(r) } } From 0855f514363146a2623a3081fd337982c5c940a5 Mon Sep 17 00:00:00 2001 From: bergquist Date: Sat, 23 Apr 2016 10:03:00 +0200 Subject: [PATCH 07/13] feat(cli): improves defer error handling --- pkg/cmd/grafana-cli/commands/install_command.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index 1ed3a8a3fe8..a07b2dcae38 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -127,15 +127,15 @@ func downloadFile(pluginName, filePath, url string) (err error) { if r := recover(); r != nil { retryCount++ if retryCount < 3 { - fmt.Println("Failed downloading. Will retry once.") - downloadFile(pluginName, filePath, url) + err = downloadFile(pluginName, filePath, url) } else { failure := fmt.Sprintf("%v", r) if failure == "runtime error: makeslice: len out of range" { - log.Errorf("Failed to extract zipped HTTP response. Please try again.\n") + err = fmt.Errorf("Failed to extract zipped HTTP response. Please try again.\n") + } else { + panic(r) } - panic(r) } } }() From 07b13e24fa43e7c171046f6d77ab13f053af3a3c Mon Sep 17 00:00:00 2001 From: bergquist Date: Sat, 23 Apr 2016 14:31:24 +0200 Subject: [PATCH 08/13] style(cli): add some color to error messages --- pkg/cmd/grafana-cli/commands/commands.go | 3 ++- pkg/cmd/grafana-cli/commands/install_command.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/grafana-cli/commands/commands.go b/pkg/cmd/grafana-cli/commands/commands.go index b3821a47844..ec454078f9b 100644 --- a/pkg/cmd/grafana-cli/commands/commands.go +++ b/pkg/cmd/grafana-cli/commands/commands.go @@ -4,6 +4,7 @@ import ( "os" "github.com/codegangsta/cli" + "github.com/fatih/color" "github.com/grafana/grafana/pkg/cmd/grafana-cli/log" ) @@ -12,7 +13,7 @@ func runCommand(command func(commandLine CommandLine) error) func(context *cli.C cmd := &contextCommandLine{context} if err := command(cmd); err != nil { - log.Error("\nError: ") + log.Errorf("\n%s: ", color.RedString("Error")) log.Errorf("%s\n\n", err) cmd.ShowHelp() diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index a07b2dcae38..eb5973d07be 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -132,7 +132,7 @@ func downloadFile(pluginName, filePath, url string) (err error) { } else { failure := fmt.Sprintf("%v", r) if failure == "runtime error: makeslice: len out of range" { - err = fmt.Errorf("Failed to extract zipped HTTP response. Please try again.\n") + err = fmt.Errorf("Corrupt http response from source. Please try again.\n") } else { panic(r) } From ee0c4cd1945b646ebf7671eaecb8f1acee358cb4 Mon Sep 17 00:00:00 2001 From: bergquist Date: Sat, 23 Apr 2016 14:37:26 +0200 Subject: [PATCH 09/13] docs(changelog): add note about cli issue closes #4651 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba8e6242243..9d4ee020dce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * **Dashlist**: Fixed issue dashboard list panel and caching tags, fixes [#4768](https://github.com/grafana/grafana/issues/4768) * **Graph**: Fixed issue with unneeded scrollbar in legend for Firefox, fixes [#4760](https://github.com/grafana/grafana/issues/4760) * **Table panel**: Fixed issue table panel formating string array properties, fixes [#4791](https://github.com/grafana/grafana/issues/4791) +* **grafana-cli**: Improve error message when failing to install plugins due to corrupt response, fixes [#4651](https://github.com/grafana/grafana/issues/4651) # 3.0.0-beta5 (2016-04-15) From 21d2a51cd335da768af4d12a77842ac2e48bf852 Mon Sep 17 00:00:00 2001 From: Tyler Mitchell Date: Sat, 23 Apr 2016 22:00:42 -0700 Subject: [PATCH 10/13] typo tabs != tags --- docs/sources/http_api/dashboard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/http_api/dashboard.md b/docs/sources/http_api/dashboard.md index 68f6ab022cc..ead63c722aa 100644 --- a/docs/sources/http_api/dashboard.md +++ b/docs/sources/http_api/dashboard.md @@ -191,7 +191,7 @@ Will return the home dashboard. `GET /api/dashboards/tags` -Get all tabs of dashboards +Get all tags of dashboards **Example Request**: From 00827ce921352807315e05ef664d3cba77c29a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 24 Apr 2016 12:50:33 +0200 Subject: [PATCH 11/13] fix(): let binding cycle complete before adding panel to dom --- public/app/core/directives/plugin_component.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/public/app/core/directives/plugin_component.ts b/public/app/core/directives/plugin_component.ts index 3e6383cc5c6..6708c0315f3 100644 --- a/public/app/core/directives/plugin_component.ts +++ b/public/app/core/directives/plugin_component.ts @@ -206,9 +206,12 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ }); $compile(child)(scope); - elem.empty(); - elem.append(child); + + // let a binding digest cycle complete before adding to dom + setTimeout(function() { + elem.append(child); + }); } function registerPluginComponent(scope, elem, attrs, componentInfo) { From 711992c8e2e811abd450cb665008bb0a1b3e29a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 25 Apr 2016 09:26:47 +0200 Subject: [PATCH 12/13] fix(influxdb): minor fix for duplicated db param to proxied influxdb call, fixes #4703 --- pkg/api/dataproxy.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go index eb88045cd51..b00ef595161 100644 --- a/pkg/api/dataproxy.go +++ b/pkg/api/dataproxy.go @@ -41,7 +41,6 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *ht req.URL.RawQuery = reqQueryVals.Encode() } else if ds.Type == m.DS_INFLUXDB { req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath) - reqQueryVals.Add("db", ds.Database) req.URL.RawQuery = reqQueryVals.Encode() if !ds.BasicAuth { req.Header.Del("Authorization") From 97656d65ec2c9a29197f37750aba392c8d6dd4be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 25 Apr 2016 11:22:13 +0200 Subject: [PATCH 13/13] db(dashboard): minor fix for dashboard delete --- pkg/services/sqlstore/dashboard.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index a64094cb65e..ef36fd75ab6 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -216,7 +216,7 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error { func DeleteDashboard(cmd *m.DeleteDashboardCommand) error { return inTransaction2(func(sess *session) error { dashboard := m.Dashboard{Slug: cmd.Slug, OrgId: cmd.OrgId} - has, err := x.Get(&dashboard) + has, err := sess.Get(&dashboard) if err != nil { return err } else if has == false {