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/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/package.json b/package.json index 73312eed646..a0b883fb463 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/core/directives/value_select_dropdown.js b/public/app/core/directives/value_select_dropdown.js index 656804a72d3..60d257d6fa8 100644 --- a/public/app/core/directives/value_select_dropdown.js +++ b/public/app/core/directives/value_select_dropdown.js @@ -179,8 +179,7 @@ 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) { + if (!vm.variable.multi) { vm.variable.current.value = vm.selectedValues[0].value; } 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/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/partials/editor.html b/public/app/features/templating/partials/editor.html index aab5f048465..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
  • @@ -79,7 +79,7 @@
    - Type + Type
    @@ -97,8 +97,9 @@
    - - + Hide + +
    @@ -107,40 +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 - -
    -
    - - -
    -
    - All format -
    - -
    +
    @@ -156,22 +152,6 @@
    -
    - All value - -
    -
    -
    - All format -
    - -
    -
    -
    - All value - -
    -
    Update @@ -180,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 267454dbecb..0df887ca483 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,38 +23,52 @@ function (angular, _) { }; this.updateTemplateData = function() { - this._values = {}; - this._texts = {}; + this._index = {}; - _.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); + 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._index[variable.name] = variable; + } }; - this.renderVariableValue = function(variable) { - var value = variable.current.value; - if (_.isString(value)) { - return value; - } else { - switch(variable.multiFormat) { - case "regex values": { - return '(' + value.join('|') + ')'; + function regexEscape(value) { + return value.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&'); + } + + function luceneEscape(value) { + return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, "\\$1"); + } + + this.formatValue = function(value, format) { + switch(format) { + case "regex": { + if (typeof value === 'string') { + return regexEscape(value); } - case "lucene": { - var quotedValues = _.map(value, function(val) { - return '\\\"' + val + '\\\"'; - }); - return '(' + quotedValues.join(' OR ') + ')'; + + var escapedValues = _.map(value, regexEscape); + return escapedValues.join('|'); + } + case "lucene": { + if (typeof value === 'string') { + return luceneEscape(value); } - 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(',') + '}'; } } }; @@ -66,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) { @@ -82,37 +96,66 @@ 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.replace = function(target, scopedVars) { + this.getAllValue = function(variable) { + if (variable.allValue) { + return variable.allValue; + } + 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) { if (!target) { return target; } - var value; + var variable, systemValue, value; 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; } + variable = self._index[g1 || g2]; + if (!variable) { + return match; + } - return self._grafanaVariables[value] || value; + systemValue = self._grafanaVariables[variable.current.value]; + if (systemValue) { + return self.formatValue(systemValue); + } + + 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 text; + var variable; this._regex.lastIndex = 0; return target.replace(this._regex, function(match, g1, g2) { @@ -121,11 +164,10 @@ function (angular, _) { if (option) { return option.text; } } - value = self._values[g1 || g2]; - text = self._texts[g1 || g2]; - if (!value) { return match; } + variable = self._index[g1 || g2]; + if (!variable) { return match; } - return self._grafanaVariables[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 2760bb42ca0..46d2f90bc03 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) { @@ -226,60 +225,17 @@ 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.regexEscape = function(value) { - return value.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&'); - }; - 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}); + return; } - variable.options.unshift({text: 'All', value: allValue}); + variable.options.unshift({text: 'All', value: "$__all"}); }; }); diff --git a/public/app/plugins/datasource/elasticsearch/datasource.js b/public/app/plugins/datasource/elasticsearch/datasource.js index 294f666abc7..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); 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/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/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index fec1dd71d6c..84c7a83394c 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -34,8 +34,8 @@ export function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv) queryTargets.push(target); // build query - var queryModel = new InfluxQuery(target); - var query = queryModel.render(); + var queryModel = new InfluxQuery(target, templateSrv, options.scopedVars); + 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 cbdb61bca24..72d08909ac4 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'; @@ -126,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; @@ -135,7 +139,7 @@ export default class InfluxQuery { } if (!operator) { - if (/^\/.*\/$/.test(tag.value)) { + if (/^\/.*\/$/.test(value)) { operator = '=~'; } else { operator = '='; @@ -144,7 +148,12 @@ export default class InfluxQuery { // quote value unless regex if (operator !== '=~' && operator !== '!~') { + if (interpolate) { + value = this.templateSrv.replace(value, this.scopedVars); + } value = "'" + value.replace('\\', '\\\\') + "'"; + } else if (interpolate){ + value = this.templateSrv.replace(value, this.scopedVars, 'regex'); } return str + '"' + tag.key + '" ' + operator + ' ' + value; @@ -167,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) { @@ -196,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(' '); @@ -220,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 2275bac559b..d59bbbbccd5 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 = [ @@ -154,6 +154,7 @@ export class InfluxQueryCtrl extends QueryCtrl { } toggleEditorMode() { + this.target.query = this.queryModel.render(false); this.target.rawQuery = !this.target.rawQuery; } 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); 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/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/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; + } } 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; } 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 2b811244210..65bd3f088ef 100644 --- a/public/test/specs/templateSrv-specs.js +++ b/public/test/specs/templateSrv-specs.js @@ -45,49 +45,93 @@ define([ }); }); - describe('render variable to string values', function() { + describe('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'); + }); + + it('should replace $test with piped value', function() { + var target = _templateSrv.replace('this=$test', {}, 'pipe'); + expect(target).to.be('this=value1|value2'); + }); + }); + + describe('variable with all option', function() { + beforeEach(function() { + _templateSrv.init([{ + name: 'test', + current: {value: '$__all' }, + options: [ + {value: '$__all'}, {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' }}]); + var target = _templateSrv.replace('this:$test', {}, 'lucene'); + expect(target).to.be("this:value\\\/4"); + }); + }); + + 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'); }); @@ -194,7 +238,6 @@ define([ }); }); - }); }); diff --git a/public/test/specs/templateValuesSrv-specs.js b/public/test/specs/templateValuesSrv-specs.js index e1e6fc347cb..0eaa9d1d99f 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,29 @@ 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.be('$__all'); }); }); - 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'); - }); - }); - }); });