mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TemplateVariables: Introduces $__searchFilter to Query Variables (#19858)
* 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
This commit is contained in:
committed by
Torkel Ödegaard
parent
c674fa1d79
commit
cb0e80e7b9
@@ -1,5 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import { Variable, containsVariable, assignModelProperties, variableTypes } from './variable';
|
||||
import { assignModelProperties, containsVariable, Variable, variableTypes } from './variable';
|
||||
import { stringToJsRegex } from '@grafana/data';
|
||||
import DatasourceSrv from '../plugins/datasource_srv';
|
||||
import { TemplateSrv } from './template_srv';
|
||||
@@ -62,6 +62,7 @@ export class QueryVariable implements Variable {
|
||||
) {
|
||||
// copy model properties to this instance
|
||||
assignModelProperties(this, model, this.defaults);
|
||||
this.updateOptionsFromMetricFindQuery.bind(this);
|
||||
}
|
||||
|
||||
getSaveModel() {
|
||||
@@ -91,10 +92,10 @@ export class QueryVariable implements Variable {
|
||||
return this.current.value;
|
||||
}
|
||||
|
||||
updateOptions() {
|
||||
updateOptions(searchFilter?: string) {
|
||||
return this.datasourceSrv
|
||||
.get(this.datasource)
|
||||
.then(this.updateOptionsFromMetricFindQuery.bind(this))
|
||||
.then(ds => this.updateOptionsFromMetricFindQuery(ds, searchFilter))
|
||||
.then(this.updateTags.bind(this))
|
||||
.then(this.variableSrv.validateVariableSelectionState.bind(this.variableSrv, this));
|
||||
}
|
||||
@@ -126,8 +127,8 @@ export class QueryVariable implements Variable {
|
||||
});
|
||||
}
|
||||
|
||||
updateOptionsFromMetricFindQuery(datasource: any) {
|
||||
return this.metricFindQuery(datasource, this.query).then((results: any) => {
|
||||
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();
|
||||
@@ -139,8 +140,8 @@ export class QueryVariable implements Variable {
|
||||
});
|
||||
}
|
||||
|
||||
metricFindQuery(datasource: any, query: string) {
|
||||
const options: any = { range: undefined, variable: this };
|
||||
metricFindQuery(datasource: any, query: string, searchFilter?: string) {
|
||||
const options: any = { range: undefined, variable: this, searchFilter };
|
||||
|
||||
if (this.refresh === 2) {
|
||||
options.range = this.timeSrv.timeRange();
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { containsVariable, assignModelProperties } from '../variable';
|
||||
import {
|
||||
assignModelProperties,
|
||||
containsSearchFilter,
|
||||
containsVariable,
|
||||
interpolateSearchFilter,
|
||||
SEARCH_FILTER_VARIABLE,
|
||||
} from '../variable';
|
||||
|
||||
describe('containsVariable', () => {
|
||||
describe('when checking if a string contains a variable', () => {
|
||||
@@ -68,3 +74,104 @@ describe('assignModelProperties', () => {
|
||||
expect(target.propC).toBe(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('containsSearchFilter', () => {
|
||||
describe('when called without query', () => {
|
||||
it('then it should return false', () => {
|
||||
const result = containsSearchFilter(null);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`when called with a query without ${SEARCH_FILTER_VARIABLE}`, () => {
|
||||
it('then it should return false', () => {
|
||||
const result = containsSearchFilter('$app.*');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`when called with a query with ${SEARCH_FILTER_VARIABLE}`, () => {
|
||||
it('then it should return false', () => {
|
||||
const result = containsSearchFilter(`$app.${SEARCH_FILTER_VARIABLE}`);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('interpolateSearchFilter', () => {
|
||||
describe('when called with a query without ${SEARCH_FILTER_VARIABLE}', () => {
|
||||
it('then it should return query', () => {
|
||||
const query = '$app.*';
|
||||
const options = { searchFilter: 'filter' };
|
||||
const wildcardChar = '*';
|
||||
const quoteLiteral = false;
|
||||
|
||||
const result = interpolateSearchFilter({
|
||||
query,
|
||||
options,
|
||||
wildcardChar,
|
||||
quoteLiteral,
|
||||
});
|
||||
|
||||
expect(result).toEqual(query);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`when called with a query with ${SEARCH_FILTER_VARIABLE}`, () => {
|
||||
const query = `$app.${SEARCH_FILTER_VARIABLE}`;
|
||||
|
||||
describe('and no searchFilter is given', () => {
|
||||
it(`then ${SEARCH_FILTER_VARIABLE} should be replaced by wildchar character`, () => {
|
||||
const options = {};
|
||||
const wildcardChar = '*';
|
||||
const quoteLiteral = false;
|
||||
|
||||
const result = interpolateSearchFilter({
|
||||
query,
|
||||
options,
|
||||
wildcardChar,
|
||||
quoteLiteral,
|
||||
});
|
||||
|
||||
expect(result).toEqual(`$app.*`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and searchFilter is given', () => {
|
||||
const options = { searchFilter: 'filter' };
|
||||
|
||||
it(`then ${SEARCH_FILTER_VARIABLE} should be replaced with searchfilter and wildchar character`, () => {
|
||||
const wildcardChar = '*';
|
||||
const quoteLiteral = false;
|
||||
|
||||
const result = interpolateSearchFilter({
|
||||
query,
|
||||
options,
|
||||
wildcardChar,
|
||||
quoteLiteral,
|
||||
});
|
||||
|
||||
expect(result).toEqual(`$app.filter*`);
|
||||
});
|
||||
|
||||
describe(`and quoteLiteral is used`, () => {
|
||||
it(`then the literal should be quoted`, () => {
|
||||
const wildcardChar = '*';
|
||||
const quoteLiteral = true;
|
||||
|
||||
const result = interpolateSearchFilter({
|
||||
query,
|
||||
options,
|
||||
wildcardChar,
|
||||
quoteLiteral,
|
||||
});
|
||||
|
||||
expect(result).toEqual(`$app.'filter*'`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,9 +15,36 @@ export const variableRegexExec = (variableString: string) => {
|
||||
return variableRegex.exec(variableString);
|
||||
};
|
||||
|
||||
export const SEARCH_FILTER_VARIABLE = '$__searchFilter';
|
||||
export const containsSearchFilter = (query: string): boolean =>
|
||||
query ? query.indexOf(SEARCH_FILTER_VARIABLE) !== -1 : false;
|
||||
|
||||
export interface InterpolateSearchFilterOptions {
|
||||
query: string;
|
||||
options: any;
|
||||
wildcardChar: string;
|
||||
quoteLiteral: boolean;
|
||||
}
|
||||
|
||||
export const interpolateSearchFilter = (args: InterpolateSearchFilterOptions): string => {
|
||||
const { query, wildcardChar, quoteLiteral } = args;
|
||||
let { options } = args;
|
||||
|
||||
if (!containsSearchFilter(query)) {
|
||||
return query;
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
const filter = options.searchFilter ? `${options.searchFilter}${wildcardChar}` : `${wildcardChar}`;
|
||||
const replaceValue = quoteLiteral ? `'${filter}'` : filter;
|
||||
|
||||
return query.replace(SEARCH_FILTER_VARIABLE, replaceValue);
|
||||
};
|
||||
|
||||
export interface Variable {
|
||||
setValue(option: any): any;
|
||||
updateOptions(): any;
|
||||
updateOptions(searchFilter?: string): any;
|
||||
dependsOn(variable: any): any;
|
||||
setValueFromUrl(urlValue: any): any;
|
||||
getValueForUrl(): any;
|
||||
|
||||
Reference in New Issue
Block a user