mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
Add second option to complex field conversion to increase flexibility
This avoids falsely equating 'JSON' with FieldType.other, and instead allows multiple parsers to be used if the 'Complex' type is selected. Currently only JSON parsing is implemented, but others could be supported easily in future.
This commit is contained in:
parent
30de927764
commit
6314ce35eb
@ -8,6 +8,7 @@ import {
|
||||
convertFieldTypes,
|
||||
convertFieldTypeTransformer,
|
||||
fieldToTimeField,
|
||||
ComplexFieldParserID,
|
||||
} from './convertFieldType';
|
||||
|
||||
describe('field convert type', () => {
|
||||
@ -183,14 +184,14 @@ describe('field convert types transformer', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('will convert field to complex objects', () => {
|
||||
it('will convert JSON fields to complex objects', () => {
|
||||
const options = {
|
||||
conversions: [
|
||||
{ targetField: 'numbers', destinationType: FieldType.other },
|
||||
{ targetField: 'objects', destinationType: FieldType.other },
|
||||
{ targetField: 'arrays', destinationType: FieldType.other },
|
||||
{ targetField: 'invalids', destinationType: FieldType.other },
|
||||
{ targetField: 'mixed', destinationType: FieldType.other },
|
||||
{ targetField: 'numbers', destinationType: FieldType.other, inputFormat: ComplexFieldParserID.JSON },
|
||||
{ targetField: 'objects', destinationType: FieldType.other, inputFormat: ComplexFieldParserID.JSON },
|
||||
{ targetField: 'arrays', destinationType: FieldType.other, inputFormat: ComplexFieldParserID.JSON },
|
||||
{ targetField: 'invalids', destinationType: FieldType.other, inputFormat: ComplexFieldParserID.JSON },
|
||||
{ targetField: 'mixed', destinationType: FieldType.other, inputFormat: ComplexFieldParserID.JSON },
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { dateTimeParse } from '../../datetime';
|
||||
import { ArrayVector } from '../../vector';
|
||||
import { fieldMatchers } from '../matchers';
|
||||
import { FieldMatcherID } from '../matchers/ids';
|
||||
import { Registry, RegistryItem } from '../../utils/Registry';
|
||||
|
||||
export interface ConvertFieldTypeTransformerOptions {
|
||||
conversions: ConvertFieldTypeOptions[];
|
||||
@ -25,6 +26,10 @@ export interface ConvertFieldTypeOptions {
|
||||
* Date format to parse a string datetime
|
||||
*/
|
||||
dateFormat?: string;
|
||||
/**
|
||||
* Format to parse a complex field as
|
||||
*/
|
||||
inputFormat?: ComplexFieldParserID;
|
||||
}
|
||||
|
||||
export const convertFieldTypeTransformer: SynchronousDataTransformerInfo<ConvertFieldTypeTransformerOptions> = {
|
||||
@ -100,7 +105,8 @@ export function convertFieldType(field: Field, opts: ConvertFieldTypeOptions): F
|
||||
case FieldType.boolean:
|
||||
return fieldToBooleanField(field);
|
||||
case FieldType.other:
|
||||
return fieldToComplexField(field);
|
||||
const parser = complexFieldParsers.get(opts.inputFormat ?? ComplexFieldParserID.JSON);
|
||||
return fieldToComplexField(field, parser);
|
||||
default:
|
||||
return field;
|
||||
}
|
||||
@ -180,15 +186,11 @@ function fieldToStringField(field: Field): Field {
|
||||
};
|
||||
}
|
||||
|
||||
function fieldToComplexField(field: Field): Field {
|
||||
function fieldToComplexField(field: Field, parser: ComplexFieldParser): Field {
|
||||
const complexValues = field.values.toArray().slice();
|
||||
|
||||
for (let s = 0; s < complexValues.length; s++) {
|
||||
try {
|
||||
complexValues[s] = JSON.parse(complexValues[s]);
|
||||
} catch {
|
||||
complexValues[s] = null;
|
||||
}
|
||||
complexValues[s] = parser.parse(complexValues[s]);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -219,3 +221,32 @@ export function ensureTimeField(field: Field, dateFormat?: string): Field {
|
||||
}
|
||||
return fieldToTimeField(field, dateFormat);
|
||||
}
|
||||
|
||||
/// Complex field parsing. Currently only JSON is supported, but more formats can be added
|
||||
/// by extending the ComplexFieldParserID enum and ComplexFieldParser interface.
|
||||
|
||||
export enum ComplexFieldParserID {
|
||||
JSON = 'json',
|
||||
}
|
||||
|
||||
export interface ComplexFieldParser extends RegistryItem {
|
||||
parse: (v: any) => any | null;
|
||||
}
|
||||
|
||||
const jsonFieldParser: ComplexFieldParser = {
|
||||
id: ComplexFieldParserID.JSON,
|
||||
name: 'JSON',
|
||||
description: 'Parse field as JSON',
|
||||
parse: (v: any) => {
|
||||
try {
|
||||
return JSON.parse(v);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Registry of complex field parsers, used with the ConvertFieldTypeTransformer.
|
||||
*/
|
||||
export const complexFieldParsers = new Registry<ComplexFieldParser>(() => [jsonFieldParser]);
|
||||
|
@ -10,7 +10,11 @@ import {
|
||||
TransformerUIProps,
|
||||
} from '@grafana/data';
|
||||
|
||||
import { ConvertFieldTypeTransformerOptions } from '@grafana/data/src/transformations/transformers/convertFieldType';
|
||||
import {
|
||||
ComplexFieldParserID,
|
||||
complexFieldParsers,
|
||||
ConvertFieldTypeTransformerOptions,
|
||||
} from '@grafana/data/src/transformations/transformers/convertFieldType';
|
||||
import { Button, InlineField, InlineFieldRow, Input, Select } from '@grafana/ui';
|
||||
import { FieldNamePicker } from '../../../../../packages/grafana-ui/src/components/MatchersUI/FieldNamePicker';
|
||||
import { ConvertFieldTypeOptions } from '../../../../../packages/grafana-data/src/transformations/transformers/convertFieldType';
|
||||
@ -29,9 +33,11 @@ export const ConvertFieldTypeTransformerEditor: React.FC<TransformerUIProps<Conv
|
||||
{ value: FieldType.string, label: 'String' },
|
||||
{ value: FieldType.time, label: 'Time' },
|
||||
{ value: FieldType.boolean, label: 'Boolean' },
|
||||
{ value: FieldType.other, label: 'JSON' },
|
||||
{ value: FieldType.other, label: 'Complex' },
|
||||
];
|
||||
|
||||
const complexFormat = complexFieldParsers.selectOptions();
|
||||
|
||||
const onSelectField = useCallback(
|
||||
(idx) => (value: string | undefined) => {
|
||||
const conversions = options.conversions;
|
||||
@ -68,6 +74,18 @@ export const ConvertFieldTypeTransformerEditor: React.FC<TransformerUIProps<Conv
|
||||
[onChange, options]
|
||||
);
|
||||
|
||||
const onComplexInputFormat = useCallback(
|
||||
(idx) => (value: SelectableValue<ComplexFieldParserID>) => {
|
||||
const conversions = options.conversions;
|
||||
conversions[idx] = { ...conversions[idx], inputFormat: value.value };
|
||||
onChange({
|
||||
...options,
|
||||
conversions: conversions,
|
||||
});
|
||||
},
|
||||
[onChange, options]
|
||||
);
|
||||
|
||||
const onAddConvertFieldType = useCallback(() => {
|
||||
onChange({
|
||||
...options,
|
||||
@ -121,6 +139,22 @@ export const ConvertFieldTypeTransformerEditor: React.FC<TransformerUIProps<Conv
|
||||
<Input value={c.dateFormat} placeholder={'e.g. YYYY-MM-DD'} onChange={onInputFormat(idx)} width={24} />
|
||||
</InlineField>
|
||||
)}
|
||||
{c.destinationType === FieldType.other && (
|
||||
<InlineField
|
||||
label="Input format"
|
||||
tooltip="Specify the format of the input field so Grafana can parse the field correctly."
|
||||
>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={complexFormat.options as Array<SelectableValue<ComplexFieldParserID>>}
|
||||
value={c.inputFormat}
|
||||
defaultValue={complexFormat.options[0]}
|
||||
placeholder={'JSON'}
|
||||
onChange={onComplexInputFormat(idx)}
|
||||
width={24}
|
||||
/>
|
||||
</InlineField>
|
||||
)}
|
||||
<Button
|
||||
size="md"
|
||||
icon="trash-alt"
|
||||
|
Loading…
Reference in New Issue
Block a user