Transformation: Add variable support for filter by value regex matcher (#90926)

* add support for template vars in filter by value transformation for regex matcher

* less code is more code

* add test to confirm interpolation works for regex

* suggestions input for regex editor

---------

Co-authored-by: Adela Almasan <adela.almasan@grafana.com>
This commit is contained in:
Nathan Marrs 2024-07-30 17:15:44 -06:00 committed by GitHub
parent 537f1fb857
commit 0a34b51055
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 78 additions and 4 deletions

View File

@ -305,6 +305,51 @@ describe('FilterByValue transformer', () => {
});
});
it('should interpolate dashboard variables for regex matcher', async () => {
mockTransformationsVariableSupport.mockReturnValue(true);
const regex: MatcherConfig<BasicValueMatcherOptions<string | number>> = {
id: ValueMatcherID.regex,
options: { value: '.*thiswillinterpolateto6' },
};
const cfg: DataTransformerConfig<FilterByValueTransformerOptions> = {
id: DataTransformerID.filterByValue,
options: {
type: FilterByValueType.include,
match: FilterByValueMatch.all,
filters: [
{
fieldName: 'numbers',
config: regex,
},
],
},
};
const ctxmock = { interpolate: jest.fn(() => '6') };
await expect(transformDataFrame([cfg], [seriesAWithSingleField], ctxmock)).toEmitValuesWith((received) => {
const processed = received[0];
expect(processed.length).toEqual(1);
expect(processed[0].fields).toEqual([
{
name: 'time',
type: FieldType.time,
values: [6000],
state: {},
},
{
name: 'numbers',
type: FieldType.number,
values: [6],
state: {},
},
]);
});
});
it('should not interpolate dashboard variables when feature toggle is off', async () => {
mockTransformationsVariableSupport.mockReturnValue(false);

View File

@ -77,9 +77,6 @@ export const filterByValueTransformer: DataTransformerInfo<FilterByValueTransfor
},
},
};
} else if (filter.config.id === ValueMatcherID.regex) {
// Due to colliding syntaxes, interpolating regex filters will cause issues.
return filter;
} else if (filter.config.options.value) {
let value = filter.config.options.value;
if (typeof filter.config.options.value === 'string') {

View File

@ -1,9 +1,12 @@
import { useCallback, useState } from 'react';
import * as React from 'react';
import { ValueMatcherID, BasicValueMatcherOptions } from '@grafana/data';
import { ValueMatcherID, BasicValueMatcherOptions, VariableOrigin } from '@grafana/data';
import { config as cfg, getTemplateSrv } from '@grafana/runtime';
import { Input } from '@grafana/ui';
import { SuggestionsInput } from '../../suggestionsInput/SuggestionsInput';
import { ValueMatcherEditorConfig, ValueMatcherUIProps, ValueMatcherUIRegistryItem } from './types';
import { convertToType } from './utils';
@ -14,6 +17,12 @@ export function regexMatcherEditor(
const { validator, converter = convertToType } = config;
const { value } = options;
const [isInvalid, setInvalid] = useState(!validator(value));
const templateSrv = getTemplateSrv();
const variables = templateSrv.getVariables().map((v) => {
return { value: v.name, label: v.label || v.name, origin: VariableOrigin.Template };
});
const onChangeValue = useCallback(
(event: React.FormEvent<HTMLInputElement>) => {
setInvalid(!validator(event.currentTarget.value));
@ -21,6 +30,17 @@ export function regexMatcherEditor(
[setInvalid, validator]
);
const onChangeVariableValue = useCallback(
(value: string) => {
setInvalid(!validator(value));
onChange({
...options,
value,
});
},
[setInvalid, validator, onChange, options]
);
const onChangeOptions = useCallback(
(event: React.FocusEvent<HTMLInputElement>) => {
if (isInvalid) {
@ -37,6 +57,18 @@ export function regexMatcherEditor(
[options, onChange, isInvalid, field, converter]
);
if (cfg.featureToggles.transformationsVariableSupport) {
return (
<SuggestionsInput
invalid={isInvalid}
value={value}
onChange={onChangeVariableValue}
placeholder="Value or variable"
suggestions={variables}
/>
);
}
return (
<Input
className="flex-grow-1"