Commit 11c8e80e by Torkel Ödegaard

Working on new query editor for influxdb 0.9, #1525

parent a258e2e6
...@@ -12,7 +12,7 @@ define([ ...@@ -12,7 +12,7 @@ define([
'./bootstrap-tagsinput', './bootstrap-tagsinput',
'./bodyClass', './bodyClass',
'./variableValueSelect', './variableValueSelect',
'./graphiteSegment', './metric.segment',
'./grafanaVersionCheck', './grafanaVersionCheck',
'./dropdown.typeahead', './dropdown.typeahead',
'./topnav', './topnav',
......
...@@ -9,7 +9,7 @@ function (angular, app, _, $) { ...@@ -9,7 +9,7 @@ function (angular, app, _, $) {
angular angular
.module('grafana.directives') .module('grafana.directives')
.directive('graphiteSegment', function($compile, $sce) { .directive('metricSegment', function($compile, $sce) {
var inputTemplate = '<input type="text" data-provide="typeahead" ' + var inputTemplate = '<input type="text" data-provide="typeahead" ' +
' class="tight-form-clear-input input-medium"' + ' class="tight-form-clear-input input-medium"' +
' spellcheck="false" style="display:none"></input>'; ' spellcheck="false" style="display:none"></input>';
...@@ -17,6 +17,12 @@ function (angular, app, _, $) { ...@@ -17,6 +17,12 @@ function (angular, app, _, $) {
var buttonTemplate = '<a class="tight-form-item" tabindex="1" focus-me="segment.focus" ng-bind-html="segment.html"></a>'; var buttonTemplate = '<a class="tight-form-item" tabindex="1" focus-me="segment.focus" ng-bind-html="segment.html"></a>';
return { return {
scope: {
segment: "=",
getAltSegments: "&",
onValueChanged: "&"
},
link: function($scope, elem) { link: function($scope, elem) {
var $input = $(inputTemplate); var $input = $(inputTemplate);
var $button = $(buttonTemplate); var $button = $(buttonTemplate);
...@@ -46,7 +52,7 @@ function (angular, app, _, $) { ...@@ -46,7 +52,7 @@ function (angular, app, _, $) {
segment.expandable = true; segment.expandable = true;
segment.fake = false; segment.fake = false;
} }
$scope.segmentValueChanged(segment, $scope.$index); $scope.onValueChanged();
}); });
}; };
...@@ -69,7 +75,8 @@ function (angular, app, _, $) { ...@@ -69,7 +75,8 @@ function (angular, app, _, $) {
if (options) { return options; } if (options) { return options; }
$scope.$apply(function() { $scope.$apply(function() {
$scope.getAltSegments($scope.$index).then(function() { $scope.getAltSegments().then(function(altSegments) {
$scope.altSegments = altSegments;
options = _.map($scope.altSegments, function(alt) { return alt.value; }); options = _.map($scope.altSegments, function(alt) { return alt.value; });
// add custom values // add custom values
......
...@@ -74,7 +74,9 @@ ...@@ -74,7 +74,9 @@
ng-show="showTextEditor" /> ng-show="showTextEditor" />
<ul class="tight-form-list" role="menu" ng-hide="showTextEditor"> <ul class="tight-form-list" role="menu" ng-hide="showTextEditor">
<li ng-repeat="segment in segments" role="menuitem" graphite-segment></li> <li ng-repeat="segment in segments" role="menuitem">
<metric-segment segment="segment" get-alt-segments="getAltSegments($index)" on-value-changed="segmentValueChanged(segment, $index)"></metric-segment>
</li>
<li ng-repeat="func in functions"> <li ng-repeat="func in functions">
<span graphite-func-editor class="tight-form-item tight-form-func"> <span graphite-func-editor class="tight-form-item tight-form-func">
</span> </span>
......
...@@ -152,23 +152,18 @@ function (angular, _, config, gfunc, Parser) { ...@@ -152,23 +152,18 @@ function (angular, _, config, gfunc, Parser) {
} }
$scope.getAltSegments = function (index) { $scope.getAltSegments = function (index) {
$scope.altSegments = [];
var query = index === 0 ? '*' : getSegmentPathUpTo(index) + '.*'; var query = index === 0 ? '*' : getSegmentPathUpTo(index) + '.*';
return $scope.datasource.metricFindQuery(query) return $scope.datasource.metricFindQuery(query).then(function(segments) {
.then(function(segments) { var altSegments = _.map(segments, function(segment) {
$scope.altSegments = _.map(segments, function(segment) {
return new MetricSegment({ value: segment.text, expandable: segment.expandable }); return new MetricSegment({ value: segment.text, expandable: segment.expandable });
}); });
if ($scope.altSegments.length === 0) { if (altSegments.length === 0) { return altSegments; }
return;
}
// add template variables // add template variables
_.each(templateSrv.variables, function(variable) { _.each(templateSrv.variables, function(variable) {
$scope.altSegments.unshift(new MetricSegment({ altSegments.unshift(new MetricSegment({
type: 'template', type: 'template',
value: '$' + variable.name, value: '$' + variable.name,
expandable: true, expandable: true,
...@@ -176,10 +171,12 @@ function (angular, _, config, gfunc, Parser) { ...@@ -176,10 +171,12 @@ function (angular, _, config, gfunc, Parser) {
}); });
// add wildcard option // add wildcard option
$scope.altSegments.unshift(new MetricSegment('*')); altSegments.unshift(new MetricSegment('*'));
return altSegments;
}) })
.then(null, function(err) { .then(null, function(err) {
$scope.parserError = err.message || 'Failed to issue metric query'; $scope.parserError = err.message || 'Failed to issue metric query';
return [];
}); });
}; };
......
...@@ -108,7 +108,7 @@ function (angular, _, $) { ...@@ -108,7 +108,7 @@ function (angular, _, $) {
function addElementsAndCompile() { function addElementsAndCompile() {
$funcLink.appendTo(elem); $funcLink.appendTo(elem);
var $paramLink = $('<a ng-click="" class="graphite-func-param-link">' + $scope.target.column + '</a>'); var $paramLink = $('<a ng-click="" class="graphite-func-param-link">value</a>');
var $input = $(paramTemplate); var $input = $(paramTemplate);
$paramLink.appendTo(elem); $paramLink.appendTo(elem);
......
...@@ -62,23 +62,54 @@ ...@@ -62,23 +62,54 @@
</li> </li>
</ul> </ul>
<input type="text" <input type="text" class="tight-form-clear-input span10" ng-model="target.query" focus-me="target.rawQuery"
class="tight-form-clear-input span10" spellcheck='false' ng-model-onblur ng-change="targetTextChanged()" ng-show="target.rawQuery" />
ng-model="target.query"
focus-me="target.rawQuery"
spellcheck='false'
ng-model-onblur ng-change="targetTextChanged()"
ng-show="target.rawQuery" />
<ul class="tight-form-list" role="menu" ng-hide="target.rawQuery"> <ul class="tight-form-list" role="menu" ng-hide="target.rawQuery">
<li ng-repeat="segment in segments" role="menuitem" graphite-segment></li> <li class="tight-form-item query-keyword">
<li ng-repeat="func in functions"> SELECT
<span graphite-func-editor class="tight-form-item tight-form-func"> </li>
</span> <li class="dropdown tight-form-item">
</li> <a gf-dropdown="functionMenu" class="dropdown-toggle" data-toggle="dropdown">
<li class="dropdown" graphite-add-func> {{target.function}}<span>(value)</span>
</li> </a>
</li>
<li class="tight-form-item query-keyword">
FROM
</li>
<li>
<metric-segment segment="measurementSegment" get-alt-segments="getMeasurements()" on-value-changed="measurementChanged()"></metric-segment>
</li>
<li class="tight-form-item query-keyword">
WHERE
</li>
<li>
<input type="text" class="input-medium tight-form-input" ng-model="target.condition"
bs-tooltip="'Add a where clause'" data-placement="right" spellcheck='false' placeholder="column ~= value" ng-blur="get_data()">
</li>
<li class="tight-form-item">
<span class="query-keyword">GROUP BY</span>
time($interval), <i class="fa fa-plus"></i>
</li>
<li class="dropdown">
<a class="tight-form-item pointer" data-toggle="dropdown" bs-tooltip="'Insert missing values, important when stacking'" data-placement="right">
<span ng-show="target.fill">
fill ({{target.fill}})
</span>
<span ng-show="!target.fill">
no fill
</span>
</a>
<ul class="dropdown-menu">
<li><a ng-click="target.fill = ''">no fill</a></li>
<li><a ng-click="target.fill = 'null'">fill (null)</a></li>
<li><a ng-click="target.fill = '0'">fill (0)</a></li>
</ul>
</li>
</ul> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
</div> </div>
......
...@@ -7,19 +7,39 @@ function (angular, _) { ...@@ -7,19 +7,39 @@ function (angular, _) {
var module = angular.module('grafana.controllers'); var module = angular.module('grafana.controllers');
module.controller('InfluxQueryCtrl', function($scope, $timeout, $sce, templateSrv, $q) { module.controller('InfluxQueryCtrl', function($scope, $timeout, $sce, templateSrv) {
$scope.functionList = [
'count', 'mean', 'sum', 'min',
'max', 'mode', 'distinct', 'median',
'derivative', 'stddev', 'first', 'last',
'difference'
];
$scope.functionMenu = _.map($scope.functionList, function(func) {
return { text: func, click: "changeFunction('" + func + "');" };
});
$scope.init = function() { $scope.init = function() {
$scope.segments = $scope.target.segments || []; var target = $scope.target;
target.function = target.function || 'mean';
$scope.functionsSelect = [ if (!target.measurement) {
'count', 'mean', 'sum', 'min', $scope.measurementSegment = MetricSegment.newSelectMeasurement();
'max', 'mode', 'distinct', 'median', } else {
'derivative', 'stddev', 'first', 'last', $scope.measurementSegment = new MetricSegment(target.measurement);
'difference' }
]; };
checkOtherSegments(0); $scope.changeFunction = function(func) {
$scope.target.function = func;
$scope.$parent.get_data();
};
$scope.measurementChanged = function() {
$scope.target.measurement = $scope.measurementSegment.value;
console.log('measurement updated', $scope.target.measurement);
$scope.$parent.get_data();
}; };
$scope.toggleQueryMode = function () { $scope.toggleQueryMode = function () {
...@@ -35,103 +55,44 @@ function (angular, _) { ...@@ -35,103 +55,44 @@ function (angular, _) {
$scope.panel.targets.push(clone); $scope.panel.targets.push(clone);
}; };
$scope.getAltSegments = function (index) { $scope.getMeasurements = function () {
$scope.altSegments = []; // var measurement = $scope.segments[0].value;
// var queryType, query;
var measurement = $scope.segments[0].value; // if (index === 0) {
var queryType, query; // queryType = 'MEASUREMENTS';
if (index === 0) { // query = 'SHOW MEASUREMENTS';
queryType = 'MEASUREMENTS'; // } else if (index % 2 === 1) {
query = 'SHOW MEASUREMENTS'; // queryType = 'TAG_KEYS';
} else if (index % 2 === 1) { // query = 'SHOW TAG KEYS FROM "' + measurement + '"';
queryType = 'TAG_KEYS'; // } else {
query = 'SHOW TAG KEYS FROM "' + measurement + '"'; // queryType = 'TAG_VALUES';
} else { // query = 'SHOW TAG VALUES FROM "' + measurement + '" WITH KEY = ' + $scope.segments[$scope.segments.length - 2].value;
queryType = 'TAG_VALUES'; // }
query = 'SHOW TAG VALUES FROM "' + measurement + '" WITH KEY = ' + $scope.segments[$scope.segments.length - 2].value; //
} // console.log('getAltSegments: query' , query);
//
console.log('getAltSegments: query' , query); console.log('get measurements');
return $scope.datasource.metricFindQuery('SHOW MEASUREMENTS', 'MEASUREMENTS').then(function(results) {
return $scope.datasource.metricFindQuery(query, queryType).then(function(results) {
console.log('get alt segments: response', results); console.log('get alt segments: response', results);
$scope.altSegments = _.map(results, function(segment) { var measurements = _.map(results, function(segment) {
return new MetricSegment({ value: segment.text, expandable: segment.expandable }); return new MetricSegment({ value: segment.text, expandable: segment.expandable });
}); });
_.each(templateSrv.variables, function(variable) { _.each(templateSrv.variables, function(variable) {
$scope.altSegments.unshift(new MetricSegment({ measurements.unshift(new MetricSegment({
type: 'template', type: 'template',
value: '$' + variable.name, value: '$' + variable.name,
expandable: true, expandable: true,
})); }));
}); });
return measurements;
}, function(err) { }, function(err) {
$scope.parserError = err.message || 'Failed to issue metric query'; $scope.parserError = err.message || 'Failed to issue metric query';
return [];
}); });
}; };
$scope.segmentValueChanged = function (segment, segmentIndex) {
delete $scope.parserError;
if (segment.expandable) {
return checkOtherSegments(segmentIndex + 1).then(function () {
setSegmentFocus(segmentIndex + 1);
$scope.targetChanged();
});
}
else {
$scope.segments = $scope.segments.splice(0, segmentIndex + 1);
}
setSegmentFocus(segmentIndex + 1);
$scope.targetChanged();
};
$scope.targetChanged = function() {
if ($scope.parserError) {
return;
}
$scope.target.measurement = '';
$scope.target.tags = {};
$scope.target.measurement = $scope.segments[0].value;
for (var i = 1; i+1 < $scope.segments.length; i += 2) {
var key = $scope.segments[i].value;
$scope.target.tags[key] = $scope.segments[i+1].value;
}
$scope.$parent.get_data();
};
function checkOtherSegments(fromIndex) {
if (fromIndex === 0) {
$scope.segments.push(MetricSegment.newSelectMetric());
return;
}
if ($scope.segments.length === 0) {
throw('should always have a scope segment?');
}
if (_.last($scope.segments).fake) {
return $q.when([]);
} else if ($scope.segments.length % 2 === 1) {
$scope.segments.push(MetricSegment.newSelectTag());
return $q.when([]);
} else {
$scope.segments.push(MetricSegment.newSelectTagValue());
return $q.when([]);
}
}
function setSegmentFocus(segmentIndex) {
_.each($scope.segments, function(segment, index) {
segment.focus = segmentIndex === index;
});
}
function MetricSegment(options) { function MetricSegment(options) {
if (options === '*' || options.value === '*') { if (options === '*' || options.value === '*') {
this.value = '*'; this.value = '*';
...@@ -153,8 +114,8 @@ function (angular, _) { ...@@ -153,8 +114,8 @@ function (angular, _) {
this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));
} }
MetricSegment.newSelectMetric = function() { MetricSegment.newSelectMeasurement = function() {
return new MetricSegment({value: 'select metric', fake: true}); return new MetricSegment({value: 'select measurement', fake: true});
}; };
MetricSegment.newSelectTag = function() { MetricSegment.newSelectTag = function() {
......
...@@ -337,3 +337,10 @@ ...@@ -337,3 +337,10 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
} }
.query-keyword {
font-weight: bold;
color: @blue;
}
...@@ -141,13 +141,15 @@ define([ ...@@ -141,13 +141,15 @@ define([
ctx.scope.target.target = 'test.count'; ctx.scope.target.target = 'test.count';
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([])); ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.scope.init(); ctx.scope.init();
ctx.scope.getAltSegments(1); ctx.scope.getAltSegments(1).then(function(results) {
ctx.altSegments = results;
});
ctx.scope.$digest(); ctx.scope.$digest();
ctx.scope.$parent = { get_data: sinon.spy() }; ctx.scope.$parent = { get_data: sinon.spy() };
}); });
it('should have no segments', function() { it('should have no segments', function() {
expect(ctx.scope.altSegments.length).to.be(0); expect(ctx.altSegments.length).to.be(0);
}); });
}); });
......
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