Transformers: Add template variable substitution (#44486)

* Transformers: Add template variable replacement to transformer operator

* Transformers: Add template variable replacement to calculateField transformer

* Transformers: Add scopedVars to transformer template replacement

* Transformers: Add calculateField template variable test

* Transformers: Fix 'undefined' is not assignable to type 'string'

* Transformers: Fix lint-frontend bug

* Fix lint check

* Fix lint
This commit is contained in:
Tom 2022-03-18 05:25:53 -05:00 committed by GitHub
parent 52bd7618dd
commit ea7d5a6185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 4 deletions

View File

@ -17,7 +17,9 @@ const getOperator =
const options = { ...defaultOptions, ...config.options };
return source.pipe(
mergeMap((before) => of(before).pipe(info.transformation.operator(options), postProcessTransform(before, info)))
mergeMap((before) =>
of(before).pipe(info.transformation.operator(options, config.replace), postProcessTransform(before, info))
)
);
};

View File

@ -7,6 +7,7 @@ import { transformDataFrame } from '../transformDataFrame';
import { CalculateFieldMode, calculateFieldTransformer, ReduceOptions } from './calculateField';
import { DataFrameView } from '../../dataframe';
import { BinaryOperationID } from '../../utils';
import { ScopedVars } from '../../types';
const seriesA = toDataFrame({
fields: [
@ -219,4 +220,63 @@ describe('calculateField transformer w/ timeseries', () => {
`);
});
});
it('uses template variable substituion', async () => {
const cfg = {
id: DataTransformerID.calculateField,
options: {
alias: '$var1',
mode: CalculateFieldMode.BinaryOperation,
binary: {
left: 'A',
operator: BinaryOperationID.Add,
right: '$var2',
},
replaceFields: true,
},
replace: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => {
if (!target) {
return '';
}
const variables: ScopedVars = {
var1: {
value: 'Test',
text: 'Test',
},
var2: {
value: 5,
text: '5',
},
__interval: {
value: 10000,
text: '10000',
},
};
for (const key of Object.keys(variables)) {
if (target === `$${key}`) {
return variables[key].value + '';
}
}
return target;
},
};
await expect(transformDataFrame([cfg], [seriesA])).toEmitValuesWith((received) => {
const data = received[0];
const filtered = data[0];
const rows = new DataFrameView(filtered).toArray();
expect(rows).toMatchInlineSnapshot(`
Array [
Object {
"Test": 6,
"TheTime": 1000,
},
Object {
"Test": 105,
"TheTime": 2000,
},
]
`);
});
});
});

View File

@ -71,10 +71,12 @@ export const calculateFieldTransformer: DataTransformerInfo<CalculateFieldTransf
reducer: ReducerID.sum,
},
},
operator: (options) => (outerSource) => {
operator: (options, replace) => (outerSource) => {
const operator =
options && options.timeSeries !== false ? ensureColumnsTransformer.operator(null) : noopTransformer.operator({});
options.alias = replace ? replace(options.alias) : options.alias;
return outerSource.pipe(
operator,
map((data) => {
@ -84,7 +86,14 @@ export const calculateFieldTransformer: DataTransformerInfo<CalculateFieldTransf
if (mode === CalculateFieldMode.ReduceRow) {
creator = getReduceRowCreator(defaults(options.reduce, defaultReduceOptions), data);
} else if (mode === CalculateFieldMode.BinaryOperation) {
creator = getBinaryCreator(defaults(options.binary, defaultBinaryOptions), data);
const binaryOptions = replace
? {
...options.binary,
left: replace ? replace(options.binary?.left) : options.binary?.left,
right: replace ? replace(options.binary?.right) : options.binary?.right,
}
: options.binary;
creator = getBinaryCreator(defaults(binaryOptions, defaultBinaryOptions), data);
}
// Nothing configured

View File

@ -2,6 +2,7 @@ import { MonoTypeOperatorFunction } from 'rxjs';
import { DataFrame, Field } from './dataFrame';
import { RegistryItemWithOptions } from '../utils/Registry';
import { ScopedVars } from './ScopedVars';
/**
* Function that transform data frames (AKA transformer)
@ -13,7 +14,10 @@ export interface DataTransformerInfo<TOptions = any> extends RegistryItemWithOpt
* Function that configures transformation and returns a transformer
* @param options
*/
operator: (options: TOptions) => MonoTypeOperatorFunction<DataFrame[]>;
operator: (
options: TOptions,
replace?: (target?: string, scopedVars?: ScopedVars, format?: string | Function) => string
) => MonoTypeOperatorFunction<DataFrame[]>;
}
/**
@ -42,6 +46,10 @@ export interface DataTransformerConfig<TOptions = any> {
* Options to be passed to the transformer
*/
options: TOptions;
/**
* Function to apply template variable substitution to the DataTransformerConfig
*/
replace?: (target?: string, scopedVars?: ScopedVars, format?: string | Function) => string;
}
export type FrameMatcher = (frame: DataFrame) => boolean;

View File

@ -183,6 +183,13 @@ export class PanelQueryRunner {
return of(data);
}
const replace = (option: string): string => {
return getTemplateSrv().replace(option, data?.request?.scopedVars);
};
transformations.forEach((transform: any) => {
transform.replace = replace;
});
return transformDataFrame(transformations, data.series).pipe(map((series) => ({ ...data, series })));
})
);