From a54063b9cc79ae23a6ca81102b360a63a5dc043f Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 17 Feb 2021 17:11:34 +0100 Subject: [PATCH] [Ingest Pipelines] Preserve unknown fields in processors (#91146) (#91630) * keep known and unknown options in processor config * added test for preserving unknown fields * refactor form for NOT stripping empty field values, also allow empty "value" for set and gsub * remove unused i18n * fix user agent form serialization, update field help text * remove out of date translation Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../pipeline_processors_editor.helpers.tsx | 11 ++++ .../pipeline_processors_editor.test.tsx | 65 +++++++++++++++---- .../processor_form.container.tsx | 17 ++++- .../common_fields/common_processor_fields.tsx | 2 + .../processors/common_fields/target_field.tsx | 3 +- .../processor_form/processors/csv.tsx | 7 +- .../processor_form/processors/date.tsx | 6 +- .../processors/date_index_name.tsx | 10 +-- .../processor_form/processors/dissect.tsx | 3 +- .../processors/dot_expander.tsx | 3 + .../processor_form/processors/geoip.tsx | 2 +- .../processor_form/processors/gsub.tsx | 15 ++--- .../processor_form/processors/kv.tsx | 3 + .../processor_form/processors/script.tsx | 2 +- .../processor_form/processors/set.tsx | 43 ++++++------ .../processor_form/processors/shared.ts | 8 ++- .../processor_form/processors/sort.tsx | 4 +- .../processor_form/processors/user_agent.tsx | 3 +- .../context/processors_context.tsx | 13 +++- .../ingest_pipelines/public/shared_imports.ts | 1 + .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - 22 files changed, 154 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.helpers.tsx index 55188805d571c..fabb6a46c4943 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.helpers.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.helpers.tsx @@ -154,6 +154,17 @@ const createActions = (testBed: TestBed) => { find(`${processorSelector}.moreMenu.duplicateButton`).simulate('click'); }); }, + openProcessorEditor: (processorSelector: string) => { + act(() => { + find(`${processorSelector}.manageItemButton`).simulate('click'); + }); + component.update(); + }, + submitProcessorForm: async () => { + await act(async () => { + find('editProcessorForm.submitButton').simulate('click'); + }); + }, }; }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.test.tsx index 1c698043a8bc7..e89e91c1cbaa9 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.test.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.test.tsx @@ -22,6 +22,13 @@ const testProcessors: Pick = { replacement: '$17$2', }, }, + { + set: { + field: 'test', + value: 'test', + unknown_field_foo: 'unknown_value', + }, + }, ], }; @@ -79,11 +86,37 @@ describe('Pipeline Editor', () => { await actions.addProcessor('processors', 'test', { if: '1 == 1' }); const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; const { processors } = onUpdateResult.getData(); - expect(processors.length).toBe(3); - const [a, b, c] = processors; + expect(processors.length).toBe(4); + const [a, b, c, d] = processors; expect(a).toEqual(testProcessors.processors[0]); expect(b).toEqual(testProcessors.processors[1]); - expect(c).toEqual({ test: { if: '1 == 1' } }); + expect(c).toEqual(testProcessors.processors[2]); + expect(d).toEqual({ test: { if: '1 == 1' } }); + }); + + it('edits a processor without removing unknown processor.options', async () => { + const { actions, exists, form } = testBed; + // Open the edit processor form for the set processor + actions.openProcessorEditor('processors>2'); + expect(exists('editProcessorForm')).toBeTruthy(); + form.setInputValue('editProcessorForm.valueFieldInput', 'test44'); + await actions.submitProcessorForm(); + const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; + const { + processors: { 2: setProcessor }, + } = onUpdateResult.getData(); + // The original field should still be unchanged + expect(testProcessors.processors[2].set.value).toBe('test'); + expect(setProcessor.set).toEqual({ + description: undefined, + field: 'test', + ignore_empty_value: undefined, + ignore_failure: undefined, + override: undefined, + // This unknown_field is not supported in the form + unknown_field_foo: 'unknown_value', + value: 'test44', + }); }); it('removes a processor', () => { @@ -92,7 +125,7 @@ describe('Pipeline Editor', () => { actions.removeProcessor('processors>0'); const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; const { processors } = onUpdateResult.getData(); - expect(processors.length).toBe(1); + expect(processors.length).toBe(2); expect(processors[0]).toEqual({ gsub: { field: '_index', @@ -107,7 +140,11 @@ describe('Pipeline Editor', () => { actions.moveProcessor('processors>0', 'dropButtonBelow-processors>1'); const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; const { processors } = onUpdateResult.getData(); - expect(processors).toEqual(testProcessors.processors.slice(0).reverse()); + expect(processors).toEqual([ + testProcessors.processors[1], + testProcessors.processors[0], + testProcessors.processors[2], + ]); }); it('adds an on-failure processor to a processor', async () => { @@ -121,7 +158,7 @@ describe('Pipeline Editor', () => { expect(exists(`${processorSelector}.addProcessor`)); const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; const { processors } = onUpdateResult.getData(); - expect(processors.length).toBe(2); + expect(processors.length).toBe(3); expect(processors[0]).toEqual(testProcessors.processors[0]); // should be unchanged expect(processors[1].gsub).toEqual({ ...testProcessors.processors[1].gsub, @@ -135,7 +172,7 @@ describe('Pipeline Editor', () => { actions.moveProcessor('processors>0', 'dropButtonBelow-processors>1>onFailure>0'); const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; const { processors } = onUpdateResult.getData(); - expect(processors.length).toBe(1); + expect(processors.length).toBe(2); expect(processors[0].gsub.on_failure).toEqual([ { test: { if: '1 == 3' }, @@ -150,7 +187,7 @@ describe('Pipeline Editor', () => { actions.duplicateProcessor('processors>1'); const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; const { processors } = onUpdateResult.getData(); - expect(processors.length).toBe(3); + expect(processors.length).toBe(4); const duplicatedProcessor = { gsub: { ...testProcessors.processors[1].gsub, @@ -161,6 +198,7 @@ describe('Pipeline Editor', () => { testProcessors.processors[0], duplicatedProcessor, duplicatedProcessor, + testProcessors.processors[2], ]); }); @@ -182,14 +220,17 @@ describe('Pipeline Editor', () => { actions.moveProcessor('processors>0', 'dropButtonBelow-onFailure>0'); const [onUpdateResult1] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; const data1 = onUpdateResult1.getData(); - expect(data1.processors.length).toBe(1); + expect(data1.processors.length).toBe(2); expect(data1.on_failure.length).toBe(2); - expect(data1.processors).toEqual([testProcessors.processors[1]]); + expect(data1.processors).toEqual([ + testProcessors.processors[1], + testProcessors.processors[2], + ]); expect(data1.on_failure).toEqual([{ test: { if: '1 == 5' } }, testProcessors.processors[0]]); actions.moveProcessor('onFailure>1', 'dropButtonAbove-processors>0'); const [onUpdateResult2] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; const data2 = onUpdateResult2.getData(); - expect(data2.processors.length).toBe(2); + expect(data2.processors.length).toBe(3); expect(data2.on_failure.length).toBe(1); expect(data2.processors).toEqual(testProcessors.processors); expect(data2.on_failure).toEqual([{ test: { if: '1 == 5' } }]); @@ -208,7 +249,7 @@ describe('Pipeline Editor', () => { actions.moveProcessor('processors>0', 'onFailure.dropButtonEmptyTree'); const [onUpdateResult2] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; const data = onUpdateResult2.getData(); - expect(data.processors).toEqual([testProcessors.processors[1]]); + expect(data.processors).toEqual([testProcessors.processors[1], testProcessors.processors[2]]); expect(data.on_failure).toEqual([testProcessors.processors[0]]); }); }); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processor_form.container.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processor_form.container.tsx index 5ce8cf09cc594..cce473ec9a214 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processor_form.container.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processor_form.container.tsx @@ -7,7 +7,13 @@ import React, { FunctionComponent, useCallback, useEffect, useRef } from 'react'; -import { useForm, OnFormUpdateArg, FormData, useKibana } from '../../../../../shared_imports'; +import { + useForm, + OnFormUpdateArg, + FormData, + FormOptions, + useKibana, +} from '../../../../../shared_imports'; import { ProcessorInternal } from '../../types'; import { EditProcessorForm } from './edit_processor_form'; @@ -33,6 +39,14 @@ interface Props { processor?: ProcessorInternal; } +const formOptions: FormOptions = { + /** + * This is important for allowing configuration of empty text fields in certain processors that + * remove values from their inputs. + */ + stripEmptyFields: false, +}; + export const ProcessorFormContainer: FunctionComponent = ({ processor, onFormUpdate, @@ -81,6 +95,7 @@ export const ProcessorFormContainer: FunctionComponent = ({ const { form } = useForm({ defaultValue: { fields: getProcessor().options }, serializer: formSerializer, + options: formOptions, }); const { subscribe } = form; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx index 12226608a4682..f6449c3cc24d4 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx @@ -38,6 +38,7 @@ const ignoreFailureConfig: FieldConfig = { }; const ifConfig: FieldConfig = { + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.commonFields.ifFieldLabel', { defaultMessage: 'Condition (optional)', }), @@ -48,6 +49,7 @@ const ifConfig: FieldConfig = { }; const tagConfig: FieldConfig = { + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.commonFields.tagFieldLabel', { defaultMessage: 'Tag (optional)', }), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/common_fields/target_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/common_fields/target_field.tsx index 1caf5ffd3fb1e..b603a131e10b0 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/common_fields/target_field.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/common_fields/target_field.tsx @@ -10,12 +10,13 @@ import { i18n } from '@kbn/i18n'; import { Field, FIELD_TYPES, UseField, FieldConfig } from '../../../../../../../shared_imports'; -import { FieldsConfig } from '../shared'; +import { FieldsConfig, from } from '../shared'; const fieldsConfig: FieldsConfig = { target_field: { type: FIELD_TYPES.TEXT, deserializer: String, + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.commonFields.targetFieldLabel', { defaultMessage: 'Target field (optional)', }), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/csv.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/csv.tsx index 19176a27a0778..b192ee0494bb3 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/csv.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/csv.tsx @@ -24,7 +24,7 @@ import { FieldsConfig } from './shared'; import { IgnoreMissingField } from './common_fields/ignore_missing_field'; import { FieldNameField } from './common_fields/field_name_field'; -import { to } from './shared'; +import { to, from } from './shared'; const { minLengthField } = fieldValidators; @@ -72,7 +72,7 @@ const fieldsConfig: FieldsConfig = { /* Optional fields config */ separator: { type: FIELD_TYPES.TEXT, - serializer: (v) => (v ? v : undefined), + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.convertForm.separatorFieldLabel', { defaultMessage: 'Separator (optional)', }), @@ -91,7 +91,7 @@ const fieldsConfig: FieldsConfig = { }, quote: { type: FIELD_TYPES.TEXT, - serializer: (v) => (v ? v : undefined), + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.convertForm.quoteFieldLabel', { defaultMessage: 'Quote (optional)', }), @@ -121,6 +121,7 @@ const fieldsConfig: FieldsConfig = { }, empty_value: { type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.convertForm.emptyValueFieldLabel', { defaultMessage: 'Empty value (optional)', }), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/date.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/date.tsx index e8e956daff207..ca541a9e6d619 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/date.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/date.tsx @@ -18,7 +18,7 @@ import { ComboBoxField, } from '../../../../../../shared_imports'; -import { FieldsConfig, to } from './shared'; +import { FieldsConfig, to, from } from './shared'; import { FieldNameField } from './common_fields/field_name_field'; import { TargetField } from './common_fields/target_field'; @@ -53,7 +53,7 @@ const fieldsConfig: FieldsConfig = { /* Optional fields config */ timezone: { type: FIELD_TYPES.TEXT, - serializer: (v) => (v ? v : undefined), + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.dateForm.timezoneFieldLabel', { defaultMessage: 'Timezone (optional)', }), @@ -67,7 +67,7 @@ const fieldsConfig: FieldsConfig = { }, locale: { type: FIELD_TYPES.TEXT, - serializer: (v) => (v ? v : undefined), + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.dateForm.localeFieldLabel', { defaultMessage: 'Locale (optional)', }), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/date_index_name.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/date_index_name.tsx index 182b9ecd845e9..5c5b5ff89fd20 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/date_index_name.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/date_index_name.tsx @@ -19,7 +19,7 @@ import { SelectField, } from '../../../../../../shared_imports'; -import { FieldsConfig, to } from './shared'; +import { FieldsConfig, to, from } from './shared'; import { FieldNameField } from './common_fields/field_name_field'; const { emptyField } = fieldValidators; @@ -57,7 +57,7 @@ const fieldsConfig: FieldsConfig = { /* Optional fields config */ index_name_prefix: { type: FIELD_TYPES.TEXT, - serializer: (v) => (v ? v : undefined), + serializer: from.emptyStringToUndefined, label: i18n.translate( 'xpack.ingestPipelines.pipelineEditor.dateIndexNameForm.indexNamePrefixFieldLabel', { @@ -71,7 +71,7 @@ const fieldsConfig: FieldsConfig = { }, index_name_format: { type: FIELD_TYPES.TEXT, - serializer: (v) => (v ? v : undefined), + serializer: from.emptyStringToUndefined, label: i18n.translate( 'xpack.ingestPipelines.pipelineEditor.dateIndexNameForm.indexNameFormatFieldLabel', { @@ -108,7 +108,7 @@ const fieldsConfig: FieldsConfig = { }, timezone: { type: FIELD_TYPES.TEXT, - serializer: (v) => (v ? v : undefined), + serializer: from.emptyStringToUndefined, label: i18n.translate( 'xpack.ingestPipelines.pipelineEditor.dateIndexNameForm.timezoneFieldLabel', { @@ -125,7 +125,7 @@ const fieldsConfig: FieldsConfig = { }, locale: { type: FIELD_TYPES.TEXT, - serializer: (v) => (v ? v : undefined), + serializer: from.emptyStringToUndefined, label: i18n.translate( 'xpack.ingestPipelines.pipelineEditor.dateIndexNameForm.localeFieldLabel', { diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/dissect.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/dissect.tsx index 641a6e73d9025..6652ad277cc26 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/dissect.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/dissect.tsx @@ -22,7 +22,7 @@ import { import { FieldNameField } from './common_fields/field_name_field'; import { IgnoreMissingField } from './common_fields/ignore_missing_field'; -import { EDITOR_PX_HEIGHT } from './shared'; +import { EDITOR_PX_HEIGHT, from } from './shared'; const { emptyField } = fieldValidators; @@ -72,6 +72,7 @@ const getFieldsConfig = (esDocUrl: string): Record => { /* Optional field config */ append_separator: { type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, label: i18n.translate( 'xpack.ingestPipelines.pipelineEditor.dissectForm.appendSeparatorparaotrFieldLabel', { diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/dot_expander.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/dot_expander.tsx index 0bbcb7a2eefb3..4bbc242cf0ef8 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/dot_expander.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/dot_expander.tsx @@ -12,9 +12,12 @@ import { FieldConfig, FIELD_TYPES, UseField, Field } from '../../../../../../sha import { FieldNameField } from './common_fields/field_name_field'; +import { from } from './shared'; + const fieldsConfig: Record = { path: { type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.dotExpanderForm.pathFieldLabel', { defaultMessage: 'Path', }), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/geoip.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/geoip.tsx index 7848872800df4..6a1f86977d8db 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/geoip.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/geoip.tsx @@ -22,7 +22,7 @@ const fieldsConfig: FieldsConfig = { /* Optional field config */ database_file: { type: FIELD_TYPES.TEXT, - serializer: (v) => (v === 'GeoLite2-City.mmdb' ? undefined : v), + serializer: (v) => (v === 'GeoLite2-City.mmdb' || v === '' ? undefined : v), label: i18n.translate('xpack.ingestPipelines.pipelineEditor.geoIPForm.databaseFileLabel', { defaultMessage: 'Database file (optional)', }), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/gsub.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/gsub.tsx index 8835f3775a90f..edfa59ea80281 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/gsub.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/gsub.tsx @@ -41,6 +41,7 @@ const fieldsConfig: FieldsConfig = { ], }, + // This is a required field, but we exclude validation because we accept empty values as '' replacement: { type: FIELD_TYPES.TEXT, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.replacementFieldLabel', { @@ -48,17 +49,11 @@ const fieldsConfig: FieldsConfig = { }), helpText: i18n.translate( 'xpack.ingestPipelines.pipelineEditor.gsubForm.replacementFieldHelpText', - { defaultMessage: 'Replacement text for matches.' } - ), - validations: [ { - validator: emptyField( - i18n.translate('xpack.ingestPipelines.pipelineEditor.gsubForm.replacementRequiredError', { - defaultMessage: 'A value is required.', - }) - ), - }, - ], + defaultMessage: + 'Replacement text for matches. A blank value will remove the matched text from the resulting text.', + } + ), }, }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/kv.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/kv.tsx index 2d17e3600cb79..694ae4e07070d 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/kv.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/kv.tsx @@ -107,6 +107,7 @@ const fieldsConfig: FieldsConfig = { prefix: { type: FIELD_TYPES.TEXT, deserializer: String, + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.kvForm.prefixFieldLabel', { defaultMessage: 'Prefix', }), @@ -118,6 +119,7 @@ const fieldsConfig: FieldsConfig = { trim_key: { type: FIELD_TYPES.TEXT, deserializer: String, + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.kvForm.trimKeyFieldLabel', { defaultMessage: 'Trim key', }), @@ -129,6 +131,7 @@ const fieldsConfig: FieldsConfig = { trim_value: { type: FIELD_TYPES.TEXT, deserializer: String, + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.kvForm.trimValueFieldLabel', { defaultMessage: 'Trim value', }), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/script.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/script.tsx index 60871fa7ba4ab..3c66279357843 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/script.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/script.tsx @@ -81,7 +81,7 @@ const fieldsConfig: FieldsConfig = { lang: { type: FIELD_TYPES.TEXT, deserializer: String, - serializer: from.undefinedIfValue('painless'), + serializer: from.emptyStringToUndefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.scriptForm.langFieldLabel', { defaultMessage: 'Language (optional)', }), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/set.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/set.tsx index 9ccfe580a53ae..89ca373b9e653 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/set.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/set.tsx @@ -10,40 +10,30 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCode } from '@elastic/eui'; -import { - FIELD_TYPES, - fieldValidators, - ToggleField, - UseField, - Field, -} from '../../../../../../shared_imports'; +import { FIELD_TYPES, ToggleField, UseField, Field } from '../../../../../../shared_imports'; import { FieldsConfig, to, from } from './shared'; import { FieldNameField } from './common_fields/field_name_field'; -const { emptyField } = fieldValidators; - const fieldsConfig: FieldsConfig = { /* Required fields config */ + // This is a required field, but we exclude validation because we accept empty values as '' value: { type: FIELD_TYPES.TEXT, deserializer: String, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.valueFieldLabel', { defaultMessage: 'Value', }), - helpText: i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.valueFieldHelpText', { - defaultMessage: 'Value for the field.', - }), - validations: [ - { - validator: emptyField( - i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.valueRequiredError', { - defaultMessage: 'A value is required.', - }) - ), - }, - ], + helpText: ( + {'""'}, + }} + /> + ), }, /* Optional fields config */ override: { @@ -101,7 +91,16 @@ export const SetProcessor: FunctionComponent = () => { })} /> - + diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/shared.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/shared.ts index daa0e548ab728..399da3c05c783 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/shared.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/shared.ts @@ -64,9 +64,11 @@ export const from = { // Ignore } } + return undefined; }, optionalArrayOfStrings: (v: string[]) => (v.length ? v : undefined), - undefinedIfValue: (value: any) => (v: boolean) => (v === value ? undefined : v), + undefinedIfValue: (value: unknown) => (v: boolean) => (v === value ? undefined : v), + emptyStringToUndefined: (v: unknown) => (v === '' ? undefined : v), }; export const EDITOR_PX_HEIGHT = { @@ -78,4 +80,6 @@ export const EDITOR_PX_HEIGHT = { export type FieldsConfig = Record>; -export type FormFieldsComponent = FunctionComponent<{ initialFieldValues?: Record }>; +export type FormFieldsComponent = FunctionComponent<{ + initialFieldValues?: Record; +}>; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/sort.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/sort.tsx index 82589e8777589..3239f54682041 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/sort.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/sort.tsx @@ -12,7 +12,7 @@ import { FIELD_TYPES, UseField, SelectField } from '../../../../../../shared_imp import { FieldNameField } from './common_fields/field_name_field'; import { TargetField } from './common_fields/target_field'; -import { FieldsConfig, from } from './shared'; +import { FieldsConfig } from './shared'; const fieldsConfig: FieldsConfig = { /* Optional fields config */ @@ -20,7 +20,7 @@ const fieldsConfig: FieldsConfig = { type: FIELD_TYPES.SELECT, defaultValue: 'asc', deserializer: (v) => (v === 'asc' || v === 'desc' ? v : 'asc'), - serializer: from.undefinedIfValue('asc'), + serializer: (v) => (v === 'asc' || v === '' ? undefined : v), label: i18n.translate('xpack.ingestPipelines.pipelineEditor.sortForm.orderFieldLabel', { defaultMessage: 'Order', }), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/user_agent.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/user_agent.tsx index d14048c4e00dc..893e52bcc0073 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/user_agent.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/user_agent.tsx @@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import { EuiComboBoxOptionOption } from '@elastic/eui'; import { FIELD_TYPES, UseField, Field } from '../../../../../../shared_imports'; -import { FieldsConfig } from './shared'; +import { FieldsConfig, from } from './shared'; import { IgnoreMissingField } from './common_fields/ignore_missing_field'; import { FieldNameField } from './common_fields/field_name_field'; import { TargetField } from './common_fields/target_field'; @@ -31,6 +31,7 @@ const fieldsConfig: FieldsConfig = { /* Optional fields config */ regex_file: { type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, deserializer: String, label: i18n.translate( 'xpack.ingestPipelines.pipelineEditor.userAgentForm.regexFileFieldLabel', diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx index 0302ff017f09f..0c43297e811d3 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { omit } from 'lodash'; import React, { createContext, FunctionComponent, @@ -150,12 +150,21 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({ }); break; case 'managingProcessor': + // These are the option names we get back from our UI + const knownOptionNames = Object.keys(processorTypeAndOptions.options); + // The processor that we are updating may have options configured the UI does not know about + const unknownOptions = omit(mode.arg.processor.options, knownOptionNames); + // In order to keep the options we don't get back from our UI, we merge the known and unknown options + const updatedProcessorOptions = { + ...processorTypeAndOptions.options, + ...unknownOptions, + }; processorsDispatch({ type: 'updateProcessor', payload: { processor: { ...mode.arg.processor, - ...processorTypeAndOptions, + options: updatedProcessorOptions, }, selector: mode.arg.selector, }, diff --git a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts index d951e2f2a0768..4afd434b89372 100644 --- a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts +++ b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts @@ -51,6 +51,7 @@ export { ValidationFunc, ValidationConfig, useFormData, + FormOptions, } from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; export { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a4bfc26a1b2e3..9c7c5eca0583c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10840,7 +10840,6 @@ "xpack.ingestPipelines.pipelineEditor.gsubForm.patternRequiredError": "値が必要です。", "xpack.ingestPipelines.pipelineEditor.gsubForm.replacementFieldHelpText": "一致の置換テキスト。", "xpack.ingestPipelines.pipelineEditor.gsubForm.replacementFieldLabel": "置換", - "xpack.ingestPipelines.pipelineEditor.gsubForm.replacementRequiredError": "値が必要です。", "xpack.ingestPipelines.pipelineEditor.htmlStripForm.fieldNameHelpText": "HTMLタグを削除するフィールド。", "xpack.ingestPipelines.pipelineEditor.inferenceForm.fieldMapHelpText": "ドキュメントフィールド名をモデルの既知のフィールド名にマッピングします。モデルのどのマッピングよりも優先されます。", "xpack.ingestPipelines.pipelineEditor.inferenceForm.fieldMapInvalidJSONError": "無効なJSON", @@ -10938,9 +10937,7 @@ "xpack.ingestPipelines.pipelineEditor.setForm.overrideFieldHelpText": "有効にすると、既存のフィールド値を上書きします。無効にすると、{nullValue}フィールドのみを更新します。", "xpack.ingestPipelines.pipelineEditor.setForm.overrideFieldLabel": "無効化", "xpack.ingestPipelines.pipelineEditor.setForm.propertiesFieldHelpText": "追加するユーザー詳細情報。フォルトは{value}です。", - "xpack.ingestPipelines.pipelineEditor.setForm.valueFieldHelpText": "フィールドの値。", "xpack.ingestPipelines.pipelineEditor.setForm.valueFieldLabel": "値", - "xpack.ingestPipelines.pipelineEditor.setForm.valueRequiredError": "値が必要です。", "xpack.ingestPipelines.pipelineEditor.setSecurityUserForm.fieldNameField": "出力フィールド。", "xpack.ingestPipelines.pipelineEditor.setSecurityUserForm.propertiesFieldLabel": "プロパティ(任意)", "xpack.ingestPipelines.pipelineEditor.settingsForm.learnMoreLabelLink.processor": "{processorLabel}ドキュメント", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 632c5f13c98a5..df7b909d4a869 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10868,7 +10868,6 @@ "xpack.ingestPipelines.pipelineEditor.gsubForm.patternRequiredError": "需要值。", "xpack.ingestPipelines.pipelineEditor.gsubForm.replacementFieldHelpText": "匹配项的替换文本。", "xpack.ingestPipelines.pipelineEditor.gsubForm.replacementFieldLabel": "替换", - "xpack.ingestPipelines.pipelineEditor.gsubForm.replacementRequiredError": "需要值。", "xpack.ingestPipelines.pipelineEditor.htmlStripForm.fieldNameHelpText": "从其中移除 HTML 标记的字段。", "xpack.ingestPipelines.pipelineEditor.inferenceForm.fieldMapHelpText": "将文档字段名称映射到模型的已知字段名称。优先于模型中的任何映射。", "xpack.ingestPipelines.pipelineEditor.inferenceForm.fieldMapInvalidJSONError": "JSON 无效", @@ -10966,9 +10965,7 @@ "xpack.ingestPipelines.pipelineEditor.setForm.overrideFieldHelpText": "如果启用,则覆盖现有字段值。如果禁用,则仅更新 {nullValue} 字段。", "xpack.ingestPipelines.pipelineEditor.setForm.overrideFieldLabel": "覆盖", "xpack.ingestPipelines.pipelineEditor.setForm.propertiesFieldHelpText": "要添加的用户详情。默认为 {value}", - "xpack.ingestPipelines.pipelineEditor.setForm.valueFieldHelpText": "字段的值。", "xpack.ingestPipelines.pipelineEditor.setForm.valueFieldLabel": "值", - "xpack.ingestPipelines.pipelineEditor.setForm.valueRequiredError": "需要值。", "xpack.ingestPipelines.pipelineEditor.setSecurityUserForm.fieldNameField": "输出字段。", "xpack.ingestPipelines.pipelineEditor.setSecurityUserForm.propertiesFieldLabel": "属性(可选)", "xpack.ingestPipelines.pipelineEditor.settingsForm.learnMoreLabelLink.processor": "{processorLabel}文档",