mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 09:33:34 -06:00
This commit is contained in:
parent
141ea7ba91
commit
2da04e72f5
@ -11,8 +11,17 @@ function (angular) {
|
||||
module.controller('InfluxTargetCtrl', function($scope, $timeout) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.target.function = $scope.target.function || 'mean';
|
||||
$scope.target.column = $scope.target.column || 'value';
|
||||
var target = $scope.target;
|
||||
|
||||
target.function = target.function || 'mean';
|
||||
target.column = target.column || 'value';
|
||||
|
||||
if (target.condition_value) {
|
||||
target.condition_expression = target.condition_key + ' ' + target.condition_op + ' ' + target.condition_value;
|
||||
delete target.condition_key;
|
||||
delete target.condition_op;
|
||||
delete target.condition_value;
|
||||
}
|
||||
|
||||
$scope.rawQuery = false;
|
||||
|
||||
@ -24,7 +33,7 @@ function (angular) {
|
||||
];
|
||||
|
||||
$scope.operators = ['=', '=~', '>', '<', '!~', '<>'];
|
||||
$scope.oldSeries = $scope.target.series;
|
||||
$scope.oldSeries = target.series;
|
||||
$scope.$on('typeahead-updated', function() {
|
||||
$timeout($scope.get_data);
|
||||
});
|
||||
|
@ -11,7 +11,7 @@ function (angular, app, _, $) {
|
||||
.module('grafana.directives')
|
||||
.directive('templateParamSelector', function($compile) {
|
||||
var inputTemplate = '<input type="text" data-provide="typeahead" ' +
|
||||
' class="grafana-target-segment-input input-medium"' +
|
||||
' class="grafana-target-text-input input-medium"' +
|
||||
' spellcheck="false" style="display:none"></input>';
|
||||
|
||||
var buttonTemplate = '<a class="grafana-target-segment tabindex="1">{{variable.current.text}}</a>';
|
||||
|
@ -1,8 +1,8 @@
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>InfluxDB Query <tip>Example: select text from events where [[timeFilter]]</tip></h5>
|
||||
<h5>InfluxDB Query <tip>Example: select text from events where [[$timeFilter]]</tip></h5>
|
||||
<div class="editor-option">
|
||||
<input type="text" class="span10" ng-model='currentAnnotation.query' placeholder="select text from events where [[timeFilter]]"></input>
|
||||
<input type="text" class="span10" ng-model='currentAnnotation.query' placeholder="select text from events where [[$timeFilter]]"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -72,6 +72,16 @@
|
||||
data-min-length=0 data-items=100
|
||||
ng-blur="seriesBlur()">
|
||||
</li>
|
||||
|
||||
<li class="grafana-target-segment">
|
||||
alias
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input type="text" class="input-medium grafana-target-text-input" ng-model="target.alias"
|
||||
spellcheck='false' placeholder="alias" ng-blur="get_data()">
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
@ -102,16 +112,6 @@
|
||||
<span influxdb-func-editor class="grafana-target-segment grafana-target-function">
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="grafana-target-segment">
|
||||
alias
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input type="text" class="input-medium grafana-target-segment-input" ng-model="target.alias"
|
||||
spellcheck='false' placeholder="alias" ng-blur="get_data()">
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="grafana-target-segment" ng-click="target.condition_filter = !target.condition_filter; get_data();"
|
||||
bs-tooltip="'Add a where clause'" role="menuitem" data-placement="right">
|
||||
@ -129,7 +129,7 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input type="text" class="input-mini grafana-target-segment-input" ng-model="target.interval"
|
||||
<input type="text" class="input-mini grafana-target-text-input" ng-model="target.interval"
|
||||
spellcheck='false' placeholder="{{interval}}" data-placement="right"
|
||||
bs-tooltip="'Leave blank for auto handling based on time range and panel width'"
|
||||
ng-model-onblur ng-change="get_data()" >
|
||||
@ -142,7 +142,7 @@
|
||||
</li>
|
||||
|
||||
<li ng-show="target.groupby_field_add">
|
||||
<input type="text" class="input-small grafana-target-segment-input" ng-model="target.groupby_field"
|
||||
<input type="text" class="input-small grafana-target-text-input" ng-model="target.groupby_field"
|
||||
placeholder="column" spellcheck="false" bs-typeahead="listColumns" data-min-length=0 ng-blur="get_data()">
|
||||
</li>
|
||||
|
||||
|
@ -101,7 +101,7 @@
|
||||
</div>
|
||||
<div class="editor-option" ng-show="current.includeAll">
|
||||
<label class="small">All format</label>
|
||||
<select class="input-medium" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex all values', 'comma list', 'custom']" ng-change="typeChanged()"></select>
|
||||
<select class="input-medium" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex all values', 'comma list', 'custom']" ng-change="typeChanged()"></select>
|
||||
</div>
|
||||
<div class="editor-option" ng-show="current.includeAll">
|
||||
<label class="small">All value</label>
|
||||
|
81
src/app/services/influxdb/influxQueryBuilder.js
Normal file
81
src/app/services/influxdb/influxQueryBuilder.js
Normal file
@ -0,0 +1,81 @@
|
||||
define([
|
||||
],
|
||||
function () {
|
||||
'use strict';
|
||||
|
||||
function InfluxQueryBuilder(target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
var p = InfluxQueryBuilder.prototype;
|
||||
|
||||
p.build = function() {
|
||||
return this.target.rawQuery ? this._modifyRawQuery() : this._buildQuery();
|
||||
};
|
||||
|
||||
p._buildQuery = function() {
|
||||
var target = this.target;
|
||||
var query = 'select ';
|
||||
var seriesName = target.series;
|
||||
|
||||
if(!seriesName.match('^/.*/')) {
|
||||
seriesName = '"' + seriesName+ '"';
|
||||
}
|
||||
|
||||
if (target.groupby_field_add) {
|
||||
query += target.groupby_field + ', ';
|
||||
}
|
||||
|
||||
query += target.function + '(' + target.column + ')';
|
||||
query += ' from ' + seriesName + ' where [[$timeFilter]]';
|
||||
|
||||
if (target.condition_filter) {
|
||||
query += ' and ' + target.condition_expression;
|
||||
}
|
||||
|
||||
query += ' group by time([[$interval]])';
|
||||
|
||||
if (target.groupby_field_add) {
|
||||
query += ', ' + target.groupby_field;
|
||||
this.groupByField = target.groupby_field;
|
||||
}
|
||||
|
||||
query += " order asc";
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
p._modifyRawQuery = function () {
|
||||
var query = this.target.query.replace(";", "");
|
||||
|
||||
var queryElements = query.split(" ");
|
||||
var lowerCaseQueryElements = query.toLowerCase().split(" ");
|
||||
var whereIndex = lowerCaseQueryElements.indexOf("where");
|
||||
var groupByIndex = lowerCaseQueryElements.indexOf("group");
|
||||
var orderIndex = lowerCaseQueryElements.indexOf("order");
|
||||
|
||||
if (lowerCaseQueryElements[1].indexOf(',') !== -1) {
|
||||
this.groupByField = lowerCaseQueryElements[1].replace(',', '');
|
||||
}
|
||||
|
||||
if (whereIndex !== -1) {
|
||||
queryElements.splice(whereIndex + 1, 0, '[[$timeFilter]]', "and");
|
||||
}
|
||||
else {
|
||||
if (groupByIndex !== -1) {
|
||||
queryElements.splice(groupByIndex, 0, "where", '[[$timeFilter]]');
|
||||
}
|
||||
else if (orderIndex !== -1) {
|
||||
queryElements.splice(orderIndex, 0, "where", '[[$timeFilter]]');
|
||||
}
|
||||
else {
|
||||
queryElements.push("where");
|
||||
queryElements.push('[[$timeFilter]]');
|
||||
}
|
||||
}
|
||||
|
||||
return queryElements.join(" ");
|
||||
};
|
||||
|
||||
return InfluxQueryBuilder;
|
||||
});
|
@ -2,9 +2,10 @@ define([
|
||||
'angular',
|
||||
'lodash',
|
||||
'kbn',
|
||||
'./influxSeries'
|
||||
'./influxSeries',
|
||||
'./influxQueryBuilder'
|
||||
],
|
||||
function (angular, _, kbn, InfluxSeries) {
|
||||
function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.services');
|
||||
@ -32,90 +33,25 @@ function (angular, _, kbn, InfluxSeries) {
|
||||
}
|
||||
|
||||
InfluxDatasource.prototype.query = function(options) {
|
||||
var promises = _.map(options.targets, function(target) {
|
||||
var query;
|
||||
var alias = '';
|
||||
var timeFilter = getTimeFilter(options);
|
||||
|
||||
var promises = _.map(options.targets, function(target) {
|
||||
if (target.hide || !((target.series && target.column) || target.query)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var timeFilter = getTimeFilter(options);
|
||||
var groupByField;
|
||||
// build query
|
||||
var queryBuilder = new InfluxQueryBuilder(target);
|
||||
var query = queryBuilder.build();
|
||||
|
||||
if (target.rawQuery) {
|
||||
query = target.query;
|
||||
query = query.replace(";", "");
|
||||
var queryElements = query.split(" ");
|
||||
var lowerCaseQueryElements = query.toLowerCase().split(" ");
|
||||
var whereIndex = lowerCaseQueryElements.indexOf("where");
|
||||
var groupByIndex = lowerCaseQueryElements.indexOf("group");
|
||||
var orderIndex = lowerCaseQueryElements.indexOf("order");
|
||||
// replace templated variables
|
||||
templateSrv.setGrafanaVariable('$timeFilter', timeFilter);
|
||||
templateSrv.setGrafanaVariable('$interval', (target.interval || options.interval));
|
||||
query = templateSrv.replace(query);
|
||||
|
||||
if (lowerCaseQueryElements[1].indexOf(',') !== -1) {
|
||||
groupByField = lowerCaseQueryElements[1].replace(',', '');
|
||||
}
|
||||
var alias = target.alias ? templateSrv.replace(target.alias) : '';
|
||||
|
||||
if (whereIndex !== -1) {
|
||||
queryElements.splice(whereIndex + 1, 0, timeFilter, "and");
|
||||
}
|
||||
else {
|
||||
if (groupByIndex !== -1) {
|
||||
queryElements.splice(groupByIndex, 0, "where", timeFilter);
|
||||
}
|
||||
else if (orderIndex !== -1) {
|
||||
queryElements.splice(orderIndex, 0, "where", timeFilter);
|
||||
}
|
||||
else {
|
||||
queryElements.push("where");
|
||||
queryElements.push(timeFilter);
|
||||
}
|
||||
}
|
||||
|
||||
query = queryElements.join(" ");
|
||||
query = templateSrv.replace(query);
|
||||
}
|
||||
else {
|
||||
query = 'select ';
|
||||
var seriesName = target.series;
|
||||
|
||||
if(!seriesName.match('^/.*/')) {
|
||||
seriesName = '"' + seriesName+ '"';
|
||||
}
|
||||
|
||||
if (target.groupby_field_add) {
|
||||
query += target.groupby_field + ', ';
|
||||
}
|
||||
|
||||
query += target.function + '(' + target.column + ')';
|
||||
query += ' from ' + seriesName + ' where ' + timeFilter;
|
||||
|
||||
if (target.condition_filter) {
|
||||
query += ' and ' + target.condition_expression;
|
||||
}
|
||||
|
||||
query += ' group by time(' + (target.interval || options.interval) + ')';
|
||||
|
||||
if (target.groupby_field_add) {
|
||||
query += ',' + target.groupby_field;
|
||||
}
|
||||
|
||||
query += " order asc";
|
||||
|
||||
query = templateSrv.replace(query);
|
||||
|
||||
if (target.groupby_field_add) {
|
||||
groupByField = target.groupby_field;
|
||||
}
|
||||
|
||||
target.query = query;
|
||||
}
|
||||
|
||||
if (target.alias) {
|
||||
alias = templateSrv.replace(target.alias);
|
||||
}
|
||||
|
||||
var handleResponse = _.partial(handleInfluxQueryResponse, alias, groupByField);
|
||||
var handleResponse = _.partial(handleInfluxQueryResponse, alias, queryBuilder.groupByField);
|
||||
return this._seriesQuery(query).then(handleResponse);
|
||||
|
||||
}, this);
|
||||
@ -123,12 +59,11 @@ function (angular, _, kbn, InfluxSeries) {
|
||||
return $q.all(promises).then(function(results) {
|
||||
return { data: _.flatten(results) };
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
InfluxDatasource.prototype.annotationQuery = function(annotation, rangeUnparsed) {
|
||||
var timeFilter = getTimeFilter({ range: rangeUnparsed });
|
||||
var query = _.template(annotation.query, { timeFilter: timeFilter }, this.templateSettings);
|
||||
var query = _.template(annotation.query, { timeFilter: timeFilter, "$timeFilter": timeFilter }, this.templateSettings);
|
||||
|
||||
return this._seriesQuery(query).then(function(results) {
|
||||
return new InfluxSeries({ seriesList: results, annotation: annotation }).getAnnotations();
|
||||
|
@ -34,6 +34,10 @@ function (angular, _) {
|
||||
this._templateData = _templateData;
|
||||
};
|
||||
|
||||
this.setGrafanaVariable = function(name, value) {
|
||||
this._templateData[name] = value;
|
||||
};
|
||||
|
||||
this.replace = function(target) {
|
||||
if (!target || target.indexOf('[[') === -1) {
|
||||
return target;
|
||||
|
@ -84,7 +84,7 @@ function (angular, _, kbn) {
|
||||
|
||||
this.metricNamesToVariableValues = function(variable, metricNames) {
|
||||
var regex, options, i, matches;
|
||||
options = [];
|
||||
options = {}; // use object hash to remove duplicates
|
||||
|
||||
if (variable.regex) {
|
||||
regex = kbn.stringToJsRegex(variable.regex);
|
||||
@ -101,10 +101,12 @@ function (angular, _, kbn) {
|
||||
}
|
||||
}
|
||||
|
||||
options.push({text: value, value: value});
|
||||
options[value] = value;
|
||||
}
|
||||
|
||||
return options;
|
||||
return _.map(_.keys(options), function(key) {
|
||||
return { text: key, value: key };
|
||||
});
|
||||
};
|
||||
|
||||
this.addAllOption = function(variable) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
define([
|
||||
'kbn'
|
||||
], function(kbn) {
|
||||
'kbn',
|
||||
'lodash'
|
||||
], function(kbn, _) {
|
||||
'use strict';
|
||||
|
||||
function ControllerTestContext() {
|
||||
@ -47,10 +48,17 @@ define([
|
||||
|
||||
function ServiceTestContext() {
|
||||
var self = this;
|
||||
self.templateSrv = new TemplateSrvStub();
|
||||
|
||||
this.providePhase = function() {
|
||||
return module(function($provide) {
|
||||
$provide.value('templateSrv', self.templateSrv);
|
||||
});
|
||||
};
|
||||
|
||||
this.createService = function(name) {
|
||||
return inject([name, '$q', '$rootScope', '$httpBackend', function(InfluxDatasource, $q, $rootScope, $httpBackend) {
|
||||
self.service = InfluxDatasource;
|
||||
return inject([name, '$q', '$rootScope', '$httpBackend', function(service, $q, $rootScope, $httpBackend) {
|
||||
self.service = service;
|
||||
self.$q = $q;
|
||||
self.$rootScope = $rootScope;
|
||||
self.$httpBackend = $httpBackend;
|
||||
@ -82,11 +90,16 @@ define([
|
||||
|
||||
function TemplateSrvStub() {
|
||||
this.variables = [];
|
||||
this.replace = function() {};
|
||||
this.templateSettings = { interpolate : /\[\[([\s\S]+?)\]\]/g };
|
||||
this.data = {};
|
||||
this.replace = function(text) {
|
||||
return _.template(text, this.data, this.templateSettings);
|
||||
};
|
||||
this.setGrafanaVariable = function(name, value) {
|
||||
this.data[name] = value;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
return {
|
||||
ControllerTestContext: ControllerTestContext,
|
||||
TimeSrvStub: TimeSrvStub,
|
||||
|
54
src/test/specs/influxQueryBuilder-specs.js
Normal file
54
src/test/specs/influxQueryBuilder-specs.js
Normal file
@ -0,0 +1,54 @@
|
||||
define([
|
||||
'services/influxdb/influxQueryBuilder'
|
||||
], function(InfluxQueryBuilder) {
|
||||
'use strict';
|
||||
|
||||
describe('InfluxQueryBuilder', function() {
|
||||
|
||||
describe('series with conditon and group by', function() {
|
||||
var builder = new InfluxQueryBuilder({
|
||||
series: 'google.test',
|
||||
column: 'value',
|
||||
function: 'mean',
|
||||
condition_filter: true,
|
||||
condition_expression: "code=1",
|
||||
groupby_field_add: true,
|
||||
groupby_field: 'code'
|
||||
});
|
||||
|
||||
var query = builder.build();
|
||||
|
||||
it('should generate correct query', function() {
|
||||
expect(query).to.be('select code, mean(value) from "google.test" where [[$timeFilter]] and code=1 ' +
|
||||
'group by time([[$interval]]), code order asc');
|
||||
});
|
||||
|
||||
it('should expose groupByFiled', function() {
|
||||
expect(builder.groupByField).to.be('code');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('old style raw query', function() {
|
||||
var builder = new InfluxQueryBuilder({
|
||||
query: 'select host, mean(value) from asd.asd where time > now() - 1h group by time(1s), code order asc',
|
||||
rawQuery: true
|
||||
});
|
||||
|
||||
var query = builder.build();
|
||||
|
||||
it('should generate correct query', function() {
|
||||
expect(query).to.be('select host, mean(value) from asd.asd where [[$timeFilter]] and time > now() - 1h ' +
|
||||
' group by time(1s), code order asc');
|
||||
});
|
||||
|
||||
it('should expose groupByFiled', function() {
|
||||
expect(builder.groupByField).to.be('host');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -8,6 +8,7 @@ define([
|
||||
var ctx = new helpers.ServiceTestContext();
|
||||
|
||||
beforeEach(module('grafana.services'));
|
||||
beforeEach(ctx.providePhase());
|
||||
beforeEach(ctx.createService('InfluxDatasource'));
|
||||
|
||||
describe('When querying influxdb with one target using query editor target spec', function() {
|
||||
|
@ -140,6 +140,18 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
describeUpdateVariable('regex pattern remove duplicates', function(ctx) {
|
||||
ctx.setup(function() {
|
||||
ctx.variable = { type: 'query', query: 'apps.*', name: 'test' };
|
||||
ctx.variable.regex = 'backend_01';
|
||||
ctx.queryResult = [{text: 'apps.backend.backend_01.counters.req'}, {text: 'apps.backend.backend_01.counters.req'}];
|
||||
});
|
||||
|
||||
it('should return matches options', function() {
|
||||
expect(ctx.variable.options.length).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
describeUpdateVariable('and existing value still exists in options', function(ctx) {
|
||||
ctx.setup(function() {
|
||||
ctx.variable = { type: 'query', query: 'apps.*', name: 'test' };
|
||||
@ -163,7 +175,29 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
describeUpdateVariable('with include all regex wildcard', function(ctx) {
|
||||
describeUpdateVariable('with include all wildcard', function(ctx) {
|
||||
ctx.setup(function() {
|
||||
ctx.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'wildcard' };
|
||||
ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}];
|
||||
});
|
||||
|
||||
it('should add All wildcard option', function() {
|
||||
expect(ctx.variable.options[0].value).to.be('*');
|
||||
});
|
||||
});
|
||||
|
||||
describeUpdateVariable('with include all wildcard', function(ctx) {
|
||||
ctx.setup(function() {
|
||||
ctx.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'regex wildcard' };
|
||||
ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}];
|
||||
});
|
||||
|
||||
it('should add All wildcard option', function() {
|
||||
expect(ctx.variable.options[0].value).to.be('.*');
|
||||
});
|
||||
});
|
||||
|
||||
describeUpdateVariable('with include all regex values', function(ctx) {
|
||||
ctx.setup(function() {
|
||||
ctx.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'wildcard' };
|
||||
ctx.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}];
|
||||
|
@ -121,6 +121,8 @@ require([
|
||||
'specs/timeSeries-specs',
|
||||
'specs/row-ctrl-specs',
|
||||
'specs/graphiteTargetCtrl-specs',
|
||||
'specs/influxSeries-specs',
|
||||
'specs/influxQueryBuilder-specs',
|
||||
'specs/influxdb-datasource-specs',
|
||||
'specs/graph-ctrl-specs',
|
||||
'specs/grafanaGraph-specs',
|
||||
@ -130,8 +132,7 @@ require([
|
||||
'specs/templateValuesSrv-specs',
|
||||
'specs/kbn-format-specs',
|
||||
'specs/dashboardSrv-specs',
|
||||
'specs/dashboardViewStateSrv-specs',
|
||||
'specs/influxSeries-specs'
|
||||
'specs/dashboardViewStateSrv-specs'
|
||||
], function () {
|
||||
window.__karma__.start();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user