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}'); + }); + }); + }); });