[Ingest Pipelines] Preserve unknown fields in processors (#91146)
* 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>
This commit is contained in:
parent
26f088970e
commit
edc11d9680
|
@ -154,6 +154,17 @@ const createActions = (testBed: TestBed<TestSubject>) => {
|
|||
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');
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -22,6 +22,13 @@ const testProcessors: Pick<Pipeline, 'processors'> = {
|
|||
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]]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<Props> = ({
|
||||
processor,
|
||||
onFormUpdate,
|
||||
|
@ -81,6 +95,7 @@ export const ProcessorFormContainer: FunctionComponent<Props> = ({
|
|||
const { form } = useForm({
|
||||
defaultValue: { fields: getProcessor().options },
|
||||
serializer: formSerializer,
|
||||
options: formOptions,
|
||||
});
|
||||
const { subscribe } = form;
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ const ignoreFailureConfig: FieldConfig<any> = {
|
|||
};
|
||||
|
||||
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)',
|
||||
}),
|
||||
|
|
|
@ -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)',
|
||||
}),
|
||||
|
|
|
@ -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)',
|
||||
}),
|
||||
|
|
|
@ -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)',
|
||||
}),
|
||||
|
|
|
@ -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',
|
||||
{
|
||||
|
|
|
@ -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<string, FieldConfig> => {
|
|||
/* Optional field config */
|
||||
append_separator: {
|
||||
type: FIELD_TYPES.TEXT,
|
||||
serializer: from.emptyStringToUndefined,
|
||||
label: i18n.translate(
|
||||
'xpack.ingestPipelines.pipelineEditor.dissectForm.appendSeparatorparaotrFieldLabel',
|
||||
{
|
||||
|
|
|
@ -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<string, FieldConfig> = {
|
||||
path: {
|
||||
type: FIELD_TYPES.TEXT,
|
||||
serializer: from.emptyStringToUndefined,
|
||||
label: i18n.translate('xpack.ingestPipelines.pipelineEditor.dotExpanderForm.pathFieldLabel', {
|
||||
defaultMessage: 'Path',
|
||||
}),
|
||||
|
|
|
@ -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)',
|
||||
}),
|
||||
|
|
|
@ -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.',
|
||||
}
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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',
|
||||
}),
|
||||
|
|
|
@ -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)',
|
||||
}),
|
||||
|
|
|
@ -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: (
|
||||
<FormattedMessage
|
||||
id="xpack.ingestPipelines.pipelineEditor.setForm.valueFieldHelpText"
|
||||
defaultMessage="Value for the field. A blank value will set {emptyString}."
|
||||
values={{
|
||||
emptyString: <EuiCode>{'""'}</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
/* Optional fields config */
|
||||
override: {
|
||||
|
@ -101,7 +91,16 @@ export const SetProcessor: FunctionComponent = () => {
|
|||
})}
|
||||
/>
|
||||
|
||||
<UseField config={fieldsConfig.value} component={Field} path="fields.value" />
|
||||
<UseField
|
||||
config={fieldsConfig.value}
|
||||
component={Field}
|
||||
componentProps={{
|
||||
euiFieldProps: {
|
||||
'data-test-subj': 'valueFieldInput',
|
||||
},
|
||||
}}
|
||||
path="fields.value"
|
||||
/>
|
||||
|
||||
<UseField config={fieldsConfig.override} component={ToggleField} path="fields.override" />
|
||||
|
||||
|
|
|
@ -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<string, FieldConfig<any>>;
|
||||
|
||||
export type FormFieldsComponent = FunctionComponent<{ initialFieldValues?: Record<string, any> }>;
|
||||
export type FormFieldsComponent = FunctionComponent<{
|
||||
initialFieldValues?: Record<string, any>;
|
||||
}>;
|
||||
|
|
|
@ -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',
|
||||
}),
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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<Props> = ({
|
|||
});
|
||||
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,
|
||||
},
|
||||
|
|
|
@ -51,6 +51,7 @@ export {
|
|||
ValidationFunc,
|
||||
ValidationConfig,
|
||||
useFormData,
|
||||
FormOptions,
|
||||
} from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
|
||||
|
||||
export {
|
||||
|
|
|
@ -10832,7 +10832,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",
|
||||
|
@ -10930,9 +10929,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}ドキュメント",
|
||||
|
|
|
@ -10860,7 +10860,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 无效",
|
||||
|
@ -10958,9 +10957,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}文档",
|
||||
|
|
Loading…
Reference in a new issue