diff --git a/src/app/controllers/influxTargetCtrl.js b/src/app/controllers/influxTargetCtrl.js
index bb34df8f608..6b27be78a0c 100644
--- a/src/app/controllers/influxTargetCtrl.js
+++ b/src/app/controllers/influxTargetCtrl.js
@@ -11,8 +11,17 @@ function (angular) {
module.controller('InfluxTargetCtrl', function($scope, $timeout) {
$scope.init = function() {
- $scope.target.function = $scope.target.function || 'mean';
- $scope.target.column = $scope.target.column || 'value';
+ var target = $scope.target;
+
+ target.function = target.function || 'mean';
+ target.column = target.column || 'value';
+
+ if (target.condition_value) {
+ target.condition_expression = target.condition_key + ' ' + target.condition_op + ' ' + target.condition_value;
+ delete target.condition_key;
+ delete target.condition_op;
+ delete target.condition_value;
+ }
$scope.rawQuery = false;
@@ -24,7 +33,7 @@ function (angular) {
];
$scope.operators = ['=', '=~', '>', '<', '!~', '<>'];
- $scope.oldSeries = $scope.target.series;
+ $scope.oldSeries = target.series;
$scope.$on('typeahead-updated', function() {
$timeout($scope.get_data);
});
diff --git a/src/app/directives/templateParamSelector.js b/src/app/directives/templateParamSelector.js
index 106f5f9ee0b..f05d6959198 100644
--- a/src/app/directives/templateParamSelector.js
+++ b/src/app/directives/templateParamSelector.js
@@ -11,7 +11,7 @@ function (angular, app, _, $) {
.module('grafana.directives')
.directive('templateParamSelector', function($compile) {
var inputTemplate = '';
var buttonTemplate = '{{variable.current.text}}';
diff --git a/src/app/partials/influxdb/annotation_editor.html b/src/app/partials/influxdb/annotation_editor.html
index 9bc2bdbca21..2bcfb549411 100644
--- a/src/app/partials/influxdb/annotation_editor.html
+++ b/src/app/partials/influxdb/annotation_editor.html
@@ -1,8 +1,8 @@
diff --git a/src/app/partials/influxdb/editor.html b/src/app/partials/influxdb/editor.html
index 995ba362121..da0a525303a 100644
--- a/src/app/partials/influxdb/editor.html
+++ b/src/app/partials/influxdb/editor.html
@@ -72,6 +72,16 @@
data-min-length=0 data-items=100
ng-blur="seriesBlur()">
+
+
+ alias
+
+
+
+
+
+
@@ -102,16 +112,6 @@
-
-
- alias
-
-
-
-
-
-
@@ -129,7 +129,7 @@
-
@@ -142,7 +142,7 @@
-
diff --git a/src/app/partials/templating_editor.html b/src/app/partials/templating_editor.html
index 659f0ee1bde..22ab029061e 100644
--- a/src/app/partials/templating_editor.html
+++ b/src/app/partials/templating_editor.html
@@ -101,7 +101,7 @@
-
+
diff --git a/src/app/services/influxdb/influxQueryBuilder.js b/src/app/services/influxdb/influxQueryBuilder.js
new file mode 100644
index 00000000000..eaa19fceb9d
--- /dev/null
+++ b/src/app/services/influxdb/influxQueryBuilder.js
@@ -0,0 +1,81 @@
+define([
+],
+function () {
+ 'use strict';
+
+ function InfluxQueryBuilder(target) {
+ this.target = target;
+ }
+
+ var p = InfluxQueryBuilder.prototype;
+
+ p.build = function() {
+ return this.target.rawQuery ? this._modifyRawQuery() : this._buildQuery();
+ };
+
+ p._buildQuery = function() {
+ var target = this.target;
+ var query = 'select ';
+ var seriesName = target.series;
+
+ if(!seriesName.match('^/.*/')) {
+ seriesName = '"' + seriesName+ '"';
+ }
+
+ if (target.groupby_field_add) {
+ query += target.groupby_field + ', ';
+ }
+
+ query += target.function + '(' + target.column + ')';
+ query += ' from ' + seriesName + ' where [[$timeFilter]]';
+
+ if (target.condition_filter) {
+ query += ' and ' + target.condition_expression;
+ }
+
+ query += ' group by time([[$interval]])';
+
+ if (target.groupby_field_add) {
+ query += ', ' + target.groupby_field;
+ this.groupByField = target.groupby_field;
+ }
+
+ query += " order asc";
+
+ return query;
+ };
+
+ p._modifyRawQuery = function () {
+ var query = this.target.query.replace(";", "");
+
+ var queryElements = query.split(" ");
+ var lowerCaseQueryElements = query.toLowerCase().split(" ");
+ var whereIndex = lowerCaseQueryElements.indexOf("where");
+ var groupByIndex = lowerCaseQueryElements.indexOf("group");
+ var orderIndex = lowerCaseQueryElements.indexOf("order");
+
+ if (lowerCaseQueryElements[1].indexOf(',') !== -1) {
+ this.groupByField = lowerCaseQueryElements[1].replace(',', '');
+ }
+
+ if (whereIndex !== -1) {
+ queryElements.splice(whereIndex + 1, 0, '[[$timeFilter]]', "and");
+ }
+ else {
+ if (groupByIndex !== -1) {
+ queryElements.splice(groupByIndex, 0, "where", '[[$timeFilter]]');
+ }
+ else if (orderIndex !== -1) {
+ queryElements.splice(orderIndex, 0, "where", '[[$timeFilter]]');
+ }
+ else {
+ queryElements.push("where");
+ queryElements.push('[[$timeFilter]]');
+ }
+ }
+
+ return queryElements.join(" ");
+ };
+
+ return InfluxQueryBuilder;
+});
diff --git a/src/app/services/influxdb/influxdbDatasource.js b/src/app/services/influxdb/influxdbDatasource.js
index 0db4f3626ba..d2a375521a5 100644
--- a/src/app/services/influxdb/influxdbDatasource.js
+++ b/src/app/services/influxdb/influxdbDatasource.js
@@ -2,9 +2,10 @@ define([
'angular',
'lodash',
'kbn',
- './influxSeries'
+ './influxSeries',
+ './influxQueryBuilder'
],
-function (angular, _, kbn, InfluxSeries) {
+function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
'use strict';
var module = angular.module('grafana.services');
@@ -32,90 +33,25 @@ function (angular, _, kbn, InfluxSeries) {
}
InfluxDatasource.prototype.query = function(options) {
- var promises = _.map(options.targets, function(target) {
- var query;
- var alias = '';
+ var timeFilter = getTimeFilter(options);
+ var promises = _.map(options.targets, function(target) {
if (target.hide || !((target.series && target.column) || target.query)) {
return [];
}
- var timeFilter = getTimeFilter(options);
- var groupByField;
-
- if (target.rawQuery) {
- query = target.query;
- query = query.replace(";", "");
- var queryElements = query.split(" ");
- var lowerCaseQueryElements = query.toLowerCase().split(" ");
- var whereIndex = lowerCaseQueryElements.indexOf("where");
- var groupByIndex = lowerCaseQueryElements.indexOf("group");
- var orderIndex = lowerCaseQueryElements.indexOf("order");
-
- if (lowerCaseQueryElements[1].indexOf(',') !== -1) {
- groupByField = lowerCaseQueryElements[1].replace(',', '');
- }
-
- if (whereIndex !== -1) {
- queryElements.splice(whereIndex + 1, 0, timeFilter, "and");
- }
- else {
- if (groupByIndex !== -1) {
- queryElements.splice(groupByIndex, 0, "where", timeFilter);
- }
- else if (orderIndex !== -1) {
- queryElements.splice(orderIndex, 0, "where", timeFilter);
- }
- else {
- queryElements.push("where");
- queryElements.push(timeFilter);
- }
- }
-
- query = queryElements.join(" ");
- query = templateSrv.replace(query);
- }
- else {
- query = 'select ';
- var seriesName = target.series;
-
- if(!seriesName.match('^/.*/')) {
- seriesName = '"' + seriesName+ '"';
- }
-
- if (target.groupby_field_add) {
- query += target.groupby_field + ', ';
- }
-
- query += target.function + '(' + target.column + ')';
- query += ' from ' + seriesName + ' where ' + timeFilter;
-
- if (target.condition_filter) {
- query += ' and ' + target.condition_expression;
- }
+ // build query
+ var queryBuilder = new InfluxQueryBuilder(target);
+ var query = queryBuilder.build();
- query += ' group by time(' + (target.interval || options.interval) + ')';
+ // replace templated variables
+ templateSrv.setGrafanaVariable('$timeFilter', timeFilter);
+ templateSrv.setGrafanaVariable('$interval', (target.interval || options.interval));
+ query = templateSrv.replace(query);
- if (target.groupby_field_add) {
- query += ',' + target.groupby_field;
- }
+ var alias = target.alias ? templateSrv.replace(target.alias) : '';
- query += " order asc";
-
- query = templateSrv.replace(query);
-
- if (target.groupby_field_add) {
- groupByField = target.groupby_field;
- }
-
- target.query = query;
- }
-
- if (target.alias) {
- alias = templateSrv.replace(target.alias);
- }
-
- var handleResponse = _.partial(handleInfluxQueryResponse, alias, groupByField);
+ var handleResponse = _.partial(handleInfluxQueryResponse, alias, queryBuilder.groupByField);
return this._seriesQuery(query).then(handleResponse);
}, this);
@@ -123,12 +59,11 @@ function (angular, _, kbn, InfluxSeries) {
return $q.all(promises).then(function(results) {
return { data: _.flatten(results) };
});
-
};
InfluxDatasource.prototype.annotationQuery = function(annotation, rangeUnparsed) {
var timeFilter = getTimeFilter({ range: rangeUnparsed });
- var query = _.template(annotation.query, { timeFilter: timeFilter }, this.templateSettings);
+ var query = _.template(annotation.query, { timeFilter: timeFilter, "$timeFilter": timeFilter }, this.templateSettings);
return this._seriesQuery(query).then(function(results) {
return new InfluxSeries({ seriesList: results, annotation: annotation }).getAnnotations();
diff --git a/src/app/services/templateSrv.js b/src/app/services/templateSrv.js
index 1cf1e832606..720b732ebc8 100644
--- a/src/app/services/templateSrv.js
+++ b/src/app/services/templateSrv.js
@@ -34,6 +34,10 @@ function (angular, _) {
this._templateData = _templateData;
};
+ this.setGrafanaVariable = function(name, value) {
+ this._templateData[name] = value;
+ };
+
this.replace = function(target) {
if (!target || target.indexOf('[[') === -1) {
return target;
diff --git a/src/app/services/templateValuesSrv.js b/src/app/services/templateValuesSrv.js
index 687d1897069..4293f567195 100644
--- a/src/app/services/templateValuesSrv.js
+++ b/src/app/services/templateValuesSrv.js
@@ -84,7 +84,7 @@ function (angular, _, kbn) {
this.metricNamesToVariableValues = function(variable, metricNames) {
var regex, options, i, matches;
- options = [];
+ options = {}; // use object hash to remove duplicates
if (variable.regex) {
regex = kbn.stringToJsRegex(variable.regex);
@@ -101,10 +101,12 @@ function (angular, _, kbn) {
}
}
- options.push({text: value, value: value});
+ options[value] = value;
}
- return options;
+ return _.map(_.keys(options), function(key) {
+ return { text: key, value: key };
+ });
};
this.addAllOption = function(variable) {
diff --git a/src/test/specs/helpers.js b/src/test/specs/helpers.js
index 9b08c02e2f6..7293678b6a2 100644
--- a/src/test/specs/helpers.js
+++ b/src/test/specs/helpers.js
@@ -1,6 +1,7 @@
define([
- 'kbn'
-], function(kbn) {
+ 'kbn',
+ 'lodash'
+], function(kbn, _) {
'use strict';
function ControllerTestContext() {
@@ -47,10 +48,17 @@ define([
function ServiceTestContext() {
var self = this;
+ self.templateSrv = new TemplateSrvStub();
+
+ this.providePhase = function() {
+ return module(function($provide) {
+ $provide.value('templateSrv', self.templateSrv);
+ });
+ };
this.createService = function(name) {
- return inject([name, '$q', '$rootScope', '$httpBackend', function(InfluxDatasource, $q, $rootScope, $httpBackend) {
- self.service = InfluxDatasource;
+ return inject([name, '$q', '$rootScope', '$httpBackend', function(service, $q, $rootScope, $httpBackend) {
+ self.service = service;
self.$q = $q;
self.$rootScope = $rootScope;
self.$httpBackend = $httpBackend;
@@ -82,11 +90,16 @@ define([
function TemplateSrvStub() {
this.variables = [];
- this.replace = function() {};
+ this.templateSettings = { interpolate : /\[\[([\s\S]+?)\]\]/g };
+ this.data = {};
+ this.replace = function(text) {
+ return _.template(text, this.data, this.templateSettings);
+ };
+ this.setGrafanaVariable = function(name, value) {
+ this.data[name] = value;
+ };
}
-
-
return {
ControllerTestContext: ControllerTestContext,
TimeSrvStub: TimeSrvStub,
diff --git a/src/test/specs/influxQueryBuilder-specs.js b/src/test/specs/influxQueryBuilder-specs.js
new file mode 100644
index 00000000000..789eb342a98
--- /dev/null
+++ b/src/test/specs/influxQueryBuilder-specs.js
@@ -0,0 +1,54 @@
+define([
+ 'services/influxdb/influxQueryBuilder'
+], function(InfluxQueryBuilder) {
+ 'use strict';
+
+ describe('InfluxQueryBuilder', function() {
+
+ describe('series with conditon and group by', function() {
+ var builder = new InfluxQueryBuilder({
+ series: 'google.test',
+ column: 'value',
+ function: 'mean',
+ condition_filter: true,
+ condition_expression: "code=1",
+ groupby_field_add: true,
+ groupby_field: 'code'
+ });
+
+ var query = builder.build();
+
+ it('should generate correct query', function() {
+ expect(query).to.be('select code, mean(value) from "google.test" where [[$timeFilter]] and code=1 ' +
+ 'group by time([[$interval]]), code order asc');
+ });
+
+ it('should expose groupByFiled', function() {
+ expect(builder.groupByField).to.be('code');
+ });
+
+ });
+
+ describe('old style raw query', function() {
+ var builder = new InfluxQueryBuilder({
+ query: 'select host, mean(value) from asd.asd where time > now() - 1h group by time(1s), code order asc',
+ rawQuery: true
+ });
+
+ var query = builder.build();
+
+ it('should generate correct query', function() {
+ expect(query).to.be('select host, mean(value) from asd.asd where [[$timeFilter]] and time > now() - 1h ' +
+ ' group by time(1s), code order asc');
+ });
+
+ it('should expose groupByFiled', function() {
+ expect(builder.groupByField).to.be('host');
+ });
+
+ });
+
+
+ });
+
+});
diff --git a/src/test/specs/influxdb-datasource-specs.js b/src/test/specs/influxdb-datasource-specs.js
index 68ae33fbc18..9b531065115 100644
--- a/src/test/specs/influxdb-datasource-specs.js
+++ b/src/test/specs/influxdb-datasource-specs.js
@@ -8,6 +8,7 @@ define([
var ctx = new helpers.ServiceTestContext();
beforeEach(module('grafana.services'));
+ beforeEach(ctx.providePhase());
beforeEach(ctx.createService('InfluxDatasource'));
describe('When querying influxdb with one target using query editor target spec', function() {
diff --git a/src/test/specs/templateValuesSrv-specs.js b/src/test/specs/templateValuesSrv-specs.js
index ec5e0376439..d0825a2239c 100644
--- a/src/test/specs/templateValuesSrv-specs.js
+++ b/src/test/specs/templateValuesSrv-specs.js
@@ -140,6 +140,18 @@ define([
});
});
+ describeUpdateVariable('regex pattern remove duplicates', function(ctx) {
+ ctx.setup(function() {
+ ctx.variable = { type: 'query', query: 'apps.*', name: 'test' };
+ ctx.variable.regex = 'backend_01';
+ ctx.queryResult = [{text: 'apps.backend.backend_01.counters.req'}, {text: 'apps.backend.backend_01.counters.req'}];
+ });
+
+ it('should return matches options', function() {
+ expect(ctx.variable.options.length).to.be(1);
+ });
+ });
+
describeUpdateVariable('and existing value still exists in options', function(ctx) {
ctx.setup(function() {
ctx.variable = { type: 'query', query: 'apps.*', name: 'test' };
@@ -163,7 +175,29 @@ define([
});
});
- describeUpdateVariable('with include all regex wildcard', function(ctx) {
+ describeUpdateVariable('with include all wildcard', function(ctx) {
+ ctx.setup(function() {
+ ctx.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'wildcard' };
+ ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}];
+ });
+
+ it('should add All wildcard option', function() {
+ expect(ctx.variable.options[0].value).to.be('*');
+ });
+ });
+
+ describeUpdateVariable('with include all wildcard', function(ctx) {
+ ctx.setup(function() {
+ ctx.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'regex wildcard' };
+ ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}];
+ });
+
+ it('should add All wildcard option', function() {
+ expect(ctx.variable.options[0].value).to.be('.*');
+ });
+ });
+
+ describeUpdateVariable('with include all regex values', function(ctx) {
ctx.setup(function() {
ctx.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'wildcard' };
ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}];
diff --git a/src/test/test-main.js b/src/test/test-main.js
index d75fe2ebd99..04f25f91005 100644
--- a/src/test/test-main.js
+++ b/src/test/test-main.js
@@ -121,6 +121,8 @@ require([
'specs/timeSeries-specs',
'specs/row-ctrl-specs',
'specs/graphiteTargetCtrl-specs',
+ 'specs/influxSeries-specs',
+ 'specs/influxQueryBuilder-specs',
'specs/influxdb-datasource-specs',
'specs/graph-ctrl-specs',
'specs/grafanaGraph-specs',
@@ -130,8 +132,7 @@ require([
'specs/templateValuesSrv-specs',
'specs/kbn-format-specs',
'specs/dashboardSrv-specs',
- 'specs/dashboardViewStateSrv-specs',
- 'specs/influxSeries-specs'
+ 'specs/dashboardViewStateSrv-specs'
], function () {
window.__karma__.start();
});