-
@@ -93,8 +91,7 @@
GROUP BY
-
-
+
diff --git a/public/app/plugins/datasource/influxdb/partials/query_part.html b/public/app/plugins/datasource/influxdb/partials/query_part.html
new file mode 100644
index 00000000000..0a28bee11f7
--- /dev/null
+++ b/public/app/plugins/datasource/influxdb/partials/query_part.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
{{part.def.name}}()
diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.js b/public/app/plugins/datasource/influxdb/query_ctrl.js
index 247205ab39d..d4c4a7f6181 100644
--- a/public/app/plugins/datasource/influxdb/query_ctrl.js
+++ b/public/app/plugins/datasource/influxdb/query_ctrl.js
@@ -10,7 +10,7 @@ function (angular, _, InfluxQueryBuilder, queryPart) {
var module = angular.module('grafana.controllers');
- module.controller('InfluxQueryCtrl', function($scope, $timeout, $sce, templateSrv, $q, uiSegmentSrv) {
+ module.controller('InfluxQueryCtrl', function($scope, templateSrv, $q, uiSegmentSrv) {
$scope.init = function() {
if (!$scope.target) { return; }
@@ -19,25 +19,14 @@ function (angular, _, InfluxQueryBuilder, queryPart) {
target.tags = target.tags || [];
target.groupBy = target.groupBy || [{type: 'time', interval: 'auto'}];
target.fields = target.fields || [{name: 'value'}];
- target.select = target.select || [[{type: 'field', params: ['value']}]];
- target.select[0] = [
- {type: 'field', params: ['value']},
- {type: 'mean', params: []},
- {type: 'derivate', params: ['10s']},
- {type: 'math', params: ['/ 100']},
- {type: 'alias', params: ['google']},
- ];
+ target.select = target.select || [[
+ {name: 'field', params: ['value']},
+ {name: 'mean', params: []},
+ ]];
- $scope.select = _.map(target.select, function(parts) {
- return _.map(parts, function(part) {
- var partModel = queryPart.create(part.type);
- partModel.params = part.params;
- partModel.updateText();
- return partModel;
- });
- });
+ $scope.updateSelectParts();
- $scope.func = queryPart.create('time', { withDefaultParams: true });
+ $scope.groupByParts = queryPart.create({name: 'time', params:['$interval']});
$scope.queryBuilder = new InfluxQueryBuilder(target);
@@ -89,14 +78,27 @@ function (angular, _, InfluxQueryBuilder, queryPart) {
};
$scope.addSelect = function() {
- $scope.target.fields.push({name: "select field", func: 'mean'});
+ $scope.target.select.push([
+ {name: 'field', params: ['value']},
+ {name: 'mean', params: []},
+ ]);
+ $scope.updateSelectParts();
};
$scope.removeSelect = function(index) {
- $scope.target.fields.splice(index, 1);
+ $scope.target.select.splice(index, 1);
+ $scope.updateSelectParts();
$scope.get_data();
};
+ $scope.updateSelectParts = function() {
+ $scope.selectParts = _.map($scope.target.select, function(parts) {
+ return _.map(parts, function(part) {
+ return queryPart.create(part);
+ });
+ });
+ };
+
$scope.changeFunction = function(func) {
$scope.target.function = func;
$scope.$parent.get_data();
diff --git a/public/app/plugins/datasource/influxdb/query_part.js b/public/app/plugins/datasource/influxdb/query_part.js
deleted file mode 100644
index 37433d54951..00000000000
--- a/public/app/plugins/datasource/influxdb/query_part.js
+++ /dev/null
@@ -1,168 +0,0 @@
-define([
- 'lodash',
- 'jquery'
-],
-function (_, $) {
- 'use strict';
-
- var index = [];
- var categories = {
- Combine: [],
- Transform: [],
- Calculate: [],
- Filter: [],
- Special: []
- };
-
- 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;
- }
-
- addFuncDef({
- name: 'field',
- category: categories.Transform,
- params: [{type: 'field'}],
- defaultParams: ['value'],
- });
-
- addFuncDef({
- name: 'mean',
- category: categories.Transform,
- params: [],
- defaultParams: [],
- });
-
- addFuncDef({
- name: 'derivate',
- category: categories.Transform,
- params: [{ name: "rate", type: "interval", options: ['1s', '10s', '1m', '5min', '10m', '15m', '1h'] }],
- defaultParams: ['10s'],
- });
-
- addFuncDef({
- name: 'time',
- category: categories.Transform,
- params: [{ name: "rate", type: "interval", options: ['$interval', '1s', '10s', '1m', '5min', '10m', '15m', '1h'] }],
- defaultParams: ['$interval'],
- });
-
- addFuncDef({
- name: 'math',
- category: categories.Transform,
- params: [{ name: "expr", type: "string"}],
- defaultParams: [' / 100'],
- });
-
- addFuncDef({
- name: 'alias',
- category: categories.Transform,
- params: [{ name: "name", type: "string"}],
- defaultParams: ['alias'],
- });
-
- _.each(categories, function(funcList, catName) {
- categories[catName] = _.sortBy(funcList, 'name');
- });
-
- function FuncInstance(funcDef, options) {
- this.def = funcDef;
- this.params = [];
-
- if (options && options.withDefaultParams) {
- this.params = funcDef.defaultParams.slice(0);
- }
-
- this.updateText();
- }
-
- FuncInstance.prototype.render = function(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)) {
- return value;
- }
-
- return "'" + value + "'";
-
- }, this);
-
- if (metricExp) {
- parameters.unshift(metricExp);
- }
-
- return str + parameters.join(', ') + ')';
- };
-
- FuncInstance.prototype._hasMultipleParamsInString = function(strValue, index) {
- if (strValue.indexOf(',') === -1) {
- return false;
- }
-
- return this.def.params[index + 1] && this.def.params[index + 1].optional;
- };
-
- FuncInstance.prototype.updateParam = function(strValue, index) {
- // handle optional parameters
- // if string contains ',' and next param is optional, split and update both
- if (this._hasMultipleParamsInString(strValue, index)) {
- _.each(strValue.split(','), function(partVal, idx) {
- this.updateParam(partVal.trim(), idx);
- }, this);
- return;
- }
-
- if (strValue === '' && this.def.params[index].optional) {
- this.params.splice(index, 1);
- }
- else {
- this.params[index] = strValue;
- }
-
- this.updateText();
- };
-
- FuncInstance.prototype.updateText = function () {
- if (this.params.length === 0) {
- this.text = this.def.name + '()';
- return;
- }
-
- var text = this.def.name + '(';
- text += this.params.join(', ');
- text += ')';
- this.text = text;
- };
-
- return {
- create: function(funcDef, options) {
- if (_.isString(funcDef)) {
- if (!index[funcDef]) {
- throw { message: 'Method not found ' + name };
- }
- funcDef = index[funcDef];
- }
- return new FuncInstance(funcDef, options);
- },
-
- getFuncDef: function(name) {
- return index[name];
- },
-
- getCategories: function() {
- return categories;
- }
- };
-
-});
diff --git a/public/app/plugins/datasource/influxdb/query_part.ts b/public/app/plugins/datasource/influxdb/query_part.ts
new file mode 100644
index 00000000000..7e5dfff3277
--- /dev/null
+++ b/public/app/plugins/datasource/influxdb/query_part.ts
@@ -0,0 +1,165 @@
+///
+
+import _ = require('lodash');
+
+var index = [];
+var categories = {
+ Combine: [],
+ Transform: [],
+ Calculate: [],
+ Filter: [],
+ Special: []
+};
+
+class QueryPartDef {
+ name: string;
+ params: any[];
+ defaultParams: any[];
+
+ constructor(options: any) {
+ this.name = options.name;
+ this.params = options.params;
+ this.defaultParams = options.defaultParams;
+ }
+
+ static register(options: any) {
+ index[options.name] = new QueryPartDef(options);
+ }
+}
+
+QueryPartDef.register({
+ name: 'field',
+ category: categories.Transform,
+ params: [{type: 'field'}],
+ defaultParams: ['value'],
+});
+
+QueryPartDef.register({
+ name: 'mean',
+ category: categories.Transform,
+ params: [],
+ defaultParams: [],
+});
+
+QueryPartDef.register({
+ name: 'derivate',
+ category: categories.Transform,
+ params: [{ name: "rate", type: "interval", options: ['1s', '10s', '1m', '5min', '10m', '15m', '1h'] }],
+ defaultParams: ['10s'],
+});
+
+QueryPartDef.register({
+ name: 'time',
+ category: categories.Transform,
+ params: [{ name: "rate", type: "interval", options: ['$interval', '1s', '10s', '1m', '5min', '10m', '15m', '1h'] }],
+ defaultParams: ['$interval'],
+});
+
+QueryPartDef.register({
+ name: 'math',
+ category: categories.Transform,
+ params: [{ name: "expr", type: "string"}],
+ defaultParams: [' / 100'],
+});
+
+QueryPartDef.register({
+ name: 'alias',
+ category: categories.Transform,
+ params: [{ name: "name", type: "string"}],
+ defaultParams: ['alias'],
+});
+
+class QueryPart {
+ part: any;
+ def: QueryPartDef;
+ params: any[];
+ text: string;
+
+ constructor(part: any) {
+ this.part = part;
+ this.def = index[part.name];
+ if (!this.def) {
+ throw {message: 'Could not find query part ' + part.name};
+ }
+
+ this.params = part.params || _.clone(this.def.defaultParams);
+ }
+
+ render(innerExpr: string) {
+ var str = this.def.name + '(';
+ var parameters = _.map(this.params, (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' && _.isNumber(value)) {
+ return value;
+ }
+
+ return "'" + value + "'";
+
+ });
+
+ if (innerExpr) {
+ parameters.unshift(innerExpr);
+ }
+
+ return str + parameters.join(', ') + ')';
+ }
+
+ hasMultipleParamsInString (strValue, index) {
+ if (strValue.indexOf(',') === -1) {
+ return false;
+ }
+
+ return this.def.params[index + 1] && this.def.params[index + 1].optional;
+ }
+
+ updateParam (strValue, index) {
+ // handle optional parameters
+ // if string contains ',' and next param is optional, split and update both
+ if (this.hasMultipleParamsInString(strValue, index)) {
+ _.each(strValue.split(','), function(partVal: string, idx) {
+ this.updateParam(partVal.trim(), idx);
+ }, this);
+ return;
+ }
+
+ if (strValue === '' && this.def.params[index].optional) {
+ this.params.splice(index, 1);
+ }
+ else {
+ this.params[index] = strValue;
+ }
+
+ this.part.params = this.params;
+ this.updateText();
+ }
+
+ updateText() {
+ if (this.params.length === 0) {
+ this.text = this.def.name + '()';
+ return;
+ }
+
+ var text = this.def.name + '(';
+ text += this.params.join(', ');
+ text += ')';
+ this.text = text;
+ }
+}
+
+export = {
+ create: function(part): any {
+ return new QueryPart(part);
+ },
+
+ getFuncDef: function(name) {
+ return index[name];
+ },
+
+ getCategories: function() {
+ return categories;
+ }
+};
diff --git a/public/app/plugins/datasource/influxdb/query_part_editor.js b/public/app/plugins/datasource/influxdb/query_part_editor.js
index fd0dc3e1a6b..6cfcda5f4e7 100644
--- a/public/app/plugins/datasource/influxdb/query_part_editor.js
+++ b/public/app/plugins/datasource/influxdb/query_part_editor.js
@@ -10,33 +10,26 @@ function (angular, _, $) {
.module('grafana.directives')
.directive('influxQueryPartEditor', function($compile, templateSrv) {
- var funcSpanTemplate = '
{{func.def.name}}(';
var paramTemplate = '
';
-
- var funcControlsTemplate =
- '
' +
- '' +
- '' +
- '
';
-
return {
- restrict: 'A',
+ restrict: 'E',
+ templateUrl: 'app/plugins/datasource/influxdb/partials/query_part.html',
+ scope: {
+ part: "="
+ },
link: function postLink($scope, elem) {
- var $funcLink = $(funcSpanTemplate);
- var $funcControls = $(funcControlsTemplate);
- var func = $scope.func;
- var funcDef = func.def;
- var scheduledRelink = false;
- var paramCountAtLink = 0;
+ var part = $scope.part;
+ var partDef = part.def;
+ var $paramsContainer = elem.find('.query-part-parameters');
+ var $controlsContainer = elem.find('.tight-form-func-controls');
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
-
var $link = $(this);
var $input = $link.next();
- $input.val(func.params[paramIndex]);
+ $input.val(part.params[paramIndex]);
$input.css('width', ($link.width() + 16) + 'px');
$link.hide();
@@ -51,32 +44,16 @@ function (angular, _, $) {
}
}
- function scheduledRelinkIfNeeded() {
- if (paramCountAtLink === func.params.length) {
- return;
- }
-
- if (!scheduledRelink) {
- scheduledRelink = true;
- setTimeout(function() {
- relink();
- scheduledRelink = false;
- }, 200);
- }
- }
-
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = $(this);
var $link = $input.prev();
var newValue = $input.val();
- if (newValue !== '' || func.def.params[paramIndex].optional) {
+ if (newValue !== '' || part.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
- func.updateParam($input.val(), paramIndex);
- scheduledRelinkIfNeeded();
-
+ part.updateParam($input.val(), paramIndex);
$scope.$apply($scope.targetChanged);
}
@@ -99,8 +76,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 = partDef.params[paramIndex].options;
+ if (partDef.params[paramIndex].type === 'int') {
options = _.map(options, function(val) { return val.toString(); });
}
@@ -123,114 +100,52 @@ function (angular, _, $) {
};
}
- function toggleFuncControls() {
+ $scope.toggleControls = function() {
var targetDiv = elem.closest('.tight-form');
if (elem.hasClass('show-function-controls')) {
elem.removeClass('show-function-controls');
targetDiv.removeClass('has-open-function');
- $funcControls.hide();
+ $controlsContainer.hide();
return;
}
elem.addClass('show-function-controls');
targetDiv.addClass('has-open-function');
-
- $funcControls.show();
- }
+ $controlsContainer.show();
+ };
function addElementsAndCompile() {
- $funcControls.appendTo(elem);
- $funcLink.appendTo(elem);
-
- _.each(funcDef.params, function(param, index) {
- if (param.optional && func.params.length <= index) {
+ _.each(partDef.params, function(param, index) {
+ if (param.optional && part.params.length <= index) {
return;
}
if (index > 0) {
- $('
, ').appendTo(elem);
+ $('
, ').appendTo($paramsContainer);
}
- var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
- var $paramLink = $('
' + paramValue + '');
+ var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
+ var $paramLink = $('
' + paramValue + '');
var $input = $(paramTemplate);
- paramCountAtLink++;
-
- $paramLink.appendTo(elem);
- $input.appendTo(elem);
+ $paramLink.appendTo($paramsContainer);
+ $input.appendTo($paramsContainer);
$input.blur(_.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
- if (funcDef.params[index].options) {
+ if (partDef.params[index].options) {
addTypeahead($input, index);
}
-
- });
-
- $('
)').appendTo(elem);
-
- $compile(elem.contents())($scope);
- }
-
- function ifJustAddedFocusFistParam() {
- if ($scope.func.added) {
- $scope.func.added = false;
- setTimeout(function() {
- elem.find('.graphite-func-param-link').first().click();
- }, 10);
- }
- }
-
- function registerFuncControlsToggle() {
- $funcLink.click(toggleFuncControls);
- }
-
- function registerFuncControlsActions() {
- $funcControls.click(function(e) {
- var $target = $(e.target);
- if ($target.hasClass('fa-remove')) {
- toggleFuncControls();
- $scope.$apply(function() {
- $scope.removeFunction($scope.func);
- });
- return;
- }
-
- if ($target.hasClass('fa-arrow-left')) {
- $scope.$apply(function() {
- _.move($scope.functions, $scope.$index, $scope.$index - 1);
- $scope.targetChanged();
- });
- return;
- }
-
- if ($target.hasClass('fa-arrow-right')) {
- $scope.$apply(function() {
- _.move($scope.functions, $scope.$index, $scope.$index + 1);
- $scope.targetChanged();
- });
- return;
- }
-
- if ($target.hasClass('fa-question-circle')) {
- window.open("http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + funcDef.name,'_blank');
- return;
- }
});
}
function relink() {
- elem.children().remove();
-
+ $paramsContainer.empty();
addElementsAndCompile();
- ifJustAddedFocusFistParam();
- registerFuncControlsToggle();
- registerFuncControlsActions();
}
relink();