grafana/public/app/features/transformers/extractFields/extractFields.ts
Ward Bekker eab806620c
Prevent exception when panel has multiple queries (#49368)
This fixes the issues that when a panel has multiple queries this throws an exception if source is not part of the current frame

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2022-05-25 09:13:43 +02:00

95 lines
2.3 KiB
TypeScript

import { isString } from 'lodash';
import { map } from 'rxjs/operators';
import {
ArrayVector,
DataFrame,
DataTransformerID,
Field,
FieldType,
getFieldTypeFromValue,
SynchronousDataTransformerInfo,
} from '@grafana/data';
import { findField } from 'app/features/dimensions';
import { FieldExtractorID, fieldExtractors } from './fieldExtractors';
export interface ExtractFieldsOptions {
source?: string;
format?: FieldExtractorID;
replace?: boolean;
}
export const extractFieldsTransformer: SynchronousDataTransformerInfo<ExtractFieldsOptions> = {
id: DataTransformerID.extractFields,
name: 'Extract fields',
description: 'Parse fields from the contends of another',
defaultOptions: {},
operator: (options) => (source) => source.pipe(map((data) => extractFieldsTransformer.transformer(options)(data))),
transformer: (options: ExtractFieldsOptions) => {
return (data: DataFrame[]) => {
return data.map((v) => addExtractedFields(v, options));
};
},
};
function addExtractedFields(frame: DataFrame, options: ExtractFieldsOptions): DataFrame {
if (!options.source) {
return frame;
}
const source = findField(frame, options.source);
if (!source) {
// this case can happen when there are multiple queries
return frame;
}
const ext = fieldExtractors.getIfExists(options.format ?? FieldExtractorID.Auto);
if (!ext) {
throw new Error('unkonwn extractor');
}
const count = frame.length;
const names: string[] = []; // keep order
const values = new Map<string, any[]>();
for (let i = 0; i < count; i++) {
let obj = source.values.get(i);
if (isString(obj)) {
try {
obj = ext.parse(obj);
} catch {
obj = {}; // empty
}
}
for (const [key, val] of Object.entries(obj)) {
let buffer = values.get(key);
if (buffer == null) {
buffer = new Array(count);
values.set(key, buffer);
names.push(key);
}
buffer[i] = val;
}
}
const fields = names.map((name) => {
const buffer = values.get(name);
return {
name,
values: new ArrayVector(buffer),
type: buffer ? getFieldTypeFromValue(buffer.find((v) => v != null)) : FieldType.other,
config: {},
} as Field;
});
if (!options.replace) {
fields.unshift(...frame.fields);
}
return {
...frame,
fields,
};
}