PanelQueryRunner: Use rxjs forkJoin (like Scenes), not lodash merge (#109220)

This commit is contained in:
Leon Sorokin
2025-09-04 19:54:47 -05:00
committed by GitHub
parent f302a3d538
commit beaa512eb3
7 changed files with 184 additions and 8 deletions
@@ -26,6 +26,7 @@ export {
type ConvertFieldTypeTransformerOptions,
convertFieldType,
} from '../transformations/transformers/convertFieldType';
export { type ConvertFrameTypeTransformerOptions, FrameType } from '../transformations/transformers/convertFrameType';
export { type FilterFieldsByNameTransformerOptions } from '../transformations/transformers/filterByName';
export { type FilterFramesByRefIdTransformerOptions } from '../transformations/transformers/filterByRefId';
export { FormatStringOutput, type FormatStringTransformerOptions } from '../transformations/transformers/formatString';
@@ -1,6 +1,7 @@
import { calculateFieldTransformer } from './transformers/calculateField';
import { concatenateTransformer } from './transformers/concat';
import { convertFieldTypeTransformer } from './transformers/convertFieldType';
import { convertFrameTypeTransformer } from './transformers/convertFrameType';
import { ensureColumnsTransformer } from './transformers/ensureColumns';
import { filterFieldsTransformer, filterFramesTransformer } from './transformers/filter';
import { filterFieldsByNameTransformer } from './transformers/filterByName';
@@ -53,6 +54,7 @@ export const standardTransformers = {
renameByRegexTransformer,
histogramTransformer,
convertFieldTypeTransformer,
convertFrameTypeTransformer,
groupingToMatrixTransformer,
limitTransformer,
groupToNestedTable,
@@ -0,0 +1,45 @@
import { DataTransformerConfig } from '@grafana/schema';
import { toDataFrame } from '../../dataframe/processDataFrame';
import { mockTransformationsRegistry } from '../../internal';
import { FieldType } from '../../types/dataFrame';
import { transformDataFrame } from '../transformDataFrame';
import { convertFrameTypeTransformer, ConvertFrameTypeTransformerOptions, FrameType } from './convertFrameType';
import { DataTransformerID } from './ids';
describe('convert frame type', () => {
beforeAll(() => {
mockTransformationsRegistry([convertFrameTypeTransformer]);
});
it('will convert a series frame into an exemplar frame', async () => {
const seriesFrame = toDataFrame({
fields: [
{ name: 'Time', type: FieldType.time, values: [1000, 2000] },
{ name: 'Value', type: FieldType.number, values: [1, 100] },
],
});
const cfg: DataTransformerConfig<ConvertFrameTypeTransformerOptions> = {
id: DataTransformerID.convertFrameType,
options: {
targetType: FrameType.Exemplar,
},
};
await expect(transformDataFrame([cfg], [seriesFrame])).toEmitValuesWith((received) => {
const processed = received[0];
expect(processed[0]).toEqual({
name: 'exemplar',
meta: { custom: { resultType: 'exemplar' }, dataTopic: 'annotations' },
length: 2,
fields: [
{ config: {}, name: 'Time', type: 'time', values: [1000, 2000] },
{ config: {}, name: 'Value', type: 'number', values: [1, 100] },
],
});
});
});
});
@@ -0,0 +1,66 @@
import { map } from 'rxjs/operators';
import { DataTopic } from '@grafana/schema';
import { DataFrame } from '../../types/dataFrame';
import { DataTransformerInfo } from '../../types/transformations';
import { DataTransformerID } from './ids';
/*
"schema": {
"meta": {
"custom": {
"resultType": "exemplar"
}
},
*/
// ResultType?
export enum FrameType {
Exemplar = 'exemplar',
TimeRegion = 'timeRegion',
Annotation = 'annotation',
}
export interface ConvertFrameTypeTransformerOptions {
targetType?: FrameType;
}
/** @alpha */
export const convertFrameTypeTransformer: DataTransformerInfo<ConvertFrameTypeTransformerOptions> = {
id: DataTransformerID.convertFrameType,
name: 'Convert frame type',
description: 'Convert data frame(s) to another type.',
operator: (options) => (source) =>
source.pipe(
map((data) => {
return convertFrameType(options, data);
})
),
};
function convertFrameType(options: ConvertFrameTypeTransformerOptions, frames: DataFrame[]): DataFrame[] {
const { targetType } = options;
return targetType === FrameType.Exemplar ? frames.map(convertSeriesToExemplar) : frames;
}
function convertSeriesToExemplar(frame: DataFrame): DataFrame {
// TODO: ensure time field
// TODO: ensure value field
return {
...frame,
name: 'exemplar',
meta: {
...frame.meta,
dataTopic: DataTopic.Annotations,
custom: {
...frame.meta?.custom,
resultType: 'exemplar',
},
},
};
}
@@ -27,6 +27,7 @@ export enum DataTransformerID {
rowsToFields = 'rowsToFields',
prepareTimeSeries = 'prepareTimeSeries',
convertFieldType = 'convertFieldType',
convertFrameType = 'convertFrameType',
fieldLookup = 'fieldLookup',
heatmap = 'heatmap',
spatial = 'spatial',