import _ from 'lodash'; import kbn from 'app/core/utils/kbn'; import { Variable, containsVariable, assignModelProperties, variableTypes } from './variable'; 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; refresh: number; hide: number; name: string; multi: boolean; includeAll: boolean; useTags: boolean; tagsQuery: string; tagValuesQuery: string; tags: any[]; defaults = { type: 'query', label: null, query: '', regex: '', sort: 0, datasource: null, refresh: 0, hide: 0, name: '', multi: false, includeAll: false, allValue: null, options: [], current: {}, tags: [], useTags: false, tagsQuery: '', tagValuesQuery: '', }; /** @ngInject **/ constructor(private model, private datasourceSrv, private templateSrv, private variableSrv, private timeSrv) { // copy model properties to this instance assignModelProperties(this, model, this.defaults); } getSaveModel() { // copy back model properties to model assignModelProperties(this.model, this, this.defaults); // remove options if (this.refresh !== 0) { this.model.options = []; } return this.model; } setValue(option) { return this.variableSrv.setOptionAsCurrent(this, option); } setValueFromUrl(urlValue) { return this.variableSrv.setOptionFromUrl(this, urlValue); } getValueForUrl() { if (this.current.text === 'All') { return 'All'; } return this.current.value; } updateOptions() { return this.datasourceSrv .get(this.datasource) .then(this.updateOptionsFromMetricFindQuery.bind(this)) .then(this.updateTags.bind(this)) .then(this.variableSrv.validateVariableSelectionState.bind(this.variableSrv, this)); } updateTags(datasource) { if (this.useTags) { return this.metricFindQuery(datasource, this.tagsQuery).then(results => { this.tags = []; for (var i = 0; i < results.length; i++) { this.tags.push(results[i].text); } return datasource; }); } else { delete this.tags; } return datasource; } getValuesForTag(tagKey) { return this.datasourceSrv.get(this.datasource).then(datasource => { var query = this.tagValuesQuery.replace('$tag', tagKey); return this.metricFindQuery(datasource, query).then(function(results) { return _.map(results, function(value) { return value.text; }); }); }); } updateOptionsFromMetricFindQuery(datasource) { return this.metricFindQuery(datasource, this.query).then(results => { this.options = this.metricNamesToVariableValues(results); if (this.includeAll) { this.addAllOption(); } if (!this.options.length) { this.options.push(getNoneOption()); } return datasource; }); } metricFindQuery(datasource, query) { var options = { range: undefined, variable: this }; if (this.refresh === 2) { options.range = this.timeSrv.timeRange(); } return datasource.metricFindQuery(query, options); } addAllOption() { this.options.unshift({ text: 'All', value: '$__all' }); } metricNamesToVariableValues(metricNames) { var regex, options, i, matches; options = []; if (this.regex) { regex = kbn.stringToJsRegex(this.templateSrv.replace(this.regex, {}, 'regex')); } for (i = 0; i < metricNames.length; i++) { var item = metricNames[i]; var text = item.text === undefined || item.text === null ? item.value : item.text; var value = item.value === undefined || item.value === null ? 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 = matches[1]; } } options.push({ text: text, value: value }); } options = _.uniqBy(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, opt => { var matches = opt.text.match(/.*?(\d+).*/); if (!matches || matches.length < 2) { return -1; } else { return parseInt(matches[1], 10); } }); } if (reverseSort) { options = options.reverse(); } return options; } dependsOn(variable) { return containsVariable(this.query, this.datasource, variable.name); } } variableTypes['query'] = { name: 'Query', ctor: QueryVariable, description: 'Variable values are fetched from a datasource query', supportsMulti: true, };