mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'query-part-refactor'
Conflicts: public/app/plugins/datasource/influxdb/query_part.ts
This commit is contained in:
commit
79986e5593
123
public/app/core/components/query_part/query_part.ts
Normal file
123
public/app/core/components/query_part/query_part.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export class QueryPartDef {
|
||||||
|
type: string;
|
||||||
|
params: any[];
|
||||||
|
defaultParams: any[];
|
||||||
|
renderer: any;
|
||||||
|
category: any;
|
||||||
|
addStrategy: any;
|
||||||
|
|
||||||
|
constructor(options: any) {
|
||||||
|
this.type = options.type;
|
||||||
|
this.params = options.params;
|
||||||
|
this.defaultParams = options.defaultParams;
|
||||||
|
this.renderer = options.renderer;
|
||||||
|
this.category = options.category;
|
||||||
|
this.addStrategy = options.addStrategy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryPart {
|
||||||
|
part: any;
|
||||||
|
def: QueryPartDef;
|
||||||
|
params: any[];
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
constructor(part: any, def: any) {
|
||||||
|
this.part = part;
|
||||||
|
this.def = def;
|
||||||
|
if (!this.def) {
|
||||||
|
throw {message: 'Could not find query part ' + part.type};
|
||||||
|
}
|
||||||
|
|
||||||
|
part.params = part.params || _.clone(this.def.defaultParams);
|
||||||
|
this.params = part.params;
|
||||||
|
this.updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(innerExpr: string) {
|
||||||
|
return this.def.renderer(this, innerExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMultipleParamsInString (strValue, index) {
|
||||||
|
if (strValue.indexOf(',') === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.def.params[index + 1] && this.def.params[index + 1].optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParam (strValue, index) {
|
||||||
|
// handle optional parameters
|
||||||
|
// if string contains ',' and next param is optional, split and update both
|
||||||
|
if (this.hasMultipleParamsInString(strValue, index)) {
|
||||||
|
_.each(strValue.split(','), function(partVal: string, idx) {
|
||||||
|
this.updateParam(partVal.trim(), idx);
|
||||||
|
}, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strValue === '' && this.def.params[index].optional) {
|
||||||
|
this.params.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
this.params[index] = strValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.part.params = this.params;
|
||||||
|
this.updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateText() {
|
||||||
|
if (this.params.length === 0) {
|
||||||
|
this.text = this.def.type + '()';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = this.def.type + '(';
|
||||||
|
text += this.params.join(', ');
|
||||||
|
text += ')';
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function functionRenderer(part, innerExpr) {
|
||||||
|
var str = part.def.type + '(';
|
||||||
|
var parameters = _.map(part.params, (value, index) => {
|
||||||
|
var paramType = part.def.params[index];
|
||||||
|
if (paramType.type === 'time') {
|
||||||
|
if (value === 'auto') {
|
||||||
|
value = '$interval';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paramType.quote === 'single') {
|
||||||
|
return "'" + value + "'";
|
||||||
|
} else if (paramType.quote === 'double') {
|
||||||
|
return '"' + value + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (innerExpr) {
|
||||||
|
parameters.unshift(innerExpr);
|
||||||
|
}
|
||||||
|
return str + parameters.join(', ') + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function suffixRenderer(part, innerExpr) {
|
||||||
|
return innerExpr + ' ' + part.params[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function identityRenderer(part, innerExpr) {
|
||||||
|
return part.params[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function quotedIdentityRenderer(part, innerExpr) {
|
||||||
|
return '"' + part.params[0] + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
183
public/app/core/components/query_part/query_part_editor.ts
Normal file
183
public/app/core/components/query_part/query_part_editor.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import coreModule from 'app/core/core_module';
|
||||||
|
|
||||||
|
var template = `
|
||||||
|
<div class="tight-form-func-controls">
|
||||||
|
<span class="pointer fa fa-remove" ng-click="removeActionInternal()"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a ng-click="toggleControls()" class="query-part-name">{{part.def.type}}</a>
|
||||||
|
<span>(</span><span class="query-part-parameters"></span><span>)</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
export function queryPartEditorDirective($compile, templateSrv) {
|
||||||
|
|
||||||
|
var paramTemplate = '<input type="text" style="display:none"' +
|
||||||
|
' class="input-mini tight-form-func-param"></input>';
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
template: template,
|
||||||
|
scope: {
|
||||||
|
part: "=",
|
||||||
|
removeAction: "&",
|
||||||
|
partUpdated: "&",
|
||||||
|
getOptions: "&",
|
||||||
|
},
|
||||||
|
link: function postLink($scope, elem) {
|
||||||
|
var part = $scope.part;
|
||||||
|
var partDef = part.def;
|
||||||
|
var $paramsContainer = elem.find('.query-part-parameters');
|
||||||
|
var $controlsContainer = elem.find('.tight-form-func-controls');
|
||||||
|
|
||||||
|
function clickFuncParam(paramIndex) {
|
||||||
|
/*jshint validthis:true */
|
||||||
|
var $link = $(this);
|
||||||
|
var $input = $link.next();
|
||||||
|
|
||||||
|
$input.val(part.params[paramIndex]);
|
||||||
|
$input.css('width', ($link.width() + 16) + 'px');
|
||||||
|
|
||||||
|
$link.hide();
|
||||||
|
$input.show();
|
||||||
|
$input.focus();
|
||||||
|
$input.select();
|
||||||
|
|
||||||
|
var typeahead = $input.data('typeahead');
|
||||||
|
if (typeahead) {
|
||||||
|
$input.val('');
|
||||||
|
typeahead.lookup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputBlur(paramIndex) {
|
||||||
|
/*jshint validthis:true */
|
||||||
|
var $input = $(this);
|
||||||
|
var $link = $input.prev();
|
||||||
|
var newValue = $input.val();
|
||||||
|
|
||||||
|
if (newValue !== '' || part.def.params[paramIndex].optional) {
|
||||||
|
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
|
||||||
|
|
||||||
|
part.updateParam($input.val(), paramIndex);
|
||||||
|
$scope.$apply($scope.partUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
$input.hide();
|
||||||
|
$link.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputKeyPress(paramIndex, e) {
|
||||||
|
/*jshint validthis:true */
|
||||||
|
if (e.which === 13) {
|
||||||
|
inputBlur.call(this, paramIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputKeyDown() {
|
||||||
|
/*jshint validthis:true */
|
||||||
|
this.style.width = (3 + this.value.length) * 8 + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTypeahead($input, param, paramIndex) {
|
||||||
|
if (!param.options && !param.dynamicLookup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeaheadSource = function (query, callback) {
|
||||||
|
if (param.options) { return param.options; }
|
||||||
|
|
||||||
|
$scope.$apply(function() {
|
||||||
|
$scope.getOptions().then(function(result) {
|
||||||
|
var dynamicOptions = _.map(result, function(op) { return op.value; });
|
||||||
|
callback(dynamicOptions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$input.attr('data-provide', 'typeahead');
|
||||||
|
var options = param.options;
|
||||||
|
if (param.type === 'int') {
|
||||||
|
options = _.map(options, function(val) { return val.toString(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
$input.typeahead({
|
||||||
|
source: typeaheadSource,
|
||||||
|
minLength: 0,
|
||||||
|
items: 1000,
|
||||||
|
updater: function (value) {
|
||||||
|
setTimeout(function() {
|
||||||
|
inputBlur.call($input[0], paramIndex);
|
||||||
|
}, 0);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var typeahead = $input.data('typeahead');
|
||||||
|
typeahead.lookup = function () {
|
||||||
|
this.query = this.$element.val() || '';
|
||||||
|
var items = this.source(this.query, $.proxy(this.process, this));
|
||||||
|
return items ? this.process(items) : items;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.toggleControls = function() {
|
||||||
|
var targetDiv = elem.closest('.tight-form');
|
||||||
|
|
||||||
|
if (elem.hasClass('show-function-controls')) {
|
||||||
|
elem.removeClass('show-function-controls');
|
||||||
|
targetDiv.removeClass('has-open-function');
|
||||||
|
$controlsContainer.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem.addClass('show-function-controls');
|
||||||
|
targetDiv.addClass('has-open-function');
|
||||||
|
$controlsContainer.show();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removeActionInternal = function() {
|
||||||
|
$scope.toggleControls();
|
||||||
|
$scope.removeAction();
|
||||||
|
};
|
||||||
|
|
||||||
|
function addElementsAndCompile() {
|
||||||
|
_.each(partDef.params, function(param, index) {
|
||||||
|
if (param.optional && part.params.length <= index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > 0) {
|
||||||
|
$('<span>, </span>').appendTo($paramsContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
|
||||||
|
var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
|
||||||
|
var $input = $(paramTemplate);
|
||||||
|
|
||||||
|
$paramLink.appendTo($paramsContainer);
|
||||||
|
$input.appendTo($paramsContainer);
|
||||||
|
|
||||||
|
$input.blur(_.partial(inputBlur, index));
|
||||||
|
$input.keyup(inputKeyDown);
|
||||||
|
$input.keypress(_.partial(inputKeyPress, index));
|
||||||
|
$paramLink.click(_.partial(clickFuncParam, index));
|
||||||
|
|
||||||
|
addTypeahead($input, param, index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function relink() {
|
||||||
|
$paramsContainer.empty();
|
||||||
|
addElementsAndCompile();
|
||||||
|
}
|
||||||
|
|
||||||
|
relink();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
coreModule.directive('queryPartEditor', queryPartEditorDirective);
|
@ -33,6 +33,7 @@ import {Emitter} from './utils/emitter';
|
|||||||
import {layoutSelector} from './components/layout_selector/layout_selector';
|
import {layoutSelector} from './components/layout_selector/layout_selector';
|
||||||
import {switchDirective} from './components/switch';
|
import {switchDirective} from './components/switch';
|
||||||
import {dashboardSelector} from './components/dashboard_selector';
|
import {dashboardSelector} from './components/dashboard_selector';
|
||||||
|
import {queryPartEditorDirective} from './components/query_part/query_part_editor';
|
||||||
import 'app/core/controllers/all';
|
import 'app/core/controllers/all';
|
||||||
import 'app/core/services/all';
|
import 'app/core/services/all';
|
||||||
import 'app/core/routes/routes';
|
import 'app/core/routes/routes';
|
||||||
@ -56,4 +57,5 @@ export {
|
|||||||
Emitter,
|
Emitter,
|
||||||
appEvents,
|
appEvents,
|
||||||
dashboardSelector,
|
dashboardSelector,
|
||||||
|
queryPartEditorDirective,
|
||||||
};
|
};
|
||||||
|
@ -35,13 +35,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form" ng-repeat="part in selectParts">
|
<div class="gf-form" ng-repeat="part in selectParts">
|
||||||
<influx-query-part-editor
|
<query-part-editor
|
||||||
class="gf-form-label query-part"
|
class="gf-form-label query-part"
|
||||||
part="part"
|
part="part"
|
||||||
remove-action="ctrl.removeSelectPart(selectParts, part)"
|
remove-action="ctrl.removeSelectPart(selectParts, part)"
|
||||||
part-updated="ctrl.selectPartUpdated(selectParts, part)"
|
part-updated="ctrl.selectPartUpdated(selectParts, part)"
|
||||||
get-options="ctrl.getPartOptions(part)">
|
get-options="ctrl.getPartOptions(part)">
|
||||||
</influx-query-part-editor>
|
</query-part-editor>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
@ -62,12 +62,12 @@
|
|||||||
<span>GROUP BY</span>
|
<span>GROUP BY</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<influx-query-part-editor
|
<query-part-editor
|
||||||
ng-repeat="part in ctrl.queryModel.groupByParts"
|
ng-repeat="part in ctrl.queryModel.groupByParts"
|
||||||
part="part"
|
part="part"
|
||||||
class="gf-form-label query-part"
|
class="gf-form-label query-part"
|
||||||
remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)">
|
remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)">
|
||||||
</influx-query-part-editor>
|
</query-part-editor>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
<div class="tight-form-func-controls">
|
|
||||||
<span class="pointer fa fa-remove" ng-click="removeActionInternal()" ></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a ng-click="toggleControls()" class="query-part-name">{{part.def.type}}</a><span>(</span><span class="query-part-parameters"></span><span>)</span>
|
|
@ -1,8 +1,5 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import './query_part_editor';
|
|
||||||
import './query_part_editor';
|
|
||||||
|
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import InfluxQueryBuilder from './query_builder';
|
import InfluxQueryBuilder from './query_builder';
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import {
|
||||||
|
QueryPartDef,
|
||||||
|
QueryPart,
|
||||||
|
functionRenderer,
|
||||||
|
suffixRenderer,
|
||||||
|
identityRenderer,
|
||||||
|
quotedIdentityRenderer,
|
||||||
|
} from 'app/core/components/query_part/query_part';
|
||||||
|
|
||||||
var index = [];
|
var index = [];
|
||||||
var categories = {
|
var categories = {
|
||||||
@ -12,71 +20,26 @@ var categories = {
|
|||||||
Fields: [],
|
Fields: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
var groupByTimeFunctions = [];
|
function createPart(part): any {
|
||||||
|
var def = index[part.type];
|
||||||
class QueryPartDef {
|
if (!def) {
|
||||||
type: string;
|
throw {message: 'Could not find query part ' + part.type};
|
||||||
params: any[];
|
|
||||||
defaultParams: any[];
|
|
||||||
renderer: any;
|
|
||||||
category: any;
|
|
||||||
addStrategy: any;
|
|
||||||
|
|
||||||
constructor(options: any) {
|
|
||||||
this.type = options.type;
|
|
||||||
this.params = options.params;
|
|
||||||
this.defaultParams = options.defaultParams;
|
|
||||||
this.renderer = options.renderer;
|
|
||||||
this.category = options.category;
|
|
||||||
this.addStrategy = options.addStrategy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static register(options: any) {
|
return new QueryPart(part, def);
|
||||||
|
};
|
||||||
|
|
||||||
|
function register(options: any) {
|
||||||
index[options.type] = new QueryPartDef(options);
|
index[options.type] = new QueryPartDef(options);
|
||||||
options.category.push(index[options.type]);
|
options.category.push(index[options.type]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function functionRenderer(part, innerExpr) {
|
var groupByTimeFunctions = [];
|
||||||
var str = part.def.type + '(';
|
|
||||||
var parameters = _.map(part.params, (value, index) => {
|
|
||||||
var paramType = part.def.params[index];
|
|
||||||
if (paramType.type === 'time') {
|
|
||||||
if (value === 'auto') {
|
|
||||||
value = '$interval';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (paramType.quote === 'single') {
|
|
||||||
return "'" + value + "'";
|
|
||||||
} else if (paramType.quote === 'double') {
|
|
||||||
return '"' + value + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (innerExpr) {
|
|
||||||
parameters.unshift(innerExpr);
|
|
||||||
}
|
|
||||||
return str + parameters.join(', ') + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
function aliasRenderer(part, innerExpr) {
|
function aliasRenderer(part, innerExpr) {
|
||||||
return innerExpr + ' AS ' + '"' + part.params[0] + '"';
|
return innerExpr + ' AS ' + '"' + part.params[0] + '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
function suffixRenderer(part, innerExpr) {
|
|
||||||
return innerExpr + ' ' + part.params[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function identityRenderer(part, innerExpr) {
|
|
||||||
return part.params[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function quotedIdentityRenderer(part, innerExpr) {
|
|
||||||
return '"' + part.params[0] + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
function fieldRenderer(part, innerExpr) {
|
function fieldRenderer(part, innerExpr) {
|
||||||
if (part.params[0] === '*') {
|
if (part.params[0] === '*') {
|
||||||
return '*';
|
return '*';
|
||||||
@ -149,13 +112,13 @@ function addAliasStrategy(selectParts, partModel) {
|
|||||||
function addFieldStrategy(selectParts, partModel, query) {
|
function addFieldStrategy(selectParts, partModel, query) {
|
||||||
// copy all parts
|
// copy all parts
|
||||||
var parts = _.map(selectParts, function(part: any) {
|
var parts = _.map(selectParts, function(part: any) {
|
||||||
return new QueryPart({type: part.def.type, params: _.clone(part.params)});
|
return createPart({type: part.def.type, params: _.clone(part.params)});
|
||||||
});
|
});
|
||||||
|
|
||||||
query.selectModels.push(parts);
|
query.selectModels.push(parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'field',
|
type: 'field',
|
||||||
addStrategy: addFieldStrategy,
|
addStrategy: addFieldStrategy,
|
||||||
category: categories.Fields,
|
category: categories.Fields,
|
||||||
@ -165,7 +128,7 @@ QueryPartDef.register({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Aggregations
|
// Aggregations
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'count',
|
type: 'count',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Aggregations,
|
category: categories.Aggregations,
|
||||||
@ -174,7 +137,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'distinct',
|
type: 'distinct',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Aggregations,
|
category: categories.Aggregations,
|
||||||
@ -183,7 +146,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'integral',
|
type: 'integral',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Aggregations,
|
category: categories.Aggregations,
|
||||||
@ -192,7 +155,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'mean',
|
type: 'mean',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Aggregations,
|
category: categories.Aggregations,
|
||||||
@ -201,7 +164,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'median',
|
type: 'median',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Aggregations,
|
category: categories.Aggregations,
|
||||||
@ -210,7 +173,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'sum',
|
type: 'sum',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Aggregations,
|
category: categories.Aggregations,
|
||||||
@ -221,7 +184,7 @@ QueryPartDef.register({
|
|||||||
|
|
||||||
// transformations
|
// transformations
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'derivative',
|
type: 'derivative',
|
||||||
addStrategy: addTransformationStrategy,
|
addStrategy: addTransformationStrategy,
|
||||||
category: categories.Transformations,
|
category: categories.Transformations,
|
||||||
@ -230,7 +193,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'spread',
|
type: 'spread',
|
||||||
addStrategy: addTransformationStrategy,
|
addStrategy: addTransformationStrategy,
|
||||||
category: categories.Transformations,
|
category: categories.Transformations,
|
||||||
@ -239,7 +202,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'non_negative_derivative',
|
type: 'non_negative_derivative',
|
||||||
addStrategy: addTransformationStrategy,
|
addStrategy: addTransformationStrategy,
|
||||||
category: categories.Transformations,
|
category: categories.Transformations,
|
||||||
@ -248,7 +211,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'difference',
|
type: 'difference',
|
||||||
addStrategy: addTransformationStrategy,
|
addStrategy: addTransformationStrategy,
|
||||||
category: categories.Transformations,
|
category: categories.Transformations,
|
||||||
@ -257,7 +220,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'moving_average',
|
type: 'moving_average',
|
||||||
addStrategy: addTransformationStrategy,
|
addStrategy: addTransformationStrategy,
|
||||||
category: categories.Transformations,
|
category: categories.Transformations,
|
||||||
@ -266,7 +229,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'stddev',
|
type: 'stddev',
|
||||||
addStrategy: addTransformationStrategy,
|
addStrategy: addTransformationStrategy,
|
||||||
category: categories.Transformations,
|
category: categories.Transformations,
|
||||||
@ -275,7 +238,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'time',
|
type: 'time',
|
||||||
category: groupByTimeFunctions,
|
category: groupByTimeFunctions,
|
||||||
params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }],
|
params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }],
|
||||||
@ -283,7 +246,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'fill',
|
type: 'fill',
|
||||||
category: groupByTimeFunctions,
|
category: groupByTimeFunctions,
|
||||||
params: [{ name: "fill", type: "string", options: ['none', 'null', '0', 'previous'] }],
|
params: [{ name: "fill", type: "string", options: ['none', 'null', '0', 'previous'] }],
|
||||||
@ -292,7 +255,7 @@ QueryPartDef.register({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Selectors
|
// Selectors
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'bottom',
|
type: 'bottom',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Selectors,
|
category: categories.Selectors,
|
||||||
@ -301,7 +264,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'first',
|
type: 'first',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Selectors,
|
category: categories.Selectors,
|
||||||
@ -310,7 +273,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'last',
|
type: 'last',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Selectors,
|
category: categories.Selectors,
|
||||||
@ -319,7 +282,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'max',
|
type: 'max',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Selectors,
|
category: categories.Selectors,
|
||||||
@ -328,7 +291,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'min',
|
type: 'min',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Selectors,
|
category: categories.Selectors,
|
||||||
@ -337,7 +300,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'percentile',
|
type: 'percentile',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Selectors,
|
category: categories.Selectors,
|
||||||
@ -346,7 +309,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'top',
|
type: 'top',
|
||||||
addStrategy: replaceAggregationAddStrategy,
|
addStrategy: replaceAggregationAddStrategy,
|
||||||
category: categories.Selectors,
|
category: categories.Selectors,
|
||||||
@ -355,7 +318,7 @@ QueryPartDef.register({
|
|||||||
renderer: functionRenderer,
|
renderer: functionRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'tag',
|
type: 'tag',
|
||||||
category: groupByTimeFunctions,
|
category: groupByTimeFunctions,
|
||||||
params: [{name: 'tag', type: 'string', dynamicLookup: true}],
|
params: [{name: 'tag', type: 'string', dynamicLookup: true}],
|
||||||
@ -363,7 +326,7 @@ QueryPartDef.register({
|
|||||||
renderer: fieldRenderer,
|
renderer: fieldRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'math',
|
type: 'math',
|
||||||
addStrategy: addMathStrategy,
|
addStrategy: addMathStrategy,
|
||||||
category: categories.Math,
|
category: categories.Math,
|
||||||
@ -372,7 +335,7 @@ QueryPartDef.register({
|
|||||||
renderer: suffixRenderer,
|
renderer: suffixRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryPartDef.register({
|
register({
|
||||||
type: 'alias',
|
type: 'alias',
|
||||||
addStrategy: addAliasStrategy,
|
addStrategy: addAliasStrategy,
|
||||||
category: categories.Aliasing,
|
category: categories.Aliasing,
|
||||||
@ -382,74 +345,9 @@ QueryPartDef.register({
|
|||||||
renderer: aliasRenderer,
|
renderer: aliasRenderer,
|
||||||
});
|
});
|
||||||
|
|
||||||
class QueryPart {
|
|
||||||
part: any;
|
|
||||||
def: QueryPartDef;
|
|
||||||
params: any[];
|
|
||||||
text: string;
|
|
||||||
|
|
||||||
constructor(part: any) {
|
|
||||||
this.part = part;
|
|
||||||
this.def = index[part.type];
|
|
||||||
if (!this.def) {
|
|
||||||
throw {message: 'Could not find query part ' + part.type};
|
|
||||||
}
|
|
||||||
|
|
||||||
part.params = part.params || _.clone(this.def.defaultParams);
|
|
||||||
this.params = part.params;
|
|
||||||
this.updateText();
|
|
||||||
}
|
|
||||||
|
|
||||||
render(innerExpr: string) {
|
|
||||||
return this.def.renderer(this, innerExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasMultipleParamsInString (strValue, index) {
|
|
||||||
if (strValue.indexOf(',') === -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.def.params[index + 1] && this.def.params[index + 1].optional;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateParam (strValue, index) {
|
|
||||||
// handle optional parameters
|
|
||||||
// if string contains ',' and next param is optional, split and update both
|
|
||||||
if (this.hasMultipleParamsInString(strValue, index)) {
|
|
||||||
_.each(strValue.split(','), function(partVal: string, idx) {
|
|
||||||
this.updateParam(partVal.trim(), idx);
|
|
||||||
}, this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strValue === '' && this.def.params[index].optional) {
|
|
||||||
this.params.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
this.params[index] = strValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.part.params = this.params;
|
|
||||||
this.updateText();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateText() {
|
|
||||||
if (this.params.length === 0) {
|
|
||||||
this.text = this.def.type + '()';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = this.def.type + '(';
|
|
||||||
text += this.params.join(', ');
|
|
||||||
text += ')';
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
create: function(part): any {
|
create: createPart,
|
||||||
return new QueryPart(part);
|
|
||||||
},
|
|
||||||
|
|
||||||
getCategories: function() {
|
getCategories: function() {
|
||||||
return categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
@ -1,178 +0,0 @@
|
|||||||
define([
|
|
||||||
'angular',
|
|
||||||
'lodash',
|
|
||||||
'jquery',
|
|
||||||
],
|
|
||||||
function (angular, _, $) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
angular
|
|
||||||
.module('grafana.directives')
|
|
||||||
.directive('influxQueryPartEditor', function($compile, templateSrv) {
|
|
||||||
|
|
||||||
var paramTemplate = '<input type="text" style="display:none"' +
|
|
||||||
' class="input-mini tight-form-func-param"></input>';
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
templateUrl: 'public/app/plugins/datasource/influxdb/partials/query_part.html',
|
|
||||||
scope: {
|
|
||||||
part: "=",
|
|
||||||
removeAction: "&",
|
|
||||||
partUpdated: "&",
|
|
||||||
getOptions: "&",
|
|
||||||
},
|
|
||||||
link: function postLink($scope, elem) {
|
|
||||||
var part = $scope.part;
|
|
||||||
var partDef = part.def;
|
|
||||||
var $paramsContainer = elem.find('.query-part-parameters');
|
|
||||||
var $controlsContainer = elem.find('.tight-form-func-controls');
|
|
||||||
|
|
||||||
function clickFuncParam(paramIndex) {
|
|
||||||
/*jshint validthis:true */
|
|
||||||
var $link = $(this);
|
|
||||||
var $input = $link.next();
|
|
||||||
|
|
||||||
$input.val(part.params[paramIndex]);
|
|
||||||
$input.css('width', ($link.width() + 16) + 'px');
|
|
||||||
|
|
||||||
$link.hide();
|
|
||||||
$input.show();
|
|
||||||
$input.focus();
|
|
||||||
$input.select();
|
|
||||||
|
|
||||||
var typeahead = $input.data('typeahead');
|
|
||||||
if (typeahead) {
|
|
||||||
$input.val('');
|
|
||||||
typeahead.lookup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function inputBlur(paramIndex) {
|
|
||||||
/*jshint validthis:true */
|
|
||||||
var $input = $(this);
|
|
||||||
var $link = $input.prev();
|
|
||||||
var newValue = $input.val();
|
|
||||||
|
|
||||||
if (newValue !== '' || part.def.params[paramIndex].optional) {
|
|
||||||
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
|
|
||||||
|
|
||||||
part.updateParam($input.val(), paramIndex);
|
|
||||||
$scope.$apply($scope.partUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
$input.hide();
|
|
||||||
$link.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function inputKeyPress(paramIndex, e) {
|
|
||||||
/*jshint validthis:true */
|
|
||||||
if(e.which === 13) {
|
|
||||||
inputBlur.call(this, paramIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function inputKeyDown() {
|
|
||||||
/*jshint validthis:true */
|
|
||||||
this.style.width = (3 + this.value.length) * 8 + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTypeahead($input, param, paramIndex) {
|
|
||||||
if (!param.options && !param.dynamicLookup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var typeaheadSource = function (query, callback) {
|
|
||||||
if (param.options) { return param.options; }
|
|
||||||
|
|
||||||
$scope.$apply(function() {
|
|
||||||
$scope.getOptions().then(function(result) {
|
|
||||||
var dynamicOptions = _.map(result, function(op) { return op.value; });
|
|
||||||
callback(dynamicOptions);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$input.attr('data-provide', 'typeahead');
|
|
||||||
var options = param.options;
|
|
||||||
if (param.type === 'int') {
|
|
||||||
options = _.map(options, function(val) { return val.toString(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
$input.typeahead({
|
|
||||||
source: typeaheadSource,
|
|
||||||
minLength: 0,
|
|
||||||
items: 1000,
|
|
||||||
updater: function (value) {
|
|
||||||
setTimeout(function() {
|
|
||||||
inputBlur.call($input[0], paramIndex);
|
|
||||||
}, 0);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var typeahead = $input.data('typeahead');
|
|
||||||
typeahead.lookup = function () {
|
|
||||||
this.query = this.$element.val() || '';
|
|
||||||
var items = this.source(this.query, $.proxy(this.process, this));
|
|
||||||
return items ? this.process(items) : items;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.toggleControls = function() {
|
|
||||||
var targetDiv = elem.closest('.tight-form');
|
|
||||||
|
|
||||||
if (elem.hasClass('show-function-controls')) {
|
|
||||||
elem.removeClass('show-function-controls');
|
|
||||||
targetDiv.removeClass('has-open-function');
|
|
||||||
$controlsContainer.hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
elem.addClass('show-function-controls');
|
|
||||||
targetDiv.addClass('has-open-function');
|
|
||||||
$controlsContainer.show();
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.removeActionInternal = function() {
|
|
||||||
$scope.toggleControls();
|
|
||||||
$scope.removeAction();
|
|
||||||
};
|
|
||||||
|
|
||||||
function addElementsAndCompile() {
|
|
||||||
_.each(partDef.params, function(param, index) {
|
|
||||||
if (param.optional && part.params.length <= index) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index > 0) {
|
|
||||||
$('<span>, </span>').appendTo($paramsContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
|
|
||||||
var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
|
|
||||||
var $input = $(paramTemplate);
|
|
||||||
|
|
||||||
$paramLink.appendTo($paramsContainer);
|
|
||||||
$input.appendTo($paramsContainer);
|
|
||||||
|
|
||||||
$input.blur(_.partial(inputBlur, index));
|
|
||||||
$input.keyup(inputKeyDown);
|
|
||||||
$input.keypress(_.partial(inputKeyPress, index));
|
|
||||||
$paramLink.click(_.partial(clickFuncParam, index));
|
|
||||||
|
|
||||||
addTypeahead($input, param, index);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function relink() {
|
|
||||||
$paramsContainer.empty();
|
|
||||||
addElementsAndCompile();
|
|
||||||
}
|
|
||||||
|
|
||||||
relink();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user