Transformations: Extended support for variables in filter by name (#75734)

* Extend support for variables in filter by name

* Simlpify help and include variable support

* Simplify regexp

* Remove id that was left from an erlier implementation attempt

* Update docs/sources/panels-visualizations/query-transform-data/transform-data/index.md

Co-authored-by: Isabel <76437239+imatwawana@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Isabel <76437239+imatwawana@users.noreply.github.com>

* Improve variable name and fix react warning

---------

Co-authored-by: Isabel <76437239+imatwawana@users.noreply.github.com>
This commit is contained in:
Oscar Kilhed
2023-10-03 15:06:08 +02:00
committed by GitHub
co-authored by Isabel
parent 1456b26075
commit e0919a340e
7 changed files with 184 additions and 37 deletions
@@ -8,6 +8,7 @@ import { FieldMatcherID, FrameMatcherID } from './ids';
export interface RegexpOrNamesMatcherOptions {
pattern?: string;
names?: string[];
variable?: string;
}
/**
@@ -196,8 +196,83 @@ describe('filterByName transformer', () => {
expect(filtered.fields[0].name).toBe('B');
});
});
it('it can use a variable with multiple comma separated', async () => {
const cfg = {
id: DataTransformerID.filterFieldsByName,
options: {
include: {
variable: '$var',
},
byVariable: true,
},
};
it('uses template variable substituion', async () => {
const ctx = {
interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => {
if (!target) {
return '';
}
const variables: ScopedVars = {
var: {
value: 'B,D',
text: 'Test',
},
};
for (const key of Object.keys(variables)) {
return target.replace(`$${key}`, variables[key]!.value);
}
return target;
},
};
await expect(transformDataFrame([cfg], [seriesWithNamesToMatch], ctx)).toEmitValuesWith((received) => {
const data = received[0];
const filtered = data[0];
expect(filtered.fields.length).toBe(2);
expect(filtered.fields[0].name).toBe('B');
expect(filtered.fields[1].name).toBe('D');
});
});
it('it can use a variable with multiple comma separated values in {}', async () => {
const cfg = {
id: DataTransformerID.filterFieldsByName,
options: {
include: {
variable: '$var',
},
byVariable: true,
},
};
const ctx = {
interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => {
if (!target) {
return '';
}
const variables: ScopedVars = {
var: {
value: '{B,D}',
text: 'Test',
},
};
for (const key of Object.keys(variables)) {
return target.replace(`$${key}`, variables[key]!.value);
}
return target;
},
};
await expect(transformDataFrame([cfg], [seriesWithNamesToMatch], ctx)).toEmitValuesWith((received) => {
const data = received[0];
const filtered = data[0];
expect(filtered.fields.length).toBe(2);
expect(filtered.fields[0].name).toBe('B');
expect(filtered.fields[1].name).toBe('D');
});
});
it('uses template variable substitution', async () => {
const cfg = {
id: DataTransformerID.filterFieldsByName,
options: {
@@ -1,4 +1,4 @@
import { DataTransformerInfo, MatcherConfig } from '../../types/transformations';
import { DataTransformContext, DataTransformerInfo, MatcherConfig } from '../../types/transformations';
import { FieldMatcherID } from '../matchers/ids';
import { RegexpOrNamesMatcherOptions } from '../matchers/nameMatcher';
@@ -8,6 +8,7 @@ import { DataTransformerID } from './ids';
export interface FilterFieldsByNameTransformerOptions {
include?: RegexpOrNamesMatcherOptions;
exclude?: RegexpOrNamesMatcherOptions;
byVariable?: boolean;
}
export const filterFieldsByNameTransformer: DataTransformerInfo<FilterFieldsByNameTransformerOptions> = {
@@ -20,25 +21,38 @@ export const filterFieldsByNameTransformer: DataTransformerInfo<FilterFieldsByNa
* Return a modified copy of the series. If the transform is not or should not
* be applied, just return the input series
*/
operator: (options, replace) => (source) =>
operator: (options, ctx) => (source) =>
source.pipe(
filterFieldsTransformer.operator(
{
include: getMatcherConfig(options.include),
exclude: getMatcherConfig(options.exclude),
include: getMatcherConfig(ctx, options.include, options.byVariable),
exclude: getMatcherConfig(ctx, options.exclude, options.byVariable),
},
replace
ctx
)
),
};
// Exported to share with other implementations, but not exported to `@grafana/data`
export const getMatcherConfig = (options?: RegexpOrNamesMatcherOptions): MatcherConfig | undefined => {
export const getMatcherConfig = (
ctx: DataTransformContext,
options?: RegexpOrNamesMatcherOptions,
byVariable?: boolean
): MatcherConfig | undefined => {
if (!options) {
return undefined;
}
const { names, pattern } = options;
const { names, pattern, variable } = options;
if (byVariable && variable) {
const stringOfNames = ctx.interpolate(variable);
if (/\{.*\}/.test(stringOfNames)) {
const namesFromString = stringOfNames.slice(1).slice(0, -1).split(',');
return { id: FieldMatcherID.byNames, options: { names: namesFromString } };
}
return { id: FieldMatcherID.byNames, options: { names: stringOfNames.split(',') } };
}
if ((!Array.isArray(names) || names.length === 0) && !pattern) {
return undefined;