graphite: datasource refactor
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
import _ from 'lodash';
|
||||
import gfunc from './gfunc';
|
||||
import {Parser} from './parser';
|
||||
|
||||
export default class GraphiteQuery {
|
||||
target: any;
|
||||
functions: any[];
|
||||
segments: any[];
|
||||
tags: any[];
|
||||
error: any;
|
||||
seriesByTagUsed: boolean;
|
||||
checkOtherSegmentsIndex: number;
|
||||
removeTagValue: string;
|
||||
templateSrv: any;
|
||||
scopedVars: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(target, templateSrv?, scopedVars?) {
|
||||
this.target = target;
|
||||
this.parseTarget();
|
||||
|
||||
this.removeTagValue = '-- remove tag --';
|
||||
}
|
||||
|
||||
parseTarget() {
|
||||
this.functions = [];
|
||||
this.segments = [];
|
||||
this.error = null;
|
||||
|
||||
if (this.target.textEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parser = new Parser(this.target.target);
|
||||
var astNode = parser.getAst();
|
||||
if (astNode === null) {
|
||||
this.checkOtherSegmentsIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (astNode.type === 'error') {
|
||||
this.error = astNode.message + " at position: " + astNode.pos;
|
||||
this.target.textEditor = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.parseTargetRecursive(astNode, null, 0);
|
||||
} catch (err) {
|
||||
console.log('error parsing target:', err.message);
|
||||
this.error = err.message;
|
||||
this.target.textEditor = true;
|
||||
}
|
||||
|
||||
this.checkOtherSegmentsIndex = this.segments.length - 1;
|
||||
this.checkForSeriesByTag();
|
||||
}
|
||||
|
||||
checkForSeriesByTag() {
|
||||
let seriesByTagFunc = _.find(this.functions, (func) => func.def.name === 'seriesByTag');
|
||||
if (seriesByTagFunc) {
|
||||
this.seriesByTagUsed = true;
|
||||
seriesByTagFunc.hidden = true;
|
||||
let tags = this.splitSeriesByTagParams(seriesByTagFunc);
|
||||
this.tags = tags;
|
||||
}
|
||||
}
|
||||
|
||||
getSegmentPathUpTo(index) {
|
||||
var arr = this.segments.slice(0, index);
|
||||
|
||||
return _.reduce(arr, function(result, segment) {
|
||||
return result ? (result + "." + segment.value) : segment.value;
|
||||
}, "");
|
||||
}
|
||||
|
||||
parseTargetRecursive(astNode, func, index) {
|
||||
if (astNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (astNode.type) {
|
||||
case 'function':
|
||||
var innerFunc = gfunc.createFuncInstance(astNode.name, { withDefaultParams: false });
|
||||
_.each(astNode.params, (param, index) => {
|
||||
this.parseTargetRecursive(param, innerFunc, index);
|
||||
});
|
||||
|
||||
innerFunc.updateText();
|
||||
this.functions.push(innerFunc);
|
||||
break;
|
||||
case 'series-ref':
|
||||
this.addFunctionParameter(func, astNode.value, index, this.segments.length > 0);
|
||||
break;
|
||||
case 'bool':
|
||||
case 'string':
|
||||
case 'number':
|
||||
if ((index-1) >= func.def.params.length) {
|
||||
throw { message: 'invalid number of parameters to method ' + func.def.name };
|
||||
}
|
||||
var shiftBack = this.isShiftParamsBack(func);
|
||||
this.addFunctionParameter(func, astNode.value, index, shiftBack);
|
||||
break;
|
||||
case 'metric':
|
||||
if (this.segments.length > 0) {
|
||||
if (astNode.segments.length !== 1) {
|
||||
throw { message: 'Multiple metric params not supported, use text editor.' };
|
||||
}
|
||||
this.addFunctionParameter(func, astNode.segments[0].value, index, true);
|
||||
break;
|
||||
}
|
||||
|
||||
this.segments = astNode.segments;
|
||||
}
|
||||
}
|
||||
|
||||
isShiftParamsBack(func) {
|
||||
return func.def.name !== 'seriesByTag';
|
||||
}
|
||||
|
||||
updateSegmentValue(segment, index) {
|
||||
this.segments[index].value = segment.value;
|
||||
}
|
||||
|
||||
addSelectMetricSegment() {
|
||||
this.segments.push({value: "select metric"});
|
||||
}
|
||||
|
||||
addFunction(newFunc) {
|
||||
this.functions.push(newFunc);
|
||||
this.moveAliasFuncLast();
|
||||
}
|
||||
|
||||
moveAliasFuncLast() {
|
||||
var aliasFunc = _.find(this.functions, function(func) {
|
||||
return func.def.name === 'alias' ||
|
||||
func.def.name === 'aliasByNode' ||
|
||||
func.def.name === 'aliasByMetric';
|
||||
});
|
||||
|
||||
if (aliasFunc) {
|
||||
this.functions = _.without(this.functions, aliasFunc);
|
||||
this.functions.push(aliasFunc);
|
||||
}
|
||||
}
|
||||
|
||||
addFunctionParameter(func, value, index, shiftBack) {
|
||||
if (shiftBack) {
|
||||
index = Math.max(index - 1, 0);
|
||||
}
|
||||
func.params[index] = value;
|
||||
}
|
||||
|
||||
removeFunction(func) {
|
||||
this.functions = _.without(this.functions, func);
|
||||
}
|
||||
|
||||
updateModelTarget(targets) {
|
||||
// render query
|
||||
if (!this.target.textEditor) {
|
||||
var metricPath = this.getSegmentPathUpTo(this.segments.length);
|
||||
this.target.target = _.reduce(this.functions, wrapFunction, metricPath);
|
||||
}
|
||||
|
||||
this.updateRenderedTarget(this.target, targets);
|
||||
|
||||
// loop through other queries and update targetFull as needed
|
||||
for (const target of targets || []) {
|
||||
if (target.refId !== this.target.refId) {
|
||||
this.updateRenderedTarget(target, targets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateRenderedTarget(target, targets) {
|
||||
// render nested query
|
||||
var targetsByRefId = _.keyBy(targets, 'refId');
|
||||
|
||||
// no references to self
|
||||
delete targetsByRefId[target.refId];
|
||||
|
||||
var nestedSeriesRefRegex = /\#([A-Z])/g;
|
||||
var targetWithNestedQueries = target.target;
|
||||
|
||||
// Keep interpolating until there are no query references
|
||||
// The reason for the loop is that the referenced query might contain another reference to another query
|
||||
while (targetWithNestedQueries.match(nestedSeriesRefRegex)) {
|
||||
var updated = targetWithNestedQueries.replace(nestedSeriesRefRegex, (match, g1) => {
|
||||
var t = targetsByRefId[g1];
|
||||
if (!t) {
|
||||
return match;
|
||||
}
|
||||
|
||||
// no circular references
|
||||
delete targetsByRefId[g1];
|
||||
return t.target;
|
||||
});
|
||||
|
||||
if (updated === targetWithNestedQueries) {
|
||||
break;
|
||||
}
|
||||
|
||||
targetWithNestedQueries = updated;
|
||||
}
|
||||
|
||||
delete target.targetFull;
|
||||
if (target.target !== targetWithNestedQueries) {
|
||||
target.targetFull = targetWithNestedQueries;
|
||||
}
|
||||
}
|
||||
|
||||
splitSeriesByTagParams(func) {
|
||||
const tagPattern = /([^\!=~]+)([\!=~]+)([^\!=~]+)/;
|
||||
return _.flatten(_.map(func.params, (param: string) => {
|
||||
let matches = tagPattern.exec(param);
|
||||
if (matches) {
|
||||
let tag = matches.slice(1);
|
||||
if (tag.length === 3) {
|
||||
return {
|
||||
key: tag[0],
|
||||
operator: tag[1],
|
||||
value: tag[2]
|
||||
};
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}));
|
||||
}
|
||||
|
||||
getSeriesByTagFuncIndex() {
|
||||
return _.findIndex(this.functions, (func) => func.def.name === 'seriesByTag');
|
||||
}
|
||||
|
||||
getSeriesByTagFunc() {
|
||||
let seriesByTagFuncIndex = this.getSeriesByTagFuncIndex();
|
||||
if (seriesByTagFuncIndex >= 0) {
|
||||
return this.functions[seriesByTagFuncIndex];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
addTag(tag) {
|
||||
let newTagParam = renderTagString(tag);
|
||||
this.getSeriesByTagFunc().params.push(newTagParam);
|
||||
this.tags.push(tag);
|
||||
}
|
||||
|
||||
removeTag(index) {
|
||||
this.getSeriesByTagFunc().params.splice(index, 1);
|
||||
this.tags.splice(index, 1);
|
||||
}
|
||||
|
||||
updateTag(tag, tagIndex) {
|
||||
this.error = null;
|
||||
|
||||
if (tag.key === this.removeTagValue) {
|
||||
this.removeTag(tagIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
let newTagParam = renderTagString(tag);
|
||||
this.getSeriesByTagFunc().params[tagIndex] = newTagParam;
|
||||
this.tags[tagIndex] = tag;
|
||||
}
|
||||
}
|
||||
|
||||
function wrapFunction(target, func) {
|
||||
return func.render(target);
|
||||
}
|
||||
|
||||
function renderTagString(tag) {
|
||||
return tag.key + tag.operator + tag.value;
|
||||
}
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
<div ng-hide="ctrl.target.textEditor">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form" ng-if="ctrl.seriesByTagUsed">
|
||||
<div class="gf-form" ng-if="ctrl.queryModel.seriesByTagUsed">
|
||||
<label class="gf-form-label query-keyword">seriesByTag</label>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="tag in ctrl.tags" class="gf-form">
|
||||
<div ng-repeat="tag in ctrl.queryModel.tags" class="gf-form">
|
||||
<gf-form-dropdown model="tag.key" lookup-text="false" allow-custom="false" label-mode="true" css-class="query-segment-key"
|
||||
get-options="ctrl.getTags()"
|
||||
on-change="ctrl.tagChanged(tag, $index)">
|
||||
@@ -25,7 +25,7 @@
|
||||
</gf-form-dropdown>
|
||||
<label class="gf-form-label query-keyword" ng-if="ctrl.showDelimiter($index)">,</label>
|
||||
</div>
|
||||
<div ng-if="ctrl.seriesByTagUsed" ng-repeat="segment in ctrl.addTagSegments" role="menuitem" class="gf-form">
|
||||
<div ng-if="ctrl.queryModel.seriesByTagUsed" ng-repeat="segment in ctrl.addTagSegments" role="menuitem" class="gf-form">
|
||||
<metric-segment segment="segment"
|
||||
get-options="ctrl.getTagsAsSegments()"
|
||||
on-change="ctrl.addNewTag(segment)">
|
||||
@@ -36,8 +36,8 @@
|
||||
<metric-segment segment="segment" get-options="ctrl.getAltSegments($index)" on-change="ctrl.segmentValueChanged(segment, $index)"></metric-segment>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="func in ctrl.functions" class="gf-form">
|
||||
<span graphite-func-editor class="gf-form-label query-part" ng-hide="ctrl.getSeriesByTagFuncIndex() === $index"></span>
|
||||
<div ng-repeat="func in ctrl.queryModel.functions" class="gf-form">
|
||||
<span graphite-func-editor class="gf-form-label query-part" ng-hide="func.hidden"></span>
|
||||
</div>
|
||||
|
||||
<div class="gf-form dropdown">
|
||||
|
||||
@@ -3,7 +3,7 @@ import './func_editor';
|
||||
|
||||
import _ from 'lodash';
|
||||
import gfunc from './gfunc';
|
||||
import {Parser} from './parser';
|
||||
import GraphiteQuery from './graphite_query';
|
||||
import {QueryCtrl} from 'app/plugins/sdk';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
@@ -12,11 +12,9 @@ const GRAPHITE_TAG_OPERATORS = ['=', '!=', '=~', '!=~'];
|
||||
export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
static templateUrl = 'partials/query.editor.html';
|
||||
|
||||
functions: any[];
|
||||
queryModel: GraphiteQuery;
|
||||
segments: any[];
|
||||
addTagSegments: any[];
|
||||
tags: any[];
|
||||
seriesByTagUsed: boolean;
|
||||
removeTagValue: string;
|
||||
|
||||
/** @ngInject **/
|
||||
@@ -25,119 +23,47 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
|
||||
if (this.target) {
|
||||
this.target.target = this.target.target || '';
|
||||
this.parseTarget();
|
||||
this.queryModel = new GraphiteQuery(this.target, templateSrv);
|
||||
this.buildSegments();
|
||||
}
|
||||
|
||||
this.removeTagValue = '-- remove tag --';
|
||||
}
|
||||
|
||||
parseTarget() {
|
||||
this.queryModel.parseTarget();
|
||||
this.buildSegments();
|
||||
}
|
||||
|
||||
toggleEditorMode() {
|
||||
this.target.textEditor = !this.target.textEditor;
|
||||
this.parseTarget();
|
||||
}
|
||||
|
||||
parseTarget() {
|
||||
this.functions = [];
|
||||
this.segments = [];
|
||||
this.error = null;
|
||||
buildSegments() {
|
||||
this.segments = _.map(this.queryModel.segments, segment => {
|
||||
return this.uiSegmentSrv.newSegment(segment);
|
||||
});
|
||||
let checkOtherSegmentsIndex = this.queryModel.checkOtherSegmentsIndex || 0;
|
||||
this.checkOtherSegments(checkOtherSegmentsIndex);
|
||||
|
||||
if (this.target.textEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parser = new Parser(this.target.target);
|
||||
var astNode = parser.getAst();
|
||||
if (astNode === null) {
|
||||
this.checkOtherSegments(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (astNode.type === 'error') {
|
||||
this.error = astNode.message + " at position: " + astNode.pos;
|
||||
this.target.textEditor = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.parseTargetRecursive(astNode, null, 0);
|
||||
} catch (err) {
|
||||
console.log('error parsing target:', err.message);
|
||||
this.error = err.message;
|
||||
this.target.textEditor = true;
|
||||
}
|
||||
|
||||
this.checkOtherSegments(this.segments.length - 1);
|
||||
this.checkForSeriesByTag();
|
||||
}
|
||||
|
||||
addFunctionParameter(func, value, index, shiftBack) {
|
||||
if (shiftBack) {
|
||||
index = Math.max(index - 1, 0);
|
||||
}
|
||||
func.params[index] = value;
|
||||
}
|
||||
|
||||
parseTargetRecursive(astNode, func, index) {
|
||||
if (astNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (astNode.type) {
|
||||
case 'function':
|
||||
var innerFunc = gfunc.createFuncInstance(astNode.name, { withDefaultParams: false });
|
||||
_.each(astNode.params, (param, index) => {
|
||||
this.parseTargetRecursive(param, innerFunc, index);
|
||||
});
|
||||
|
||||
innerFunc.updateText();
|
||||
this.functions.push(innerFunc);
|
||||
break;
|
||||
case 'series-ref':
|
||||
this.addFunctionParameter(func, astNode.value, index, this.segments.length > 0);
|
||||
break;
|
||||
case 'bool':
|
||||
case 'string':
|
||||
case 'number':
|
||||
if ((index-1) >= func.def.params.length) {
|
||||
throw { message: 'invalid number of parameters to method ' + func.def.name };
|
||||
}
|
||||
var shiftBack = this.isShiftParamsBack(func);
|
||||
this.addFunctionParameter(func, astNode.value, index, shiftBack);
|
||||
break;
|
||||
case 'metric':
|
||||
if (this.segments.length > 0) {
|
||||
if (astNode.segments.length !== 1) {
|
||||
throw { message: 'Multiple metric params not supported, use text editor.' };
|
||||
}
|
||||
this.addFunctionParameter(func, astNode.segments[0].value, index, true);
|
||||
break;
|
||||
}
|
||||
|
||||
this.segments = _.map(astNode.segments, segment => {
|
||||
return this.uiSegmentSrv.newSegment(segment);
|
||||
});
|
||||
if (this.queryModel.seriesByTagUsed) {
|
||||
this.fixTagSegments();
|
||||
}
|
||||
}
|
||||
|
||||
isShiftParamsBack(func) {
|
||||
return func.def.name !== 'seriesByTag';
|
||||
}
|
||||
|
||||
getSegmentPathUpTo(index) {
|
||||
var arr = this.segments.slice(0, index);
|
||||
|
||||
return _.reduce(arr, function(result, segment) {
|
||||
return result ? (result + "." + segment.value) : segment.value;
|
||||
}, "");
|
||||
addSelectMetricSegment() {
|
||||
this.queryModel.addSelectMetricSegment();
|
||||
this.segments.push(this.uiSegmentSrv.newSelectMetric());
|
||||
}
|
||||
|
||||
checkOtherSegments(fromIndex) {
|
||||
if (fromIndex === 0) {
|
||||
this.segments.push(this.uiSegmentSrv.newSelectMetric());
|
||||
this.addSelectMetricSegment();
|
||||
return;
|
||||
}
|
||||
|
||||
var path = this.getSegmentPathUpTo(fromIndex + 1);
|
||||
var path = this.queryModel.getSegmentPathUpTo(fromIndex + 1);
|
||||
if (path === "") {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -145,12 +71,13 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
return this.datasource.metricFindQuery(path).then(segments => {
|
||||
if (segments.length === 0) {
|
||||
if (path !== '') {
|
||||
this.queryModel.segments = this.queryModel.segments.splice(0, fromIndex);
|
||||
this.segments = this.segments.splice(0, fromIndex);
|
||||
this.segments.push(this.uiSegmentSrv.newSelectMetric());
|
||||
this.addSelectMetricSegment();
|
||||
}
|
||||
} else if (segments[0].expandable) {
|
||||
if (this.segments.length === fromIndex) {
|
||||
this.segments.push(this.uiSegmentSrv.newSelectMetric());
|
||||
this.addSelectMetricSegment();
|
||||
} else {
|
||||
return this.checkOtherSegments(fromIndex + 1);
|
||||
}
|
||||
@@ -166,12 +93,8 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
wrapFunction(target, func) {
|
||||
return func.render(target);
|
||||
}
|
||||
|
||||
getAltSegments(index) {
|
||||
var query = index === 0 ? '*' : this.getSegmentPathUpTo(index) + '.*';
|
||||
var query = index === 0 ? '*' : this.queryModel.getSegmentPathUpTo(index) + '.*';
|
||||
var options = {range: this.panelCtrl.range, requestId: "get-alt-segments"};
|
||||
|
||||
return this.datasource.metricFindQuery(query, options).then(segments => {
|
||||
@@ -200,9 +123,10 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
|
||||
segmentValueChanged(segment, segmentIndex) {
|
||||
this.error = null;
|
||||
this.queryModel.updateSegmentValue(segment, segmentIndex);
|
||||
|
||||
if (this.functions.length > 0 && this.functions[0].def.fake) {
|
||||
this.functions = [];
|
||||
if (this.queryModel.functions.length > 0 && this.queryModel.functions[0].def.fake) {
|
||||
this.queryModel.functions = [];
|
||||
}
|
||||
|
||||
if (segment.expandable) {
|
||||
@@ -211,81 +135,41 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
this.targetChanged();
|
||||
});
|
||||
} else {
|
||||
this.segments = this.segments.splice(0, segmentIndex + 1);
|
||||
this.spliceSegments(segmentIndex + 1);
|
||||
}
|
||||
|
||||
this.setSegmentFocus(segmentIndex + 1);
|
||||
this.targetChanged();
|
||||
}
|
||||
|
||||
spliceSegments(index) {
|
||||
this.segments = this.segments.splice(0, index);
|
||||
this.queryModel.segments = this.queryModel.segments.splice(0, index);
|
||||
}
|
||||
|
||||
emptySegments() {
|
||||
this.queryModel.segments = [];
|
||||
this.segments = [];
|
||||
}
|
||||
|
||||
targetTextChanged() {
|
||||
this.updateModelTarget();
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
updateModelTarget() {
|
||||
// render query
|
||||
if (!this.target.textEditor) {
|
||||
var metricPath = this.getSegmentPathUpTo(this.segments.length);
|
||||
this.target.target = _.reduce(this.functions, this.wrapFunction, metricPath);
|
||||
}
|
||||
|
||||
this.updateRenderedTarget(this.target);
|
||||
|
||||
// loop through other queries and update targetFull as needed
|
||||
for (const target of this.panelCtrl.panel.targets || []) {
|
||||
if (target.refId !== this.target.refId) {
|
||||
this.updateRenderedTarget(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateRenderedTarget(target) {
|
||||
// render nested query
|
||||
var targetsByRefId = _.keyBy(this.panelCtrl.panel.targets, 'refId');
|
||||
|
||||
// no references to self
|
||||
delete targetsByRefId[target.refId];
|
||||
|
||||
var nestedSeriesRefRegex = /\#([A-Z])/g;
|
||||
var targetWithNestedQueries = target.target;
|
||||
|
||||
// Keep interpolating until there are no query references
|
||||
// The reason for the loop is that the referenced query might contain another reference to another query
|
||||
while (targetWithNestedQueries.match(nestedSeriesRefRegex)) {
|
||||
var updated = targetWithNestedQueries.replace(nestedSeriesRefRegex, (match, g1) => {
|
||||
var t = targetsByRefId[g1];
|
||||
if (!t) {
|
||||
return match;
|
||||
}
|
||||
|
||||
// no circular references
|
||||
delete targetsByRefId[g1];
|
||||
return t.target;
|
||||
});
|
||||
|
||||
if (updated === targetWithNestedQueries) {
|
||||
break;
|
||||
}
|
||||
|
||||
targetWithNestedQueries = updated;
|
||||
}
|
||||
|
||||
delete target.targetFull;
|
||||
if (target.target !== targetWithNestedQueries) {
|
||||
target.targetFull = targetWithNestedQueries;
|
||||
}
|
||||
this.queryModel.updateModelTarget(this.panelCtrl.panel.targets);
|
||||
}
|
||||
|
||||
targetChanged() {
|
||||
if (this.error) {
|
||||
if (this.queryModel.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
var oldTarget = this.target.target;
|
||||
var oldTarget = this.queryModel.target.target;
|
||||
this.updateModelTarget();
|
||||
|
||||
if (this.target.target !== oldTarget) {
|
||||
if (this.queryModel.target !== oldTarget) {
|
||||
var lastSegment = this.segments.length > 0 ? this.segments[this.segments.length - 1] : {};
|
||||
if (lastSegment.value !== 'select metric') {
|
||||
this.panelCtrl.refresh();
|
||||
@@ -293,21 +177,14 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
removeFunction(func) {
|
||||
this.functions = _.without(this.functions, func);
|
||||
this.targetChanged();
|
||||
}
|
||||
|
||||
addFunction(funcDef) {
|
||||
var newFunc = gfunc.createFuncInstance(funcDef, { withDefaultParams: true });
|
||||
newFunc.added = true;
|
||||
this.functions.push(newFunc);
|
||||
|
||||
this.moveAliasFuncLast();
|
||||
this.queryModel.addFunction(newFunc);
|
||||
this.smartlyHandleNewAliasByNode(newFunc);
|
||||
|
||||
if (this.segments.length === 1 && this.segments[0].fake) {
|
||||
this.segments = [];
|
||||
this.emptySegments();
|
||||
}
|
||||
|
||||
if (!newFunc.params.length && newFunc.added) {
|
||||
@@ -319,17 +196,9 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
moveAliasFuncLast() {
|
||||
var aliasFunc = _.find(this.functions, function(func) {
|
||||
return func.def.name === 'alias' ||
|
||||
func.def.name === 'aliasByNode' ||
|
||||
func.def.name === 'aliasByMetric';
|
||||
});
|
||||
|
||||
if (aliasFunc) {
|
||||
this.functions = _.without(this.functions, aliasFunc);
|
||||
this.functions.push(aliasFunc);
|
||||
}
|
||||
removeFunction(func) {
|
||||
this.queryModel.removeFunction(func);
|
||||
this.targetChanged();
|
||||
}
|
||||
|
||||
smartlyHandleNewAliasByNode(func) {
|
||||
@@ -338,7 +207,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.segments.length; i++) {
|
||||
if (this.segments[i].value.indexOf('*') >= 0) {
|
||||
if (this.segments[i].value.indexOf('*') >= 0) {
|
||||
func.params[0] = i;
|
||||
func.added = false;
|
||||
this.targetChanged();
|
||||
@@ -347,38 +216,6 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// Graphite seriesByTag support //
|
||||
//////////////////////////////////
|
||||
|
||||
checkForSeriesByTag() {
|
||||
let seriesByTagFunc = _.find(this.functions, (func) => func.def.name === 'seriesByTag');
|
||||
if (seriesByTagFunc) {
|
||||
this.seriesByTagUsed = true;
|
||||
let tags = this.splitSeriesByTagParams(seriesByTagFunc);
|
||||
this.tags = tags;
|
||||
this.fixTagSegments();
|
||||
}
|
||||
}
|
||||
|
||||
splitSeriesByTagParams(func) {
|
||||
const tagPattern = /([^\!=~]+)([\!=~]+)([^\!=~]+)/;
|
||||
return _.flatten(_.map(func.params, (param: string) => {
|
||||
let matches = tagPattern.exec(param);
|
||||
if (matches) {
|
||||
let tag = matches.slice(1);
|
||||
if (tag.length === 3) {
|
||||
return {
|
||||
key: tag[0],
|
||||
operator: tag[1],
|
||||
value: tag[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}));
|
||||
}
|
||||
|
||||
getTags() {
|
||||
return this.datasource.getTags().then((values) => {
|
||||
let altTags = _.map(values, 'text');
|
||||
@@ -408,45 +245,20 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
}
|
||||
|
||||
tagChanged(tag, tagIndex) {
|
||||
this.error = null;
|
||||
|
||||
if (tag.key === this.removeTagValue) {
|
||||
this.removeTag(tagIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
let newTagParam = renderTagString(tag);
|
||||
this.getSeriesByTagFunc().params[tagIndex] = newTagParam;
|
||||
this.tags[tagIndex] = tag;
|
||||
this.queryModel.updateTag(tag, tagIndex);
|
||||
this.targetChanged();
|
||||
}
|
||||
|
||||
getSeriesByTagFuncIndex() {
|
||||
return _.findIndex(this.functions, (func) => func.def.name === 'seriesByTag');
|
||||
}
|
||||
|
||||
getSeriesByTagFunc() {
|
||||
let seriesByTagFuncIndex = this.getSeriesByTagFuncIndex();
|
||||
if (seriesByTagFuncIndex >= 0) {
|
||||
return this.functions[seriesByTagFuncIndex];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
addNewTag(segment) {
|
||||
let newTagKey = segment.value;
|
||||
let newTag = {key: newTagKey, operator: '=', value: 'select tag value'};
|
||||
let newTagParam = renderTagString(newTag);
|
||||
this.getSeriesByTagFunc().params.push(newTagParam);
|
||||
this.tags.push(newTag);
|
||||
this.queryModel.addTag(newTag);
|
||||
this.targetChanged();
|
||||
this.fixTagSegments();
|
||||
}
|
||||
|
||||
removeTag(index) {
|
||||
this.getSeriesByTagFunc().params.splice(index, 1);
|
||||
this.tags.splice(index, 1);
|
||||
this.queryModel.removeTag(index);
|
||||
this.targetChanged();
|
||||
}
|
||||
|
||||
@@ -456,14 +268,10 @@ export class GraphiteQueryCtrl extends QueryCtrl {
|
||||
}
|
||||
|
||||
showDelimiter(index) {
|
||||
return index !== this.tags.length - 1;
|
||||
return index !== this.queryModel.tags.length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
function renderTagString(tag) {
|
||||
return tag.key + tag.operator + tag.value;
|
||||
}
|
||||
|
||||
function mapToDropdownOptions(results) {
|
||||
return _.map(results, (value) => {
|
||||
return {text: value, value: value};
|
||||
|
||||
@@ -48,7 +48,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
});
|
||||
|
||||
it('should parse expression and build function model', function() {
|
||||
expect(ctx.ctrl.functions.length).to.be(2);
|
||||
expect(ctx.ctrl.queryModel.functions.length).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
});
|
||||
|
||||
it('should add function with correct node number', function() {
|
||||
expect(ctx.ctrl.functions[0].params[0]).to.be(2);
|
||||
expect(ctx.ctrl.queryModel.functions[0].params[0]).to.be(2);
|
||||
});
|
||||
|
||||
it('should update target', function() {
|
||||
@@ -99,7 +99,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
});
|
||||
|
||||
it('should add both series refs as params', function() {
|
||||
expect(ctx.ctrl.functions[0].params.length).to.be(2);
|
||||
expect(ctx.ctrl.queryModel.functions[0].params.length).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
});
|
||||
|
||||
it('should add function param', function() {
|
||||
expect(ctx.ctrl.functions[0].params.length).to.be(1);
|
||||
expect(ctx.ctrl.queryModel.functions[0].params.length).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,7 +131,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
});
|
||||
|
||||
it('should have correct func params', function() {
|
||||
expect(ctx.ctrl.functions[0].params.length).to.be(1);
|
||||
expect(ctx.ctrl.queryModel.functions[0].params.length).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -219,11 +219,11 @@ describe('GraphiteQueryCtrl', function() {
|
||||
});
|
||||
|
||||
it('should update functions', function() {
|
||||
expect(ctx.ctrl.getSeriesByTagFuncIndex()).to.be(0);
|
||||
expect(ctx.ctrl.queryModel.getSeriesByTagFuncIndex()).to.be(0);
|
||||
});
|
||||
|
||||
it('should update seriesByTagUsed flag', function() {
|
||||
expect(ctx.ctrl.seriesByTagUsed).to.be(true);
|
||||
expect(ctx.ctrl.queryModel.seriesByTagUsed).to.be(true);
|
||||
});
|
||||
|
||||
it('should update target', function() {
|
||||
@@ -247,7 +247,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
{key: 'tag1', operator: '=', value: 'value1'},
|
||||
{key: 'tag2', operator: '!=~', value: 'value2'}
|
||||
];
|
||||
expect(ctx.ctrl.tags).to.eql(expected);
|
||||
expect(ctx.ctrl.queryModel.tags).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should add plus button', function() {
|
||||
@@ -267,7 +267,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
const expected = [
|
||||
{key: 'tag1', operator: '=', value: 'select tag value'}
|
||||
];
|
||||
expect(ctx.ctrl.tags).to.eql(expected);
|
||||
expect(ctx.ctrl.queryModel.tags).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should update target', function() {
|
||||
@@ -289,7 +289,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
{key: 'tag1', operator: '=', value: 'new_value'},
|
||||
{key: 'tag2', operator: '!=~', value: 'value2'}
|
||||
];
|
||||
expect(ctx.ctrl.tags).to.eql(expected);
|
||||
expect(ctx.ctrl.queryModel.tags).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should update target', function() {
|
||||
@@ -310,7 +310,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
const expected = [
|
||||
{key: 'tag2', operator: '!=~', value: 'value2'}
|
||||
];
|
||||
expect(ctx.ctrl.tags).to.eql(expected);
|
||||
expect(ctx.ctrl.queryModel.tags).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should update target', function() {
|
||||
|
||||
Reference in New Issue
Block a user