Variables: Adds queryparam formatting option (#30858)

* Variables: Adds queryparam formatting option

* Chore: fixes strict errors

* Chore: changes after PR comments
This commit is contained in:
Hugo Häggmark
2021-02-05 07:16:06 +01:00
committed by GitHub
parent 95efd3e51d
commit 2a3aa95163
11 changed files with 163 additions and 34 deletions

View File

@@ -3,6 +3,8 @@ import { dateTime, Registry, RegistryItem, textUtil, VariableModel } from '@graf
import { isArray, map, replace } from 'lodash';
import { formatVariableLabel } from '../variables/shared/formatVariable';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../variables/state/types';
import { variableAdapters } from '../variables/adapters';
import { VariableModel as ExtendedVariableModel } from '../variables/types';
export interface FormatOptions {
value: any;
@@ -217,6 +219,23 @@ export const formatRegistry = new Registry<FormatRegistryItem>(() => {
return formatVariableLabel(variable);
},
},
{
id: 'queryparam',
name: 'Query Parameter',
description:
'Format variables as url parameter. Example in multi variable scenario A + B + C => var-foo=A&var-foo=B&var-foo=C.',
formatter: (options, variable) => {
const { name, type } = variable;
const adapter = variableAdapters.get(type);
const valueForUrl = adapter.getValueForUrl(variable as ExtendedVariableModel);
if (Array.isArray(valueForUrl)) {
return valueForUrl.map((v) => formatQueryParameter(name, v)).join('&');
}
return formatQueryParameter(name, valueForUrl);
},
},
];
return formats;
@@ -236,3 +255,11 @@ function encodeURIComponentStrict(str: string) {
return '%' + c.charCodeAt(0).toString(16).toUpperCase();
});
}
function formatQueryParameter(name: string, value: string): string {
return `var-${name}=${encodeURIComponentStrict(value)}`;
}
export function isAllValue(value: any) {
return value === ALL_VARIABLE_VALUE || (Array.isArray(value) && value[0] === ALL_VARIABLE_VALUE);
}

View File

@@ -1,6 +1,15 @@
import { dateTime, TimeRange } from '@grafana/data';
import { initTemplateSrv } from '../../../test/helpers/initTemplateSrv';
import { silenceConsoleOutput } from '../../../test/core/utils/silenceConsoleOutput';
import { VariableAdapter, variableAdapters } from '../variables/adapters';
import { createQueryVariableAdapter } from '../variables/query/adapter';
import { createAdHocVariableAdapter } from '../variables/adhoc/adapter';
import { VariableModel } from '../variables/types';
variableAdapters.setInit(() => [
(createQueryVariableAdapter() as unknown) as VariableAdapter<VariableModel>,
(createAdHocVariableAdapter() as unknown) as VariableAdapter<VariableModel>,
]);
describe('templateSrv', () => {
silenceConsoleOutput();
@@ -225,6 +234,11 @@ describe('templateSrv', () => {
const target = _templateSrv.replace('${test:pipe},$test', {}, 'glob');
expect(target).toBe('value1|value2,{value1,value2}');
});
it('should replace ${test:queryparam} with correct query parameter', () => {
const target = _templateSrv.replace('${test:queryparam}', {});
expect(target).toBe('var-test=All');
});
});
describe('variable with all option and custom value', () => {
@@ -264,6 +278,11 @@ describe('templateSrv', () => {
const target = _templateSrv.replace('this.$test', {}, 'regex');
expect(target).toBe('this.*');
});
it('should replace ${test:queryparam} with correct query parameter', () => {
const target = _templateSrv.replace('${test:queryparam}', {});
expect(target).toBe('var-test=All');
});
});
describe('lucene format', () => {
@@ -640,4 +659,43 @@ describe('templateSrv', () => {
expect(passedValue).toBe('hello');
});
});
describe('queryparam', () => {
beforeEach(() => {
_templateSrv = initTemplateSrv([
{
type: 'query',
name: 'single',
current: { value: 'value1' },
options: [{ value: 'value1' }, { value: 'value2' }],
},
{
type: 'query',
name: 'multi',
current: { value: ['value1', 'value2'] },
options: [{ value: 'value1' }, { value: 'value2' }],
},
]);
});
it('query variable with single value with queryparam format should return correct queryparam', () => {
const target = _templateSrv.replace('${single:queryparam}', {});
expect(target).toBe('var-single=value1');
});
it('query variable with single value and queryparam format should return correct queryparam', () => {
const target = _templateSrv.replace('${single}', {}, 'queryparam');
expect(target).toBe('var-single=value1');
});
it('query variable with multi value with queryparam format should return correct queryparam', () => {
const target = _templateSrv.replace('${multi:queryparam}', {});
expect(target).toBe('var-multi=value1&var-multi=value2');
});
it('query variable with multi value and queryparam format should return correct queryparam', () => {
const target = _templateSrv.replace('${multi}', {}, 'queryparam');
expect(target).toBe('var-multi=value1&var-multi=value2');
});
});
});

View File

