Commit 7e8b2798 by Torkel Ödegaard

feat(templating): good progress on new variable update code, #6048

parent 0f4a9f1e
......@@ -2,7 +2,7 @@ define([
'./panellinks/module',
'./dashlinks/module',
'./annotations/annotations_srv',
'./templating/templateSrv',
'./templating/all',
'./dashboard/all',
'./playlist/all',
'./snapshot/all',
......
import './templateSrv';
import './templateValuesSrv';
import './editorCtrl';
import {VariableSrv} from './variable_srv';
import {IntervalVariable} from './interval_variable';
import {QueryVariable} from './query_variable';
export {
VariableSrv,
IntervalVariable,
QueryVariable,
}
///<reference path="../../headers/common.d.ts" />
import _ from 'lodash';
import kbn from 'app/core/utils/kbn';
import {Variable} from './variable';
import {VariableSrv, variableConstructorMap} from './variable_srv';
export class IntervalVariable implements Variable {
auto_count: number;
auto_min: number;
options: any;
auto: boolean;
query: string;
/** @ngInject */
constructor(private model, private timeSrv, private templateSrv) {
_.extend(this, model);
}
setValue(option) {
if (this.auto) {
this.updateAutoValue();
}
}
updateAutoValue() {
// add auto option if missing
if (this.options.length && this.options[0].text !== 'auto') {
this.options.unshift({ text: 'auto', value: '$__auto_interval' });
}
var interval = kbn.calculateInterval(this.timeSrv.timeRange(), this.auto_count, (this.auto_min ? ">"+this.auto_min : null));
this.templateSrv.setGrafanaVariable('$__auto_interval', interval);
}
updateOptions() {
// extract options in comma separated string
this.options = _.map(this.query.split(/[,]+/), function(text) {
return {text: text.trim(), value: text.trim()};
});
if (this.auto) {
this.updateAutoValue();
}
}
}
variableConstructorMap['interval'] = IntervalVariable;
///<reference path="../../headers/common.d.ts" />
import _ from 'lodash';
import kbn from 'app/core/utils/kbn';
import {Variable} from './variable';
import {VariableSrv, variableConstructorMap} from './variable_srv';
function getNoneOption() {
return { text: 'None', value: '', isNone: true };
}
export class QueryVariable implements Variable {
datasource: any;
query: any;
regex: any;
sort: any;
options: any;
current: any;
includeAll: boolean;
constructor(private model, private datasourceSrv, private templateSrv, private variableSrv) {
_.extend(this, model);
}
setValue(option){
this.current = _.cloneDeep(option);
if (_.isArray(this.current.text)) {
this.current.text = this.current.text.join(' + ');
}
this.variableSrv.selectOptionsForCurrentValue(this);
return this.variableSrv.variableUpdated(this);
}
updateOptions() {
return this.datasourceSrv.get(this.datasource)
.then(this.updateOptionsFromMetricFindQuery.bind(this))
.then(() => {
this.variableSrv.validateVariableSelectionState(this);
});
}
updateOptionsFromMetricFindQuery(datasource) {
return datasource.metricFindQuery(this.query).then(results => {
this.options = this.metricNamesToVariableValues(results);
if (this.includeAll) {
this.addAllOption();
}
if (!this.options.length) {
this.options.push(getNoneOption());
}
return datasource;
});
}
addAllOption() {
this.options.unshift({text: 'All', value: "$__all"});
}
metricNamesToVariableValues(metricNames) {
var regex, options, i, matches;
options = [];
if (this.model.regex) {
regex = kbn.stringToJsRegex(this.templateSrv.replace(this.regex));
}
for (i = 0; i < metricNames.length; i++) {
var item = metricNames[i];
var value = item.value || item.text;
var text = item.text || item.value;
if (_.isNumber(value)) {
value = value.toString();
}
if (_.isNumber(text)) {
text = text.toString();
}
if (regex) {
matches = regex.exec(value);
if (!matches) { continue; }
if (matches.length > 1) {
value = matches[1];
text = value;
}
}
options.push({text: text, value: value});
}
options = _.uniq(options, 'value');
return this.sortVariableValues(options, this.sort);
}
sortVariableValues(options, sortOrder) {
if (sortOrder === 0) {
return options;
}
var sortType = Math.ceil(sortOrder / 2);
var reverseSort = (sortOrder % 2 === 0);
if (sortType === 1) {
options = _.sortBy(options, 'text');
} else if (sortType === 2) {
options = _.sortBy(options, function(opt) {
var matches = opt.text.match(new RegExp(".*?(\d+).*"));
if (!matches) {
return 0;
} else {
return parseInt(matches[1], 10);
}
});
}
if (reverseSort) {
options = options.reverse();
}
return options;
}
}
variableConstructorMap['query'] = QueryVariable;
define([
'angular',
'lodash',
'./editorCtrl',
'./variable_srv',
'./templateValuesSrv',
],
function (angular, _) {
'use strict';
......
export interface Variable {
setValue(option);
}
......@@ -3,49 +3,13 @@
import angular from 'angular';
import _ from 'lodash';
import $ from 'jquery';
import kbn from 'app/core/utils/kbn';
import coreModule from 'app/core/core_module';
import appEvents from 'app/core/app_events';
import {IntervalVariable} from './interval_variable';
import {Variable} from './variable';
interface Variable {
}
class ConstantVariable implements Variable {
constructor(private model) {
}
}
class CustomVariable implements Variable {
constructor(private model) {
}
}
class IntervalVariable implements Variable {
constructor(private model) {
}
}
class QueryVariable implements Variable {
constructor(private model,
private variableSrv: VariableSrv,
private datasourceSrv) {
_.extend(this, model);
}
updateOptions() {
return this.datasourceSrv.get(this.datasource)
.then(_.partial(this.updateOptionsFromMetricFindQuery, variable))
.then(_.partial(this.updateTags, variable))
.then(_.partial(this.validateVariableSelectionState, variable));
}
}
class DatasourceVariable implements Variable {
constructor(private model) {
}
}
export var variableConstructorMap: any = {};
export class VariableSrv {
dashboard: any;
......@@ -55,53 +19,116 @@ export class VariableSrv {
/** @ngInject */
constructor(
private $q,
private $rootScope,
private datasourceSrv,
private $q,
private $location,
private templateSrv,
private timeSrv) {
private $injector,
private templateSrv) {
}
init(dashboard) {
this.variableLock = {};
this.dashboard = dashboard;
this.variables = [];
}
dashboard.templating.list.map(this.addVariable.bind(this));
this.templateSrv.init(this.variables);
init(dashboard) {
this.variableLock = {};
this.dashboard = dashboard;
return this.$q.when();
}
this.variables = dashboard.templating.list.map(item => {
return new QueryVariable(item, this);
});
addVariable(model) {
var ctor = variableConstructorMap[model.type];
if (!ctor) {
throw "Unable to find variable constructor for " + model.type;
}
this.templateSrv.init(this.variables);
return this.$q.when();
}
var variable = this.$injector.instantiate(ctor, {model: model});
this.variables.push(variable);
this.dashboard.templating.list.push(model);
updateOptions(variable) {
return variable.updateOptions();
}
return variable;
}
variableUpdated(variable) {
// if there is a variable lock ignore cascading update because we are in a boot up scenario
if (this.variableLock[variable.name]) {
return this.$q.when();
updateOptions(variable) {
return variable.updateOptions();
}
var promises = _.map(this.variables, otherVariable => {
if (otherVariable === variable) {
return;
variableUpdated(variable) {
// if there is a variable lock ignore cascading update because we are in a boot up scenario
if (this.variableLock[variable.name]) {
return this.$q.when();
}
if (this.templateSrv.containsVariable(otherVariable.regex, variable.name) ||
this.templateSrv.containsVariable(otherVariable.query, variable.name) ||
this.templateSrv.containsVariable(otherVariable.datasource, variable.name)) {
var promises = _.map(this.variables, otherVariable => {
if (otherVariable === variable) {
return;
}
if (this.templateSrv.containsVariable(otherVariable.regex, variable.name) ||
this.templateSrv.containsVariable(otherVariable.query, variable.name) ||
this.templateSrv.containsVariable(otherVariable.datasource, variable.name)) {
return this.updateOptions(otherVariable);
}
});
});
return this.$q.all(promises);
}
return this.$q.all(promises);
}
selectOptionsForCurrentValue(variable) {
var i, y, value, option;
var selected: any = [];
for (i = 0; i < variable.options.length; i++) {
option = variable.options[i];
option.selected = false;
if (_.isArray(variable.current.value)) {
for (y = 0; y < variable.current.value.length; y++) {
value = variable.current.value[y];
if (option.value === value) {
option.selected = true;
selected.push(option);
}
}
} else if (option.value === variable.current.value) {
option.selected = true;
selected.push(option);
}
}
return selected;
}
validateVariableSelectionState(variable) {
if (!variable.current) {
if (!variable.options.length) { return Promise.resolve(); }
return variable.setValue(variable.options[0]);
}
if (_.isArray(variable.current.value)) {
var selected = this.selectOptionsForCurrentValue(variable);
// if none pick first
if (selected.length === 0) {
selected = variable.options[0];
} else {
selected = {
value: _.map(selected, function(val) {return val.value;}),
text: _.map(selected, function(val) {return val.text;}).join(' + '),
};
}
return variable.setValue(selected);
} else {
var currentOption = _.find(variable.options, {text: variable.current.text});
if (currentOption) {
return variable.setValue(currentOption);
} else {
if (!variable.options.length) { return Promise.resolve(); }
return variable.setValue(variable.options[0]);
}
}
}
}
coreModule.service('variableSrv', VariableSrv);
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