diff --git a/src/app/components/kbn.js b/src/app/components/kbn.js
index 46a1b8dfa79..b18ad567b54 100644
--- a/src/app/components/kbn.js
+++ b/src/app/components/kbn.js
@@ -652,5 +652,14 @@ function($, _, moment) {
}
};
+ kbn.stringToJsRegex = function(str) {
+ if (str[0] !== '/') {
+ return new RegExp(str);
+ }
+
+ var match = str.match(new RegExp('^/(.*?)/(g?i?m?y?)$'));
+ return new RegExp(match[1], match[2]);
+ };
+
return kbn;
});
diff --git a/src/app/components/timeSeries.js b/src/app/components/timeSeries.js
index 85c27f1da88..4c58c211cc3 100644
--- a/src/app/components/timeSeries.js
+++ b/src/app/components/timeSeries.js
@@ -15,8 +15,7 @@ function (_, kbn) {
if (!aliasOrRegex) { return false; }
if (aliasOrRegex[0] === '/') {
- var match = aliasOrRegex.match(new RegExp('^/(.*?)/(g?i?m?y?)$'));
- var regex = new RegExp(match[1], match[2]);
+ var regex = kbn.stringToJsRegex(aliasOrRegex);
return seriesAlias.match(regex) != null;
}
diff --git a/src/app/controllers/templateEditorCtrl.js b/src/app/controllers/templateEditorCtrl.js
index 649fefc7cfa..d133413ca22 100644
--- a/src/app/controllers/templateEditorCtrl.js
+++ b/src/app/controllers/templateEditorCtrl.js
@@ -7,7 +7,7 @@ function (angular, _) {
var module = angular.module('grafana.controllers');
- module.controller('TemplateEditorCtrl', function($scope, datasourceSrv, templateSrv, templateValuesSrv) {
+ module.controller('TemplateEditorCtrl', function($scope, datasourceSrv, templateSrv, templateValuesSrv, alertSrv) {
var replacementDefaults = {
type: 'query',
@@ -38,7 +38,10 @@ function (angular, _) {
};
$scope.runQuery = function() {
- templateValuesSrv.updateOptions($scope.current);
+ return templateValuesSrv.updateOptions($scope.current).then(function() {
+ }, function(err) {
+ alertSrv.set('Templating', 'Failed to run query for variable values: ' + err.message, 'error');
+ });
};
$scope.edit = function(variable) {
@@ -54,9 +57,10 @@ function (angular, _) {
};
$scope.update = function() {
- templateValuesSrv.updateOptions($scope.current);
- $scope.reset();
- $scope.editor.index = 0;
+ $scope.runQuery().then(function() {
+ $scope.reset();
+ $scope.editor.index = 0;
+ });
};
$scope.reset = function() {
@@ -68,9 +72,6 @@ function (angular, _) {
if ($scope.current.type === 'time period') {
$scope.current.query = '1m,10m,30m,1h,6h,12h,1d,7d,14d,30d';
}
- else {
- $scope.current.query = '';
- }
};
$scope.removeVariable = function(variable) {
diff --git a/src/app/partials/templating_editor.html b/src/app/partials/templating_editor.html
index 61aa68fbeda..fcef0ca8fba 100644
--- a/src/app/partials/templating_editor.html
+++ b/src/app/partials/templating_editor.html
@@ -113,7 +113,7 @@
-
+
-
{{option.text}}
diff --git a/src/app/services/dashboard/dashboardSrv.js b/src/app/services/dashboard/dashboardSrv.js
index c618e5074da..0c7e433e138 100644
--- a/src/app/services/dashboard/dashboardSrv.js
+++ b/src/app/services/dashboard/dashboardSrv.js
@@ -214,6 +214,7 @@ function (angular, $, kbn, _, moment) {
for (i = 0 ; i < this.templating.list.length; i++) {
var variable = this.templating.list[i];
if (variable.datasource === void 0) { variable.datasource = null; }
+ if (variable.type === 'filter') { variable.type = 'query'; }
if (variable.type === void 0) { variable.type = 'query'; }
if (variable.allFormat === void 0) { variable.allFormat = 'Glob'; }
}
diff --git a/src/app/services/influxdb/influxdbDatasource.js b/src/app/services/influxdb/influxdbDatasource.js
index ae2a223602e..d2ca7ae6be5 100644
--- a/src/app/services/influxdb/influxdbDatasource.js
+++ b/src/app/services/influxdb/influxdbDatasource.js
@@ -161,7 +161,7 @@ function (angular, _, kbn, InfluxSeries) {
});
};
- InfluxDatasource.prototype.metricFindQuery = function (filterSrv, query) {
+ InfluxDatasource.prototype.metricFindQuery = function (query) {
var interpolated;
try {
interpolated = templateSrv.replace(query);
diff --git a/src/app/services/templateValuesSrv.js b/src/app/services/templateValuesSrv.js
index fd766d07700..687d1897069 100644
--- a/src/app/services/templateValuesSrv.js
+++ b/src/app/services/templateValuesSrv.js
@@ -2,9 +2,8 @@ define([
'angular',
'lodash',
'kbn',
- 'store'
],
-function (angular, _) {
+function (angular, _, kbn) {
'use strict';
var module = angular.module('grafana.services');
@@ -30,7 +29,7 @@ function (angular, _) {
templateSrv.updateTemplateData();
- return this.applyFilterToOtherFilters(variable)
+ return this.updateOptionsInChildVariables(variable)
.then(function() {
if (!recursive) {
$rootScope.$broadcast('refresh');
@@ -38,7 +37,7 @@ function (angular, _) {
});
};
- this.applyFilterToOtherFilters = function(updatedVariable) {
+ this.updateOptionsInChildVariables = function(updatedVariable) {
var promises = _.map(self.variables, function(otherVariable) {
if (otherVariable === updatedVariable) {
return;
@@ -63,9 +62,8 @@ function (angular, _) {
var datasource = datasourceSrv.get(variable.datasource);
return datasource.metricFindQuery(variable.query)
.then(function (results) {
- variable.options = _.map(results, function(node) {
- return { text: node.text, value: node.text };
- });
+
+ variable.options = self.metricNamesToVariableValues(variable, results);
if (variable.includeAll) {
self.addAllOption(variable);
@@ -84,6 +82,31 @@ function (angular, _) {
});
};
+ this.metricNamesToVariableValues = function(variable, metricNames) {
+ var regex, options, i, matches;
+ options = [];
+
+ if (variable.regex) {
+ regex = kbn.stringToJsRegex(variable.regex);
+ }
+
+ for (i = 0; i < metricNames.length; i++) {
+ var value = metricNames[i].text;
+
+ if (regex) {
+ matches = regex.exec(value);
+ if (!matches) { continue; }
+ if (matches.length > 1) {
+ value = matches[1];
+ }
+ }
+
+ options.push({text: value, value: value});
+ }
+
+ return options;
+ };
+
this.addAllOption = function(variable) {
var allValue = '';
switch(variable.allFormat) {
diff --git a/src/test/specs/templateValuesSrv-specs.js b/src/test/specs/templateValuesSrv-specs.js
index 0486dfed73b..37824cbd953 100644
--- a/src/test/specs/templateValuesSrv-specs.js
+++ b/src/test/specs/templateValuesSrv-specs.js
@@ -8,26 +8,27 @@ define([
describe('templateValuesSrv', function() {
var _templateValuesSrv;
var _dashboard;
+ var _datasourceSrv = {};
+ var _q;
+ var _rootScope;
beforeEach(module('grafana.services'));
beforeEach(module(function($provide) {
- $provide.value('datasourceSrv', {});
+ $provide.value('datasourceSrv', _datasourceSrv);
$provide.value('templateSrv', {
updateTemplateData: function() {}
});
_dashboard = dashboardMock.create();
}));
- beforeEach(inject(function(templateValuesSrv) {
+ beforeEach(inject(function(templateValuesSrv, $rootScope, $q) {
_templateValuesSrv = templateValuesSrv;
+ _rootScope = $rootScope;
+ _q = $q;
}));
describe('update time period variable options', function() {
- var variable = {
- type: 'time period',
- query: 'auto,1s,2h,5h,1d',
- name: 'test'
- };
+ var variable = { type: 'time period', query: 'auto,1s,2h,5h,1d', name: 'test' };
beforeEach(function() {
_templateValuesSrv.updateOptions(variable);
@@ -40,6 +41,128 @@ define([
});
});
+ function describeUpdateVariable(desc, fn) {
+ describe(desc, function() {
+ var ctx = {};
+ ctx.setup = function(setupFn) {
+ ctx.setupFn = setupFn;
+ };
+
+ beforeEach(function() {
+ ctx.setupFn();
+ var ds = {};
+ ds.metricFindQuery = sinon.stub().returns(_q.when(ctx.queryResult));
+ _datasourceSrv.get = sinon.stub().returns(ds);
+
+ _templateValuesSrv.updateOptions(ctx.variable);
+ _rootScope.$digest();
+ });
+
+ fn(ctx);
+ });
+ }
+
+ describeUpdateVariable('time period variable ', function(ctx) {
+ ctx.setup(function() {
+ ctx.variable = { type: 'time period', query: 'auto,1s,2h,5h,1d', name: 'test' };
+ });
+
+ it('should update options array', function() {
+ expect(ctx.variable.options.length).to.be(5);
+ expect(ctx.variable.options[1].text).to.be('1s');
+ expect(ctx.variable.options[1].value).to.be('1s');
+ });
+ });
+
+ describeUpdateVariable('basic query variable', function(ctx) {
+ ctx.setup(function() {
+ ctx.variable = { type: 'query', query: 'apps.*', name: 'test' };
+ ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}];
+ });
+
+ it('should update options array', function() {
+ expect(ctx.variable.options.length).to.be(2);
+ expect(ctx.variable.options[0].text).to.be('backend1');
+ expect(ctx.variable.options[0].value).to.be('backend1');
+ expect(ctx.variable.options[1].value).to.be('backend2');
+ });
+
+ it('should select first option as value', function() {
+ expect(ctx.variable.current.value).to.be('backend1');
+ });
+ });
+
+ describeUpdateVariable('and existing value still exists in options', function(ctx) {
+ ctx.setup(function() {
+ ctx.variable = { type: 'query', query: 'apps.*', name: 'test' };
+ ctx.variable.current = { value: 'backend2'};
+ ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}];
+ });
+
+ it('should keep variable value', function() {
+ expect(ctx.variable.current.value).to.be('backend2');
+ });
+ });
+
+ describeUpdateVariable('and regex pattern exists', function(ctx) {
+ ctx.setup(function() {
+ ctx.variable = { type: 'query', query: 'apps.*', name: 'test' };
+ ctx.variable.regex = '/apps.*(backend_[0-9]+)/';
+ ctx.queryResult = [{text: 'apps.backend.backend_01.counters.req'}, {text: 'apps.backend.backend_02.counters.req'}];
+ });
+
+ it('should extract and use match group', function() {
+ expect(ctx.variable.options[0].value).to.be('backend_01');
+ });
+ });
+
+ describeUpdateVariable('and regex pattern exists and no match', function(ctx) {
+ ctx.setup(function() {
+ ctx.variable = { type: 'query', query: 'apps.*', name: 'test' };
+ ctx.variable.regex = '/apps.*(backendasd[0-9]+)/';
+ ctx.queryResult = [{text: 'apps.backend.backend_01.counters.req'}, {text: 'apps.backend.backend_02.counters.req'}];
+ });
+
+ it('should not add non matching items', function() {
+ expect(ctx.variable.options.length).to.be(0);
+ });
+ });
+
+ describeUpdateVariable('regex pattern without slashes', 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_02.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' };
+ ctx.variable.current = { value: 'backend2'};
+ ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}];
+ });
+
+ it('should keep variable value', function() {
+ expect(ctx.variable.current.value).to.be('backend2');
+ });
+ });
+
+ describeUpdateVariable('with include All glob syntax', function(ctx) {
+ ctx.setup(function() {
+ ctx.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'Glob' };
+ ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}];
+ });
+
+ it('should add All Glob option', function() {
+ expect(ctx.variable.options[0].value).to.be('{backend1,backend2,backend3}');
+ });
+ });
+
});
});