The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/templating/templateValuesSrv.js

356 lines
11 KiB

define([
'angular',
'lodash',
'app/core/utils/kbn',
],
function (angular, _, kbn) {
'use strict';
var module = angular.module('grafana.services');
module.service('templateValuesSrv', function($q, $rootScope, datasourceSrv, $location, templateSrv, timeSrv) {
var self = this;
function getNoneOption() { return { text: 'None', value: '', isNone: true }; }
// update time variant variables
$rootScope.onAppEvent('refresh', function() {
// look for interval variables
var intervalVariable = _.findWhere(self.variables, { type: 'interval' });
if (intervalVariable) {
self.updateAutoInterval(intervalVariable);
}
// update variables with refresh === 2
var promises = self.variables
.filter(function(variable) {
return variable.refresh === 2;
}).map(function(variable) {
return self.updateOptions(variable);
});
return $q.all(promises);
}, $rootScope);
this.init = function(dashboard) {
this.variables = dashboard.templating.list;
templateSrv.init(this.variables);
var queryParams = $location.search();
var promises = [];
// use promises to delay processing variables that
// depend on other variables.
this.variableLock = {};
_.forEach(this.variables, function(variable) {
self.variableLock[variable.name] = $q.defer();
});
for (var i = 0; i < this.variables.length; i++) {
var variable = this.variables[i];
promises.push(this.processVariable(variable, queryParams));
}
return $q.all(promises);
};
this.processVariable = function(variable, queryParams) {
var dependencies = [];
var lock = self.variableLock[variable.name];
// determine our dependencies.
if (variable.type === "query") {
_.forEach(this.variables, function(v) {
// both query and datasource can contain variable
if (templateSrv.containsVariable(variable.query, v.name) ||
templateSrv.containsVariable(variable.datasource, v.name)) {
dependencies.push(self.variableLock[v.name].promise);
}
});
}
return $q.all(dependencies).then(function() {
var urlValue = queryParams['var-' + variable.name];
if (urlValue !== void 0) {
return self.setVariableFromUrl(variable, urlValue).then(lock.resolve);
}
else if (variable.refresh === 1 || variable.refresh === 2) {
return self.updateOptions(variable).then(function() {
if (_.isEmpty(variable.current) && variable.options.length) {
console.log("setting current for %s", variable.name);
self.setVariableValue(variable, variable.options[0]);
}
lock.resolve();
});
}
else if (variable.type === 'interval') {
self.updateAutoInterval(variable);
lock.resolve();
} else {
lock.resolve();
}
});
};
this.setVariableFromUrl = function(variable, urlValue) {
var promise = $q.when(true);
if (variable.refresh) {
promise = this.updateOptions(variable);
}
return promise.then(function() {
var option = _.findWhere(variable.options, { text: urlValue });
option = option || { text: urlValue, value: urlValue };
self.updateAutoInterval(variable);
return self.setVariableValue(variable, option, true);
});
};
this.updateAutoInterval = function(variable) {
if (!variable.auto) { return; }
// add auto option if missing
if (variable.options.length && variable.options[0].text !== 'auto') {
variable.options.unshift({ text: 'auto', value: '$__auto_interval' });
}
var interval = kbn.calculateInterval(timeSrv.timeRange(), variable.auto_count, (variable.auto_min ? ">"+variable.auto_min : null));
templateSrv.setGrafanaVariable('$__auto_interval', interval);
};
this.setVariableValue = function(variable, option, initPhase) {
variable.current = angular.copy(option);
if (_.isArray(variable.current.value)) {
variable.current.text = variable.current.value.join(' + ');
}
self.selectOptionsForCurrentValue(variable);
templateSrv.updateTemplateData();
// on first load, variable loading is ordered to ensure
// that parents are updated before children.
if (initPhase) {
return $q.when();
}
return self.updateOptionsInChildVariables(variable);
};
this.variableUpdated = function(variable) {
templateSrv.updateTemplateData();
return this.updateOptionsInChildVariables(variable);
};
this.updateOptionsInChildVariables = function(updatedVariable) {
var promises = _.map(self.variables, function(otherVariable) {
if (otherVariable === updatedVariable) {
return;
}
if (templateSrv.containsVariable(otherVariable.query, updatedVariable.name) ||
templateSrv.containsVariable(otherVariable.datasource, updatedVariable.name)) {
return self.updateOptions(otherVariable);
}
});
return $q.all(promises);
};
this._updateNonQueryVariable = function(variable) {
if (variable.type === 'datasource') {
self.updateDataSourceVariable(variable);
return;
}
// extract options in comma seperated string
variable.options = _.map(variable.query.split(/[,]+/), function(text) {
return { text: text.trim(), value: text.trim() };
});
if (variable.type === 'interval') {
self.updateAutoInterval(variable);
}
if (variable.type === 'custom' && variable.includeAll) {
self.addAllOption(variable);
}
};
this.updateDataSourceVariable = function(variable) {
var options = [];
var sources = datasourceSrv.getMetricSources({skipVariables: true});
var regex;
if (variable.regex) {
regex = kbn.stringToJsRegex(templateSrv.replace(variable.regex));
}
for (var i = 0; i < sources.length; i++) {
var source = sources[i];
// must match on type
if (source.meta.id !== variable.query) {
continue;
}
if (regex && !regex.exec(source.name)) {
continue;
}
options.push({text: source.name, value: source.name});
}
if (options.length === 0) {
options.push({text: 'No data sources found', value: ''});
}
variable.options = options;
};
this.updateOptions = function(variable) {
if (variable.type !== 'query') {
self._updateNonQueryVariable(variable);
return self.validateVariableSelectionState(variable);
}
return datasourceSrv.get(variable.datasource)
.then(_.partial(this.updateOptionsFromMetricFindQuery, variable))
.then(_.partial(this.updateTags, variable))
.then(_.partial(this.validateVariableSelectionState, variable));
};
this.selectOptionsForCurrentValue = function(variable) {
var i, y, value, option;
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;
}
}
} else if (option.value === variable.current.value) {
option.selected = true;
}
}
};
this.validateVariableSelectionState = function(variable) {
if (!variable.current) {
if (!variable.options.length) { return; }
return self.setVariableValue(variable, variable.options[0], false);
}
if (_.isArray(variable.current.value)) {
self.selectOptionsForCurrentValue(variable);
// updated selected value
var selected = {
value: _.map(_.filter(variable.options, {selected: true}), function(op) {
return op.value;
})
};
// if none pick first
if (selected.value.length === 0) {
selected = variable.options[0];
}
return self.setVariableValue(variable, selected, false);
} else {
var currentOption = _.findWhere(variable.options, {text: variable.current.text});
if (currentOption) {
return self.setVariableValue(variable, currentOption, false);
} else {
if (!variable.options.length) { return; }
return self.setVariableValue(variable, variable.options[0]);
}
}
};
this.updateTags = function(variable, datasource) {
if (variable.useTags) {
return datasource.metricFindQuery(variable.tagsQuery).then(function (results) {
variable.tags = [];
for (var i = 0; i < results.length; i++) {
variable.tags.push(results[i].text);
}
return datasource;
});
} else {
delete variable.tags;
}
return datasource;
};
this.updateOptionsFromMetricFindQuery = function(variable, datasource) {
return datasource.metricFindQuery(variable.query).then(function (results) {
variable.options = self.metricNamesToVariableValues(variable, results);
if (variable.includeAll) {
self.addAllOption(variable);
}
if (!variable.options.length) {
variable.options.push(getNoneOption());
}
return datasource;
});
};
this.getValuesForTag = function(variable, tagKey) {
return datasourceSrv.get(variable.datasource).then(function(datasource) {
var query = variable.tagValuesQuery.replace('$tag', tagKey);
return datasource.metricFindQuery(query).then(function (results) {
return _.map(results, function(value) {
return value.text;
});
});
});
};
this.metricNamesToVariableValues = function(variable, metricNames) {
var regex, options, i, matches;
options = {}; // use object hash to remove duplicates
if (variable.regex) {
regex = kbn.stringToJsRegex(templateSrv.replace(variable.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[value] = {text: text, value: value};
}
return _.sortBy(options, 'text');
};
this.addAllOption = function(variable) {
variable.options.unshift({text: 'All', value: "$__all"});
};
});
});