From c62d4c36c19e6751a02f30527f19287ed43ebe0c Mon Sep 17 00:00:00 2001
From: Alison Goryachev
Date: Thu, 20 Aug 2020 11:52:58 -0400
Subject: [PATCH] [Ingest pipelines] Test pipeline enhancements (#74964)
---
.../helpers/pipeline_form.helpers.ts | 7 +-
.../ingest_pipelines_create.test.tsx | 2 +-
.../on_failure_processors_title.tsx | 74 +++---
.../pipeline_form/pipeline_form.scss | 8 +
.../pipeline_form/pipeline_form_fields.tsx | 11 +-
.../pipeline_form/processors_header.tsx | 33 +--
.../documents_dropdown.scss | 3 +
.../documents_dropdown/documents_dropdown.tsx | 69 ++++++
.../components/documents_dropdown/index.ts | 7 +
.../components/index.ts | 4 +-
.../components/load_from_json/button.tsx | 8 +-
.../manage_processor_form.tsx | 51 +++-
.../processor_output.tsx | 217 ++++++++++++++++++
.../pipeline_processors_editor_item.scss | 5 +
.../pipeline_processors_editor_item.tsx | 21 ++
...pipeline_processors_editor_item_status.tsx | 85 +++++++
.../processors_tree/processors_tree.scss | 1 -
.../{button.tsx => add_documents_button.tsx} | 29 +--
.../test_pipeline/flyout_provider.tsx | 172 --------------
.../components/test_pipeline/index.ts | 2 +-
.../test_pipeline/test_output_button.tsx | 60 +++++
.../test_pipeline/test_pipeline_actions.tsx | 84 +++++++
.../test_pipeline/test_pipeline_flyout.tsx | 197 ++++++++++++++++
.../documents_schema.tsx} | 0
.../index.ts | 2 +-
.../tab_documents.tsx | 56 +++--
.../tab_output.tsx | 45 ++--
.../test_pipeline_tabs.tsx} | 14 +-
.../context/context.tsx | 6 +-
.../context/index.ts | 7 +-
.../context/processors_context.tsx | 22 +-
.../context/test_config_context.tsx | 57 -----
.../context/test_pipeline_context.tsx | 189 +++++++++++++++
.../pipeline_processors_editor/deserialize.ts | 47 +++-
.../pipeline_processors_editor/index.ts | 7 +-
.../pipeline_processors_editor/serialize.ts | 44 +++-
.../pipeline_processors_editor/types.ts | 32 +++
.../public/application/services/api.ts | 4 +-
.../translations/translations/ja-JP.json | 1 -
.../translations/translations/zh-CN.json | 1 -
40 files changed, 1291 insertions(+), 393 deletions(-)
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.scss
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/index.ts
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_output.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item_status.tsx
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/{button.tsx => add_documents_button.tsx} (51%)
delete mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_provider.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_output_button.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_actions.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout.tsx
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/{flyout_tabs/schema.tsx => test_pipeline_flyout_tabs/documents_schema.tsx} (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/{flyout_tabs => test_pipeline_flyout_tabs}/index.ts (83%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/{flyout_tabs => test_pipeline_flyout_tabs}/tab_documents.tsx (68%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/{flyout_tabs => test_pipeline_flyout_tabs}/tab_output.tsx (68%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/{flyout_tabs/pipeline_test_tabs.tsx => test_pipeline_flyout_tabs/test_pipeline_tabs.tsx} (77%)
delete mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/test_config_context.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/test_pipeline_context.tsx
diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipeline_form.helpers.ts b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipeline_form.helpers.ts
index 85848b3d2f73cb..752ffef51b43b5 100644
--- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipeline_form.helpers.ts
+++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipeline_form.helpers.ts
@@ -13,8 +13,8 @@ export const getFormActions = (testBed: TestBed) => {
find('submitButton').simulate('click');
};
- const clickTestPipelineButton = () => {
- find('testPipelineButton').simulate('click');
+ const clickAddDocumentsButton = () => {
+ find('addDocumentsButton').simulate('click');
};
const clickShowRequestLink = () => {
@@ -34,11 +34,12 @@ export const getFormActions = (testBed: TestBed) => {
clickShowRequestLink,
toggleVersionSwitch,
toggleOnFailureSwitch,
- clickTestPipelineButton,
+ clickAddDocumentsButton,
};
};
export type PipelineFormTestSubjects =
+ | 'addDocumentsButton'
| 'submitButton'
| 'pageTitle'
| 'savePipelineError'
diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx
index 813057813f1398..6074c64d2bdb08 100644
--- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx
+++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx
@@ -201,7 +201,7 @@ describe('', () => {
const { actions, exists, find, waitFor } = testBed;
await act(async () => {
- actions.clickTestPipelineButton();
+ actions.clickAddDocumentsButton();
await waitFor('testPipelineFlyout');
});
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/on_failure_processors_title.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/on_failure_processors_title.tsx
index d2c001b0aaa138..0beb5657b54cb9 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/on_failure_processors_title.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/on_failure_processors_title.tsx
@@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiTitle } from '@elastic/eui';
+import { EuiLink, EuiText, EuiTitle } from '@elastic/eui';
import React, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -12,47 +12,41 @@ import { useKibana } from '../../../shared_imports';
export const OnFailureProcessorsTitle: FunctionComponent = () => {
const { services } = useKibana();
+
return (
-
-
-
-
-
-
-
-
+
+
+
- {i18n.translate(
- 'xpack.ingestPipelines.pipelineEditor.onFailureProcessorsDocumentationLink',
- {
- defaultMessage: 'Learn more.',
- }
- )}
-
- ),
- }}
+ id="xpack.ingestPipelines.pipelineEditor.onFailureTreeTitle"
+ defaultMessage="Failure processors"
/>
-
-
-
+
+
+
+
+ {i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.onFailureProcessorsDocumentationLink',
+ {
+ defaultMessage: 'Learn more.',
+ }
+ )}
+
+ ),
+ }}
+ />
+
+
);
};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.scss
index 73eb54827e04fb..d5592b87dda51b 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.scss
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.scss
@@ -1,3 +1,11 @@
.pipelineProcessorsEditor {
margin-bottom: $euiSizeXL;
+
+ &__container {
+ background-color: $euiColorLightestShade;
+ }
+
+ &__onFailureTitle {
+ padding-left: $euiSizeS;
+ }
}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx
index 3a97e6408b144b..6033f34af6825d 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx
@@ -129,16 +129,13 @@ export const PipelineFormFields: React.FunctionComponent = ({
-
+
-
-
+
-
-
+
-
-
+
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/processors_header.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/processors_header.tsx
index 1f27d611e54d46..43477affa8d947 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/processors_header.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/processors_header.tsx
@@ -14,7 +14,7 @@ import { useKibana } from '../../../shared_imports';
import {
LoadFromJsonButton,
OnDoneLoadJsonHandler,
- TestPipelineButton,
+ TestPipelineActions,
} from '../pipeline_processors_editor';
export interface Props {
@@ -23,6 +23,7 @@ export interface Props {
export const ProcessorsHeader: FunctionComponent = ({ onLoadJson }) => {
const { services } = useKibana();
+
return (
= ({ onLoadJson }) => {
justifyContent="spaceBetween"
responsive={false}
>
-
-
-
- {i18n.translate('xpack.ingestPipelines.pipelineEditor.processorsTreeTitle', {
- defaultMessage: 'Processors',
- })}
-
-
+
+
+
+
+
+ {i18n.translate('xpack.ingestPipelines.pipelineEditor.processorsTreeTitle', {
+ defaultMessage: 'Processors',
+ })}
+
+
+
+
+
+
+
+
= ({ onLoadJson }) => {
{i18n.translate(
'xpack.ingestPipelines.pipelineEditor.processorsDocumentationLink',
@@ -61,10 +71,7 @@ export const ProcessorsHeader: FunctionComponent = ({ onLoadJson }) => {
-
-
-
-
+
);
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.scss
new file mode 100644
index 00000000000000..c5b14dc129b0e3
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.scss
@@ -0,0 +1,3 @@
+.documentsDropdown__selectContainer {
+ max-width: 200px;
+}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.tsx
new file mode 100644
index 00000000000000..e9aa5c1d56f734
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.tsx
@@ -0,0 +1,69 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { i18n } from '@kbn/i18n';
+import React, { FunctionComponent } from 'react';
+import { EuiSelect, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
+
+import { Document } from '../../types';
+
+import './documents_dropdown.scss';
+
+const i18nTexts = {
+ ariaLabel: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.testPipeline.documentsDropdownAriaLabel',
+ {
+ defaultMessage: 'Select documents',
+ }
+ ),
+ dropdownLabel: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.testPipeline.documentsdropdownLabel',
+ {
+ defaultMessage: 'Documents:',
+ }
+ ),
+ buttonLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.testPipeline.buttonLabel', {
+ defaultMessage: 'Add documents',
+ }),
+};
+
+const getDocumentOptions = (documents: Document[]) =>
+ documents.map((doc, index) => ({
+ value: index,
+ text: doc._id,
+ }));
+
+interface Props {
+ documents: Document[];
+ selectedDocumentIndex: number;
+ updateSelectedDocument: (index: number) => void;
+}
+
+export const DocumentsDropdown: FunctionComponent = ({
+ documents,
+ selectedDocumentIndex,
+ updateSelectedDocument,
+}) => {
+ return (
+
+
+
+ {i18nTexts.dropdownLabel}
+
+
+
+ {
+ updateSelectedDocument(Number(e.target.value));
+ }}
+ aria-label={i18nTexts.ariaLabel}
+ />
+
+
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/index.ts
new file mode 100644
index 00000000000000..a8b55788647fb0
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { DocumentsDropdown } from './documents_dropdown';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts
index 3b0ae477c871f9..435d0ed66c4b00 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts
@@ -20,6 +20,8 @@ export { ProcessorRemoveModal } from './processor_remove_modal';
export { OnDoneLoadJsonHandler, LoadFromJsonButton } from './load_from_json';
-export { TestPipelineButton } from './test_pipeline';
+export { TestPipelineActions } from './test_pipeline';
+
+export { DocumentsDropdown } from './documents_dropdown';
export { PipelineProcessorsItemTooltip, Position } from './pipeline_processors_editor_item_tooltip';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/load_from_json/button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/load_from_json/button.tsx
index 482878d1bda587..21d15fc86a0ce6 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/load_from_json/button.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/load_from_json/button.tsx
@@ -5,7 +5,7 @@
*/
import { i18n } from '@kbn/i18n';
import React, { FunctionComponent } from 'react';
-import { EuiButton } from '@elastic/eui';
+import { EuiButtonEmpty } from '@elastic/eui';
import { ModalProvider, OnDoneLoadJsonHandler } from './modal_provider';
@@ -15,7 +15,7 @@ interface Props {
const i18nTexts = {
buttonLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.loadFromJson.buttonLabel', {
- defaultMessage: 'Load JSON',
+ defaultMessage: 'Import',
}),
};
@@ -24,9 +24,9 @@ export const LoadFromJsonButton: FunctionComponent = ({ onDone }) => {
{(openModal) => {
return (
-
+
{i18nTexts.buttonLabel}
-
+
);
}}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/manage_processor_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/manage_processor_form.tsx
index ad6d191be802df..ee8ca71e584461 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/manage_processor_form.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/manage_processor_form.tsx
@@ -24,10 +24,12 @@ import {
import { Form, FormDataProvider, FormHook } from '../../../../../shared_imports';
import { ProcessorInternal } from '../../types';
+import { useTestPipelineContext } from '../../context';
import { getProcessorDescriptor } from '../shared';
import { ProcessorSettingsFields } from './processor_settings_fields';
import { DocumentationButton } from './documentation_button';
+import { ProcessorOutput } from './processor_output';
export interface Props {
isOnFailure: boolean;
@@ -53,7 +55,7 @@ const cancelButtonLabel = i18n.translate(
{ defaultMessage: 'Cancel' }
);
-export type TabType = 'configuration';
+export type TabType = 'configuration' | 'output';
interface Tab {
id: TabType;
@@ -70,6 +72,12 @@ const tabs: Tab[] = [
}
),
},
+ {
+ id: 'output',
+ name: i18n.translate('xpack.ingestPipelines.settingsFormOnFailureFlyout.outputTabTitle', {
+ defaultMessage: 'Output',
+ }),
+ },
];
const getFlyoutTitle = (isOnFailure: boolean, isExistingProcessor: boolean) => {
@@ -102,6 +110,28 @@ const getFlyoutTitle = (isOnFailure: boolean, isExistingProcessor: boolean) => {
export const ManageProcessorForm: FunctionComponent = memo(
({ processor, form, isOnFailure, onClose, onOpen, esDocsBasePath }) => {
+ const { testPipelineData, setCurrentTestPipelineData } = useTestPipelineContext();
+ const {
+ testOutputPerProcessor,
+ config: { selectedDocumentIndex, documents },
+ } = testPipelineData;
+
+ const processorOutput =
+ processor &&
+ testOutputPerProcessor &&
+ testOutputPerProcessor[selectedDocumentIndex][processor.id];
+
+ const updateSelectedDocument = (index: number) => {
+ setCurrentTestPipelineData({
+ type: 'updateActiveDocument',
+ payload: {
+ config: {
+ selectedDocumentIndex: index,
+ },
+ },
+ });
+ };
+
useEffect(
() => {
onOpen();
@@ -111,7 +141,20 @@ export const ManageProcessorForm: FunctionComponent = memo(
const [activeTab, setActiveTab] = useState('configuration');
- const flyoutContent = ;
+ let flyoutContent: React.ReactNode;
+
+ if (activeTab === 'output') {
+ flyoutContent = (
+
+ );
+ } else {
+ flyoutContent = ;
+ }
return (
}>
+
+
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss
index 061c9adb5d4437..a54cc994ab730a 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss
@@ -3,7 +3,6 @@
.pipelineProcessorsEditor__tree {
&__container {
- background-color: $euiColorLightestShade;
padding: $euiSizeS;
}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/add_documents_button.tsx
similarity index 51%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/button.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/add_documents_button.tsx
index 0e8e23ba80ea87..e3ef9a9ee5390e 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/button.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/add_documents_button.tsx
@@ -5,26 +5,27 @@
*/
import { i18n } from '@kbn/i18n';
import React, { FunctionComponent } from 'react';
-import { EuiButton } from '@elastic/eui';
-
-import { FlyoutProvider } from './flyout_provider';
+import { EuiButtonEmpty } from '@elastic/eui';
const i18nTexts = {
buttonLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.testPipeline.buttonLabel', {
- defaultMessage: 'Test pipeline',
+ defaultMessage: 'Add documents',
}),
};
-export const TestPipelineButton: FunctionComponent = () => {
+interface Props {
+ openTestPipelineFlyout: () => void;
+}
+
+export const AddDocumentsButton: FunctionComponent = ({ openTestPipelineFlyout }) => {
return (
-
- {(openFlyout) => {
- return (
-
- {i18nTexts.buttonLabel}
-
- );
- }}
-
+
+ {i18nTexts.buttonLabel}
+
);
};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_provider.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_provider.tsx
deleted file mode 100644
index 53aeb9fdc08ba0..00000000000000
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_provider.tsx
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React, { useState, useEffect, useCallback } from 'react';
-import { FormattedMessage } from '@kbn/i18n/react';
-import { i18n } from '@kbn/i18n';
-
-import {
- EuiFlyout,
- EuiFlyoutBody,
- EuiFlyoutHeader,
- EuiSpacer,
- EuiTitle,
- EuiCallOut,
-} from '@elastic/eui';
-
-import { useKibana } from '../../../../../shared_imports';
-
-import { usePipelineProcessorsContext, useTestConfigContext } from '../../context';
-import { serialize } from '../../serialize';
-
-import { Tabs, Tab, OutputTab, DocumentsTab } from './flyout_tabs';
-
-export interface Props {
- children: (openFlyout: () => void) => React.ReactNode;
-}
-
-export const FlyoutProvider: React.FunctionComponent = ({ children }) => {
- const { services } = useKibana();
- const {
- state: { processors },
- } = usePipelineProcessorsContext();
-
- const serializedProcessors = serialize(processors.state);
-
- const { testConfig } = useTestConfigContext();
- const { documents: cachedDocuments, verbose: cachedVerbose } = testConfig;
-
- const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
-
- const initialSelectedTab = cachedDocuments ? 'output' : 'documents';
- const [selectedTab, setSelectedTab] = useState(initialSelectedTab);
-
- const [shouldExecuteImmediately, setShouldExecuteImmediately] = useState(false);
- const [isExecuting, setIsExecuting] = useState(false);
- const [executeError, setExecuteError] = useState(null);
- const [executeOutput, setExecuteOutput] = useState(undefined);
-
- const handleExecute = useCallback(
- async (documents: object[], verbose?: boolean) => {
- setIsExecuting(true);
- setExecuteError(null);
-
- const { error, data: output } = await services.api.simulatePipeline({
- documents,
- verbose,
- pipeline: { ...serializedProcessors },
- });
-
- setIsExecuting(false);
-
- if (error) {
- setExecuteError(error);
- return;
- }
-
- setExecuteOutput(output);
-
- services.notifications.toasts.addSuccess(
- i18n.translate('xpack.ingestPipelines.testPipelineFlyout.successNotificationText', {
- defaultMessage: 'Pipeline executed',
- }),
- {
- toastLifeTimeMs: 1000,
- }
- );
-
- setSelectedTab('output');
- },
- [services.api, services.notifications.toasts, serializedProcessors]
- );
-
- useEffect(() => {
- if (isFlyoutVisible === false && cachedDocuments) {
- setShouldExecuteImmediately(true);
- }
- }, [isFlyoutVisible, cachedDocuments]);
-
- useEffect(() => {
- // If the user has already tested the pipeline once,
- // use the cached test config and automatically execute the pipeline
- if (isFlyoutVisible && shouldExecuteImmediately && cachedDocuments) {
- setShouldExecuteImmediately(false);
- handleExecute(cachedDocuments!, cachedVerbose);
- }
- }, [handleExecute, cachedDocuments, cachedVerbose, isFlyoutVisible, shouldExecuteImmediately]);
-
- let tabContent;
-
- if (selectedTab === 'output') {
- tabContent = (
-
- );
- } else {
- // default to "Documents" tab
- tabContent = ;
- }
-
- return (
- <>
- {children(() => setIsFlyoutVisible(true))}
-
- {isFlyoutVisible && (
- setIsFlyoutVisible(false)}
- data-test-subj="testPipelineFlyout"
- >
-
-
-
-
-
-
-
-
-
- !executeOutput && tabId === 'output'}
- />
-
-
-
- {/* Execute error */}
- {executeError ? (
- <>
-
- }
- color="danger"
- iconType="alert"
- >
- {executeError.message}
-
-
- >
- ) : null}
-
- {/* Documents or output tab content */}
- {tabContent}
-
-
- )}
- >
- );
-};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/index.ts
index 8e5037c15bac41..4050971d0930ae 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/index.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/index.ts
@@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { TestPipelineButton } from './button';
+export { TestPipelineActions } from './test_pipeline_actions';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_output_button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_output_button.tsx
new file mode 100644
index 00000000000000..361e32c77d59bc
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_output_button.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { i18n } from '@kbn/i18n';
+import React, { FunctionComponent } from 'react';
+import { EuiButton, EuiToolTip } from '@elastic/eui';
+
+const i18nTexts = {
+ buttonLabel: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.testPipeline.outputButtonLabel',
+ {
+ defaultMessage: 'View output',
+ }
+ ),
+ disabledButtonTooltipLabel: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.testPipeline.outputButtonTooltipLabel',
+ {
+ defaultMessage: 'Add documents to view the output',
+ }
+ ),
+};
+
+interface Props {
+ isDisabled: boolean;
+ openTestPipelineFlyout: () => void;
+}
+
+export const TestOutputButton: FunctionComponent = ({
+ isDisabled,
+ openTestPipelineFlyout,
+}) => {
+ if (isDisabled) {
+ return (
+ {i18nTexts.disabledButtonTooltipLabel}}>
+
+ {i18nTexts.buttonLabel}
+
+
+ );
+ }
+
+ return (
+
+ {i18nTexts.buttonLabel}
+
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_actions.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_actions.tsx
new file mode 100644
index 00000000000000..eb9d9352e4b906
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_actions.tsx
@@ -0,0 +1,84 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React, { FunctionComponent, useState } from 'react';
+
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { useTestPipelineContext, usePipelineProcessorsContext } from '../../context';
+
+import { DocumentsDropdown } from '../documents_dropdown';
+import { TestPipelineFlyoutTab } from './test_pipeline_flyout_tabs';
+import { AddDocumentsButton } from './add_documents_button';
+import { TestOutputButton } from './test_output_button';
+import { TestPipelineFlyout } from './test_pipeline_flyout';
+
+export const TestPipelineActions: FunctionComponent = () => {
+ const { testPipelineData, setCurrentTestPipelineData } = useTestPipelineContext();
+
+ const {
+ state: { processors },
+ } = usePipelineProcessorsContext();
+
+ const {
+ testOutputPerProcessor,
+ config: { documents, selectedDocumentIndex },
+ } = testPipelineData;
+
+ const [openTestPipelineFlyout, setOpenTestPipelineFlyout] = useState(false);
+ const [activeFlyoutTab, setActiveFlyoutTab] = useState('documents');
+
+ const updateSelectedDocument = (index: number) => {
+ setCurrentTestPipelineData({
+ type: 'updateActiveDocument',
+ payload: {
+ config: {
+ selectedDocumentIndex: index,
+ },
+ },
+ });
+ };
+
+ return (
+ <>
+
+
+ {documents ? (
+
+ ) : (
+ {
+ setOpenTestPipelineFlyout(true);
+ setActiveFlyoutTab('documents');
+ }}
+ />
+ )}
+
+
+ {
+ setOpenTestPipelineFlyout(true);
+ setActiveFlyoutTab('output');
+ }}
+ />
+
+
+ {openTestPipelineFlyout && (
+ setOpenTestPipelineFlyout(false)}
+ />
+ )}
+ >
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout.tsx
new file mode 100644
index 00000000000000..e8bb1aa1d357f8
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout.tsx
@@ -0,0 +1,197 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useState, useCallback, useEffect } from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { i18n } from '@kbn/i18n';
+
+import {
+ EuiFlyout,
+ EuiFlyoutBody,
+ EuiFlyoutHeader,
+ EuiSpacer,
+ EuiTitle,
+ EuiCallOut,
+} from '@elastic/eui';
+
+import { useKibana } from '../../../../../shared_imports';
+import { useTestPipelineContext } from '../../context';
+import { serialize } from '../../serialize';
+import { DeserializeResult } from '../../deserialize';
+import { Document } from '../../types';
+
+import { Tabs, TestPipelineFlyoutTab, OutputTab, DocumentsTab } from './test_pipeline_flyout_tabs';
+
+export interface Props {
+ activeTab: TestPipelineFlyoutTab;
+ onClose: () => void;
+ processors: DeserializeResult;
+}
+
+export interface HandleTestPipelineArgs {
+ documents: Document[];
+ verbose?: boolean;
+}
+
+export const TestPipelineFlyout: React.FunctionComponent = ({
+ onClose,
+ activeTab,
+ processors,
+}) => {
+ const { services } = useKibana();
+
+ const {
+ testPipelineData,
+ setCurrentTestPipelineData,
+ updateTestOutputPerProcessor,
+ } = useTestPipelineContext();
+
+ const {
+ config: { documents: cachedDocuments, verbose: cachedVerbose },
+ } = testPipelineData;
+
+ const [selectedTab, setSelectedTab] = useState(activeTab);
+
+ const [shouldTestImmediately, setShouldTestImmediately] = useState(false);
+ const [isRunningTest, setIsRunningTest] = useState(false);
+ const [testingError, setTestingError] = useState(null);
+ const [testOutput, setTestOutput] = useState(undefined);
+
+ const handleTestPipeline = useCallback(
+ async ({ documents, verbose }: HandleTestPipelineArgs) => {
+ const serializedProcessors = serialize({ pipeline: processors });
+
+ setIsRunningTest(true);
+ setTestingError(null);
+
+ const { error, data: currentTestOutput } = await services.api.simulatePipeline({
+ documents,
+ verbose,
+ pipeline: { ...serializedProcessors },
+ });
+
+ setIsRunningTest(false);
+
+ if (error) {
+ setTestingError(error);
+ return;
+ }
+
+ setCurrentTestPipelineData({
+ type: 'updateConfig',
+ payload: {
+ config: {
+ documents,
+ verbose,
+ },
+ },
+ });
+
+ setTestOutput(currentTestOutput);
+
+ services.notifications.toasts.addSuccess(
+ i18n.translate('xpack.ingestPipelines.testPipelineFlyout.successNotificationText', {
+ defaultMessage: 'Pipeline executed',
+ }),
+ {
+ toastLifeTimeMs: 1000,
+ }
+ );
+
+ setSelectedTab('output');
+ },
+ [services.api, processors, setCurrentTestPipelineData, services.notifications.toasts]
+ );
+
+ useEffect(() => {
+ if (cachedDocuments) {
+ setShouldTestImmediately(true);
+ }
+ // We only want to know on initial mount if there are cached documents
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ useEffect(() => {
+ // If the user has already tested the pipeline once,
+ // use the cached test config and automatically execute the pipeline
+ if (shouldTestImmediately) {
+ setShouldTestImmediately(false);
+ handleTestPipeline({ documents: cachedDocuments!, verbose: cachedVerbose });
+ }
+ }, [handleTestPipeline, cachedDocuments, cachedVerbose, shouldTestImmediately]);
+
+ let tabContent;
+
+ if (selectedTab === 'output') {
+ tabContent = (
+
+ );
+ } else {
+ // default to "Documents" tab
+ tabContent = (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ !testOutput && tabId === 'output'}
+ />
+
+
+
+ {/* Testing error callout */}
+ {testingError ? (
+ <>
+
+ }
+ color="danger"
+ iconType="alert"
+ >
+ {testingError.message}
+
+
+ >
+ ) : null}
+
+ {/* Documents or output tab content */}
+ {tabContent}
+
+
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_tabs/schema.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/documents_schema.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_tabs/schema.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/documents_schema.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_tabs/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/index.ts
similarity index 83%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_tabs/index.ts
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/index.ts
index ea8fe2cd923507..1f306f96b4bd64 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_tabs/index.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/index.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { Tabs, Tab } from './pipeline_test_tabs';
+export { Tabs, TestPipelineFlyoutTab } from './test_pipeline_tabs';
export { DocumentsTab } from './tab_documents';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_tabs/tab_documents.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_documents.tsx
similarity index 68%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_tabs/tab_documents.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_documents.tsx
index 794d9355712107..8968416683c3e8 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/flyout_tabs/tab_documents.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_documents.tsx
@@ -16,50 +16,59 @@ import {
JsonEditorField,
Form,
useForm,
- FormConfig,
useKibana,
} from '../../../../../../shared_imports';
-import { useTestConfigContext, TestConfig } from '../../../context';
-
-import { documentsSchema } from './schema';
+import { TestPipelineContext } from '../../../context';
+import { Document } from '../../../types';
+import { DeserializeResult } from '../../../deserialize';
+import { HandleTestPipelineArgs } from '../test_pipeline_flyout';
+import { documentsSchema } from './documents_schema';
const UseField = getUseField({ component: Field });
interface Props {
- handleExecute: (documents: object[], verbose: boolean) => void;
- isExecuting: boolean;
+ handleTestPipeline: (data: HandleTestPipelineArgs) => void;
+ setPerProcessorOutput: (documents: Document[] | undefined, processors: DeserializeResult) => void;
+ isRunningTest: boolean;
+ processors: DeserializeResult;
+ testPipelineData: TestPipelineContext['testPipelineData'];
}
-export const DocumentsTab: React.FunctionComponent = ({ handleExecute, isExecuting }) => {
+export const DocumentsTab: React.FunctionComponent = ({
+ handleTestPipeline,
+ isRunningTest,
+ setPerProcessorOutput,
+ processors,
+ testPipelineData,
+}) => {
const { services } = useKibana();
- const { setCurrentTestConfig, testConfig } = useTestConfigContext();
- const { verbose: cachedVerbose, documents: cachedDocuments } = testConfig;
+ const {
+ config: { documents: cachedDocuments, verbose: cachedVerbose },
+ } = testPipelineData;
+
+ const testPipeline = async () => {
+ const { isValid, data } = await form.submit();
- const executePipeline: FormConfig['onSubmit'] = async (formData, isValid) => {
if (!isValid) {
return;
}
- const { documents } = formData as TestConfig;
+ const { documents } = data as { documents: Document[] };
- // Update context
- setCurrentTestConfig({
- ...testConfig,
- documents,
- });
+ await handleTestPipeline({ documents: documents!, verbose: cachedVerbose });
- handleExecute(documents!, cachedVerbose);
+ // This is necessary to update the status and output of each processor
+ // as verbose may not be enabled
+ setPerProcessorOutput(documents, processors);
};
const { form } = useForm({
schema: documentsSchema,
defaultValue: {
documents: cachedDocuments || '',
- verbose: cachedVerbose || false,
},
- onSubmit: executePipeline,
});
return (
@@ -79,7 +88,7 @@ export const DocumentsTab: React.FunctionComponent = ({ handleExecute, is
{i18n.translate(
'xpack.ingestPipelines.testPipelineFlyout.documentsTab.simulateDocumentionLink',
{
- defaultMessage: 'Learn more',
+ defaultMessage: 'Learn more.',
}
)}
@@ -95,6 +104,7 @@ export const DocumentsTab: React.FunctionComponent = ({ handleExecute, is
form={form}
data-test-subj="testPipelineForm"
isInvalid={form.isSubmitted && !form.isValid}
+ onSubmit={testPipeline}
error={form.getErrors()}
>
{/* Documents editor */}
@@ -118,12 +128,12 @@ export const DocumentsTab: React.FunctionComponent = ({ handleExecute, is
- {isExecuting ? (
+ {isRunningTest ? (
void;
- isExecuting: boolean;
+ handleTestPipeline: (data: HandleTestPipelineArgs) => void;
+ isRunningTest: boolean;
+ cachedVerbose?: boolean;
+ cachedDocuments: Document[];
+ testOutput?: any;
}
export const OutputTab: React.FunctionComponent = ({
- executeOutput,
- handleExecute,
- isExecuting,
+ handleTestPipeline,
+ isRunningTest,
+ cachedVerbose,
+ cachedDocuments,
+ testOutput,
}) => {
- const { setCurrentTestConfig, testConfig } = useTestConfigContext();
- const { verbose: cachedVerbose, documents: cachedDocuments } = testConfig;
+ const [isVerboseEnabled, setIsVerboseEnabled] = useState(Boolean(cachedVerbose));
- const onEnableVerbose = (isVerboseEnabled: boolean) => {
- setCurrentTestConfig({
- ...testConfig,
- verbose: isVerboseEnabled,
- });
+ const onEnableVerbose = (isVerbose: boolean) => {
+ setIsVerboseEnabled(isVerbose);
- handleExecute(cachedDocuments!, isVerboseEnabled);
+ handleTestPipeline({ documents: cachedDocuments!, verbose: isVerbose });
};
let content: React.ReactNode | undefined;
- if (isExecuting) {
+ if (isRunningTest) {
content = ;
- } else if (executeOutput) {
+ } else if (testOutput) {
content = (
- {JSON.stringify(executeOutput, null, 2)}
+ {JSON.stringify(testOutput, null, 2)}
);
}
@@ -76,14 +77,16 @@ export const OutputTab: React.FunctionComponent = ({
defaultMessage="View verbose output"
/>
}
- checked={cachedVerbose}
+ checked={isVerboseEnabled}
onChange={(e) => onEnableVerbose(e.target.checked)}
/>
handleExecute(cachedDocuments!, cachedVerbose)}
+ onClick={() =>
+ handleTestPipeline({ documents: cachedDocuments!, verbose: isVerboseEnabled })
+ }
iconType="refresh"
>
void;
- selectedTab: Tab;
- getIsDisabled: (tab: Tab) => boolean;
+ onTabChange: (tab: TestPipelineFlyoutTab) => void;
+ selectedTab: TestPipelineFlyoutTab;
+ getIsDisabled: (tab: TestPipelineFlyoutTab) => boolean;
}
export const Tabs: React.FunctionComponent = ({
@@ -22,15 +22,15 @@ export const Tabs: React.FunctionComponent = ({
getIsDisabled,
}) => {
const tabs: Array<{
- id: Tab;
+ id: TestPipelineFlyoutTab;
name: React.ReactNode;
}> = [
{
id: 'documents',
name: (
),
},
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/context.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/context.tsx
index a1ea0fd9d0b9ed..1023385ccc2998 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/context.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/context.tsx
@@ -10,7 +10,7 @@ import {
PipelineProcessorsContextProvider,
Props as ProcessorsContextProps,
} from './processors_context';
-import { TestConfigContextProvider } from './test_config_context';
+import { TestPipelineContextProvider } from './test_pipeline_context';
interface Props extends ProcessorsContextProps {
children: React.ReactNode;
@@ -23,7 +23,7 @@ export const ProcessorsEditorContextProvider: FunctionComponent = ({
onFlyoutOpen,
}: Props) => {
return (
-
+
= ({
>
{children}
-
+
);
};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/index.ts
index 1664b3410c1c0d..5b152f074f9cdf 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/index.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/index.ts
@@ -6,7 +6,12 @@
export { ProcessorsEditorContextProvider } from './context';
-export { TestConfigContextProvider, useTestConfigContext, TestConfig } from './test_config_context';
+export {
+ TestPipelineContextProvider,
+ useTestPipelineContext,
+ TestPipelineData,
+ TestPipelineContext,
+} from './test_pipeline_context';
export {
PipelineProcessorsContextProvider,
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/processors_context.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/processors_context.tsx
index f83803da7bf912..8c59d484acd08e 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/processors_context.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/processors_context.tsx
@@ -44,6 +44,8 @@ import {
import { getValue } from '../utils';
+import { useTestPipelineContext } from './test_pipeline_context';
+
const PipelineProcessorsContext = createContext({} as any);
export interface Props {
@@ -79,6 +81,12 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({
);
const [processorsState, processorsDispatch] = useProcessorsState(deserializedResult);
+ const { updateTestOutputPerProcessor, testPipelineData } = useTestPipelineContext();
+
+ const {
+ config: { documents },
+ } = testPipelineData;
+
useEffect(() => {
if (initRef.current) {
processorsDispatch({
@@ -120,8 +128,10 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({
},
getData: () =>
serialize({
- onFailure: onFailureProcessors,
- processors,
+ pipeline: {
+ onFailure: onFailureProcessors,
+ processors,
+ },
}),
});
}, [processors, onFailureProcessors, onUpdate, formState, mode]);
@@ -183,7 +193,7 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({
break;
}
},
- [processorsDispatch, setMode]
+ [processorsDispatch]
);
// Memoize the state object to ensure we do not trigger unnecessary re-renders and so
@@ -198,6 +208,12 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({
};
}, [mode, setMode, processorsState, processorsDispatch]);
+ // Update the test output whenever the processorsState changes (e.g., on move, update, delete)
+ // Note: updateTestOutputPerProcessor() will only simulate if the user has added sample documents
+ useEffect(() => {
+ updateTestOutputPerProcessor(documents, processorsState);
+ }, [documents, processorsState, updateTestOutputPerProcessor]);
+
return (
void;
-}
-
-const TEST_CONFIG_DEFAULT_VALUE = {
- testConfig: {
- verbose: false,
- },
- setCurrentTestConfig: () => {},
-};
-
-const TestConfigContext = React.createContext(TEST_CONFIG_DEFAULT_VALUE);
-
-export const useTestConfigContext = () => {
- const ctx = useContext(TestConfigContext);
- if (!ctx) {
- throw new Error(
- '"useTestConfigContext" can only be called inside of TestConfigContext.Provider!'
- );
- }
- return ctx;
-};
-
-export const TestConfigContextProvider = ({ children }: { children: React.ReactNode }) => {
- const [testConfig, setTestConfig] = useState({
- verbose: false,
- });
-
- const setCurrentTestConfig = useCallback((currentTestConfig: TestConfig): void => {
- setTestConfig(currentTestConfig);
- }, []);
-
- return (
-
- {children}
-
- );
-};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/test_pipeline_context.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/test_pipeline_context.tsx
new file mode 100644
index 00000000000000..f764f403de79b3
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/test_pipeline_context.tsx
@@ -0,0 +1,189 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useCallback, useContext, useReducer, Reducer } from 'react';
+import { useKibana } from '../../../../shared_imports';
+import {
+ DeserializedProcessorResult,
+ deserializeVerboseTestOutput,
+ DeserializeResult,
+} from '../deserialize';
+import { serialize } from '../serialize';
+import { Document } from '../types';
+
+export interface TestPipelineData {
+ config: {
+ documents?: Document[];
+ verbose?: boolean;
+ selectedDocumentIndex: number;
+ };
+ testOutputPerProcessor?: DeserializedProcessorResult[];
+ isExecutingPipeline?: boolean;
+}
+
+type Action =
+ | {
+ type: 'updateOutputPerProcessor';
+ payload: {
+ testOutputPerProcessor?: DeserializedProcessorResult[];
+ isExecutingPipeline: boolean;
+ };
+ }
+ | {
+ type: 'updateConfig';
+ payload: {
+ config: {
+ documents: Document[];
+ verbose?: boolean;
+ };
+ };
+ }
+ | {
+ type: 'updateActiveDocument';
+ payload: Pick;
+ }
+ | {
+ type: 'updateIsExecutingPipeline';
+ payload: Pick;
+ };
+
+export interface TestPipelineContext {
+ testPipelineData: TestPipelineData;
+ setCurrentTestPipelineData: (data: Action) => void;
+ updateTestOutputPerProcessor: (
+ documents: Document[] | undefined,
+ processors: DeserializeResult
+ ) => void;
+}
+
+const DEFAULT_TEST_PIPELINE_CONTEXT = {
+ testPipelineData: {
+ config: {
+ selectedDocumentIndex: 0,
+ },
+ isExecutingPipeline: false,
+ },
+ setCurrentTestPipelineData: () => {},
+ updateTestOutputPerProcessor: () => {},
+};
+
+const TestPipelineContext = React.createContext(DEFAULT_TEST_PIPELINE_CONTEXT);
+
+export const useTestPipelineContext = () => {
+ const ctx = useContext(TestPipelineContext);
+ if (!ctx) {
+ throw new Error(
+ '"useTestPipelineContext" can only be called inside of TestPipelineContextProvider.Provider!'
+ );
+ }
+ return ctx;
+};
+
+export const reducer: Reducer = (state, action) => {
+ if (action.type === 'updateOutputPerProcessor') {
+ return {
+ ...state,
+ testOutputPerProcessor: action.payload.testOutputPerProcessor,
+ isExecutingPipeline: false,
+ };
+ }
+
+ if (action.type === 'updateConfig') {
+ return {
+ ...action.payload,
+ config: {
+ ...action.payload.config,
+ selectedDocumentIndex: state.config.selectedDocumentIndex,
+ },
+ testOutputPerProcessor: state.testOutputPerProcessor,
+ };
+ }
+
+ if (action.type === 'updateActiveDocument') {
+ return {
+ ...state,
+ config: {
+ ...state.config,
+ selectedDocumentIndex: action.payload.config.selectedDocumentIndex,
+ },
+ };
+ }
+
+ if (action.type === 'updateIsExecutingPipeline') {
+ return {
+ ...state,
+ isExecutingPipeline: action.payload.isExecutingPipeline,
+ };
+ }
+
+ return state;
+};
+
+export const TestPipelineContextProvider = ({ children }: { children: React.ReactNode }) => {
+ const [state, dispatch] = useReducer(reducer, DEFAULT_TEST_PIPELINE_CONTEXT.testPipelineData);
+ const { services } = useKibana();
+
+ const updateTestOutputPerProcessor = useCallback(
+ async (documents: Document[] | undefined, processors: DeserializeResult) => {
+ if (!documents) {
+ return;
+ }
+
+ dispatch({
+ type: 'updateIsExecutingPipeline',
+ payload: {
+ isExecutingPipeline: true,
+ },
+ });
+
+ const serializedProcessorsWithTag = serialize({
+ pipeline: { processors: processors.processors, onFailure: processors.onFailure },
+ copyIdToTag: true,
+ });
+
+ const { data: verboseResults, error } = await services.api.simulatePipeline({
+ documents,
+ verbose: true,
+ pipeline: { ...serializedProcessorsWithTag },
+ });
+
+ if (error) {
+ dispatch({
+ type: 'updateOutputPerProcessor',
+ payload: {
+ isExecutingPipeline: false,
+ // reset the output if there is an error
+ // this will result to the status changing to "inactive"
+ testOutputPerProcessor: undefined,
+ },
+ });
+
+ return;
+ }
+
+ dispatch({
+ type: 'updateOutputPerProcessor',
+ payload: {
+ testOutputPerProcessor: deserializeVerboseTestOutput(verboseResults),
+ isExecutingPipeline: false,
+ },
+ });
+ },
+ [services.api]
+ );
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/deserialize.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/deserialize.ts
index 1e9a97e189a5ee..01788c49ec2f14 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/deserialize.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/deserialize.ts
@@ -5,7 +5,7 @@
*/
import uuid from 'uuid';
import { Processor } from '../../../../common/types';
-import { ProcessorInternal } from './types';
+import { ProcessorInternal, VerboseTestOutput, ProcessorResult } from './types';
export interface DeserializeArgs {
processors: Processor[];
@@ -58,3 +58,48 @@ export const deserialize = ({ processors, onFailure }: DeserializeArgs): Deseria
onFailure: onFailure ? convertProcessors(onFailure) : undefined,
};
};
+
+export interface DeserializedProcessorResult {
+ [key: string]: ProcessorResult;
+}
+/**
+ * This function takes the verbose response of the simulate API
+ * and maps the results to each processor in the pipeline by the "tag" field
+ */
+export const deserializeVerboseTestOutput = (
+ output: VerboseTestOutput
+): DeserializedProcessorResult[] => {
+ const { docs } = output;
+
+ const deserializedOutput = docs.map((doc) => {
+ return doc.processor_results.reduce(
+ (
+ processorResultsById: DeserializedProcessorResult,
+ currentResult: ProcessorResult,
+ index: number
+ ) => {
+ const result = { ...currentResult };
+ const resultId = result.tag;
+
+ if (index !== 0) {
+ // Add the result from the previous processor so that the user
+ // can easily compare current output to the previous output
+ // This may be a result from an on_failure processor
+ result.prevProcessorResult = doc.processor_results[index - 1];
+ }
+
+ // The tag is added programatically as a way to map
+ // the results to each processor
+ // It is not something we need to surface to the user, so we delete it
+ delete result.tag;
+
+ processorResultsById[resultId] = result;
+
+ return processorResultsById;
+ },
+ {}
+ );
+ });
+
+ return deserializedOutput;
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts
index d2342bbd2ab1a2..71b2e2fa8f7f19 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts
@@ -14,4 +14,9 @@ export { OnUpdateHandlerArg, OnUpdateHandler } from './types';
export { SerializeResult } from './serialize';
-export { LoadFromJsonButton, OnDoneLoadJsonHandler, TestPipelineButton } from './components';
+export {
+ LoadFromJsonButton,
+ OnDoneLoadJsonHandler,
+ TestPipelineActions,
+ DocumentsDropdown,
+} from './components';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/serialize.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/serialize.ts
index 153c9e252ccc0f..edf787f12620c2 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/serialize.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/serialize.ts
@@ -5,18 +5,32 @@
*/
import { Processor } from '../../../../common/types';
-import { DeserializeResult } from './deserialize';
import { ProcessorInternal } from './types';
-type SerializeArgs = DeserializeResult;
+interface SerializeArgs {
+ /**
+ * The deserialized pipeline to convert
+ */
+ pipeline: {
+ processors: ProcessorInternal[];
+ onFailure?: ProcessorInternal[];
+ };
+ /**
+ * For simulation, we add the "tag" field equal to the internal processor id so that we can map the simulate results to each processor
+ */
+ copyIdToTag?: boolean;
+}
export interface SerializeResult {
processors: Processor[];
on_failure?: Processor[];
}
-const convertProcessorInternalToProcessor = (processor: ProcessorInternal): Processor => {
- const { options, onFailure, type } = processor;
+const convertProcessorInternalToProcessor = (
+ processor: ProcessorInternal,
+ copyIdToTag?: boolean
+): Processor => {
+ const { options, onFailure, type, id } = processor;
const outProcessor = {
[type]: {
...options,
@@ -24,26 +38,32 @@ const convertProcessorInternalToProcessor = (processor: ProcessorInternal): Proc
};
if (onFailure?.length) {
- outProcessor[type].on_failure = convertProcessors(onFailure);
- } else if (onFailure) {
- outProcessor[type].on_failure = [];
+ outProcessor[type].on_failure = convertProcessors(onFailure, copyIdToTag);
+ }
+
+ if (copyIdToTag) {
+ outProcessor[type].tag = id;
}
return outProcessor;
};
-const convertProcessors = (processors: ProcessorInternal[]) => {
+const convertProcessors = (processors: ProcessorInternal[], copyIdToTag?: boolean) => {
const convertedProcessors = [];
for (const processor of processors) {
- convertedProcessors.push(convertProcessorInternalToProcessor(processor));
+ convertedProcessors.push(convertProcessorInternalToProcessor(processor, copyIdToTag));
}
+
return convertedProcessors;
};
-export const serialize = ({ processors, onFailure }: SerializeArgs): SerializeResult => {
+export const serialize = ({
+ pipeline: { processors, onFailure },
+ copyIdToTag = false,
+}: SerializeArgs): SerializeResult => {
return {
- processors: convertProcessors(processors),
- on_failure: onFailure?.length ? convertProcessors(onFailure) : undefined,
+ processors: convertProcessors(processors, copyIdToTag),
+ on_failure: onFailure?.length ? convertProcessors(onFailure, copyIdToTag) : undefined,
};
};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/types.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/types.ts
index 67920ffafb71a0..9083985b0ff2eb 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/types.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/types.ts
@@ -73,3 +73,35 @@ export interface ContextValue {
onTreeAction: OnActionHandler;
state: ContextValueState;
}
+
+export interface Document {
+ _id: string;
+ [key: string]: any;
+}
+
+export type ProcessorStatus =
+ | 'success'
+ | 'error'
+ | 'error_ignored'
+ | 'dropped'
+ | 'skipped'
+ | 'inactive';
+
+export interface ProcessorResult {
+ processor_type: string;
+ status: ProcessorStatus;
+ doc: Document;
+ tag: string;
+ ignored_error?: any;
+ error?: any;
+ prevProcessorResult?: ProcessorResult;
+ [key: string]: any;
+}
+
+export interface ProcessorResults {
+ processor_results: ProcessorResult[];
+}
+
+export interface VerboseTestOutput {
+ docs: ProcessorResults[];
+}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/services/api.ts b/x-pack/plugins/ingest_pipelines/public/application/services/api.ts
index 0ca1bc328987ff..552e0ed0c41b22 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/services/api.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/services/api.ts
@@ -105,7 +105,7 @@ export class ApiService {
return result;
}
- public async simulatePipeline(testConfig: {
+ public async simulatePipeline(reqBody: {
documents: object[];
verbose?: boolean;
pipeline: Pick;
@@ -113,7 +113,7 @@ export class ApiService {
const result = await this.sendRequest({
path: `${API_BASE_PATH}/simulate`,
method: 'post',
- body: JSON.stringify(testConfig),
+ body: JSON.stringify(reqBody),
});
this.trackUiMetric(UIM_PIPELINE_SIMULATE);
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index d7de5f780d032b..245fa36040cd65 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -9829,7 +9829,6 @@
"xpack.ingestPipelines.requestFlyout.unnamedTitle": "リクエスト",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.addButtonLabel": "追加",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.cancelButtonLabel": "キャンセル",
- "xpack.ingestPipelines.tabs.documentsTabTitle": "ドキュメント",
"xpack.ingestPipelines.tabs.outputTabTitle": "アウトプット",
"xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsFieldLabel": "ドキュメント",
"xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsJsonError": "ドキュメントJSONが無効です。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 7795959a8e3622..77b2825be0298d 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -9832,7 +9832,6 @@
"xpack.ingestPipelines.requestFlyout.unnamedTitle": "请求",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.addButtonLabel": "添加",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.cancelButtonLabel": "取消",
- "xpack.ingestPipelines.tabs.documentsTabTitle": "文档",
"xpack.ingestPipelines.tabs.outputTabTitle": "输出",
"xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsFieldLabel": "文档",
"xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsJsonError": "文档 JSON 无效。",