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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user