From 4ef79d250d4a15960fa7f550a70cf27b1a38233f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 28 Feb 2016 11:44:11 +0100 Subject: [PATCH 01/12] feat(templating): initial work on rethink of value formating --- .../dashboard/partials/shareModal.html | 31 ++++++++--------- public/app/features/templating/templateSrv.js | 33 +++++++++++-------- public/test/specs/templateSrv-specs.js | 16 +++++++++ 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/public/app/features/dashboard/partials/shareModal.html b/public/app/features/dashboard/partials/shareModal.html index 8d76213de91..17f9d390c45 100644 --- a/public/app/features/dashboard/partials/shareModal.html +++ b/public/app/features/dashboard/partials/shareModal.html @@ -136,22 +136,23 @@ - -
- - -
- -
- Did you make a mistake? delete snapshot. -
+
+ + +
+ +
+ Did you make a mistake? delete snapshot. +
+ + diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js index 267454dbecb..6a2fa26ed6f 100644 --- a/public/app/features/templating/templateSrv.js +++ b/public/app/features/templating/templateSrv.js @@ -24,22 +24,18 @@ function (angular, _) { this.updateTemplateData = function() { this._values = {}; - this._texts = {}; _.each(this.variables, function(variable) { - if (!variable.current || !variable.current.isNone && !variable.current.value) { return; } - - this._values[variable.name] = this.renderVariableValue(variable); - this._texts[variable.name] = variable.current.text; - }, this); + if (!variable.current || !variable.current.isNone && !variable.current.value) { return; } + this._values[variable.name] = variable.current.value; + }, this); }; - this.renderVariableValue = function(variable) { - var value = variable.current.value; + this.formatValue = function(value, format) { if (_.isString(value)) { return value; } else { - switch(variable.multiFormat) { + switch(format) { case "regex values": { return '(' + value.join('|') + ')'; } @@ -89,22 +85,31 @@ function (angular, _) { }); }; - this.replace = function(target, scopedVars) { + this.replace = function(target, scopedVars, format) { if (!target) { return target; } - var value; + var value, systemValue; this._regex.lastIndex = 0; return target.replace(this._regex, function(match, g1, g2) { if (scopedVars) { value = scopedVars[g1 || g2]; - if (value) { return value.value; } + if (value) { + return self.formatValue(value.value); + } } value = self._values[g1 || g2]; - if (!value) { return match; } + if (!value) { + return match; + } - return self._grafanaVariables[value] || value; + systemValue = self._grafanaVariables[value]; + if (systemValue) { + return self.formatValue(systemValue); + } + + return self.formatValue(value, format); }); }; diff --git a/public/test/specs/templateSrv-specs.js b/public/test/specs/templateSrv-specs.js index 2b811244210..0728abc49cf 100644 --- a/public/test/specs/templateSrv-specs.js +++ b/public/test/specs/templateSrv-specs.js @@ -45,6 +45,22 @@ define([ }); }); + describe.only('replace can pass multi / all format', function() { + beforeEach(function() { + _templateSrv.init([{name: 'test', current: {value: ['value1', 'value2'] }}]); + }); + + it('should replace $test with globbed value', function() { + var target = _templateSrv.replace('this.$test.filters', {}, 'glob'); + expect(target).to.be('this.{value1,value2}.filters'); + }); + + it('should replace $test with piped value', function() { + var target = _templateSrv.replace('this=$test', {}, 'pipe'); + expect(target).to.be('this=value1|value2'); + }); + }); + describe('render variable to string values', function() { it('single value should return value', function() { var result = _templateSrv.renderVariableValue({current: {value: 'test'}}); From 078e69d06d4a2f8e23e9b7ca8a2363e7f6eb501a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 29 Feb 2016 17:07:53 +0100 Subject: [PATCH 02/12] feat(templating): simplified templating forms removed all formats --- .../core/directives/value_select_dropdown.js | 5 -- .../features/templating/partials/editor.html | 29 ++------ .../features/templating/templateValuesSrv.js | 72 +++++++++---------- .../datasource/elasticsearch/datasource.js | 2 +- 4 files changed, 44 insertions(+), 64 deletions(-) diff --git a/public/app/core/directives/value_select_dropdown.js b/public/app/core/directives/value_select_dropdown.js index 656804a72d3..e6e2a709a1a 100644 --- a/public/app/core/directives/value_select_dropdown.js +++ b/public/app/core/directives/value_select_dropdown.js @@ -179,11 +179,6 @@ function (angular, _, coreModule) { vm.variable.current.text = _.pluck(vm.selectedValues, 'text').join(' + '); vm.variable.current.tags = vm.selectedTags; - // only single value - if (vm.selectedValues.length === 1) { - vm.variable.current.value = vm.selectedValues[0].value; - } - if (commitChange) { vm.commitChanges(); } diff --git a/public/app/features/templating/partials/editor.html b/public/app/features/templating/partials/editor.html index aab5f048465..78132a1d04a 100644 --- a/public/app/features/templating/partials/editor.html +++ b/public/app/features/templating/partials/editor.html @@ -79,7 +79,7 @@
- Type + Type
@@ -97,8 +97,9 @@
- - + Hide + +
@@ -133,14 +134,9 @@
+ Include All option + - -
-
- All format -
- -
@@ -158,20 +154,9 @@
All value +
-
-
- All format -
- -
-
-
- All value - -
-
Update diff --git a/public/app/features/templating/templateValuesSrv.js b/public/app/features/templating/templateValuesSrv.js index 2760bb42ca0..517a69f4a6a 100644 --- a/public/app/features/templating/templateValuesSrv.js +++ b/public/app/features/templating/templateValuesSrv.js @@ -108,7 +108,6 @@ function (angular, _, kbn) { if (variable.type === 'custom' && variable.includeAll) { self.addAllOption(variable); } - }; this.updateOptions = function(variable) { @@ -245,41 +244,42 @@ function (angular, _, kbn) { }; this.addAllOption = function(variable) { - var allValue = ''; - switch(variable.allFormat) { - case 'wildcard': { - allValue = '*'; - break; - } - case 'regex wildcard': { - allValue = '.*'; - break; - } - case 'lucene': { - var quotedValues = _.map(variable.options, function(val) { - return '\\\"' + val.text + '\\\"'; - }); - allValue = '(' + quotedValues.join(' OR ') + ')'; - break; - } - case 'regex values': { - allValue = '(' + _.map(variable.options, function(option) { - return self.regexEscape(option.text); - }).join('|') + ')'; - break; - } - case 'pipe': { - allValue = _.pluck(variable.options, 'text').join('|'); - break; - } - default: { - allValue = '{'; - allValue += _.pluck(variable.options, 'text').join(','); - allValue += '}'; - } - } - - variable.options.unshift({text: 'All', value: allValue}); + // var allValue = ''; + // switch(variable.allFormat) { + // case 'wildcard': { + // allValue = '*'; + // break; + // } + // case 'regex wildcard': { + // allValue = '.*'; + // break; + // } + // case 'lucene': { + // var quotedValues = _.map(variable.options, function(val) { + // return '\\\"' + val.text + '\\\"'; + // }); + // allValue = '(' + quotedValues.join(' OR ') + ')'; + // break; + // } + // case 'regex values': { + // allValue = '(' + _.map(variable.options, function(option) { + // return self.regexEscape(option.text); + // }).join('|') + ')'; + // break; + // } + // case 'pipe': { + // allValue = _.pluck(variable.options, 'text').join('|'); + // break; + // } + // default: { + // allValue = '{'; + // allValue += _.pluck(variable.options, 'text').join(','); + // allValue += '}'; + // } + // } + // + var value =_.pluck(variable.options, 'text'); + variable.options.unshift({text: 'All', value: value}); }; }); diff --git a/public/app/plugins/datasource/elasticsearch/datasource.js b/public/app/plugins/datasource/elasticsearch/datasource.js index 294f666abc7..cc721de32ff 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.js +++ b/public/app/plugins/datasource/elasticsearch/datasource.js @@ -190,7 +190,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes payload = payload.replace(/\$interval/g, options.interval); payload = payload.replace(/\$timeFrom/g, options.range.from.valueOf()); payload = payload.replace(/\$timeTo/g, options.range.to.valueOf()); - payload = templateSrv.replace(payload, options.scopedVars); + payload = templateSrv.replace(payload, options.scopedVars, 'lucene'); return this._post('_msearch', payload).then(function(res) { return new ElasticResponse(sentTargets, res).getTimeSeries(); From cb8b038795e305f8155dda328b5b83d976476f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 29 Feb 2016 17:31:31 +0100 Subject: [PATCH 03/12] feat(templating): progress on templating rethink --- .../core/directives/value_select_dropdown.js | 4 ++++ .../features/dashboard/partials/import.html | 18 ++++++++---------- public/app/features/templating/templateSrv.js | 9 +++++++-- .../features/templating/templateValuesSrv.js | 19 ++++++++----------- .../plugins/datasource/influxdb/datasource.ts | 2 +- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/public/app/core/directives/value_select_dropdown.js b/public/app/core/directives/value_select_dropdown.js index e6e2a709a1a..60d257d6fa8 100644 --- a/public/app/core/directives/value_select_dropdown.js +++ b/public/app/core/directives/value_select_dropdown.js @@ -179,6 +179,10 @@ function (angular, _, coreModule) { vm.variable.current.text = _.pluck(vm.selectedValues, 'text').join(' + '); vm.variable.current.tags = vm.selectedTags; + if (!vm.variable.multi) { + vm.variable.current.value = vm.selectedValues[0].value; + } + if (commitChange) { vm.commitChanges(); } diff --git a/public/app/features/dashboard/partials/import.html b/public/app/features/dashboard/partials/import.html index d1f028f7222..3f23e4cb682 100644 --- a/public/app/features/dashboard/partials/import.html +++ b/public/app/features/dashboard/partials/import.html @@ -15,26 +15,24 @@
-
+
Migrate dashboards Import dashboards from Elasticsearch or InfluxDB
-
+
Dashboard source
-
-
- -
-
-
- +
+
+
+ +
-
{{infoText}}
+
{{infoText}}
diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js index 6a2fa26ed6f..1e1ec72b02f 100644 --- a/public/app/features/templating/templateSrv.js +++ b/public/app/features/templating/templateSrv.js @@ -31,13 +31,18 @@ function (angular, _) { }, this); }; + this.regexEscape = function(value) { + return value.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&'); + }; + this.formatValue = function(value, format) { if (_.isString(value)) { return value; } else { switch(format) { - case "regex values": { - return '(' + value.join('|') + ')'; + case "regex": { + var escapedValues = _.map(value, this.regexEscape); + return '(' + escapedValues.join('|') + ')'; } case "lucene": { var quotedValues = _.map(value, function(val) { diff --git a/public/app/features/templating/templateValuesSrv.js b/public/app/features/templating/templateValuesSrv.js index 517a69f4a6a..a88d4e9a700 100644 --- a/public/app/features/templating/templateValuesSrv.js +++ b/public/app/features/templating/templateValuesSrv.js @@ -226,22 +226,19 @@ function (angular, _, kbn) { return _.map(_.keys(options).sort(), function(key) { var option = { text: key, value: key }; - // check if values need to be regex escaped - if (self.shouldRegexEscape(variable)) { - option.value = self.regexEscape(option.value); - } + // // check if values need to be regex escaped + // if (self.shouldRegexEscape(variable)) { + // option.value = self.regexEscape(option.value); + // } return option; }); }; - this.shouldRegexEscape = function(variable) { - return (variable.includeAll || variable.multi) && variable.allFormat.indexOf('regex') !== -1; - }; - - this.regexEscape = function(value) { - return value.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&'); - }; + // this.shouldRegexEscape = function(variable) { + // return (variable.includeAll || variable.multi) && variable.allFormat.indexOf('regex') !== -1; + // }; + // this.addAllOption = function(variable) { // var allValue = ''; diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index fec1dd71d6c..d6583477c94 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -45,7 +45,7 @@ export function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv) allQueries = allQueries.replace(/\$timeFilter/g, timeFilter); // replace templated variables - allQueries = templateSrv.replace(allQueries, options.scopedVars); + allQueries = templateSrv.replace(allQueries, options.scopedVars, 'regex'); return this._seriesQuery(allQueries).then(function(data): any { if (!data || !data.results) { From f3ad71d751fe1da2e762eb47686bdcbc21a3f742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 1 Mar 2016 08:43:54 +0100 Subject: [PATCH 04/12] feat(templating): lots of progress on template variable context specific formats, #2918, all elasticsearch / lucene use cases seem to work now --- .jshintrc | 4 +- package.json | 4 +- public/app/features/templating/templateSrv.js | 50 +++++++++++-------- .../datasource/elasticsearch/datasource.js | 15 +++--- .../elasticsearch/elastic_response.js | 18 ++++++- public/test/.jshintrc | 4 +- public/test/specs/templateSrv-specs.js | 15 +++++- 7 files changed, 75 insertions(+), 35 deletions(-) diff --git a/.jshintrc b/.jshintrc index bca48639a70..510726ee46b 100644 --- a/.jshintrc +++ b/.jshintrc @@ -4,7 +4,7 @@ "bitwise":false, "curly": true, "eqnull": true, - "globalstrict": true, + "strict": true, "devel": true, "eqeqeq": true, "forin": false, @@ -12,7 +12,7 @@ "supernew": true, "expr": true, "indent": 2, - "latedef": true, + "latedef": false, "newcap": true, "noarg": true, "noempty": true, diff --git a/package.json b/package.json index 0df426954d3..a79280c30ba 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "grunt-contrib-copy": "~0.8.2", "grunt-contrib-cssmin": "~0.14.0", "grunt-contrib-htmlmin": "~0.6.0", - "grunt-contrib-jshint": "~0.11.3", + "grunt-contrib-jshint": "~1.0.0", "grunt-contrib-less": "~0.7.0", "grunt-contrib-uglify": "~0.11.0", "grunt-contrib-watch": "^0.6.1", @@ -39,7 +39,7 @@ "grunt-tslint": "^3.0.2", "grunt-typescript": "^0.8.0", "grunt-usemin": "3.0.0", - "jshint-stylish": "~0.1.5", + "jshint-stylish": "~2.1.0", "karma": "~0.13.15", "karma-chrome-launcher": "~0.2.2", "karma-coverage": "0.5.3", diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js index 1e1ec72b02f..5ccf8a4ec2d 100644 --- a/public/app/features/templating/templateSrv.js +++ b/public/app/features/templating/templateSrv.js @@ -31,31 +31,37 @@ function (angular, _) { }, this); }; - this.regexEscape = function(value) { + function regexEscape(value) { return value.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&'); - }; + } + + function luceneEscape(value) { + return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, "\\$1"); + } this.formatValue = function(value, format) { - if (_.isString(value)) { - return value; - } else { - switch(format) { - case "regex": { - var escapedValues = _.map(value, this.regexEscape); - return '(' + escapedValues.join('|') + ')'; + switch(format) { + case "regex": { + var escapedValues = _.map(value, regexEscape); + return '(' + escapedValues.join('|') + ')'; + } + case "lucene": { + if (typeof value === 'string') { + return luceneEscape(value); } - case "lucene": { - var quotedValues = _.map(value, function(val) { - return '\\\"' + val + '\\\"'; - }); - return '(' + quotedValues.join(' OR ') + ')'; - } - case "pipe": { - return value.join('|'); - } - default: { - return '{' + value.join(',') + '}'; + var quotedValues = _.map(value, function(val) { + return '\"' + luceneEscape(val) + '\"'; + }); + return '(' + quotedValues.join(' OR ') + ')'; + } + case "pipe": { + return value.join('|'); + } + default: { + if (typeof value === 'string') { + return value; } + return '{' + value.join(',') + '}'; } } }; @@ -114,7 +120,9 @@ function (angular, _) { return self.formatValue(systemValue); } - return self.formatValue(value, format); + var res = self.formatValue(value, format); + console.log('replace: ' + value, res); + return res; }); }; diff --git a/public/app/plugins/datasource/elasticsearch/datasource.js b/public/app/plugins/datasource/elasticsearch/datasource.js index cc721de32ff..b048c206edb 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.js +++ b/public/app/plugins/datasource/elasticsearch/datasource.js @@ -47,15 +47,15 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes }; this._get = function(url) { - return this._request('GET', this.indexPattern.getIndexForToday() + url) - .then(function(results) { + return this._request('GET', this.indexPattern.getIndexForToday() + url).then(function(results) { + results.data.$$config = results.config; return results.data; }); }; this._post = function(url, data) { - return this._request('POST', url, data) - .then(function(results) { + return this._request('POST', url, data).then(function(results) { + results.data.$$config = results.config; return results.data; }); }; @@ -170,7 +170,10 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes var queryObj = this.queryBuilder.build(target); var esQuery = angular.toJson(queryObj); - var luceneQuery = angular.toJson(target.query || '*'); + var luceneQuery = target.query || '*'; + luceneQuery = templateSrv.replace(luceneQuery, options.scopedVars, 'lucene'); + luceneQuery = angular.toJson(luceneQuery); + // remove inner quotes luceneQuery = luceneQuery.substr(1, luceneQuery.length - 2); esQuery = esQuery.replace("$lucene_query", luceneQuery); @@ -190,7 +193,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes payload = payload.replace(/\$interval/g, options.interval); payload = payload.replace(/\$timeFrom/g, options.range.from.valueOf()); payload = payload.replace(/\$timeTo/g, options.range.to.valueOf()); - payload = templateSrv.replace(payload, options.scopedVars, 'lucene'); + payload = templateSrv.replace(payload, options.scopedVars); return this._post('_msearch', payload).then(function(res) { return new ElasticResponse(sentTargets, res).getTimeSeries(); diff --git a/public/app/plugins/datasource/elasticsearch/elastic_response.js b/public/app/plugins/datasource/elasticsearch/elastic_response.js index 7ae4c204207..99b5af0d737 100644 --- a/public/app/plugins/datasource/elasticsearch/elastic_response.js +++ b/public/app/plugins/datasource/elasticsearch/elastic_response.js @@ -284,13 +284,29 @@ function (_, queryDef) { } }; + ElasticResponse.prototype.getErrorFromElasticResponse = function(response, err) { + var result = {}; + result.data = JSON.stringify(err, null, 4); + if (err.root_cause && err.root_cause.length > 0 && err.root_cause[0].reason) { + result.message = err.root_cause[0].reason; + } else { + result.message = err.reason || 'Unkown elatic error response'; + } + + if (response.$$config) { + result.config = response.$$config; + } + + return result; + }; + ElasticResponse.prototype.getTimeSeries = function() { var seriesList = []; for (var i = 0; i < this.response.responses.length; i++) { var response = this.response.responses[i]; if (response.error) { - throw { message: response.error }; + throw this.getErrorFromElasticResponse(this.response, response.error); } if (response.hits && response.hits.hits.length > 0) { diff --git a/public/test/.jshintrc b/public/test/.jshintrc index f808d281741..3478f503dda 100644 --- a/public/test/.jshintrc +++ b/public/test/.jshintrc @@ -3,7 +3,7 @@ "bitwise":false, "curly": true, "eqnull": true, - "globalstrict": true, + "strict": true, "devel": true, "eqeqeq": true, "forin": false, @@ -11,7 +11,7 @@ "supernew": true, "expr": true, "indent": 2, - "latedef": true, + "latedef": false, "newcap": true, "noarg": true, "noempty": true, diff --git a/public/test/specs/templateSrv-specs.js b/public/test/specs/templateSrv-specs.js index 0728abc49cf..56b23f89798 100644 --- a/public/test/specs/templateSrv-specs.js +++ b/public/test/specs/templateSrv-specs.js @@ -45,7 +45,7 @@ define([ }); }); - describe.only('replace can pass multi / all format', function() { + describe('replace can pass multi / all format', function() { beforeEach(function() { _templateSrv.init([{name: 'test', current: {value: ['value1', 'value2'] }}]); }); @@ -59,6 +59,19 @@ define([ var target = _templateSrv.replace('this=$test', {}, 'pipe'); expect(target).to.be('this=value1|value2'); }); + + it('should replace $test with piped value', function() { + var target = _templateSrv.replace('this=$test', {}, 'pipe'); + expect(target).to.be('this=value1|value2'); + }); + }); + + describe.only('lucene format', function() { + it('should properly escape $test with lucene escape sequences', function() { + _templateSrv.init([{name: 'test', current: {value: 'value/4' }}]); + var target = _templateSrv.replace('this:$test', {}, 'lucene'); + expect(target).to.be("this:value\\\/4"); + }); }); describe('render variable to string values', function() { From 8f5a7f17648a34a679a3d03aa1b00c8e5b2970c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 1 Mar 2016 10:51:39 +0100 Subject: [PATCH 05/12] feat(templating): more work on context specific varaiable formats, #2918 --- public/app/features/templating/templateSrv.js | 24 +++-- .../features/templating/templateValuesSrv.js | 52 ++-------- .../elasticsearch/specs/datasource_specs.ts | 2 +- public/test/specs/templateSrv-specs.js | 39 ++------ public/test/specs/templateValuesSrv-specs.js | 97 ++----------------- 5 files changed, 40 insertions(+), 174 deletions(-) diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js index 5ccf8a4ec2d..19847984393 100644 --- a/public/app/features/templating/templateSrv.js +++ b/public/app/features/templating/templateSrv.js @@ -25,10 +25,13 @@ function (angular, _) { this.updateTemplateData = function() { this._values = {}; - _.each(this.variables, function(variable) { - if (!variable.current || !variable.current.isNone && !variable.current.value) { return; } - this._values[variable.name] = variable.current.value; - }, this); + for (var i = 0; i < this.variables.length; i++) { + var variable = this.variables[i]; + if (!variable.current || !variable.current.isNone && !variable.current.value) { + continue; + } + this._values[variable.name] = variable.current; + } }; function regexEscape(value) { @@ -42,6 +45,10 @@ function (angular, _) { this.formatValue = function(value, format) { switch(format) { case "regex": { + if (typeof value === 'string') { + return regexEscape(value); + } + var escapedValues = _.map(value, regexEscape); return '(' + escapedValues.join('|') + ')'; } @@ -115,13 +122,12 @@ function (angular, _) { return match; } - systemValue = self._grafanaVariables[value]; + systemValue = self._grafanaVariables[value.value]; if (systemValue) { return self.formatValue(systemValue); } - var res = self.formatValue(value, format); - console.log('replace: ' + value, res); + var res = self.formatValue(value.value, format); return res; }); }; @@ -130,7 +136,6 @@ function (angular, _) { if (!target) { return target; } var value; - var text; this._regex.lastIndex = 0; return target.replace(this._regex, function(match, g1, g2) { @@ -140,10 +145,9 @@ function (angular, _) { } value = self._values[g1 || g2]; - text = self._texts[g1 || g2]; if (!value) { return match; } - return self._grafanaVariables[value] || text; + return self._grafanaVariables[value.value] || value.text; }); }; diff --git a/public/app/features/templating/templateValuesSrv.js b/public/app/features/templating/templateValuesSrv.js index a88d4e9a700..b0c56812ad5 100644 --- a/public/app/features/templating/templateValuesSrv.js +++ b/public/app/features/templating/templateValuesSrv.js @@ -225,58 +225,18 @@ function (angular, _, kbn) { return _.map(_.keys(options).sort(), function(key) { var option = { text: key, value: key }; - - // // check if values need to be regex escaped - // if (self.shouldRegexEscape(variable)) { - // option.value = self.regexEscape(option.value); - // } - return option; }); }; - // this.shouldRegexEscape = function(variable) { - // return (variable.includeAll || variable.multi) && variable.allFormat.indexOf('regex') !== -1; - // }; - // - this.addAllOption = function(variable) { - // var allValue = ''; - // switch(variable.allFormat) { - // case 'wildcard': { - // allValue = '*'; - // break; - // } - // case 'regex wildcard': { - // allValue = '.*'; - // break; - // } - // case 'lucene': { - // var quotedValues = _.map(variable.options, function(val) { - // return '\\\"' + val.text + '\\\"'; - // }); - // allValue = '(' + quotedValues.join(' OR ') + ')'; - // break; - // } - // case 'regex values': { - // allValue = '(' + _.map(variable.options, function(option) { - // return self.regexEscape(option.text); - // }).join('|') + ')'; - // break; - // } - // case 'pipe': { - // allValue = _.pluck(variable.options, 'text').join('|'); - // break; - // } - // default: { - // allValue = '{'; - // allValue += _.pluck(variable.options, 'text').join(','); - // allValue += '}'; - // } - // } - // + if (variable.allValue) { + variable.options.unshift({text: 'All', value: variable.allValue, isAll: true}); + return; + } + var value =_.pluck(variable.options, 'text'); - variable.options.unshift({text: 'All', value: value}); + variable.options.unshift({text: 'All', value: value, isAll: true}); }; }); diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts index 133d138be82..bc1da316810 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts @@ -33,7 +33,7 @@ describe('ElasticDatasource', function() { var requestOptions; ctx.backendSrv.datasourceRequest = function(options) { requestOptions = options; - return ctx.$q.when({}); + return ctx.$q.when({data: {}}); }; ctx.ds.testDatasource(); diff --git a/public/test/specs/templateSrv-specs.js b/public/test/specs/templateSrv-specs.js index 56b23f89798..1e06d66a2a1 100644 --- a/public/test/specs/templateSrv-specs.js +++ b/public/test/specs/templateSrv-specs.js @@ -66,7 +66,7 @@ define([ }); }); - describe.only('lucene format', function() { + describe('lucene format', function() { it('should properly escape $test with lucene escape sequences', function() { _templateSrv.init([{name: 'test', current: {value: 'value/4' }}]); var target = _templateSrv.replace('this:$test', {}, 'lucene'); @@ -74,49 +74,29 @@ define([ }); }); - describe('render variable to string values', function() { + describe('format variable to string values', function() { it('single value should return value', function() { - var result = _templateSrv.renderVariableValue({current: {value: 'test'}}); + var result = _templateSrv.formatValue('test'); expect(result).to.be('test'); }); it('multi value and glob format should render glob string', function() { - var result = _templateSrv.renderVariableValue({ - multiFormat: 'glob', - current: { - value: ['test','test2'], - } - }); + var result = _templateSrv.formatValue(['test','test2'], 'glob'); expect(result).to.be('{test,test2}'); }); it('multi value and lucene should render as lucene expr', function() { - var result = _templateSrv.renderVariableValue({ - multiFormat: 'lucene', - current: { - value: ['test','test2'], - } - }); - expect(result).to.be('(\\\"test\\\" OR \\\"test2\\\")'); + var result = _templateSrv.formatValue(['test','test2'], 'lucene'); + expect(result).to.be('("test" OR "test2")'); }); it('multi value and regex format should render regex string', function() { - var result = _templateSrv.renderVariableValue({ - multiFormat: 'regex values', - current: { - value: ['test','test2'], - } - }); - expect(result).to.be('(test|test2)'); + var result = _templateSrv.formatValue(['test.','test2'], 'regex'); + expect(result).to.be('(test\\.|test2)'); }); it('multi value and pipe should render pipe string', function() { - var result = _templateSrv.renderVariableValue({ - multiFormat: 'pipe', - current: { - value: ['test','test2'], - } - }); + var result = _templateSrv.formatValue(['test','test2'], 'pipe'); expect(result).to.be('test|test2'); }); @@ -223,7 +203,6 @@ define([ }); }); - }); }); diff --git a/public/test/specs/templateValuesSrv-specs.js b/public/test/specs/templateValuesSrv-specs.js index e1e6fc347cb..44e648816f2 100644 --- a/public/test/specs/templateValuesSrv-specs.js +++ b/public/test/specs/templateValuesSrv-specs.js @@ -247,7 +247,7 @@ define([ }); }); - describeUpdateVariable('regex pattern remove duplicates', function(scenario) { + describeUpdateVariable('regex pattern remove duplicates', function(scenario) { scenario.setup(function() { scenario.variable = { type: 'query', query: 'apps.*', name: 'test' }; scenario.variable.regex = 'backend_01'; @@ -259,107 +259,30 @@ define([ }); }); - describeUpdateVariable('with include All glob syntax', function(scenario) { + describeUpdateVariable('with include All', function(scenario) { scenario.setup(function() { - scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'glob' }; + scenario.variable = {type: 'query', query: 'apps.*', name: 'test', includeAll: true}; scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}]; }); - it('should add All Glob option', function() { - expect(scenario.variable.options[0].value).to.be('{backend1,backend2,backend3}'); + it('should add All option', function() { + expect(scenario.variable.options[0].text).to.be('All'); + expect(scenario.variable.options[0].value).to.eql(['backend1', 'backend2', 'backend3']); + expect(scenario.variable.options[0].isAll).to.be(true); }); }); - describeUpdateVariable('with include all wildcard', function(scenario) { + describeUpdateVariable('with include all and custom value', function(scenario) { scenario.setup(function() { - scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'wildcard' }; + scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allValue: '*' }; scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}]; }); - it('should add All wildcard option', function() { + it('should add All option with custom value', function() { expect(scenario.variable.options[0].value).to.be('*'); }); }); - describeUpdateVariable('with include all wildcard', function(scenario) { - scenario.setup(function() { - scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'regex wildcard' }; - scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}]; - }); - - it('should add All wildcard option', function() { - expect(scenario.variable.options[0].value).to.be('.*'); - }); - }); - - describeUpdateVariable('with include all regex values', function(scenario) { - scenario.setup(function() { - scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'wildcard' }; - scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}]; - }); - - it('should add All wildcard option', function() { - expect(scenario.variable.options[0].value).to.be('*'); - }); - }); - - describeUpdateVariable('with include all glob no values', function(scenario) { - scenario.setup(function() { - scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'glob' }; - scenario.queryResult = []; - }); - - it('should add empty glob', function() { - expect(scenario.variable.options[0].value).to.be('{}'); - }); - }); - - describeUpdateVariable('with include all lucene and values', function(scenario) { - scenario.setup(function() { - scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'lucene' }; - scenario.queryResult = [{text: 'backend1'}, { text: 'backend2'}]; - }); - - it('should add lucene glob', function() { - expect(scenario.variable.options[0].value).to.be('(\\\"backend1\\\" OR \\\"backend2\\\")'); - }); - }); - - describeUpdateVariable('with include all regex all values', function(scenario) { - scenario.setup(function() { - scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'regex values' }; - scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}]; - }); - - it('should add empty glob', function() { - expect(scenario.variable.options[0].value).to.be('(backend1|backend2|backend3)'); - }); - }); - - describeUpdateVariable('with include all regex values and values require escaping', function(scenario) { - scenario.setup(function() { - scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'regex values' }; - scenario.queryResult = [{text: '/root'}, {text: '/var'}, { text: '/lib'}]; - }); - - it('should regex escape options', function() { - expect(scenario.variable.options[0].value).to.be('(\\/lib|\\/root|\\/var)'); - expect(scenario.variable.options[1].value).to.be('\\/lib'); - expect(scenario.variable.options[1].text).to.be('/lib'); - }); - }); - - describeUpdateVariable('with include all pipe all values', function(scenario) { - scenario.setup(function() { - scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'pipe' }; - scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}]; - }); - - it('should add pipe delimited string', function() { - expect(scenario.variable.options[0].value).to.be('backend1|backend2|backend3'); - }); - }); - }); }); From f4b97979c4132afddd1c85194845cbcf93217378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 1 Mar 2016 12:26:31 +0100 Subject: [PATCH 06/12] ux(): minor fix --- public/sass/base/_type.scss | 1 - public/sass/components/_infobox.scss | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/public/sass/base/_type.scss b/public/sass/base/_type.scss index 7cdf3de06aa..e819f71b540 100644 --- a/public/sass/base/_type.scss +++ b/public/sass/base/_type.scss @@ -131,7 +131,6 @@ mark, // Unordered and Ordered lists ul, ol { padding: 0; - padding-left: $spacer; } ul ul, ul ol, diff --git a/public/sass/components/_infobox.scss b/public/sass/components/_infobox.scss index c4dbb2820e1..a7e5917d939 100644 --- a/public/sass/components/_infobox.scss +++ b/public/sass/components/_infobox.scss @@ -16,5 +16,8 @@ h5 { margin-top: 5px; } + ul { + padding-left: $spacer; + } } From fa73b1ce802bc2f18eee3d63cd85f6eec9d9ac2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 1 Mar 2016 13:47:02 +0100 Subject: [PATCH 07/12] feat(templating): changed how the All templating value works --- public/app/features/templating/templateSrv.js | 44 +++++++++++++------ .../features/templating/templateValuesSrv.js | 5 +-- public/test/specs/templateSrv-specs.js | 35 +++++++++++++++ public/test/specs/templateValuesSrv-specs.js | 3 +- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js index 19847984393..1f1153bda44 100644 --- a/public/app/features/templating/templateSrv.js +++ b/public/app/features/templating/templateSrv.js @@ -13,7 +13,7 @@ function (angular, _) { var self = this; this._regex = /\$(\w+)|\[\[([\s\S]+?)\]\]/g; - this._values = {}; + this._index = {}; this._texts = {}; this._grafanaVariables = {}; @@ -23,14 +23,14 @@ function (angular, _) { }; this.updateTemplateData = function() { - this._values = {}; + this._index = {}; for (var i = 0; i < this.variables.length; i++) { var variable = this.variables[i]; if (!variable.current || !variable.current.isNone && !variable.current.value) { continue; } - this._values[variable.name] = variable.current; + this._index[variable.name] = variable; } }; @@ -80,7 +80,7 @@ function (angular, _) { this.variableExists = function(expression) { this._regex.lastIndex = 0; var match = this._regex.exec(expression); - return match && (self._values[match[1] || match[2]] !== void 0); + return match && (self._index[match[1] || match[2]] !== void 0); }; this.containsVariable = function(str, variableName) { @@ -96,17 +96,24 @@ function (angular, _) { str = _.escape(str); this._regex.lastIndex = 0; return str.replace(this._regex, function(match, g1, g2) { - if (self._values[g1 || g2]) { + if (self._index[g1 || g2]) { return '' + match + ''; } return match; }); }; + this.getAllValue = function(variable) { + if (variable.allValue) { + return variable.allValue; + } + return _.pluck(variable.options, 'value'); + }; + this.replace = function(target, scopedVars, format) { if (!target) { return target; } - var value, systemValue; + var variable, systemValue, value; this._regex.lastIndex = 0; return target.replace(this._regex, function(match, g1, g2) { @@ -117,25 +124,34 @@ function (angular, _) { } } - value = self._values[g1 || g2]; - if (!value) { + variable = self._index[g1 || g2]; + if (!variable) { return match; } - systemValue = self._grafanaVariables[value.value]; + systemValue = self._grafanaVariables[variable.current.value]; if (systemValue) { return self.formatValue(systemValue); } - var res = self.formatValue(value.value, format); + value = variable.current.value; + if (self.isAllValue(value)) { + value = self.getAllValue(variable); + } + + var res = self.formatValue(value, format); return res; }); }; + this.isAllValue = function(value) { + return value === '$__all' || Array.isArray(value) && value[0] === '$__all'; + }; + this.replaceWithText = function(target, scopedVars) { if (!target) { return target; } - var value; + var variable; this._regex.lastIndex = 0; return target.replace(this._regex, function(match, g1, g2) { @@ -144,10 +160,10 @@ function (angular, _) { if (option) { return option.text; } } - value = self._values[g1 || g2]; - if (!value) { return match; } + variable = self._index[g1 || g2]; + if (!variable) { return match; } - return self._grafanaVariables[value.value] || value.text; + return self._grafanaVariables[variable.current.value] || variable.current.text; }); }; diff --git a/public/app/features/templating/templateValuesSrv.js b/public/app/features/templating/templateValuesSrv.js index b0c56812ad5..46d2f90bc03 100644 --- a/public/app/features/templating/templateValuesSrv.js +++ b/public/app/features/templating/templateValuesSrv.js @@ -231,12 +231,11 @@ function (angular, _, kbn) { this.addAllOption = function(variable) { if (variable.allValue) { - variable.options.unshift({text: 'All', value: variable.allValue, isAll: true}); + variable.options.unshift({text: 'All', value: variable.allValue}); return; } - var value =_.pluck(variable.options, 'text'); - variable.options.unshift({text: 'All', value: value, isAll: true}); + variable.options.unshift({text: 'All', value: "$__all"}); }; }); diff --git a/public/test/specs/templateSrv-specs.js b/public/test/specs/templateSrv-specs.js index 1e06d66a2a1..669dc14c8e4 100644 --- a/public/test/specs/templateSrv-specs.js +++ b/public/test/specs/templateSrv-specs.js @@ -66,6 +66,41 @@ define([ }); }); + describe('variable with all option', function() { + beforeEach(function() { + _templateSrv.init([{ + name: 'test', + current: {value: '$__all' }, + options: [ + {value: 'value1'}, {value: 'value2'} + ] + }]); + }); + + it('should replace $test with formatted all value', function() { + var target = _templateSrv.replace('this.$test.filters', {}, 'glob'); + expect(target).to.be('this.{value1,value2}.filters'); + }); + }); + + describe('variable with all option and custom value', function() { + beforeEach(function() { + _templateSrv.init([{ + name: 'test', + current: {value: '$__all' }, + allValue: '*', + options: [ + {value: 'value1'}, {value: 'value2'} + ] + }]); + }); + + it('should replace $test with formatted all value', function() { + var target = _templateSrv.replace('this.$test.filters', {}, 'glob'); + expect(target).to.be('this.*.filters'); + }); + }); + describe('lucene format', function() { it('should properly escape $test with lucene escape sequences', function() { _templateSrv.init([{name: 'test', current: {value: 'value/4' }}]); diff --git a/public/test/specs/templateValuesSrv-specs.js b/public/test/specs/templateValuesSrv-specs.js index 44e648816f2..0eaa9d1d99f 100644 --- a/public/test/specs/templateValuesSrv-specs.js +++ b/public/test/specs/templateValuesSrv-specs.js @@ -267,8 +267,7 @@ define([ it('should add All option', function() { expect(scenario.variable.options[0].text).to.be('All'); - expect(scenario.variable.options[0].value).to.eql(['backend1', 'backend2', 'backend3']); - expect(scenario.variable.options[0].isAll).to.be(true); + expect(scenario.variable.options[0].value).to.be('$__all'); }); }); From 2d9a2506b450ca05e91d3cd1e0bcb1247ba528e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 1 Mar 2016 13:50:24 +0100 Subject: [PATCH 08/12] ux(): fixed panel margin --- public/sass/pages/_dashboard.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index b04a9ffa084..5e853c80ae6 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -13,10 +13,6 @@ color: $variable; } -.grafana-row { - margin-bottom: 5px; -} - .row-tab { .dropdown-menu-right { top: 0; @@ -121,7 +117,7 @@ div.flot-text { } .panel-margin { - margin: 0 0.4rem 0.4rem 0.4rem; + margin: 0 0.4rem 0.8rem 0.4rem; display: block; } From 23a136d9ef040cd98a9ee96cb90dab471a6b40eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 1 Mar 2016 14:36:55 +0100 Subject: [PATCH 09/12] feat(templating): moved template interpolation into query building --- .../plugins/datasource/influxdb/datasource.ts | 4 +-- .../datasource/influxdb/influx_query.ts | 11 ++++-- .../plugins/datasource/influxdb/query_ctrl.ts | 2 +- .../influxdb/specs/influx_query_specs.ts | 35 ++++++++++--------- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index d6583477c94..56591078f93 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -34,7 +34,7 @@ export function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv) queryTargets.push(target); // build query - var queryModel = new InfluxQuery(target); + var queryModel = new InfluxQuery(target, templateSrv, options.scopedVars); var query = queryModel.render(); query = query.replace(/\$interval/g, (target.interval || options.interval)); return query; @@ -45,7 +45,7 @@ export function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv) allQueries = allQueries.replace(/\$timeFilter/g, timeFilter); // replace templated variables - allQueries = templateSrv.replace(allQueries, options.scopedVars, 'regex'); + allQueries = templateSrv.replace(allQueries, options.scopedVars); return this._seriesQuery(allQueries).then(function(data): any { if (!data || !data.results) { diff --git a/public/app/plugins/datasource/influxdb/influx_query.ts b/public/app/plugins/datasource/influxdb/influx_query.ts index cbdb61bca24..6770da0708e 100644 --- a/public/app/plugins/datasource/influxdb/influx_query.ts +++ b/public/app/plugins/datasource/influxdb/influx_query.ts @@ -8,9 +8,13 @@ export default class InfluxQuery { selectModels: any[]; queryBuilder: any; groupByParts: any; + templateSrv: any; + scopedVars: any; - constructor(target) { + constructor(target, templateSrv, scopedVars) { this.target = target; + this.templateSrv = templateSrv; + this.scopedVars = scopedVars; target.policy = target.policy || 'default'; target.dsType = 'influxdb'; @@ -135,7 +139,7 @@ export default class InfluxQuery { } if (!operator) { - if (/^\/.*\/$/.test(tag.value)) { + if (/^\/.*\/$/.test(value)) { operator = '=~'; } else { operator = '='; @@ -144,7 +148,10 @@ export default class InfluxQuery { // quote value unless regex if (operator !== '=~' && operator !== '!~') { + value = this.templateSrv.replace(value, this.scopedVars); value = "'" + value.replace('\\', '\\\\') + "'"; + } else { + value = this.templateSrv.replace(value, this.scopedVars, 'regex'); } return str + '"' + tag.key + '" ' + operator + ' ' + value; diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.ts b/public/app/plugins/datasource/influxdb/query_ctrl.ts index 2275bac559b..38dbcacae64 100644 --- a/public/app/plugins/datasource/influxdb/query_ctrl.ts +++ b/public/app/plugins/datasource/influxdb/query_ctrl.ts @@ -28,7 +28,7 @@ export class InfluxQueryCtrl extends QueryCtrl { super($scope, $injector); this.target = this.target; - this.queryModel = new InfluxQuery(this.target); + this.queryModel = new InfluxQuery(this.target, templateSrv, this.panel.scopedVars); this.queryBuilder = new InfluxQueryBuilder(this.target, this.datasource.database); this.groupBySegment = this.uiSegmentSrv.newPlusButton(); this.resultFormats = [ diff --git a/public/app/plugins/datasource/influxdb/specs/influx_query_specs.ts b/public/app/plugins/datasource/influxdb/specs/influx_query_specs.ts index 3893b8fbc2d..157b9ed206a 100644 --- a/public/app/plugins/datasource/influxdb/specs/influx_query_specs.ts +++ b/public/app/plugins/datasource/influxdb/specs/influx_query_specs.ts @@ -3,12 +3,13 @@ import {describe, beforeEach, it, sinon, expect} from 'test/lib/common'; import InfluxQuery from '../influx_query'; describe('InfluxQuery', function() { + var templateSrv = {replace: val => val}; describe('render series with mesurement only', function() { it('should generate correct query', function() { var query = new InfluxQuery({ measurement: 'cpu', - }); + }, templateSrv, {}); var queryText = query.render(); expect(queryText).to.be('SELECT mean("value") FROM "cpu" WHERE $timeFilter GROUP BY time($interval) fill(null)'); @@ -20,7 +21,7 @@ describe('InfluxQuery', function() { var query = new InfluxQuery({ measurement: 'cpu', policy: '5m_avg' - }); + }, templateSrv, {}); var queryText = query.render(); expect(queryText).to.be('SELECT mean("value") FROM "5m_avg"."cpu" WHERE $timeFilter GROUP BY time($interval) fill(null)'); @@ -39,7 +40,7 @@ describe('InfluxQuery', function() { {type: 'alias', params: ['text']}, ] ] - }); + }, templateSrv, {}); var queryText = query.render(); expect(queryText).to.be('SELECT mean("value") /100 AS "text" FROM "cpu" WHERE $timeFilter GROUP BY time($interval) fill(null)'); @@ -52,7 +53,7 @@ describe('InfluxQuery', function() { measurement: 'cpu', groupBy: [{type: 'time', params: ['auto']}], tags: [{key: 'hostname', value: 'server\\1'}] - }); + }, templateSrv, {}); var queryText = query.render(); @@ -65,7 +66,7 @@ describe('InfluxQuery', function() { measurement: 'cpu', groupBy: [{type: 'time', params: ['auto']}], tags: [{key: 'app', value: '/e.*/'}] - }); + }, templateSrv, {}); var queryText = query.render(); expect(queryText).to.be('SELECT mean("value") FROM "cpu" WHERE "app" =~ /e.*/ AND $timeFilter GROUP BY time($interval)'); @@ -78,7 +79,7 @@ describe('InfluxQuery', function() { measurement: 'cpu', groupBy: [{type: 'time', params: ['auto']}], tags: [{key: 'hostname', value: 'server1'}, {key: 'app', value: 'email', condition: "AND"}] - }); + }, templateSrv, {}); var queryText = query.render(); expect(queryText).to.be('SELECT mean("value") FROM "cpu" WHERE "hostname" = \'server1\' AND "app" = \'email\' AND ' + @@ -92,7 +93,7 @@ describe('InfluxQuery', function() { measurement: 'cpu', groupBy: [{type: 'time', params: ['auto']}], tags: [{key: 'hostname', value: 'server1'}, {key: 'hostname', value: 'server2', condition: "OR"}] - }); + }, templateSrv, {}); var queryText = query.render(); expect(queryText).to.be('SELECT mean("value") FROM "cpu" WHERE "hostname" = \'server1\' OR "hostname" = \'server2\' AND ' + @@ -106,7 +107,7 @@ describe('InfluxQuery', function() { measurement: 'cpu', tags: [], groupBy: [{type: 'time', interval: 'auto'}, {type: 'tag', params: ['host']}], - }); + }, templateSrv, {}); var queryText = query.render(); expect(queryText).to.be('SELECT mean("value") FROM "cpu" WHERE $timeFilter ' + @@ -120,7 +121,7 @@ describe('InfluxQuery', function() { measurement: 'cpu', select: [[{type: 'field', params: ['value']}]], groupBy: [], - }); + }, templateSrv, {}); var queryText = query.render(); expect(queryText).to.be('SELECT "value" FROM "cpu" WHERE $timeFilter'); }); @@ -132,7 +133,7 @@ describe('InfluxQuery', function() { measurement: 'cpu', select: [[{type: 'field', params: ['value']}]], groupBy: [{type: 'time'}, {type: 'fill', params: ['0']}], - }); + }, templateSrv, {}); var queryText = query.render(); expect(queryText).to.be('SELECT "value" FROM "cpu" WHERE $timeFilter GROUP BY time($interval) fill(0)'); }); @@ -144,7 +145,7 @@ describe('InfluxQuery', function() { var query = new InfluxQuery({ measurement: 'cpu', groupBy: [{type: 'time'}, {type: 'fill'}] - }); + }, templateSrv, {}); query.addGroupBy('tag(host)'); expect(query.target.groupBy.length).to.be(3); @@ -157,7 +158,7 @@ describe('InfluxQuery', function() { var query = new InfluxQuery({ measurement: 'cpu', groupBy: [] - }); + }, templateSrv, {}); query.addGroupBy('tag(host)'); expect(query.target.groupBy.length).to.be(1); @@ -172,7 +173,7 @@ describe('InfluxQuery', function() { var query = new InfluxQuery({ measurement: 'cpu', select: [[{type: 'field', params: ['value']}]] - }); + }, templateSrv, {}); query.addSelectPart(query.selectModels[0], 'mean'); expect(query.target.select[0].length).to.be(2); @@ -183,7 +184,7 @@ describe('InfluxQuery', function() { var query = new InfluxQuery({ measurement: 'cpu', select: [[{type: 'field', params: ['value']}, {type: 'mean'}]] - }); + }, templateSrv, {}); query.addSelectPart(query.selectModels[0], 'sum'); expect(query.target.select[0].length).to.be(2); @@ -194,7 +195,7 @@ describe('InfluxQuery', function() { var query = new InfluxQuery({ measurement: 'cpu', select: [[{type: 'field', params: ['value']}, {type: 'mean'}, {type: 'alias'}]] - }); + }, templateSrv, {}); query.addSelectPart(query.selectModels[0], 'math'); expect(query.target.select[0].length).to.be(4); @@ -205,7 +206,7 @@ describe('InfluxQuery', function() { var query = new InfluxQuery({ measurement: 'cpu', select: [[{type: 'field', params: ['value']}, {type: 'mean'}]] - }); + }, templateSrv, {}); query.addSelectPart(query.selectModels[0], 'math'); expect(query.target.select[0].length).to.be(3); @@ -216,7 +217,7 @@ describe('InfluxQuery', function() { var query = new InfluxQuery({ measurement: 'cpu', select: [[{type: 'field', params: ['value']}, {type: 'mean'}, {type: 'math'}]] - }); + }, templateSrv, {}); query.addSelectPart(query.selectModels[0], 'math'); expect(query.target.select[0].length).to.be(3); From d1bac6cde079c76959261097d846f6a46935aef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 1 Mar 2016 16:11:05 +0100 Subject: [PATCH 10/12] feat(templating): prometheus now works with new template variable formating/escaping, #2918 --- docker/blocks/prometheus/prometheus.yml | 2 +- public/app/features/templating/templateSrv.js | 2 +- public/app/plugins/datasource/prometheus/datasource.ts | 2 +- public/app/plugins/datasource/prometheus/plugin.json | 3 +-- public/test/specs/templateSrv-specs.js | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docker/blocks/prometheus/prometheus.yml b/docker/blocks/prometheus/prometheus.yml index b0fc2a919cd..5c853622af3 100644 --- a/docker/blocks/prometheus/prometheus.yml +++ b/docker/blocks/prometheus/prometheus.yml @@ -23,4 +23,4 @@ scrape_configs: # scheme defaults to 'http'. target_groups: - - targets: ['localhost:9090', '172.17.42.1:9091'] + - targets: ['localhost:9090', '172.17.0.1:9091'] diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js index 1f1153bda44..011b3150e2f 100644 --- a/public/app/features/templating/templateSrv.js +++ b/public/app/features/templating/templateSrv.js @@ -50,7 +50,7 @@ function (angular, _) { } var escapedValues = _.map(value, regexEscape); - return '(' + escapedValues.join('|') + ')'; + return escapedValues.join('|'); } case "lucene": { if (typeof value === 'string') { diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 6c4cbc216ae..55ce628d53d 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -52,7 +52,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS } var query: any = {}; - query.expr = templateSrv.replace(target.expr, options.scopedVars); + query.expr = templateSrv.replace(target.expr, options.scopedVars, 'regex'); var interval = target.interval || options.interval; var intervalFactor = target.intervalFactor || 1; diff --git a/public/app/plugins/datasource/prometheus/plugin.json b/public/app/plugins/datasource/prometheus/plugin.json index 1b9d569f6df..66dd9145e6a 100644 --- a/public/app/plugins/datasource/prometheus/plugin.json +++ b/public/app/plugins/datasource/prometheus/plugin.json @@ -4,6 +4,5 @@ "id": "prometheus", "metrics": true, - "annotations": true, - "defaultMatchFormat": "pipe" + "annotations": true } diff --git a/public/test/specs/templateSrv-specs.js b/public/test/specs/templateSrv-specs.js index 669dc14c8e4..d48cd04e7bf 100644 --- a/public/test/specs/templateSrv-specs.js +++ b/public/test/specs/templateSrv-specs.js @@ -127,7 +127,7 @@ define([ it('multi value and regex format should render regex string', function() { var result = _templateSrv.formatValue(['test.','test2'], 'regex'); - expect(result).to.be('(test\\.|test2)'); + expect(result).to.be('test\\.|test2'); }); it('multi value and pipe should render pipe string', function() { From 56bef637bd01ad9bc2c34ac875e4fadc8f8c1030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 1 Mar 2016 16:33:01 +0100 Subject: [PATCH 11/12] feat(templating): fixes for influxdb template variable formating, #2918 --- .../plugins/datasource/influxdb/datasource.ts | 2 +- .../datasource/influxdb/influx_query.ts | 22 +++++++++++-------- .../plugins/datasource/influxdb/query_ctrl.ts | 1 + 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 56591078f93..84c7a83394c 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -35,7 +35,7 @@ export function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv) // build query var queryModel = new InfluxQuery(target, templateSrv, options.scopedVars); - var query = queryModel.render(); + var query = queryModel.render(true); query = query.replace(/\$interval/g, (target.interval || options.interval)); return query; diff --git a/public/app/plugins/datasource/influxdb/influx_query.ts b/public/app/plugins/datasource/influxdb/influx_query.ts index 6770da0708e..72d08909ac4 100644 --- a/public/app/plugins/datasource/influxdb/influx_query.ts +++ b/public/app/plugins/datasource/influxdb/influx_query.ts @@ -11,7 +11,7 @@ export default class InfluxQuery { templateSrv: any; scopedVars: any; - constructor(target, templateSrv, scopedVars) { + constructor(target, templateSrv?, scopedVars?) { this.target = target; this.templateSrv = templateSrv; this.scopedVars = scopedVars; @@ -130,7 +130,7 @@ export default class InfluxQuery { this.updatePersistedParts(); } - private renderTagCondition(tag, index) { + private renderTagCondition(tag, index, interpolate) { var str = ""; var operator = tag.operator; var value = tag.value; @@ -148,9 +148,11 @@ export default class InfluxQuery { // quote value unless regex if (operator !== '=~' && operator !== '!~') { - value = this.templateSrv.replace(value, this.scopedVars); + if (interpolate) { + value = this.templateSrv.replace(value, this.scopedVars); + } value = "'" + value.replace('\\', '\\\\') + "'"; - } else { + } else if (interpolate){ value = this.templateSrv.replace(value, this.scopedVars, 'regex'); } @@ -174,11 +176,15 @@ export default class InfluxQuery { return policy + measurement; } - render() { + render(interpolate?) { var target = this.target; if (target.rawQuery) { - return target.query; + if (interpolate) { + return this.templateSrv.replace(target.query, this.scopedVars, 'regex'); + } else { + return target.query; + } } if (!target.measurement) { @@ -203,7 +209,7 @@ export default class InfluxQuery { query += ' FROM ' + this.getMeasurementAndPolicy() + ' WHERE '; var conditions = _.map(target.tags, (tag, index) => { - return this.renderTagCondition(tag, index); + return this.renderTagCondition(tag, index, interpolate); }); query += conditions.join(' '); @@ -227,8 +233,6 @@ export default class InfluxQuery { query += ' fill(' + target.fill + ')'; } - target.query = query; - return query; } } diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.ts b/public/app/plugins/datasource/influxdb/query_ctrl.ts index 38dbcacae64..d59bbbbccd5 100644 --- a/public/app/plugins/datasource/influxdb/query_ctrl.ts +++ b/public/app/plugins/datasource/influxdb/query_ctrl.ts @@ -154,6 +154,7 @@ export class InfluxQueryCtrl extends QueryCtrl { } toggleEditorMode() { + this.target.query = this.queryModel.render(false); this.target.rawQuery = !this.target.rawQuery; } From 57315f1e27081ce0b30d6bf0b1edaface4ceff1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 1 Mar 2016 19:12:54 +0100 Subject: [PATCH 12/12] feat(templating): updates to template variable forms --- .../features/templating/partials/editor.html | 63 ++++++++++--------- public/app/features/templating/templateSrv.js | 6 +- .../plugins/datasource/opentsdb/datasource.js | 4 +- public/test/specs/templateSrv-specs.js | 2 +- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/public/app/features/templating/partials/editor.html b/public/app/features/templating/partials/editor.html index 78132a1d04a..442ee0c9f2a 100644 --- a/public/app/features/templating/partials/editor.html +++ b/public/app/features/templating/partials/editor.html @@ -12,7 +12,7 @@
  • - {{current.name}} + Edit
  • @@ -108,35 +108,35 @@
    Value Options
    - Values - + Values +
    - - - Auto interval steps How many times should the current time range be divided to calculate the value - -
    - + Auto option + +
    +
    +
    + + Auto steps How many times should the current time range be divided to calculate the value + +
    + +
    +
    +
    + + Min interval The calculated value will not go below this threshold + +
    -
    -
    - - Auto interval min value The calculated value will not go below this threshold - -
    Values seperated by comma - -
    -
    - Include All option - - +
    @@ -152,11 +152,6 @@
    -
    - All value - - -
    Update @@ -165,13 +160,19 @@
    -
    Multi-value selection Enables multiple values to be selected at the same time
    +
    Selection Options
    + Multi-value - Multi format -
    - -
    + Enables multiple values to be selected at the same time +
    +
    + Include All option + +
    +
    + Custom all value +
    diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js index 011b3150e2f..0df887ca483 100644 --- a/public/app/features/templating/templateSrv.js +++ b/public/app/features/templating/templateSrv.js @@ -107,7 +107,11 @@ function (angular, _) { if (variable.allValue) { return variable.allValue; } - return _.pluck(variable.options, 'value'); + var values = []; + for (var i = 1; i < variable.options.length; i++) { + values.push(variable.options[i].value); + } + return values; }; this.replace = function(target, scopedVars, format) { diff --git a/public/app/plugins/datasource/opentsdb/datasource.js b/public/app/plugins/datasource/opentsdb/datasource.js index 14605151752..e9662f8f289 100644 --- a/public/app/plugins/datasource/opentsdb/datasource.js +++ b/public/app/plugins/datasource/opentsdb/datasource.js @@ -335,7 +335,7 @@ function (angular, _, dateMath) { query.tags = angular.copy(target.tags); if(query.tags){ for(var key in query.tags){ - query.tags[key] = templateSrv.replace(query.tags[key], options.scopedVars); + query.tags[key] = templateSrv.replace(query.tags[key], options.scopedVars, 'pipe'); } } } @@ -355,7 +355,7 @@ function (angular, _, dateMath) { } else { return target.metric === metricData.metric && _.all(target.tags, function(tagV, tagK) { - interpolatedTagValue = templateSrv.replace(tagV, options.scopedVars); + interpolatedTagValue = templateSrv.replace(tagV, options.scopedVars, 'pipe'); return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*"; }); } diff --git a/public/test/specs/templateSrv-specs.js b/public/test/specs/templateSrv-specs.js index d48cd04e7bf..65bd3f088ef 100644 --- a/public/test/specs/templateSrv-specs.js +++ b/public/test/specs/templateSrv-specs.js @@ -72,7 +72,7 @@ define([ name: 'test', current: {value: '$__all' }, options: [ - {value: 'value1'}, {value: 'value2'} + {value: '$__all'}, {value: 'value1'}, {value: 'value2'} ] }]); });