@@ -282,7 +282,7 @@ export class TemplateSrv implements BaseTemplateSrv {
value = this.getAllValue(variable);
text = ALL_VARIABLE_TEXT;
// skip formatting of custom all values
if (variable.allValue && fmt !== 'text') {
if (variable.allValue && fmt !== 'text' && fmt !== 'queryparam') {
return this.replace(value);
}
}

View File

@@ -11,6 +11,7 @@ import {
QueryEditorProps,
StandardVariableQuery,
StandardVariableSupport,
VariableModel,
VariableSupportType,
} from '@grafana/data';
@@ -18,7 +19,6 @@ import {
AdHocVariableModel,
ConstantVariableModel,
QueryVariableModel,
VariableModel,
VariableQueryEditorType,
VariableWithMultiSupport,
VariableWithOptions,

View File

@@ -3,23 +3,42 @@ import { VariableRefresh } from './types';
describe('isAllVariable', () => {
it.each`
variable | expected
${null} | ${false}
${undefined} | ${false}
${{}} | ${false}
${{ current: {} }} | ${false}
${{ current: { text: '' } }} | ${false}
${{ current: { text: null } }} | ${false}
${{ current: { text: undefined } }} | ${false}
${{ current: { text: 'Alll' } }} | ${false}
${{ current: { text: 'All' } }} | ${true}
${{ current: { text: [] } }} | ${false}
${{ current: { text: [null] } }} | ${false}
${{ current: { text: [undefined] } }} | ${false}
${{ current: { text: ['Alll'] } }} | ${false}
${{ current: { text: ['Alll', 'All'] } }} | ${false}
${{ current: { text: ['All'] } }} | ${true}
${{ current: { text: { prop1: 'test' } } }} | ${false}
variable | expected
${null} | ${false}
${undefined} | ${false}
${{}} | ${false}
${{ current: {} }} | ${false}
${{ current: { text: '' } }} | ${false}
${{ current: { text: null } }} | ${false}
${{ current: { text: undefined } }} | ${false}
${{ current: { text: 'Alll' } }} | ${false}
${{ current: { text: 'All' } }} | ${true}
${{ current: { text: [] } }} | ${false}
${{ current: { text: [null] } }} | ${false}
${{ current: { text: [undefined] } }} | ${false}
${{ current: { text: ['Alll'] } }} | ${false}
${{ current: { text: ['Alll', 'All'] } }} | ${false}
${{ current: { text: ['All'] } }} | ${true}
${{ current: { text: ['All', 'Alll'] } }} | ${true}
${{ current: { text: { prop1: 'test' } } }} | ${false}
${{ current: { value: '' } }} | ${false}
${{ current: { value: null } }} | ${false}
${{ current: { value: undefined } }} | ${false}
${{ current: { value: '$__alll' } }} | ${false}
${{ current: { value: '$__all' } }} | ${true}
${{ current: { value: [] } }} | ${false}
${{ current: { value: [null] } }} | ${false}
${{ current: { value: [undefined] } }} | ${false}
${{ current: { value: ['$__alll'] } }} | ${false}
${{ current: { value: ['$__alll', '$__all'] } }} | ${false}
${{ current: { value: ['$__all'] } }} | ${true}
${{ current: { value: ['$__all', '$__alll'] } }} | ${true}
${{ current: { value: { prop1: 'test' } } }} | ${false}
${{ current: { value: '', text: '' } }} | ${false}
${{ current: { value: '', text: 'All' } }} | ${true}
${{ current: { value: '$__all', text: '' } }} | ${true}
${{ current: { value: '', text: ['All'] } }} | ${true}
${{ current: { value: ['$__all'], text: '' } }} | ${true}
`("when called with params: 'variable': '$variable' then result should be '$expected'", ({ variable, expected }) => {
expect(isAllVariable(variable)).toEqual(expected);
});

View File

@@ -2,7 +2,7 @@ import isString from 'lodash/isString';
import { ScopedVars, VariableType } from '@grafana/data';
import { getTemplateSrv } from '@grafana/runtime';
import { ALL_VARIABLE_TEXT } from './state/types';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from './state/types';
import { QueryVariableModel, VariableModel, VariableRefresh } from './types';
import { getTimeSrv } from '../dashboard/services/TimeSrv';
import { variableAdapters } from './adapters';
@@ -74,15 +74,29 @@ export const isAllVariable = (variable: any): boolean => {
return false;
}
if (!variable.current.text) {
return false;
if (variable.current.value) {
const isArray = Array.isArray(variable.current.value);
if (isArray && variable.current.value.length && variable.current.value[0] === ALL_VARIABLE_VALUE) {
return true;
}
if (!isArray && variable.current.value === ALL_VARIABLE_VALUE) {
return true;
}
}
if (Array.isArray(variable.current.text)) {
return variable.current.text.length ? variable.current.text[0] === ALL_VARIABLE_TEXT : false;
if (variable.current.text) {
const isArray = Array.isArray(variable.current.text);
if (isArray && variable.current.text.length && variable.current.text[0] === ALL_VARIABLE_TEXT) {
return true;
}
if (!isArray && variable.current.text === ALL_VARIABLE_TEXT) {
return true;
}
}
return variable.current.text === ALL_VARIABLE_TEXT;
return false;
};
export const getCurrentText = (variable: any): string => {