Commit 2cf1193b by Torkel Ödegaard

Merge branch 'variable-value-formatting-rethink'

parents d8f7bce9 57315f1e
......@@ -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,
......
......@@ -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']
......@@ -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",
......
......@@ -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;
}
......
......@@ -15,26 +15,24 @@
</form>
</div>
<h5 class="page-heading">
<h5 class="section-heading">
Migrate dashboards
<em style="font-size: 14px;padding-left: 10px;"><i class="fa fa-info-circle"></i> Import dashboards from Elasticsearch or InfluxDB</em>
</h5>
<div class="gf-form-group last">
<div class="gf-form-inline gf-form-group">
<div class="gf-form">
<div class="gf-form-label">Dashboard source</div>
<div>
<div class="gf-form-select-wrapper">
<select class="gf-form-input gf-size-auto" ng-model="sourceName" ng-options="f for f in datasources"></select>
</div>
</div>
<div class="gf-form-btn">
<button class="btn btn-success" ng-click="startImport()">Import</button>
<div class="gf-form-select-wrapper">
<select class="gf-form-input gf-size-auto" ng-model="sourceName" ng-options="f for f in datasources"></select>
</div>
</div>
<div class="gf-form">
<button class="btn btn-success gf-form-btn" ng-click="startImport()">Import</button>
</div>
</div>
<h5 class="page-heading" ng-if="importing">{{infoText}}</h5>
<h5 class="section-heading" ng-if="importing">{{infoText}}</h5>
<div class="editor-row" ng-if="importing">
<div class="editor-row row">
<table class="grafana-options-table span5">
......
......@@ -136,22 +136,23 @@
<button class="btn btn-inverse btn-large" data-clipboard-text="{{snapshotUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy Link</button>
</div>
</div>
</div>
<div ng-if="step === 1" class="gf-form-buttons-row">
<button class="btn btn-success btn-large" ng-click="createSnapshot()" ng-disabled="loading">
<i class="fa fa-save"></i>
Local Snapshot
</button>
<button class="btn btn-primary btn-large" ng-if="externalEnabled" ng-click="createSnapshot(true)" ng-disabled="loading">
<i class="fa fa-cloud-upload"></i>
{{sharingButtonText}}
</button>
</div>
<div class="pull-right" ng-if="step === 2" style="padding: 5px">
Did you make a mistake? <a class="pointer" ng-click="deleteSnapshot()" target="_blank">delete snapshot.</a>
</div>
<div ng-if="step === 1" class="gf-form-buttons-row">
<button class="btn btn-success btn-large" ng-click="createSnapshot()" ng-disabled="loading">
<i class="fa fa-save"></i>
Local Snapshot
</button>
<button class="btn btn-primary btn-large" ng-if="externalEnabled" ng-click="createSnapshot(true)" ng-disabled="loading">
<i class="fa fa-cloud-upload"></i>
{{sharingButtonText}}
</button>
</div>
<div class="pull-right" ng-if="step === 2" style="padding: 5px">
Did you make a mistake? <a class="pointer" ng-click="deleteSnapshot()" target="_blank">delete snapshot.</a>
</div>
</div>
</div>
</script>
......@@ -12,7 +12,7 @@
</li>
<li class="gf-tabs-item" ng-show="mode === 'edit'">
<a class="gf-tabs-link" ng-class="{active: mode === 'edit'}">
{{current.name}}
Edit
</a>
</li>
<li class="gf-tabs-item" ng-show="mode === 'new'">
......@@ -79,7 +79,7 @@
<input type="text" class="gf-form-input max-width-14" placeholder="name" ng-model='current.name'></input>
</div>
<div class="gf-form">
<span class="gf-form-label width-7">Type</span>
<span class="gf-form-label width-4">Type</span>
<div class="gf-form-select-wrapper max-width-10">
<select class="gf-form-input max-width-10" ng-model="current.type" ng-options="f for f in ['query', 'interval', 'custom']" ng-change="typeChanged()"></select>
</div>
......@@ -97,8 +97,9 @@
<input type="text" class="gf-form-input max-width-14" ng-model='current.label' placeholder="optional display name"></input>
</div>
<div class="gf-form">
<editor-checkbox class="width-10" text="Hide label" model="current.hideLabel" change="runQuery()"></editor-checkbox>
<editor-checkbox class="width-11" text="Hide variable" model="current.hideVariable" change="runQuery()"></editor-checkbox>
<span class="gf-form-label width-4">Hide</span>
<editor-checkbox text="Label" model="current.hideLabel" change="runQuery()"></editor-checkbox>
<editor-checkbox text="Variable" model="current.hideVariable" change="runQuery()"></editor-checkbox>
</div>
</div>
......@@ -107,40 +108,35 @@
<h5 class="section-heading">Value Options</h5>
<div ng-show="current.type === 'interval'" class="gf-form-group">
<div class="gf-form">
<span class="gf-form-label width-7">Values</span>
<input type="text" class="gf-form-input max-width-28" placeholder="name" ng-model='current.query' placeholder="1m,10m,1h,6h,1d,7d" ng-model-onblur ng-change="runQuery()"></input>
<span class="gf-form-label width-9">Values</span>
<input type="text" class="gf-form-input" placeholder="name" ng-model='current.query' placeholder="1m,10m,1h,6h,1d,7d" ng-model-onblur ng-change="runQuery()"></input>
</div>
<div class="gf-form">
<editor-checkbox text="Include auto interval" model="current.auto" change="runQuery()"></editor-checkbox>
<span class="gf-form-label" ng-show="current.auto">
Auto interval steps <tip>How many times should the current time range be divided to calculate the value</tip>
</span>
<div class="gf-form-select-wrapper max-width-10" ng-show="current.auto">
<select class="gf-form-input" ng-model="current.auto_count" ng-options="f for f in [2,3,4,5,10,20,30,40,50,100,200,300,400,500]" ng-change="runQuery()"></select>
</div>
<span class="gf-form-label width-9">Auto option</span>
<editor-checkbox text="Enable" model="current.auto" change="runQuery()"></editor-checkbox>
</div>
<div class="gf-form">
<span class="gf-form-label" ng-show="current.auto">
Auto interval min value <tip>The calculated value will not go below this threshold</tip>
</span>
<input type="text" class="gf-form-input max-width-10" ng-show="current.auto" ng-model="current.auto_min" ng-change="runQuery()"></input>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-9" ng-show="current.auto">
Auto steps <tip>How many times should the current time range be divided to calculate the value</tip>
</span>
<div class="gf-form-select-wrapper max-width-10" ng-show="current.auto">
<select class="gf-form-input" ng-model="current.auto_count" ng-options="f for f in [2,3,4,5,10,20,30,40,50,100,200,300,400,500]" ng-change="runQuery()"></select>
</div>
</div>
<div class="gf-form">
<span class="gf-form-label" ng-show="current.auto">
Min interval <tip>The calculated value will not go below this threshold</tip>
</span>
<input type="text" class="gf-form-input max-width-10" ng-show="current.auto" ng-model="current.auto_min" ng-change="runQuery()" placeholder="10s"></input>
</div>
</div>
</div>
<div ng-show="current.type === 'custom'" class="gf-form-group">
<div class="gf-form">
<span class="gf-form-label width-13">Values seperated by comma</span>
<input type="text" class="gf-form-input max-width-22" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue"></input>
</div>
<div class="gf-form ">
<editor-checkbox class="width-13" text="All value" model="current.includeAll" change="runQuery()"></editor-checkbox>
<input ng-show="current.includeAll" type="text" class="gf-form-input max-width-22" ng-model='current.options[0].value' style="margin-left: 4px;"></input>
</div>
<div class="gf-form">
<span class="gf-form-label width-13" ng-show="current.includeAll">All format</span>
<div class="gf-form-select-wrapper max-width-10" ng-show="current.includeAll">
<select class="gf-form-input" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
</div>
<input type="text" class="gf-form-input" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue"></input>
</div>
</div>
......@@ -157,22 +153,6 @@
<input type="text" class="gf-form-input" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
</div>
<div class="gf-form">
<span class="gf-form-label width-7">All value</span>
<editor-checkbox class="width-13" text="Enable" model="current.includeAll" change="runQuery()"></editor-checkbox>
</div>
<div class="gf-form-inline" ng-show="current.includeAll">
<div class="gf-form">
<span class="gf-form-label width-7">All format</span>
<div class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
</div>
</div>
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">All value</span>
<input type="text" class="gf-form-input" ng-model='current.options[0].value'></input>
</div>
</div>
<div class="gf-form">
<span class="gf-form-label width-7">Update</span>
<editor-checkbox text="On Dashboard Load" model="current.refresh"></editor-checkbox>
<tip>Check if you want values to be updated on dashboard load, will slow down dashboard load time</tip>
......@@ -180,13 +160,19 @@
</div>
<div class="gf-form-group" >
<h5 class="section-heading">Multi-value selection <tip>Enables multiple values to be selected at the same time</tip></h5>
<h5 class="section-heading">Selection Options</h5>
<div class="gf-form">
<span class="gf-form-label width-10">Multi-value</span>
<editor-checkbox text="Enable" model="current.multi" change="runQuery()"></editor-checkbox>
<span class="gf-form-label" ng-show="current.multi">Multi format</span>
<div class="gf-form-select-wrapper max-width-10" ng-show="current.multi">
<select class="gf-form-input" ng-model="current.multiFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'regex values', 'lucene', 'pipe']"></select>
</div>
<tip>Enables multiple values to be selected at the same time</tip>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Include All option</span>
<editor-checkbox class="width-13" text="Enable" model="current.includeAll" change="runQuery()"></editor-checkbox>
</div>
<div class="gf-form" ng-if="current.includeAll">
<span class="gf-form-label width-10">Custom all value</span>
<input type="text" class="gf-form-input max-width-15" g-model='current.allValue' placeholder="blank = auto"></input>
</div>
</div>
......
......@@ -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('|') + ')';
}
case "lucene": {
var quotedValues = _.map(value, function(val) {
return '\\\"' + val + '\\\"';
});
return '(' + quotedValues.join(' OR ') + ')';
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 "pipe": {
return value.join('|');
var escapedValues = _.map(value, regexEscape);
return escapedValues.join('|');
}
case "lucene": {
if (typeof value === 'string') {
return luceneEscape(value);
}
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 '<span class="template-variable">' + match + '</span>';
}
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;
});
};
......
......@@ -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"});
};
});
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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();
......
......@@ -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;
......
......@@ -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;
}
}
......@@ -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;
}
......
......@@ -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);
......
......@@ -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 === "*";
});
}
......
......@@ -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;
......
......@@ -4,6 +4,5 @@
"id": "prometheus",
"metrics": true,
"annotations": true,
"defaultMatchFormat": "pipe"
"annotations": true
}
......@@ -131,7 +131,6 @@ mark,
// Unordered and Ordered lists
ul, ol {
padding: 0;
padding-left: $spacer;
}
ul ul,
ul ol,
......
......@@ -16,5 +16,8 @@
h5 {
margin-top: 5px;
}
ul {
padding-left: $spacer;
}
}
......@@ -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;
}
......
......@@ -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,
......
......@@ -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([
});
});
});
});
......@@ -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');
});
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment