diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx
index 183777ca765b4..9101e64278dc6 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx
@@ -172,8 +172,6 @@ type TestSubject =
| 'valueFieldInput'
| 'mediaTypeSelectorField'
| 'networkDirectionField.input'
- | 'sourceIpField.input'
- | 'destinationIpField.input'
| 'toggleCustomField'
| 'ignoreEmptyField.input'
| 'overrideField.input'
@@ -189,4 +187,5 @@ type TestSubject =
| 'ianaField.input'
| 'transportField.input'
| 'seedField.input'
+ | 'copyFromInput'
| 'trimSwitch.input';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/set.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/set.test.tsx
index d7351c9dbf65f..544b8aeb51c05 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/set.test.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/set.test.tsx
@@ -8,19 +8,6 @@
import { act } from 'react-dom/test-utils';
import { setup, SetupResult, getProcessorValue } from './processor.helpers';
-// Default parameter values automatically added to the set processor when saved
-const defaultSetParameters = {
- value: '',
- if: undefined,
- tag: undefined,
- override: undefined,
- media_type: undefined,
- description: undefined,
- ignore_missing: undefined,
- ignore_failure: undefined,
- ignore_empty_value: undefined,
-};
-
const SET_TYPE = 'set';
describe('Processor: Set', () => {
@@ -66,7 +53,10 @@ describe('Processor: Set', () => {
await saveNewProcessor();
// Expect form error as "field" is required parameter
- expect(form.getErrorsMessages()).toEqual(['A field value is required.']);
+ expect(form.getErrorsMessages()).toEqual([
+ 'A field value is required.',
+ 'A value is required.',
+ ]);
});
test('saves with default parameter value', async () => {
@@ -75,15 +65,43 @@ describe('Processor: Set', () => {
form,
} = testBed;
- // Add "field" value (required)
+ // Add required fields
+ form.setInputValue('valueFieldInput', 'value');
form.setInputValue('fieldNameField.input', 'field_1');
// Save the field
await saveNewProcessor();
const processors = getProcessorValue(onUpdate, SET_TYPE);
expect(processors[0][SET_TYPE]).toEqual({
- ...defaultSetParameters,
field: 'field_1',
+ value: 'value',
+ });
+ });
+
+ test('allows to save the the copy_from value', async () => {
+ const {
+ actions: { saveNewProcessor },
+ form,
+ find,
+ } = testBed;
+
+ // Add required fields
+ form.setInputValue('fieldNameField.input', 'field_1');
+
+ // Set value field
+ form.setInputValue('valueFieldInput', 'value');
+
+ // Toggle to copy_from field and set a random value
+ find('toggleCustomField').simulate('click');
+ form.setInputValue('copyFromInput', 'copy_from');
+
+ // Save the field with new changes
+ await saveNewProcessor();
+
+ const processors = getProcessorValue(onUpdate, SET_TYPE);
+ expect(processors[0][SET_TYPE]).toEqual({
+ field: 'field_1',
+ copy_from: 'copy_from',
});
});
@@ -110,7 +128,6 @@ describe('Processor: Set', () => {
const processors = getProcessorValue(onUpdate, SET_TYPE);
expect(processors[0][SET_TYPE]).toEqual({
- ...defaultSetParameters,
field: 'field_1',
value: '{{{hello}}}',
media_type: 'text/plain',
@@ -136,7 +153,6 @@ describe('Processor: Set', () => {
const processors = getProcessorValue(onUpdate, SET_TYPE);
expect(processors[0][SET_TYPE]).toEqual({
- ...defaultSetParameters,
field: 'field_1',
value: '{{{hello}}}',
ignore_empty_value: true,
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx
index 2026a77bc6566..22f2226d80b10 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx
@@ -9,7 +9,7 @@ import React, { FunctionComponent, useState, useCallback, useMemo } from 'react'
import { i18n } from '@kbn/i18n';
import { isEmpty } from 'lodash';
import { FormattedMessage } from '@kbn/i18n/react';
-import { EuiButtonEmpty, EuiCode } from '@elastic/eui';
+import { EuiCode, EuiLink, EuiText } from '@elastic/eui';
import {
FIELD_TYPES,
@@ -131,11 +131,14 @@ const getInternalNetworkConfig: (
),
},
labelAppend: (
-
- {i18n.translate('xpack.ingestPipelines.pipelineEditor.internalNetworkCustomLabel', {
- defaultMessage: 'Use custom field',
- })}
-
+
+
+
+
+
),
key: 'preset',
},
@@ -171,11 +174,14 @@ const getInternalNetworkConfig: (
),
},
labelAppend: (
-
- {i18n.translate('xpack.ingestPipelines.pipelineEditor.internalNetworkPredefinedLabel', {
- defaultMessage: 'Use preset field',
- })}
-
+
+
+
+
+
),
key: 'custom',
},
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 fda34f8700b33..16d89fcbfb119 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
@@ -5,10 +5,11 @@
* 2.0.
*/
-import React, { FunctionComponent } from 'react';
+import React, { FunctionComponent, useState, useCallback, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
+import { isEmpty } from 'lodash';
+import { EuiCode, EuiLink, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { EuiCode } from '@elastic/eui';
import {
FIELD_TYPES,
@@ -17,6 +18,9 @@ import {
ToggleField,
UseField,
Field,
+ FieldHook,
+ FieldConfig,
+ useFormContext,
} from '../../../../../../shared_imports';
import { hasTemplateSnippet } from '../../../utils';
@@ -24,25 +28,17 @@ import { FieldsConfig, to, from } from './shared';
import { FieldNameField } from './common_fields/field_name_field';
+interface ValueToggleTypes {
+ value: string;
+ copy_from: string;
+}
+
+type ValueToggleFields = {
+ [K in keyof ValueToggleTypes]: FieldHook;
+};
+
+// Optional fields config
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: (
- {'""'},
- }}
- />
- ),
- },
mediaType: {
type: FIELD_TYPES.SELECT,
defaultValue: 'application/json',
@@ -57,7 +53,6 @@ const fieldsConfig: FieldsConfig = {
/>
),
},
- /* Optional fields config */
override: {
type: FIELD_TYPES.TOGGLE,
defaultValue: true,
@@ -101,11 +96,134 @@ const fieldsConfig: FieldsConfig = {
},
};
+// Required fields config
+const getValueConfig: (
+ toggleCustom: () => void
+) => Record<
+ keyof ValueToggleFields,
+ {
+ path: string;
+ config?: FieldConfig;
+ euiFieldProps?: Record;
+ labelAppend: JSX.Element;
+ }
+> = (toggleCustom: () => void) => ({
+ value: {
+ path: 'fields.value',
+ euiFieldProps: {
+ 'data-test-subj': 'valueFieldInput',
+ },
+ config: {
+ type: FIELD_TYPES.TEXT,
+ serializer: from.emptyStringToUndefined,
+ label: i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.valueFieldLabel', {
+ defaultMessage: 'Value',
+ }),
+ helpText: (
+
+ ),
+ fieldsToValidateOnChange: ['fields.value', 'fields.copy_from'],
+ validations: [
+ {
+ validator: ({ value, path, formData }) => {
+ if (isEmpty(value) && isEmpty(formData['fields.copy_from'])) {
+ return {
+ path,
+ message: i18n.translate('xpack.ingestPipelines.pipelineEditor.requiredValue', {
+ defaultMessage: 'A value is required.',
+ }),
+ };
+ }
+ },
+ },
+ ],
+ },
+ labelAppend: (
+
+
+
+
+
+ ),
+ key: 'value',
+ },
+ copy_from: {
+ path: 'fields.copy_from',
+ euiFieldProps: {
+ 'data-test-subj': 'copyFromInput',
+ },
+ config: {
+ type: FIELD_TYPES.TEXT,
+ serializer: from.emptyStringToUndefined,
+ fieldsToValidateOnChange: ['fields.value', 'fields.copy_from'],
+ validations: [
+ {
+ validator: ({ value, path, formData }) => {
+ if (isEmpty(value) && isEmpty(formData['fields.value'])) {
+ return {
+ path,
+ message: i18n.translate('xpack.ingestPipelines.pipelineEditor.requiredCopyFrom', {
+ defaultMessage: 'A copy from value is required.',
+ }),
+ };
+ }
+ },
+ },
+ ],
+ label: i18n.translate('xpack.ingestPipelines.pipelineEditor.setForm.copyFromFieldLabel', {
+ defaultMessage: 'Copy from',
+ }),
+ helpText: (
+ {'Field'},
+ }}
+ />
+ ),
+ },
+ labelAppend: (
+
+
+
+
+
+ ),
+ key: 'copy_from',
+ },
+});
+
/**
* Disambiguate name from the Set data structure
*/
export const SetProcessor: FunctionComponent = () => {
- const [{ fields }] = useFormData({ watch: 'fields.value' });
+ const { getFieldDefaultValue } = useFormContext();
+ const [{ fields }] = useFormData({ watch: ['fields.value', 'fields.copy_from'] });
+
+ const isCopyFromDefined = getFieldDefaultValue('fields.copy_from') !== undefined;
+ const [isCopyFromEnabled, setIsCopyFrom] = useState(isCopyFromDefined);
+
+ const toggleCustom = useCallback(() => {
+ setIsCopyFrom((prev) => !prev);
+ }, []);
+
+ const valueFieldProps = useMemo(
+ () =>
+ isCopyFromEnabled
+ ? getValueConfig(toggleCustom).copy_from
+ : getValueConfig(toggleCustom).value,
+ [isCopyFromEnabled, toggleCustom]
+ );
return (
<>
@@ -115,16 +233,7 @@ export const SetProcessor: FunctionComponent = () => {
})}
/>
-
+
{hasTemplateSnippet(fields?.value) && (
- i18n.translate('xpack.ingestPipelines.processors.defaultDescription.set', {
+ getDefaultDescription: ({ field, value, copy_from: copyFrom }) => {
+ if (copyFrom) {
+ return i18n.translate('xpack.ingestPipelines.processors.defaultDescription.setCopyFrom', {
+ defaultMessage: 'Sets value of "{field}" to the value of "{copyFrom}"',
+ values: {
+ field,
+ copyFrom,
+ },
+ });
+ }
+
+ return i18n.translate('xpack.ingestPipelines.processors.defaultDescription.set', {
defaultMessage: 'Sets value of "{field}" to "{value}"',
values: {
field,
value,
},
- }),
+ });
+ },
},
set_security_user: {
FieldsComponent: SetSecurityUser,
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 6233b220ae773..f6848a7f2bf45 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
@@ -156,6 +156,8 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({
// We manually add fields that we **don't** want to be treated as "unknownOptions"
'internal_networks',
'internal_networks_field',
+ 'value',
+ 'copy_from',
];
// If the processor type is changed while editing, we need to ignore unkownOptions as they
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 362b5c416bf19..4044d5c10f86d 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -6702,10 +6702,6 @@
"xpack.canvas.functions.if.args.elseHelpText": "条件が {BOOLEAN_FALSE} の場合の戻り値です。指定されておらず、条件が満たされていない場合は、元の {CONTEXT} が戻されます。",
"xpack.canvas.functions.if.args.thenHelpText": "条件が {BOOLEAN_TRUE} の場合の戻り値です。指定されておらず、条件が満たされている場合は、元の {CONTEXT} が戻されます。",
"xpack.canvas.functions.ifHelpText": "条件付きロジックを実行します。",
- "expressionImage.functions.image.args.dataurlHelpText": "画像の {https} {URL} または {BASE64} データ {URL} です。",
- "expressionImage.functions.image.args.modeHelpText": "{contain} はサイズに合わせて拡大・縮小して画像全体を表示し、{cover} はコンテナーを画像で埋め、必要に応じて両端や下をクロップします。{stretch} は画像の高さと幅をコンテナーの 100% になるよう変更します。",
- "expressionImage.functions.image.invalidImageModeErrorMessage": "「mode」は「{contain}」、「{cover}」、または「{stretch}」でなければなりません",
- "expressionImage.functions.imageHelpText": "画像を表示します。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。",
"xpack.canvas.functions.joinRows.args.columnHelpText": "値を抽出する列またはフィールド。",
"xpack.canvas.functions.joinRows.args.distinctHelpText": "一意の値のみを抽出しますか?",
"xpack.canvas.functions.joinRows.args.quoteHelpText": "各抽出された値を囲む引用符文字。",
@@ -6723,11 +6719,6 @@
"xpack.canvas.functions.markdown.args.fontHelpText": "コンテンツの {CSS} フォントプロパティです。たとえば、{fontFamily} または {fontWeight} です。",
"xpack.canvas.functions.markdown.args.openLinkHelpText": "新しいタブでリンクを開くためのtrue/false値。デフォルト値は「false」です。「true」に設定するとすべてのリンクが新しいタブで開くようになります。",
"xpack.canvas.functions.markdownHelpText": "{MARKDOWN} テキストをレンダリングするエレメントを追加します。ヒント:単一の数字、メトリック、テキストの段落には {markdownFn} 関数を使います。",
- "expressionMetric.functions.metric.args.labelFontHelpText": "ラベルの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
- "expressionMetric.functions.metric.args.labelHelpText": "メトリックを説明するテキストです。",
- "expressionMetric.functions.metric.args.metricFontHelpText": "メトリックの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
- "expressionMetric.functions.metric.args.metricFormatHelpText": "{NUMERALJS} 形式の文字列。例:{example1} または {example2}。",
- "expressionMetric.functions.metricHelpText": "ラベルの上に数字を表示します。",
"xpack.canvas.functions.neq.args.valueHelpText": "{CONTEXT} と比較される値です。",
"xpack.canvas.functions.neqHelpText": "{CONTEXT} が引数と等しくないかを戻します。",
"xpack.canvas.functions.pie.args.fontHelpText": "ラベルの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
@@ -6959,8 +6950,6 @@
"expressionImage.renderer.image.helpDescription": "画像をレンダリングします",
"xpack.canvas.renderer.markdown.displayName": "マークダウン",
"xpack.canvas.renderer.markdown.helpDescription": "{MARKDOWN} インプットを使用して {HTML} を表示",
- "expressionMetric.renderer.metric.displayName": "メトリック",
- "expressionMetric.renderer.metric.helpDescription": "ラベルの上に数字をレンダリングします",
"xpack.canvas.renderer.pie.displayName": "円グラフ",
"xpack.canvas.renderer.pie.helpDescription": "データから円グラフをレンダリングします",
"xpack.canvas.renderer.plot.displayName": "座標プロット",
@@ -13270,7 +13259,6 @@
"xpack.ingestPipelines.pipelineEditor.setForm.overrideFieldHelpText": "有効にすると、既存のフィールド値を上書きします。無効にすると、{nullValue}フィールドのみを更新します。",
"xpack.ingestPipelines.pipelineEditor.setForm.overrideFieldLabel": "無効化",
"xpack.ingestPipelines.pipelineEditor.setForm.propertiesFieldHelpText": "追加するユーザー詳細情報。フォルトは{value}です。",
- "xpack.ingestPipelines.pipelineEditor.setForm.valueFieldHelpText": "フィールドの値。空白の値は{emptyString}を設定します。",
"xpack.ingestPipelines.pipelineEditor.setForm.valueFieldLabel": "値",
"xpack.ingestPipelines.pipelineEditor.setSecurityUserForm.fieldNameField": "出力フィールド。",
"xpack.ingestPipelines.pipelineEditor.setSecurityUserForm.propertiesFieldLabel": "プロパティ (任意) ",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 1a99ec15fb008..cc6393b640308 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -6743,10 +6743,6 @@
"xpack.canvas.functions.if.args.elseHelpText": "条件为 {BOOLEAN_FALSE} 时的返回值。未指定且条件未满足时,将返回原始 {CONTEXT}。",
"xpack.canvas.functions.if.args.thenHelpText": "条件为 {BOOLEAN_TRUE} 时的返回值。未指定且条件满足时,将返回原始 {CONTEXT}。",
"xpack.canvas.functions.ifHelpText": "执行条件逻辑。",
- "expressionImage.functions.image.args.dataurlHelpText": "图像的 {https} {URL} 或 {BASE64} 数据 {URL}。",
- "expressionImage.functions.image.args.modeHelpText": "{contain} 将显示整个图像,图像缩放至适合大小。{cover} 将使用该图像填充容器,根据需要在两边或底部裁剪图像。{stretch} 将图像的高和宽调整为容器的 100%。",
- "expressionImage.functions.image.invalidImageModeErrorMessage": "“mode”必须为“{contain}”、“{cover}”或“{stretch}”",
- "expressionImage.functions.imageHelpText": "显示图像。以 {BASE64} 数据 {URL} 的形式提供图像资产或传入子表达式。",
"xpack.canvas.functions.joinRows.args.columnHelpText": "从其中提取值的列或字段。",
"xpack.canvas.functions.joinRows.args.distinctHelpText": "仅提取唯一值?",
"xpack.canvas.functions.joinRows.args.quoteHelpText": "要将每个提取的值引起来的引号字符。",
@@ -6764,11 +6760,6 @@
"xpack.canvas.functions.markdown.args.fontHelpText": "内容的 {CSS} 字体属性。例如 {fontFamily} 或 {fontWeight}。",
"xpack.canvas.functions.markdown.args.openLinkHelpText": "用于在新标签页中打开链接的 true 或 false 值。默认值为 `false`。设置为 `true` 时将在新标签页中打开所有链接。",
"xpack.canvas.functions.markdownHelpText": "添加呈现 {MARKDOWN} 文本的元素。提示:将 {markdownFn} 函数用于单个数字、指标和文本段落。",
- "expressionMetric.functions.metric.args.labelFontHelpText": "标签的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
- "expressionMetric.functions.metric.args.labelHelpText": "描述指标的文本。",
- "expressionMetric.functions.metric.args.metricFontHelpText": "指标的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
- "expressionMetric.functions.metric.args.metricFormatHelpText": "{NUMERALJS} 格式字符串。例如 {example1} 或 {example2}。",
- "expressionMetric.functions.metricHelpText": "在标签上显示数字。",
"xpack.canvas.functions.neq.args.valueHelpText": "与 {CONTEXT} 比较的值。",
"xpack.canvas.functions.neqHelpText": "返回 {CONTEXT} 是否不等于参数。",
"xpack.canvas.functions.pie.args.fontHelpText": "标签的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
@@ -7000,8 +6991,6 @@
"expressionImage.renderer.image.helpDescription": "呈现图像",
"xpack.canvas.renderer.markdown.displayName": "Markdown",
"xpack.canvas.renderer.markdown.helpDescription": "使用 {MARKDOWN} 输入呈现 {HTML}",
- "expressionMetric.renderer.metric.displayName": "指标",
- "expressionMetric.renderer.metric.helpDescription": "在标签上呈现数字",
"xpack.canvas.renderer.pie.displayName": "饼图",
"xpack.canvas.renderer.pie.helpDescription": "根据您的数据呈现饼图",
"xpack.canvas.renderer.plot.displayName": "坐标图",
@@ -13440,7 +13429,6 @@
"xpack.ingestPipelines.pipelineEditor.setForm.overrideFieldHelpText": "如果启用,则覆盖现有字段值。如果禁用,则仅更新 {nullValue} 字段。",
"xpack.ingestPipelines.pipelineEditor.setForm.overrideFieldLabel": "覆盖",
"xpack.ingestPipelines.pipelineEditor.setForm.propertiesFieldHelpText": "要添加的用户详情。默认为 {value}",
- "xpack.ingestPipelines.pipelineEditor.setForm.valueFieldHelpText": "字段的值。空值将设置 {emptyString}。",
"xpack.ingestPipelines.pipelineEditor.setForm.valueFieldLabel": "值",
"xpack.ingestPipelines.pipelineEditor.setSecurityUserForm.fieldNameField": "输出字段。",
"xpack.ingestPipelines.pipelineEditor.setSecurityUserForm.propertiesFieldLabel": "属性(可选)",