mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
* WIP: Initial hardcoded version * Feature: Introduces SearchFiltering to Graphite * Feature: Adds searchFiltering to MySql * Tests: Adds tests to Graphite and MySql * Feature: Adds $__searchFilter to TestData * Refactor: Adds searchFilter to Postgres and extracts function * Tests: Adds tests to variable * Refactor: Adds debounce and lodash import optimization * Docs: Adds documentation * Refactor: Removes unused function and fixes typo * Docs: Updates docs * Fixed issue with UI not updating when no was used due to async func and no .apply in the non lazy path
239 lines
5.9 KiB
TypeScript
239 lines
5.9 KiB
TypeScript
import _ from 'lodash';
|
|
import { assignModelProperties, containsVariable, Variable, variableTypes } from './variable';
|
|
import { stringToJsRegex } from '@grafana/data';
|
|
import DatasourceSrv from '../plugins/datasource_srv';
|
|
import { TemplateSrv } from './template_srv';
|
|
import { VariableSrv } from './variable_srv';
|
|
import { TimeSrv } from '../dashboard/services/TimeSrv';
|
|
|
|
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[];
|
|
skipUrlSync: boolean;
|
|
definition: string;
|
|
|
|
defaults: any = {
|
|
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: '',
|
|
skipUrlSync: false,
|
|
definition: '',
|
|
};
|
|
|
|
/** @ngInject */
|
|
constructor(
|
|
private model: any,
|
|
private datasourceSrv: DatasourceSrv,
|
|
private templateSrv: TemplateSrv,
|
|
private variableSrv: VariableSrv,
|
|
private timeSrv: TimeSrv
|
|
) {
|
|
// copy model properties to this instance
|
|
assignModelProperties(this, model, this.defaults);
|
|
this.updateOptionsFromMetricFindQuery.bind(this);
|
|
}
|
|
|
|
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: any) {
|
|
return this.variableSrv.setOptionAsCurrent(this, option);
|
|
}
|
|
|
|
setValueFromUrl(urlValue: any) {
|
|
return this.variableSrv.setOptionFromUrl(this, urlValue);
|
|
}
|
|
|
|
getValueForUrl() {
|
|
if (this.current.text === 'All') {
|
|
return 'All';
|
|
}
|
|
return this.current.value;
|
|
}
|
|
|
|
updateOptions(searchFilter?: string) {
|
|
return this.datasourceSrv
|
|
.get(this.datasource)
|
|
.then(ds => this.updateOptionsFromMetricFindQuery(ds, searchFilter))
|
|
.then(this.updateTags.bind(this))
|
|
.then(this.variableSrv.validateVariableSelectionState.bind(this.variableSrv, this));
|
|
}
|
|
|
|
updateTags(datasource: any) {
|
|
if (this.useTags) {
|
|
return this.metricFindQuery(datasource, this.tagsQuery).then((results: any[]) => {
|
|
this.tags = [];
|
|
for (let i = 0; i < results.length; i++) {
|
|
this.tags.push(results[i].text);
|
|
}
|
|
return datasource;
|
|
});
|
|
} else {
|
|
delete this.tags;
|
|
}
|
|
|
|
return datasource;
|
|
}
|
|
|
|
getValuesForTag(tagKey: string) {
|
|
return this.datasourceSrv.get(this.datasource).then(datasource => {
|
|
const query = this.tagValuesQuery.replace('$tag', tagKey);
|
|
return this.metricFindQuery(datasource, query).then((results: any) => {
|
|
return _.map(results, value => {
|
|
return value.text;
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
updateOptionsFromMetricFindQuery(datasource: any, searchFilter?: string) {
|
|
return this.metricFindQuery(datasource, this.query, searchFilter).then((results: any) => {
|
|
this.options = this.metricNamesToVariableValues(results);
|
|
if (this.includeAll) {
|
|
this.addAllOption();
|
|
}
|
|
if (!this.options.length) {
|
|
this.options.push(getNoneOption());
|
|
}
|
|
return datasource;
|
|
});
|
|
}
|
|
|
|
metricFindQuery(datasource: any, query: string, searchFilter?: string) {
|
|
const options: any = { range: undefined, variable: this, searchFilter };
|
|
|
|
if (this.refresh === 2) {
|
|
options.range = this.timeSrv.timeRange();
|
|
}
|
|
|
|
return datasource.metricFindQuery(query, options);
|
|
}
|
|
|
|
addAllOption() {
|
|
this.options.unshift({ text: 'All', value: '$__all' });
|
|
}
|
|
|
|
metricNamesToVariableValues(metricNames: any[]) {
|
|
let regex, options, i, matches;
|
|
options = [];
|
|
|
|
if (this.regex) {
|
|
regex = stringToJsRegex(this.templateSrv.replace(this.regex, {}, 'regex'));
|
|
}
|
|
for (i = 0; i < metricNames.length; i++) {
|
|
const item = metricNames[i];
|
|
let text = item.text === undefined || item.text === null ? item.value : item.text;
|
|
|
|
let 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: any[], sortOrder: number) {
|
|
if (sortOrder === 0) {
|
|
return options;
|
|
}
|
|
|
|
const sortType = Math.ceil(sortOrder / 2);
|
|
const reverseSort = sortOrder % 2 === 0;
|
|
|
|
if (sortType === 1) {
|
|
options = _.sortBy(options, 'text');
|
|
} else if (sortType === 2) {
|
|
options = _.sortBy(options, opt => {
|
|
const matches = opt.text.match(/.*?(\d+).*/);
|
|
if (!matches || matches.length < 2) {
|
|
return -1;
|
|
} else {
|
|
return parseInt(matches[1], 10);
|
|
}
|
|
});
|
|
} else if (sortType === 3) {
|
|
options = _.sortBy(options, opt => {
|
|
return _.toLower(opt.text);
|
|
});
|
|
}
|
|
|
|
if (reverseSort) {
|
|
options = options.reverse();
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
dependsOn(variable: any) {
|
|
return containsVariable(this.query, this.datasource, this.regex, variable.name);
|
|
}
|
|
}
|
|
// @ts-ignore
|
|
variableTypes['query'] = {
|
|
name: 'Query',
|
|
ctor: QueryVariable,
|
|
description: 'Variable values are fetched from a datasource query',
|
|
supportsMulti: true,
|
|
};
|