diff --git a/public/app/features/transformers/extractFields/ExtractFieldsTransformerEditor.tsx b/public/app/features/transformers/extractFields/ExtractFieldsTransformerEditor.tsx index e08b4065732..728e9687f27 100644 --- a/public/app/features/transformers/extractFields/ExtractFieldsTransformerEditor.tsx +++ b/public/app/features/transformers/extractFields/ExtractFieldsTransformerEditor.tsx @@ -9,7 +9,7 @@ import { StandardEditorsRegistryItem, TransformerCategory, } from '@grafana/data'; -import { InlineField, InlineFieldRow, Select, InlineSwitch, Input } from '@grafana/ui'; +import { InlineField, InlineFieldRow, Select, InlineSwitch, Input, Combobox, ComboboxOption } from '@grafana/ui'; import { FieldNamePicker } from '@grafana/ui/src/components/MatchersUI/FieldNamePicker'; import { getTransformationContent } from '../docs/getTransformationContent'; @@ -31,7 +31,7 @@ const fieldNamePickerSettings: StandardEditorsRegistryItem) => { const onPickSourceField = (source?: string) => { @@ -62,6 +62,13 @@ export const extractFieldsTransformerEditor = ({ }); }; + const onDelimiterChange = (val: ComboboxOption) => { + onChange({ + ...options, + delimiter: val.value, + }); + }; + const onToggleReplace = () => { if (options.replace) { options.keepTime = false; @@ -115,6 +122,19 @@ export const extractFieldsTransformerEditor = ({ {options.format === FieldExtractorID.JSON && ( )} + {options.format === FieldExtractorID.Delimiter && ( + + + + + + )} diff --git a/public/app/features/transformers/extractFields/extractFields.ts b/public/app/features/transformers/extractFields/extractFields.ts index d686d262332..288dea54d78 100644 --- a/public/app/features/transformers/extractFields/extractFields.ts +++ b/public/app/features/transformers/extractFields/extractFields.ts @@ -20,7 +20,9 @@ export const extractFieldsTransformer: SynchronousDataTransformerInfo (source) => source.pipe(map((data) => extractFieldsTransformer.transformer(options, ctx)(data))), diff --git a/public/app/features/transformers/extractFields/fieldExtractor.test.ts b/public/app/features/transformers/extractFields/fieldExtractor.test.ts index 820c3843e1a..7c80703733b 100644 --- a/public/app/features/transformers/extractFields/fieldExtractor.test.ts +++ b/public/app/features/transformers/extractFields/fieldExtractor.test.ts @@ -151,4 +151,35 @@ describe('Extract fields from text', () => { } `); }); + + describe('Delimiter', () => { + it('splits by comma', async () => { + const extractor = fieldExtractors.get(FieldExtractorID.Delimiter); + const parse = extractor.getParser({}); + const out = parse('a,b,c'); + + expect(out).toMatchInlineSnapshot(` + { + "a": 1, + "b": 1, + "c": 1, + } + `); + }); + + it('trims whitespace', async () => { + const extractor = fieldExtractors.get(FieldExtractorID.Delimiter); + const parse = extractor.getParser({}); + const out = parse(` a, b,c, d `); + + expect(out).toMatchInlineSnapshot(` + { + "a": 1, + "b": 1, + "c": 1, + "d": 1, + } + `); + }); + }); }); diff --git a/public/app/features/transformers/extractFields/fieldExtractors.ts b/public/app/features/transformers/extractFields/fieldExtractors.ts index 348984cd9db..ec4ffd87888 100644 --- a/public/app/features/transformers/extractFields/fieldExtractors.ts +++ b/public/app/features/transformers/extractFields/fieldExtractors.ts @@ -1,4 +1,4 @@ -import { Registry, RegistryItem, stringStartsAsRegEx, stringToJsRegex } from '@grafana/data'; +import { escapeStringForRegex, Registry, RegistryItem, stringStartsAsRegEx, stringToJsRegex } from '@grafana/data'; import { ExtractFieldsOptions, FieldExtractorID } from './types'; @@ -133,7 +133,27 @@ const extLabels: FieldExtractor = { getParser: (options) => parseKeyValuePairs, }; -const fmts = [extJSON, extLabels, extRegExp]; +const extDelimiter: FieldExtractor = { + id: FieldExtractorID.Delimiter, + name: 'Split by delimiter', + description: 'Splits at delimited values, such as commas', + getParser: ({ delimiter = ',' }) => { + // Match for delimiter with surrounding whitesapce (\s) + const splitRegExp = new RegExp(`\\s*${escapeStringForRegex(delimiter)}\\s*`, 'g'); + + return (raw: string) => { + // Try to split delimited values + const parts = raw.trim().split(splitRegExp); + const acc: Record = {}; + for (const part of parts) { + acc[part] = 1; + } + return acc; + }; + }, +}; + +const fmts = [extJSON, extLabels, extDelimiter, extRegExp]; const extAuto: FieldExtractor = { id: FieldExtractorID.Auto, diff --git a/public/app/features/transformers/extractFields/types.ts b/public/app/features/transformers/extractFields/types.ts index b0204f14377..6841861e03d 100644 --- a/public/app/features/transformers/extractFields/types.ts +++ b/public/app/features/transformers/extractFields/types.ts @@ -3,6 +3,7 @@ export enum FieldExtractorID { KeyValues = 'kvp', Auto = 'auto', RegExp = 'regexp', + Delimiter = 'delimiter', } export interface JSONPath { @@ -12,6 +13,7 @@ export interface JSONPath { export interface ExtractFieldsOptions { source?: string; jsonPaths?: JSONPath[]; + delimiter?: string; regExp?: string; format?: FieldExtractorID; replace?: boolean;