import { map } from 'rxjs/operators'; import { ArrayVector, DataFrame, DataTransformerID, DataTransformerInfo, Field, getFieldDisplayName, Labels, } from '@grafana/data'; import { getFieldConfigFromFrame, FieldToConfigMapping, evaluteFieldMappings, EvaluatedMappingResult, FieldConfigHandlerKey, } from '../fieldToConfigMapping/fieldToConfigMapping'; export interface RowToFieldsTransformOptions { nameField?: string; valueField?: string; mappings?: FieldToConfigMapping[]; } export const rowsToFieldsTransformer: DataTransformerInfo = { id: DataTransformerID.rowsToFields, name: 'Rows to fields', description: 'Convert each row into a field with dynamic config', defaultOptions: {}, /** * Return a modified copy of the series. If the transform is not or should not * be applied, just return the input series */ operator: (options) => (source) => source.pipe( map((data) => { return data.map((frame) => rowsToFields(options, frame)); }) ), }; export function rowsToFields(options: RowToFieldsTransformOptions, data: DataFrame): DataFrame { const mappingResult = evaluteFieldMappings(data, options.mappings ?? [], true); const { nameField, valueField } = mappingResult; if (!nameField || !valueField) { return data; } const outFields: Field[] = []; for (let index = 0; index < nameField.values.length; index++) { const name = nameField.values.get(index); const value = valueField.values.get(index); const config = getFieldConfigFromFrame(data, index, mappingResult); const labels = getLabelsFromRow(data, index, mappingResult); const field: Field = { name: `${name}`, type: valueField.type, values: new ArrayVector([value]), config: config, labels, }; outFields.push(field); } return { fields: outFields, length: 1, }; } function getLabelsFromRow(frame: DataFrame, index: number, mappingResult: EvaluatedMappingResult): Labels { const labels = { ...mappingResult.nameField!.labels }; for (let i = 0; i < frame.fields.length; i++) { const field = frame.fields[i]; const fieldName = getFieldDisplayName(field, frame); const fieldMapping = mappingResult.index[fieldName]; if (fieldMapping.handler && fieldMapping.handler.key !== FieldConfigHandlerKey.Label) { continue; } const value = field.values.get(index); if (value != null) { labels[fieldName] = value; } } return labels; }