' +
@@ -29,19 +30,20 @@ function (angular, _, $) {
var $funcControls = $(funcControlsTemplate);
var ctrl = $scope.ctrl;
var func = $scope.func;
- var funcDef = func.def;
var scheduledRelink = false;
var paramCountAtLink = 0;
+ var cancelBlur = null;
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = $(this);
+ var $comma = $link.prev('.comma');
var $input = $link.next();
$input.val(func.params[paramIndex]);
- $input.css('width', ($link.width() + 16) + 'px');
+ $comma.removeClass('last');
$link.hide();
$input.show();
$input.focus();
@@ -68,31 +70,64 @@ function (angular, _, $) {
}
}
- function inputBlur(paramIndex) {
+ function paramDef(index) {
+ if (index < func.def.params.length) {
+ return func.def.params[index];
+ }
+ if (_.last(func.def.params).multiple) {
+ return _.assign({}, _.last(func.def.params), {optional: true});
+ }
+ return {};
+ }
+
+ function switchToLink(inputElem, paramIndex) {
/*jshint validthis:true */
- var $input = $(this);
+ var $input = $(inputElem);
+
+ clearTimeout(cancelBlur);
+ cancelBlur = null;
+
var $link = $input.prev();
+ var $comma = $link.prev('.comma');
var newValue = $input.val();
- if (newValue !== '' || func.def.params[paramIndex].optional) {
- $link.html(templateSrv.highlightVariablesAsHtml(newValue));
-
- func.updateParam($input.val(), paramIndex);
- scheduledRelinkIfNeeded();
-
- $scope.$apply(function() {
- ctrl.targetChanged();
- });
-
- $input.hide();
- $link.show();
+ // remove optional empty params
+ if (newValue !== '' || paramDef(paramIndex).optional) {
+ func.updateParam(newValue, paramIndex);
+ $link.html(newValue ? templateSrv.highlightVariablesAsHtml(newValue) : ' ');
}
+
+ scheduledRelinkIfNeeded();
+
+ $scope.$apply(function() {
+ ctrl.targetChanged();
+ });
+
+ if ($link.hasClass('last') && newValue === '') {
+ $comma.addClass('last');
+ } else {
+ $link.removeClass('last');
+ }
+
+ $input.hide();
+ $link.show();
+ }
+
+ // this = input element
+ function inputBlur(paramIndex) {
+ /*jshint validthis:true */
+ var inputElem = this;
+ // happens long before the click event on the typeahead options
+ // need to have long delay because the blur
+ cancelBlur = setTimeout(function() {
+ switchToLink(inputElem, paramIndex);
+ }, 200);
}
function inputKeyPress(paramIndex, e) {
/*jshint validthis:true */
if(e.which === 13) {
- inputBlur.call(this, paramIndex);
+ $(this).blur();
}
}
@@ -104,8 +139,8 @@ function (angular, _, $) {
function addTypeahead($input, paramIndex) {
$input.attr('data-provide', 'typeahead');
- var options = funcDef.params[paramIndex].options;
- if (funcDef.params[paramIndex].type === 'int') {
+ var options = paramDef(paramIndex).options;
+ if (paramDef(paramIndex).type === 'int') {
options = _.map(options, function(val) { return val.toString(); });
}
@@ -114,9 +149,8 @@ function (angular, _, $) {
minLength: 0,
items: 20,
updater: function (value) {
- setTimeout(function() {
- inputBlur.call($input[0], paramIndex);
- }, 0);
+ $input.val(value);
+ switchToLink($input[0], paramIndex);
return value;
}
});
@@ -148,18 +182,34 @@ function (angular, _, $) {
$funcControls.appendTo(elem);
$funcLink.appendTo(elem);
- _.each(funcDef.params, function(param, index) {
- if (param.optional && func.params.length <= index) {
- return;
- }
+ var defParams = _.clone(func.def.params);
+ var lastParam = _.last(func.def.params);
- if (index > 0) {
- $('
, ').appendTo(elem);
+ while (func.params.length >= defParams.length && lastParam && lastParam.multiple) {
+ defParams.push(_.assign({}, lastParam, {optional: true}));
+ }
+
+ _.each(defParams, function(param, index) {
+ if (param.optional && func.params.length < index) {
+ return false;
}
var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
- var $paramLink = $('
' + paramValue + ' ');
+
+ var last = (index >= func.params.length - 1) && param.optional && !paramValue;
+ if (last && param.multiple) {
+ paramValue = '+';
+ }
+
+ if (index > 0) {
+ $('
, ').appendTo(elem);
+ }
+
+ var $paramLink = $(
+ '
'
+ + (paramValue || ' ') + ' ');
var $input = $(paramTemplate);
+ $input.attr('placeholder', param.name);
paramCountAtLink++;
@@ -171,10 +221,9 @@ function (angular, _, $) {
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
- if (funcDef.params[index].options) {
+ if (param.options) {
addTypeahead($input, index);
}
-
});
$('
) ').appendTo(elem);
@@ -182,7 +231,7 @@ function (angular, _, $) {
$compile(elem.contents())($scope);
}
- function ifJustAddedFocusFistParam() {
+ function ifJustAddedFocusFirstParam() {
if ($scope.func.added) {
$scope.func.added = false;
setTimeout(function() {
@@ -223,7 +272,20 @@ function (angular, _, $) {
}
if ($target.hasClass('fa-question-circle')) {
- window.open("http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + funcDef.name,'_blank');
+ var funcDef = ctrl.datasource.getFuncDef(func.def.name);
+ if (funcDef && funcDef.description) {
+ popoverSrv.show({
+ element: e.target,
+ position: 'bottom left',
+ classNames: 'drop-popover drop-function-def',
+ template: '
'
+ + '
' + funcDef.name + ' ' + rst2html(funcDef.description) + '',
+ openOn: 'click',
+ });
+ } else {
+ window.open(
+ "http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + func.def.name,'_blank');
+ }
return;
}
});
@@ -233,7 +295,7 @@ function (angular, _, $) {
elem.children().remove();
addElementsAndCompile();
- ifJustAddedFocusFistParam();
+ ifJustAddedFocusFirstParam();
registerFuncControlsToggle();
registerFuncControlsActions();
}
diff --git a/public/app/plugins/datasource/graphite/gfunc.ts b/public/app/plugins/datasource/graphite/gfunc.ts
index 282de306518..3d33d0f1005 100644
--- a/public/app/plugins/datasource/graphite/gfunc.ts
+++ b/public/app/plugins/datasource/graphite/gfunc.ts
@@ -1,71 +1,56 @@
import _ from 'lodash';
-import $ from 'jquery';
import { isVersionGtOrEq } from 'app/core/utils/version';
-var index = [];
-var categories = {
- Combine: [],
- Transform: [],
- Calculate: [],
- Filter: [],
- Special: [],
-};
+var index = {};
function addFuncDef(funcDef) {
funcDef.params = funcDef.params || [];
funcDef.defaultParams = funcDef.defaultParams || [];
- if (funcDef.category) {
- funcDef.category.push(funcDef);
- }
index[funcDef.name] = funcDef;
- index[funcDef.shortName || funcDef.name] = funcDef;
+ if (funcDef.shortName) {
+ index[funcDef.shortName] = funcDef;
+ }
}
-var optionalSeriesRefArgs = [
- { name: 'other', type: 'value_or_series', optional: true },
- { name: 'other', type: 'value_or_series', optional: true },
- { name: 'other', type: 'value_or_series', optional: true },
- { name: 'other', type: 'value_or_series', optional: true },
- { name: 'other', type: 'value_or_series', optional: true },
-];
+var optionalSeriesRefArgs = [{ name: 'other', type: 'value_or_series', optional: true, multiple: true }];
addFuncDef({
name: 'scaleToSeconds',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'seconds', type: 'int' }],
defaultParams: [1],
});
addFuncDef({
name: 'perSecond',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'max value', type: 'int', optional: true }],
defaultParams: [],
});
addFuncDef({
name: 'holtWintersForecast',
- category: categories.Calculate,
+ category: 'Calculate',
});
addFuncDef({
name: 'holtWintersConfidenceBands',
- category: categories.Calculate,
+ category: 'Calculate',
params: [{ name: 'delta', type: 'int' }],
defaultParams: [3],
});
addFuncDef({
name: 'holtWintersAberration',
- category: categories.Calculate,
+ category: 'Calculate',
params: [{ name: 'delta', type: 'int' }],
defaultParams: [3],
});
addFuncDef({
name: 'nPercentile',
- category: categories.Calculate,
+ category: 'Calculate',
params: [{ name: 'Nth percentile', type: 'int' }],
defaultParams: [95],
});
@@ -74,48 +59,48 @@ addFuncDef({
name: 'diffSeries',
params: optionalSeriesRefArgs,
defaultParams: ['#A'],
- category: categories.Calculate,
+ category: 'Combine',
});
addFuncDef({
name: 'stddevSeries',
params: optionalSeriesRefArgs,
defaultParams: [''],
- category: categories.Calculate,
+ category: 'Combine',
});
addFuncDef({
name: 'divideSeries',
params: optionalSeriesRefArgs,
defaultParams: ['#A'],
- category: categories.Calculate,
+ category: 'Combine',
});
addFuncDef({
name: 'multiplySeries',
params: optionalSeriesRefArgs,
defaultParams: ['#A'],
- category: categories.Calculate,
+ category: 'Combine',
});
addFuncDef({
name: 'asPercent',
params: optionalSeriesRefArgs,
defaultParams: ['#A'],
- category: categories.Calculate,
+ category: 'Combine',
});
addFuncDef({
name: 'group',
params: optionalSeriesRefArgs,
defaultParams: ['#A', '#B'],
- category: categories.Combine,
+ category: 'Combine',
});
addFuncDef({
name: 'sumSeries',
shortName: 'sum',
- category: categories.Combine,
+ category: 'Combine',
params: optionalSeriesRefArgs,
defaultParams: [''],
});
@@ -123,78 +108,66 @@ addFuncDef({
addFuncDef({
name: 'averageSeries',
shortName: 'avg',
- category: categories.Combine,
+ category: 'Combine',
params: optionalSeriesRefArgs,
defaultParams: [''],
});
addFuncDef({
name: 'rangeOfSeries',
- category: categories.Combine,
+ category: 'Combine',
});
addFuncDef({
name: 'percentileOfSeries',
- category: categories.Combine,
+ category: 'Combine',
params: [{ name: 'n', type: 'int' }, { name: 'interpolate', type: 'boolean', options: ['true', 'false'] }],
defaultParams: [95, 'false'],
});
addFuncDef({
name: 'sumSeriesWithWildcards',
- category: categories.Combine,
- params: [
- { name: 'node', type: 'int' },
- { name: 'node', type: 'int', optional: true },
- { name: 'node', type: 'int', optional: true },
- { name: 'node', type: 'int', optional: true },
- ],
+ category: 'Combine',
+ params: [{ name: 'node', type: 'int', multiple: true }],
defaultParams: [3],
});
addFuncDef({
name: 'maxSeries',
shortName: 'max',
- category: categories.Combine,
+ category: 'Combine',
});
addFuncDef({
name: 'minSeries',
shortName: 'min',
- category: categories.Combine,
+ category: 'Combine',
});
addFuncDef({
name: 'averageSeriesWithWildcards',
- category: categories.Combine,
- params: [{ name: 'node', type: 'int' }, { name: 'node', type: 'int', optional: true }],
+ category: 'Combine',
+ params: [{ name: 'node', type: 'int', multiple: true }],
defaultParams: [3],
});
addFuncDef({
name: 'alias',
- category: categories.Special,
+ category: 'Alias',
params: [{ name: 'alias', type: 'string' }],
defaultParams: ['alias'],
});
addFuncDef({
name: 'aliasSub',
- category: categories.Special,
+ category: 'Alias',
params: [{ name: 'search', type: 'string' }, { name: 'replace', type: 'string' }],
defaultParams: ['', '\\1'],
});
-addFuncDef({
- name: 'stacked',
- category: categories.Special,
- params: [{ name: 'stack', type: 'string' }],
- defaultParams: ['stacked'],
-});
-
addFuncDef({
name: 'consolidateBy',
- category: categories.Special,
+ category: 'Special',
params: [
{
name: 'function',
@@ -207,14 +180,14 @@ addFuncDef({
addFuncDef({
name: 'cumulative',
- category: categories.Special,
+ category: 'Special',
params: [],
defaultParams: [],
});
addFuncDef({
name: 'groupByNode',
- category: categories.Special,
+ category: 'Combine',
params: [
{
name: 'node',
@@ -232,30 +205,13 @@ addFuncDef({
addFuncDef({
name: 'aliasByNode',
- category: categories.Special,
+ category: 'Alias',
params: [
{
name: 'node',
type: 'int',
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],
- },
- {
- name: 'node',
- type: 'int',
- options: [0, -1, -2, -3, -4, -5, -6, -7],
- optional: true,
- },
- {
- name: 'node',
- type: 'int',
- options: [0, -1, -2, -3, -4, -5, -6, -7],
- optional: true,
- },
- {
- name: 'node',
- type: 'int',
- options: [0, -1, -2, -3, -4, -5, -6, -7],
- optional: true,
+ multiple: true,
},
],
defaultParams: [3],
@@ -263,7 +219,7 @@ addFuncDef({
addFuncDef({
name: 'substr',
- category: categories.Special,
+ category: 'Special',
params: [
{
name: 'start',
@@ -281,7 +237,7 @@ addFuncDef({
addFuncDef({
name: 'sortByName',
- category: categories.Special,
+ category: 'Sorting',
params: [
{
name: 'natural',
@@ -295,104 +251,104 @@ addFuncDef({
addFuncDef({
name: 'sortByMaxima',
- category: categories.Special,
+ category: 'Sorting',
});
addFuncDef({
name: 'sortByMinima',
- category: categories.Special,
+ category: 'Sorting',
});
addFuncDef({
name: 'sortByTotal',
- category: categories.Special,
+ category: 'Sorting',
});
addFuncDef({
name: 'aliasByMetric',
- category: categories.Special,
+ category: 'Alias',
});
addFuncDef({
name: 'randomWalk',
fake: true,
- category: categories.Special,
+ category: 'Special',
params: [{ name: 'name', type: 'string' }],
defaultParams: ['randomWalk'],
});
addFuncDef({
name: 'countSeries',
- category: categories.Special,
+ category: 'Combine',
});
addFuncDef({
name: 'constantLine',
- category: categories.Special,
+ category: 'Special',
params: [{ name: 'value', type: 'int' }],
defaultParams: [10],
});
addFuncDef({
name: 'cactiStyle',
- category: categories.Special,
+ category: 'Special',
});
addFuncDef({
name: 'keepLastValue',
- category: categories.Special,
+ category: 'Transform',
params: [{ name: 'n', type: 'int' }],
defaultParams: [100],
});
addFuncDef({
name: 'changed',
- category: categories.Special,
+ category: 'Special',
params: [],
defaultParams: [],
});
addFuncDef({
name: 'scale',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'factor', type: 'int' }],
defaultParams: [1],
});
addFuncDef({
name: 'offset',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'amount', type: 'int' }],
defaultParams: [10],
});
addFuncDef({
name: 'transformNull',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'amount', type: 'int' }],
defaultParams: [0],
});
addFuncDef({
name: 'integral',
- category: categories.Transform,
+ category: 'Transform',
});
addFuncDef({
name: 'derivative',
- category: categories.Transform,
+ category: 'Transform',
});
addFuncDef({
name: 'nonNegativeDerivative',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'max value or 0', type: 'int', optional: true }],
defaultParams: [''],
});
addFuncDef({
name: 'timeShift',
- category: categories.Transform,
+ category: 'Transform',
params: [
{
name: 'amount',
@@ -405,7 +361,7 @@ addFuncDef({
addFuncDef({
name: 'timeStack',
- category: categories.Transform,
+ category: 'Transform',
params: [
{
name: 'timeShiftUnit',
@@ -420,7 +376,7 @@ addFuncDef({
addFuncDef({
name: 'summarize',
- category: categories.Transform,
+ category: 'Transform',
params: [
{ name: 'interval', type: 'string' },
{
@@ -440,7 +396,7 @@ addFuncDef({
addFuncDef({
name: 'smartSummarize',
- category: categories.Transform,
+ category: 'Transform',
params: [
{ name: 'interval', type: 'string' },
{
@@ -454,124 +410,124 @@ addFuncDef({
addFuncDef({
name: 'absolute',
- category: categories.Transform,
+ category: 'Transform',
});
addFuncDef({
name: 'hitcount',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'interval', type: 'string' }],
defaultParams: ['10s'],
});
addFuncDef({
name: 'log',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'base', type: 'int' }],
defaultParams: ['10'],
});
addFuncDef({
name: 'averageAbove',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'n', type: 'int' }],
defaultParams: [25],
});
addFuncDef({
name: 'averageBelow',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'n', type: 'int' }],
defaultParams: [25],
});
addFuncDef({
name: 'currentAbove',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'n', type: 'int' }],
defaultParams: [25],
});
addFuncDef({
name: 'currentBelow',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'n', type: 'int' }],
defaultParams: [25],
});
addFuncDef({
name: 'maximumAbove',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'value', type: 'int' }],
defaultParams: [0],
});
addFuncDef({
name: 'maximumBelow',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'value', type: 'int' }],
defaultParams: [0],
});
addFuncDef({
name: 'minimumAbove',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'value', type: 'int' }],
defaultParams: [0],
});
addFuncDef({
name: 'minimumBelow',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'value', type: 'int' }],
defaultParams: [0],
});
addFuncDef({
name: 'limit',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'n', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'mostDeviant',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'n', type: 'int' }],
defaultParams: [10],
});
addFuncDef({
name: 'exclude',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'exclude', type: 'string' }],
defaultParams: ['exclude'],
});
addFuncDef({
name: 'highestCurrent',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'count', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'highestMax',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'count', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'lowestCurrent',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'count', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'movingAverage',
- category: categories.Filter,
+ category: 'Calculate',
params: [
{
name: 'windowSize',
@@ -584,7 +540,7 @@ addFuncDef({
addFuncDef({
name: 'movingMedian',
- category: categories.Filter,
+ category: 'Calculate',
params: [
{
name: 'windowSize',
@@ -597,56 +553,56 @@ addFuncDef({
addFuncDef({
name: 'stdev',
- category: categories.Filter,
+ category: 'Calculate',
params: [{ name: 'n', type: 'int' }, { name: 'tolerance', type: 'int' }],
defaultParams: [5, 0.1],
});
addFuncDef({
name: 'highestAverage',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'count', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'lowestAverage',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'count', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'removeAbovePercentile',
- category: categories.Filter,
+ category: 'Filter Data',
params: [{ name: 'n', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'removeAboveValue',
- category: categories.Filter,
+ category: 'Filter Data',
params: [{ name: 'n', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'removeBelowPercentile',
- category: categories.Filter,
+ category: 'Filter Data',
params: [{ name: 'n', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'removeBelowValue',
- category: categories.Filter,
+ category: 'Filter Data',
params: [{ name: 'n', type: 'int' }],
defaultParams: [5],
});
addFuncDef({
name: 'useSeriesAbove',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'value', type: 'int' }, { name: 'search', type: 'string' }, { name: 'replace', type: 'string' }],
defaultParams: [0, 'search', 'replace'],
});
@@ -657,7 +613,7 @@ addFuncDef({
addFuncDef({
name: 'aggregateLine',
- category: categories.Combine,
+ category: 'Calculate',
params: [
{
name: 'func',
@@ -671,7 +627,7 @@ addFuncDef({
addFuncDef({
name: 'averageOutsidePercentile',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'n', type: 'int' }],
defaultParams: [95],
version: '1.0',
@@ -679,7 +635,7 @@ addFuncDef({
addFuncDef({
name: 'delay',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'steps', type: 'int' }],
defaultParams: [1],
version: '1.0',
@@ -687,7 +643,7 @@ addFuncDef({
addFuncDef({
name: 'exponentialMovingAverage',
- category: categories.Calculate,
+ category: 'Calculate',
params: [
{
name: 'windowSize',
@@ -701,7 +657,7 @@ addFuncDef({
addFuncDef({
name: 'fallbackSeries',
- category: categories.Special,
+ category: 'Special',
params: [{ name: 'fallback', type: 'string' }],
defaultParams: ['constantLine(0)'],
version: '1.0',
@@ -709,7 +665,7 @@ addFuncDef({
addFuncDef({
name: 'grep',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'grep', type: 'string' }],
defaultParams: ['grep'],
version: '1.0',
@@ -717,7 +673,7 @@ addFuncDef({
addFuncDef({
name: 'groupByNodes',
- category: categories.Special,
+ category: 'Combine',
params: [
{
name: 'function',
@@ -728,24 +684,7 @@ addFuncDef({
name: 'node',
type: 'int',
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],
- },
- {
- name: 'node',
- type: 'int',
- options: [0, -1, -2, -3, -4, -5, -6, -7],
- optional: true,
- },
- {
- name: 'node',
- type: 'int',
- options: [0, -1, -2, -3, -4, -5, -6, -7],
- optional: true,
- },
- {
- name: 'node',
- type: 'int',
- options: [0, -1, -2, -3, -4, -5, -6, -7],
- optional: true,
+ multiple: true,
},
],
defaultParams: ['sum', 3],
@@ -754,7 +693,7 @@ addFuncDef({
addFuncDef({
name: 'integralByInterval',
- category: categories.Transform,
+ category: 'Transform',
params: [
{
name: 'intervalUnit',
@@ -768,7 +707,7 @@ addFuncDef({
addFuncDef({
name: 'interpolate',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'limit', type: 'int', optional: true }],
defaultParams: [],
version: '1.0',
@@ -776,19 +715,19 @@ addFuncDef({
addFuncDef({
name: 'invert',
- category: categories.Transform,
+ category: 'Transform',
version: '1.0',
});
addFuncDef({
name: 'isNonNull',
- category: categories.Combine,
+ category: 'Combine',
version: '1.0',
});
addFuncDef({
name: 'linearRegression',
- category: categories.Calculate,
+ category: 'Calculate',
params: [
{
name: 'startSourceAt',
@@ -812,13 +751,13 @@ addFuncDef({
shortName: 'map',
params: [{ name: 'node', type: 'int' }],
defaultParams: [3],
- category: categories.Combine,
+ category: 'Combine',
version: '1.0',
});
addFuncDef({
name: 'movingMin',
- category: categories.Calculate,
+ category: 'Calculate',
params: [
{
name: 'windowSize',
@@ -832,7 +771,7 @@ addFuncDef({
addFuncDef({
name: 'movingMax',
- category: categories.Calculate,
+ category: 'Calculate',
params: [
{
name: 'windowSize',
@@ -846,7 +785,7 @@ addFuncDef({
addFuncDef({
name: 'movingSum',
- category: categories.Calculate,
+ category: 'Calculate',
params: [
{
name: 'windowSize',
@@ -860,30 +799,13 @@ addFuncDef({
addFuncDef({
name: 'multiplySeriesWithWildcards',
- category: categories.Calculate,
+ category: 'Combine',
params: [
{
name: 'position',
type: 'int',
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12],
- },
- {
- name: 'position',
- type: 'int',
- options: [0, -1, -2, -3, -4, -5, -6, -7],
- optional: true,
- },
- {
- name: 'position',
- type: 'int',
- options: [0, -1, -2, -3, -4, -5, -6, -7],
- optional: true,
- },
- {
- name: 'position',
- type: 'int',
- options: [0, -1, -2, -3, -4, -5, -6, -7],
- optional: true,
+ multiple: true,
},
],
defaultParams: [2],
@@ -892,13 +814,13 @@ addFuncDef({
addFuncDef({
name: 'offsetToZero',
- category: categories.Transform,
+ category: 'Transform',
version: '1.0',
});
addFuncDef({
name: 'pow',
- category: categories.Transform,
+ category: 'Transform',
params: [{ name: 'factor', type: 'int' }],
defaultParams: [10],
version: '1.0',
@@ -906,7 +828,7 @@ addFuncDef({
addFuncDef({
name: 'powSeries',
- category: categories.Transform,
+ category: 'Transform',
params: optionalSeriesRefArgs,
defaultParams: [''],
version: '1.0',
@@ -926,17 +848,16 @@ addFuncDef({
type: 'int',
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
},
- { name: 'reduceMatchers', type: 'string' },
- { name: 'reduceMatchers', type: 'string' },
+ { name: 'reduceMatchers', type: 'string', multiple: true },
],
- defaultParams: ['asPercent', 2, 'used_bytes', 'total_bytes'],
- category: categories.Combine,
+ defaultParams: ['asPercent', 2, 'used_bytes'],
+ category: 'Combine',
version: '1.0',
});
addFuncDef({
name: 'removeBetweenPercentile',
- category: categories.Filter,
+ category: 'Filter Series',
params: [{ name: 'n', type: 'int' }],
defaultParams: [95],
version: '1.0',
@@ -944,19 +865,19 @@ addFuncDef({
addFuncDef({
name: 'removeEmptySeries',
- category: categories.Filter,
+ category: 'Filter Series',
version: '1.0',
});
addFuncDef({
name: 'squareRoot',
- category: categories.Transform,
+ category: 'Transform',
version: '1.0',
});
addFuncDef({
name: 'timeSlice',
- category: categories.Transform,
+ category: 'Transform',
params: [
{
name: 'startSliceAt',
@@ -976,7 +897,7 @@ addFuncDef({
addFuncDef({
name: 'weightedAverage',
- category: categories.Filter,
+ category: 'Combine',
params: [
{ name: 'other', type: 'value_or_series', optional: true },
{
@@ -991,29 +912,21 @@ addFuncDef({
addFuncDef({
name: 'seriesByTag',
- category: categories.Special,
- params: [
- { name: 'tagExpression', type: 'string' },
- { name: 'tagExpression', type: 'string', optional: true },
- { name: 'tagExpression', type: 'string', optional: true },
- { name: 'tagExpression', type: 'string', optional: true },
- ],
+ category: 'Special',
+ params: [{ name: 'tagExpression', type: 'string', multiple: true }],
version: '1.1',
});
addFuncDef({
name: 'groupByTags',
- category: categories.Special,
+ category: 'Combine',
params: [
{
name: 'function',
type: 'string',
options: ['sum', 'avg', 'maxSeries'],
},
- { name: 'tag', type: 'string' },
- { name: 'tag', type: 'string', optional: true },
- { name: 'tag', type: 'string', optional: true },
- { name: 'tag', type: 'string', optional: true },
+ { name: 'tag', type: 'string', multiple: true },
],
defaultParams: ['sum', 'tag'],
version: '1.1',
@@ -1021,23 +934,14 @@ addFuncDef({
addFuncDef({
name: 'aliasByTags',
- category: categories.Special,
- params: [
- { name: 'tag', type: 'string' },
- { name: 'tag', type: 'string', optional: true },
- { name: 'tag', type: 'string', optional: true },
- { name: 'tag', type: 'string', optional: true },
- ],
+ category: 'Alias',
+ params: [{ name: 'tag', type: 'string', multiple: true }],
defaultParams: ['tag'],
version: '1.1',
});
-_.each(categories, function(funcList, catName) {
- categories[catName] = _.sortBy(funcList, 'name');
-});
-
-function isVersionRelatedFunction(func, graphiteVersion) {
- return isVersionGtOrEq(graphiteVersion, func.version) || !func.version;
+function isVersionRelatedFunction(obj, graphiteVersion) {
+ return !obj.version || isVersionGtOrEq(graphiteVersion, obj.version);
}
export class FuncInstance {
@@ -1059,20 +963,34 @@ export class FuncInstance {
render(metricExp) {
var str = this.def.name + '(';
+
var parameters = _.map(
this.params,
function(value, index) {
- var paramType = this.def.params[index].type;
- if (paramType === 'int' || paramType === 'value_or_series' || paramType === 'boolean') {
- return value;
- } else if (paramType === 'int_or_interval' && $.isNumeric(value)) {
+ var paramType;
+ if (index < this.def.params.length) {
+ paramType = this.def.params[index].type;
+ } else if (_.get(_.last(this.def.params), 'multiple')) {
+ paramType = _.get(_.last(this.def.params), 'type');
+ }
+ if (paramType === 'value_or_series') {
return value;
}
-
+ if (paramType === 'boolean' && _.includes(['true', 'false'], value)) {
+ return value;
+ }
+ if (_.includes(['int', 'float', 'int_or_interval', 'node_or_tag', 'node'], paramType) && _.isFinite(+value)) {
+ return _.toString(+value);
+ }
return "'" + value + "'";
}.bind(this)
);
+ // don't send any blank parameters to graphite
+ while (parameters[parameters.length - 1] === '') {
+ parameters.pop();
+ }
+
if (metricExp) {
parameters.unshift(metricExp);
}
@@ -1085,7 +1003,15 @@ export class FuncInstance {
return false;
}
- return this.def.params[index + 1] && this.def.params[index + 1].optional;
+ if (this.def.params[index + 1] && this.def.params[index + 1].optional) {
+ return true;
+ }
+
+ if (index + 1 >= this.def.params.length && _.get(_.last(this.def.params), 'multiple')) {
+ return true;
+ }
+
+ return false;
}
updateParam(strValue, index) {
@@ -1101,7 +1027,7 @@ export class FuncInstance {
return;
}
- if (strValue === '' && this.def.params[index].optional) {
+ if (strValue === '' && (index >= this.def.params.length || this.def.params[index].optional)) {
this.params.splice(index, 1);
} else {
this.params[index] = strValue;
@@ -1123,32 +1049,131 @@ export class FuncInstance {
}
}
-export default {
- createFuncInstance: function(funcDef, options?) {
- if (_.isString(funcDef)) {
- if (!index[funcDef]) {
- throw { message: 'Method not found ' + name };
- }
- funcDef = index[funcDef];
- }
- return new FuncInstance(funcDef, options);
- },
+function createFuncInstance(funcDef, options?, idx?) {
+ if (_.isString(funcDef)) {
+ funcDef = getFuncDef(funcDef, idx);
+ }
+ return new FuncInstance(funcDef, options);
+}
- getFuncDef: function(name) {
- return index[name];
- },
+function getFuncDef(name, idx?) {
+ if (!(idx || index)[name]) {
+ throw { message: 'Method not found ' + name };
+ }
+ return (idx || index)[name];
+}
- getCategories: function(graphiteVersion) {
- var filteredCategories: any = {};
- _.each(categories, function(functions, category) {
- var filteredFuncs = _.filter(functions, function(func) {
- return isVersionRelatedFunction(func, graphiteVersion);
+function getFuncDefs(graphiteVersion, idx?) {
+ var funcs = {};
+ _.forEach(idx || index, function(funcDef) {
+ if (isVersionRelatedFunction(funcDef, graphiteVersion)) {
+ funcs[funcDef.name] = _.assign({}, funcDef, {
+ params: _.filter(funcDef.params, function(param) {
+ return isVersionRelatedFunction(param, graphiteVersion);
+ }),
});
- if (filteredFuncs.length) {
- filteredCategories[category] = filteredFuncs;
+ }
+ });
+ return funcs;
+}
+
+// parse response from graphite /functions endpoint into internal format
+function parseFuncDefs(rawDefs) {
+ var funcDefs = {};
+
+ _.forEach(rawDefs || {}, (funcDef, funcName) => {
+ // skip graphite graph functions
+ if (funcDef.group === 'Graph') {
+ return;
+ }
+
+ var description = funcDef.description;
+ if (description) {
+ // tidy up some pydoc syntax that rst2html can't handle
+ description = description
+ .replace(/:py:func:`(.+)( <[^>]*>)?`/g, '``$1``')
+ .replace(/.. seealso:: /g, 'See also: ')
+ .replace(/.. code-block *:: *none/g, '.. code-block::');
+ }
+
+ var func = {
+ name: funcDef.name,
+ description: description,
+ category: funcDef.group,
+ params: [],
+ defaultParams: [],
+ fake: false,
+ };
+
+ // get rid of the first "seriesList" param
+ if (/^seriesLists?$/.test(_.get(funcDef, 'params[0].type', ''))) {
+ // handle functions that accept multiple seriesLists
+ // we leave the param in place but mark it optional, so users can add more series if they wish
+ if (funcDef.params[0].multiple) {
+ funcDef.params[0].required = false;
+ // otherwise chop off the first param, it'll be handled separately
+ } else {
+ funcDef.params.shift();
}
+ // tag function as fake
+ } else {
+ func.fake = true;
+ }
+
+ _.forEach(funcDef.params, rawParam => {
+ var param = {
+ name: rawParam.name,
+ type: 'string',
+ optional: !rawParam.required,
+ multiple: !!rawParam.multiple,
+ options: undefined,
+ };
+
+ if (rawParam.default !== undefined) {
+ func.defaultParams.push(_.toString(rawParam.default));
+ } else if (rawParam.suggestions) {
+ func.defaultParams.push(_.toString(rawParam.suggestions[0]));
+ } else {
+ func.defaultParams.push('');
+ }
+
+ if (rawParam.type === 'boolean') {
+ param.type = 'boolean';
+ param.options = ['true', 'false'];
+ } else if (rawParam.type === 'integer') {
+ param.type = 'int';
+ } else if (rawParam.type === 'float') {
+ param.type = 'float';
+ } else if (rawParam.type === 'node') {
+ param.type = 'node';
+ param.options = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
+ } else if (rawParam.type === 'nodeOrTag') {
+ param.type = 'node_or_tag';
+ param.options = ['name', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
+ } else if (rawParam.type === 'intOrInterval') {
+ param.type = 'int_or_interval';
+ } else if (rawParam.type === 'seriesList') {
+ param.type = 'value_or_series';
+ }
+
+ if (rawParam.options) {
+ param.options = _.map(rawParam.options, _.toString);
+ } else if (rawParam.suggestions) {
+ param.options = _.map(rawParam.suggestions, _.toString);
+ }
+
+ func.params.push(param);
});
- return filteredCategories;
- },
+ funcDefs[funcName] = func;
+ });
+
+ return funcDefs;
+}
+
+export default {
+ createFuncInstance: createFuncInstance,
+ getFuncDef: getFuncDef,
+ getFuncDefs: getFuncDefs,
+ parseFuncDefs: parseFuncDefs,
};
diff --git a/public/app/plugins/datasource/graphite/graphite_query.ts b/public/app/plugins/datasource/graphite/graphite_query.ts
index 9f9d4d12701..10e0144e3ff 100644
--- a/public/app/plugins/datasource/graphite/graphite_query.ts
+++ b/public/app/plugins/datasource/graphite/graphite_query.ts
@@ -1,8 +1,8 @@
import _ from 'lodash';
-import gfunc from './gfunc';
import { Parser } from './parser';
export default class GraphiteQuery {
+ datasource: any;
target: any;
functions: any[];
segments: any[];
@@ -15,7 +15,8 @@ export default class GraphiteQuery {
scopedVars: any;
/** @ngInject */
- constructor(target, templateSrv?, scopedVars?) {
+ constructor(datasource, target, templateSrv?, scopedVars?) {
+ this.datasource = datasource;
this.target = target;
this.parseTarget();
@@ -86,7 +87,7 @@ export default class GraphiteQuery {
switch (astNode.type) {
case 'function':
- var innerFunc = gfunc.createFuncInstance(astNode.name, {
+ var innerFunc = this.datasource.createFuncInstance(astNode.name, {
withDefaultParams: false,
});
_.each(astNode.params, param => {
@@ -133,7 +134,7 @@ export default class GraphiteQuery {
moveAliasFuncLast() {
var aliasFunc = _.find(this.functions, function(func) {
- return func.def.name === 'alias' || func.def.name === 'aliasByNode' || func.def.name === 'aliasByMetric';
+ return func.def.name.startsWith('alias');
});
if (aliasFunc) {
@@ -143,7 +144,7 @@ export default class GraphiteQuery {
}
addFunctionParameter(func, value) {
- if (func.params.length >= func.def.params.length) {
+ if (func.params.length >= func.def.params.length && !_.get(_.last(func.def.params), 'multiple', false)) {
throw { message: 'too many parameters for function ' + func.def.name };
}
func.params.push(value);
@@ -208,7 +209,7 @@ export default class GraphiteQuery {
}
splitSeriesByTagParams(func) {
- const tagPattern = /([^\!=~]+)([\!=~]+)([^\!=~]+)/;
+ const tagPattern = /([^\!=~]+)(\!?=~?)(.*)/;
return _.flatten(
_.map(func.params, (param: string) => {
let matches = tagPattern.exec(param);
diff --git a/public/app/plugins/datasource/graphite/partials/query.editor.html b/public/app/plugins/datasource/graphite/partials/query.editor.html
index b79164e1781..6414a39c94f 100755
--- a/public/app/plugins/datasource/graphite/partials/query.editor.html
+++ b/public/app/plugins/datasource/graphite/partials/query.editor.html
@@ -10,30 +10,50 @@
Series
-