From 93e37af4f188e6611c7ccec1eabc499c37f885ac Mon Sep 17 00:00:00 2001
From: Tyler Smalley
Date: Tue, 15 Sep 2020 12:07:28 -0700
Subject: [PATCH 1/9] [@kbn/utils] Adds missing dependency (#77536)
Signed-off-by: Tyler Smalley
---
packages/kbn-utils/package.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/kbn-utils/package.json b/packages/kbn-utils/package.json
index 2ad742650f74d..15fe5c6df5648 100644
--- a/packages/kbn-utils/package.json
+++ b/packages/kbn-utils/package.json
@@ -10,7 +10,8 @@
"kbn:watch": "yarn build --watch"
},
"dependencies": {
- "@kbn/config-schema": "1.0.0"
+ "@kbn/config-schema": "1.0.0",
+ "load-json-file": "^6.2.0"
},
"devDependencies": {
"typescript": "4.0.2"
From 309fe76742f934f4f328927c1492dbc146eb7231 Mon Sep 17 00:00:00 2001
From: Alison Goryachev
Date: Tue, 15 Sep 2020 16:24:04 -0400
Subject: [PATCH 2/9] [Ingest pipelines] Polish pipeline debugging workflow
(#76058)
---
.../pipeline_processors_editor.helpers.tsx | 10 +-
.../pipeline_processors_editor.test.tsx | 2 +-
.../__jest__/test_pipeline.helpers.tsx | 7 +-
.../__jest__/test_pipeline.test.tsx | 17 +-
.../documents_dropdown.scss | 3 -
.../documents_dropdown/documents_dropdown.tsx | 70 -----
.../components/index.ts | 8 +-
.../manage_processor_form.container.tsx | 74 -----
.../manage_processor_form.tsx | 236 ----------------
.../processor_output.tsx | 217 ---------------
.../pipeline_processors_editor_item.scss | 2 +-
...pipeline_processors_editor_item_status.tsx | 21 +-
.../processor_form/add_processor_form.tsx | 134 ++++++++++
.../documentation_button.tsx | 0
.../processor_form/edit_processor_form.tsx | 253 ++++++++++++++++++
.../field_components/index.ts | 0
.../field_components/text_editor.tsx | 0
.../field_components/xjson_editor.tsx | 0
.../index.ts | 6 +-
.../processor_form.container.tsx | 127 +++++++++
.../processor_form/processor_output/index.ts | 7 +
.../processor_output/processor_output.scss | 12 +
.../processor_output/processor_output.tsx | 240 +++++++++++++++++
.../processor_settings_fields.tsx | 0
.../processors/append.tsx | 0
.../processors/bytes.tsx | 0
.../processors/circle.tsx | 0
.../common_fields/common_processor_fields.tsx | 0
.../common_fields/field_name_field.tsx | 0
.../common_fields/ignore_missing_field.tsx | 0
.../processors/common_fields/index.ts | 0
.../common_fields/processor_type_field.tsx | 0
.../common_fields/properties_field.tsx | 0
.../processors/common_fields/target_field.tsx | 0
.../processors/convert.tsx | 0
.../processors/csv.tsx | 0
.../processors/custom.tsx | 3 +-
.../processors/date.tsx | 0
.../processors/date_index_name.tsx | 0
.../processors/dissect.tsx | 0
.../processors/dot_expander.tsx | 0
.../processors/drop.tsx | 0
.../processors/enrich.tsx | 0
.../processors/fail.tsx | 0
.../processors/foreach.tsx | 0
.../processors/geoip.tsx | 0
.../processors/grok.tsx | 0
.../processors/gsub.tsx | 0
.../processors/html_strip.tsx | 0
.../processors/index.ts | 0
.../processors/inference.tsx | 0
.../processors/join.tsx | 0
.../processors/json.tsx | 0
.../processors/kv.tsx | 0
.../processors/lowercase.tsx | 0
.../processors/pipeline.tsx | 0
.../processors/remove.tsx | 0
.../processors/rename.tsx | 0
.../processors/script.tsx | 0
.../processors/set.tsx | 0
.../processors/set_security_user.tsx | 0
.../processors/shared.ts | 0
.../processors/sort.tsx | 0
.../processors/split.tsx | 0
.../processors/trim.tsx | 0
.../processors/uppercase.tsx | 0
.../processors/url_decode.tsx | 0
.../processors/user_agent.tsx | 0
.../components/shared/index.ts | 2 +
.../shared/map_processor_type_to_form.tsx | 2 +-
.../shared/status_icons/error_icon.tsx | 18 ++
.../status_icons/error_ignored_icon.tsx | 24 ++
.../components/shared/status_icons/index.ts | 9 +
.../shared/status_icons/skipped_icon.tsx | 18 ++
.../test_pipeline/add_documents_button.tsx | 8 +-
.../documents_dropdown.scss | 3 +
.../documents_dropdown/documents_dropdown.tsx | 138 ++++++++++
.../documents_dropdown/index.ts | 0
.../test_pipeline/test_output_button.tsx | 40 +--
.../test_pipeline/test_pipeline_actions.tsx | 56 ++--
.../test_pipeline_flyout.container.tsx | 177 ++++++++++++
.../test_pipeline/test_pipeline_flyout.tsx | 151 ++++-------
.../tab_documents.tsx | 137 +++-------
.../test_pipeline_flyout_tabs/tab_output.tsx | 27 +-
.../test_pipeline_tabs.tsx | 12 +-
.../context/processors_context.tsx | 12 +-
.../context/test_pipeline_context.tsx | 10 +-
.../pipeline_processors_editor/deserialize.ts | 41 ++-
.../pipeline_processors_editor/index.ts | 7 +-
.../pipeline_processors_editor/types.ts | 2 +-
.../translations/translations/ja-JP.json | 3 -
.../translations/translations/zh-CN.json | 3 -
92 files changed, 1411 insertions(+), 938 deletions(-)
delete mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.scss
delete mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.tsx
delete mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/manage_processor_form.container.tsx
delete mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/manage_processor_form.tsx
delete 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/processor_form/add_processor_form.tsx
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/documentation_button.tsx (100%)
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/edit_processor_form.tsx
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/field_components/index.ts (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/field_components/text_editor.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/field_components/xjson_editor.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/index.ts (71%)
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_form.container.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/index.ts
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.scss
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.tsx
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processor_settings_fields.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/append.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/bytes.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/circle.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/common_fields/common_processor_fields.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/common_fields/field_name_field.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/common_fields/ignore_missing_field.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/common_fields/index.ts (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/common_fields/processor_type_field.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/common_fields/properties_field.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/common_fields/target_field.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/convert.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/csv.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/custom.tsx (96%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/date.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/date_index_name.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/dissect.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/dot_expander.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/drop.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/enrich.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/fail.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/foreach.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/geoip.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/grok.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/gsub.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/html_strip.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/index.ts (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/inference.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/join.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/json.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/kv.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/lowercase.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/pipeline.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/remove.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/rename.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/script.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/set.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/set_security_user.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/shared.ts (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/sort.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/split.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/trim.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/uppercase.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/url_decode.tsx (100%)
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{manage_processor_form => processor_form}/processors/user_agent.tsx (100%)
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/error_icon.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/error_ignored_icon.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/index.ts
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/skipped_icon.tsx
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/documents_dropdown/documents_dropdown.scss
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/documents_dropdown/documents_dropdown.tsx
rename x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/{ => test_pipeline}/documents_dropdown/index.ts (100%)
create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout.container.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx
index 2e7a47e0c93de..e46e5156e30f3 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx
@@ -126,7 +126,7 @@ const createActions = (testBed: TestBed) => {
});
});
await act(async () => {
- find('processorSettingsForm.submitButton').simulate('click');
+ find('addProcessorForm.submitButton').simulate('click');
});
},
@@ -166,7 +166,7 @@ const createActions = (testBed: TestBed) => {
});
});
await act(async () => {
- find('processorSettingsForm.submitButton').simulate('click');
+ find('addProcessorForm.submitButton').simulate('click');
});
},
@@ -202,8 +202,10 @@ type TestSubject =
| 'pipelineEditorDoneButton'
| 'pipelineEditorOnFailureToggle'
| 'addProcessorsButtonLevel1'
- | 'processorSettingsForm'
- | 'processorSettingsForm.submitButton'
+ | 'editProcessorForm'
+ | 'editProcessorForm.submitButton'
+ | 'addProcessorForm.submitButton'
+ | 'addProcessorForm'
| 'processorOptionsEditor'
| 'processorSettingsFormFlyout'
| 'processorTypeSelector'
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx
index 38c652f41e5e1..74ae8b8894b9f 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx
@@ -180,7 +180,7 @@ describe('Pipeline Editor', () => {
it('prevents moving a processor while in edit mode', () => {
const { find, exists } = testBed;
find('processors>0.manageItemButton').simulate('click');
- expect(exists('processorSettingsForm')).toBe(true);
+ expect(exists('editProcessorForm')).toBe(true);
expect(find('processors>0.moveItemButton').props().disabled).toBe(true);
expect(find('processors>1.moveItemButton').props().disabled).toBe(true);
});
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.helpers.tsx
index fec3259fa019b..f4c89d7a1058a 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.helpers.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.helpers.tsx
@@ -140,8 +140,8 @@ const createActions = (testBed: TestBed) => {
component.update();
},
- clickProcessorOutputTab() {
- act(() => {
+ async clickProcessorOutputTab() {
+ await act(async () => {
find('outputTab').simulate('click');
});
component.update();
@@ -224,7 +224,8 @@ type TestSubject =
| 'processorStatusIcon'
| 'documentsTab'
| 'manageItemButton'
- | 'processorSettingsForm'
+ | 'addProcessorForm'
+ | 'editProcessorForm'
| 'configurationTab'
| 'outputTab'
| 'processorOutputTabContent'
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.test.tsx
index 339c840bb86f1..e5118a6e465af 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.test.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.test.tsx
@@ -44,7 +44,7 @@ describe('Test pipeline', () => {
describe('Test pipeline actions', () => {
it('should successfully add sample documents and execute the pipeline', async () => {
- const { find, actions, exists } = testBed;
+ const { actions, exists } = testBed;
httpRequestsMockHelpers.setSimulatePipelineResponse(SIMULATE_RESPONSE);
@@ -59,7 +59,6 @@ describe('Test pipeline', () => {
expect(exists('testPipelineFlyout')).toBe(true);
expect(exists('documentsTabContent')).toBe(true);
expect(exists('outputTabContent')).toBe(false);
- expect(find('outputTab').props().disabled).toEqual(true);
// Add sample documents and click run
actions.addDocumentsJson(JSON.stringify(DOCUMENTS));
@@ -89,21 +88,25 @@ describe('Test pipeline', () => {
});
// Verify output tab is active
- expect(find('outputTab').props().disabled).toEqual(false);
expect(exists('documentsTabContent')).toBe(false);
expect(exists('outputTabContent')).toBe(true);
// Click reload button and verify request
const totalRequests = server.requests.length;
await actions.clickRefreshOutputButton();
- expect(server.requests.length).toBe(totalRequests + 1);
+ // There will be two requests made to the simulate API
+ // the second request will have verbose enabled to update the processor results
+ expect(server.requests.length).toBe(totalRequests + 2);
+ expect(server.requests[server.requests.length - 2].url).toBe(
+ '/api/ingest_pipelines/simulate'
+ );
expect(server.requests[server.requests.length - 1].url).toBe(
'/api/ingest_pipelines/simulate'
);
// Click verbose toggle and verify request
await actions.toggleVerboseSwitch();
- expect(server.requests.length).toBe(totalRequests + 2);
+ expect(server.requests.length).toBe(totalRequests + 3);
expect(server.requests[server.requests.length - 1].url).toBe(
'/api/ingest_pipelines/simulate'
);
@@ -228,10 +231,10 @@ describe('Test pipeline', () => {
// Click processor to open manage flyout
await actions.clickProcessor('processors>0');
// Verify flyout opened
- expect(exists('processorSettingsForm')).toBe(true);
+ expect(exists('editProcessorForm')).toBe(true);
// Navigate to "Output" tab
- actions.clickProcessorOutputTab();
+ await actions.clickProcessorOutputTab();
// Verify content
expect(exists('processorOutputTabContent')).toBe(true);
});
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
deleted file mode 100644
index c5b14dc129b0e..0000000000000
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.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
deleted file mode 100644
index e26b6a2890fe4..0000000000000
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/documents_dropdown.tsx
+++ /dev/null
@@ -1,70 +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 { 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}
- data-test-subj="documentsDropdown"
- />
-
-
- );
-};
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 435d0ed66c4b0..d476202aa43bb 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
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export {
- ManageProcessorForm,
- ManageProcessorFormOnSubmitArg,
- OnSubmitHandler,
-} from './manage_processor_form';
+export { ProcessorForm, ProcessorFormOnSubmitArg, OnSubmitHandler } from './processor_form';
export { ProcessorsTree, ProcessorInfo, OnActionHandler } from './processors_tree';
@@ -22,6 +18,4 @@ export { OnDoneLoadJsonHandler, LoadFromJsonButton } from './load_from_json';
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/manage_processor_form/manage_processor_form.container.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/manage_processor_form.container.tsx
deleted file mode 100644
index 083529921b0a7..0000000000000
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/manage_processor_form.container.tsx
+++ /dev/null
@@ -1,74 +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, { FunctionComponent, useCallback, useEffect } from 'react';
-
-import { useForm, OnFormUpdateArg, FormData, useKibana } from '../../../../../shared_imports';
-import { ProcessorInternal } from '../../types';
-
-import { ManageProcessorForm as ViewComponent } from './manage_processor_form';
-
-export type ManageProcessorFormOnSubmitArg = Omit;
-
-export type OnSubmitHandler = (processor: ManageProcessorFormOnSubmitArg) => void;
-
-export type OnFormUpdateHandler = (form: OnFormUpdateArg) => void;
-
-interface Props {
- onFormUpdate: OnFormUpdateHandler;
- onSubmit: OnSubmitHandler;
- isOnFailure: boolean;
- onOpen: () => void;
- onClose: () => void;
- processor?: ProcessorInternal;
-}
-
-export const ManageProcessorForm: FunctionComponent = ({
- processor,
- onFormUpdate,
- onSubmit,
- ...rest
-}) => {
- const { services } = useKibana();
-
- const handleSubmit = useCallback(
- async (data: FormData, isValid: boolean) => {
- if (isValid) {
- const { type, customOptions, fields } = data;
- onSubmit({
- type,
- options: customOptions ? customOptions : fields,
- });
- }
- },
- [onSubmit]
- );
-
- const maybeProcessorOptions = processor?.options;
- const { form } = useForm({
- defaultValue: { fields: maybeProcessorOptions ?? {} },
- onSubmit: handleSubmit,
- });
-
- useEffect(() => {
- const subscription = form.subscribe(onFormUpdate);
- return subscription.unsubscribe;
-
- // TODO: Address this issue
- // For some reason adding `form` object to the dependencies array here is causing an
- // infinite update loop.
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [onFormUpdate]);
-
- return (
-
- );
-};
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
deleted file mode 100644
index ee8ca71e58446..0000000000000
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/manage_processor_form.tsx
+++ /dev/null
@@ -1,236 +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 { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-import React, { FunctionComponent, memo, useEffect, useState } from 'react';
-import {
- EuiButton,
- EuiButtonEmpty,
- EuiFlyout,
- EuiFlyoutHeader,
- EuiFlyoutBody,
- EuiFlyoutFooter,
- EuiTabs,
- EuiTab,
- EuiTitle,
- EuiFlexGroup,
- EuiFlexItem,
- EuiSpacer,
-} from '@elastic/eui';
-
-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;
- processor?: ProcessorInternal;
- form: FormHook;
- onClose: () => void;
- onOpen: () => void;
- esDocsBasePath: string;
-}
-
-const updateButtonLabel = i18n.translate(
- 'xpack.ingestPipelines.settingsFormOnFailureFlyout.updateButtonLabel',
- { defaultMessage: 'Update' }
-);
-
-const addButtonLabel = i18n.translate(
- 'xpack.ingestPipelines.settingsFormOnFailureFlyout.addButtonLabel',
- { defaultMessage: 'Add' }
-);
-
-const cancelButtonLabel = i18n.translate(
- 'xpack.ingestPipelines.settingsFormOnFailureFlyout.cancelButtonLabel',
- { defaultMessage: 'Cancel' }
-);
-
-export type TabType = 'configuration' | 'output';
-
-interface Tab {
- id: TabType;
- name: string;
-}
-
-const tabs: Tab[] = [
- {
- id: 'configuration',
- name: i18n.translate(
- 'xpack.ingestPipelines.settingsFormOnFailureFlyout.configurationTabTitle',
- {
- defaultMessage: 'Configuration',
- }
- ),
- },
- {
- id: 'output',
- name: i18n.translate('xpack.ingestPipelines.settingsFormOnFailureFlyout.outputTabTitle', {
- defaultMessage: 'Output',
- }),
- },
-];
-
-const getFlyoutTitle = (isOnFailure: boolean, isExistingProcessor: boolean) => {
- if (isExistingProcessor) {
- return isOnFailure ? (
-
- ) : (
-
- );
- }
-
- return isOnFailure ? (
-
- ) : (
-
- );
-};
-
-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();
- },
- [] /* eslint-disable-line react-hooks/exhaustive-deps */
- );
-
- const [activeTab, setActiveTab] = useState('configuration');
-
- let flyoutContent: React.ReactNode;
-
- if (activeTab === 'output') {
- flyoutContent = (
-
- );
- } else {
- flyoutContent = ;
- }
-
- return (
-
- );
- },
- (previous, current) => {
- return previous.processor === current.processor;
- }
-);
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_output.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_output.tsx
deleted file mode 100644
index c30fdad969b24..0000000000000
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_output.tsx
+++ /dev/null
@@ -1,217 +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 from 'react';
-import { i18n } from '@kbn/i18n';
-
-import {
- EuiAccordion,
- EuiCallOut,
- EuiCodeBlock,
- EuiText,
- EuiFlexGroup,
- EuiFlexItem,
- EuiSpacer,
-} from '@elastic/eui';
-
-import { ProcessorResult, Document } from '../../types';
-import { DocumentsDropdown } from '../documents_dropdown';
-
-export interface Props {
- processorOutput?: ProcessorResult;
- documents: Document[];
- selectedDocumentIndex: number;
- updateSelectedDocument: (index: number) => void;
-}
-
-const i18nTexts = {
- noOutputCalloutTitle: i18n.translate(
- 'xpack.ingestPipelines.processorOutput.noOutputCalloutTitle',
- {
- defaultMessage: 'Unable to load the processor output.',
- }
- ),
- tabDescription: i18n.translate('xpack.ingestPipelines.processorOutput.descriptionText', {
- defaultMessage:
- 'View how the processor affects the ingest document as it passes through the pipeline.',
- }),
- skippedCalloutTitle: i18n.translate('xpack.ingestPipelines.processorOutput.skippedCalloutTitle', {
- defaultMessage: 'The processor was not run.',
- }),
- droppedCalloutTitle: i18n.translate('xpack.ingestPipelines.processorOutput.droppedCalloutTitle', {
- defaultMessage: 'The document was dropped.',
- }),
- processorOutputLabel: i18n.translate(
- 'xpack.ingestPipelines.processorOutput.processorOutputCodeBlockLabel',
- {
- defaultMessage: 'Processor output',
- }
- ),
- processorErrorLabel: i18n.translate(
- 'xpack.ingestPipelines.processorOutput.processorErrorCodeBlockLabel',
- {
- defaultMessage: 'Processor error',
- }
- ),
- prevProcessorLabel: i18n.translate(
- 'xpack.ingestPipelines.processorOutput.previousOutputCodeBlockLabel',
- {
- defaultMessage: 'View previous processor output',
- }
- ),
- processorIgnoredErrorLabel: i18n.translate(
- 'xpack.ingestPipelines.processorOutput.ignoredErrorCodeBlockLabel',
- {
- defaultMessage: 'View ignored error',
- }
- ),
-};
-
-export const ProcessorOutput: React.FunctionComponent = ({
- processorOutput,
- documents,
- selectedDocumentIndex,
- updateSelectedDocument,
-}) => {
- // This code should not be reached,
- // but if for some reason the output is undefined, we render a callout message
- if (!processorOutput) {
- return ;
- }
-
- const {
- prevProcessorResult,
- doc: currentResult,
- ignored_error: ignoredError,
- error,
- status,
- } = processorOutput!;
-
- return (
-
-
- {i18nTexts.tabDescription}
-
-
- {/* There is no output for "skipped" status, so we render an info callout */}
- {status === 'skipped' && (
- <>
-
-
- >
- )}
-
- {/* There is no output for "dropped status", so we render a warning callout */}
- {status === 'dropped' && (
- <>
-
-
- >
- )}
-
- {currentResult && (
- <>
-
-
-
-
- {i18nTexts.processorOutputLabel}
-
-
-
-
-
-
-
-
-
- {JSON.stringify(currentResult, null, 2)}
-
- >
- )}
-
- {error && (
- <>
-
-
-
-
- {i18nTexts.processorErrorLabel}
-
-
-
-
-
-
-
-
-
- {JSON.stringify(error, null, 2)}
-
- >
- )}
-
- {prevProcessorResult?.doc && (
- <>
-
-
-
- {i18nTexts.prevProcessorLabel}
-
- }
- >
- <>
-
-
-
- {JSON.stringify(prevProcessorResult.doc, null, 2)}
-
- >
-
- >
- )}
-
- {ignoredError && (
- <>
-
-
-
- {i18nTexts.processorIgnoredErrorLabel}
-
- }
- >
- <>
-
-
-
- {JSON.stringify(ignoredError, null, 2)}
-
- >
-
- >
- )}
-
- );
-};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.scss
index d9c3d84eec082..55630fa96d9b0 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.scss
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.scss
@@ -63,6 +63,6 @@
&__statusContainer {
// Prevent content jump when spinner renders
- min-width: 12px;
+ min-width: 15px;
}
}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item_status.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item_status.tsx
index a58d482022b4d..08d456b47180c 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item_status.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item_status.tsx
@@ -6,11 +6,12 @@
import React, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
-import { EuiToolTip, EuiIcon } from '@elastic/eui';
+import { EuiToolTip, EuiIcon, IconType } from '@elastic/eui';
import { ProcessorStatus } from '../types';
+import { ErrorIcon, ErrorIgnoredIcon, SkippedIcon } from './shared';
interface ProcessorStatusIcon {
- icon: string;
+ icon: IconType;
iconColor: string;
label: string;
}
@@ -24,28 +25,28 @@ const processorStatusToIconMap: Record = {
}),
},
error: {
- icon: 'crossInACircleFilled',
+ icon: ErrorIcon,
iconColor: 'danger',
label: i18n.translate('xpack.ingestPipelines.pipelineEditorItem.errorStatusAriaLabel', {
defaultMessage: 'Error',
}),
},
error_ignored: {
- icon: 'alert',
- iconColor: 'warning',
+ icon: ErrorIgnoredIcon,
+ iconColor: 'danger',
label: i18n.translate('xpack.ingestPipelines.pipelineEditorItem.errorIgnoredStatusAriaLabel', {
defaultMessage: 'Error ignored',
}),
},
dropped: {
- icon: 'alert',
- iconColor: 'warning',
+ icon: 'indexClose',
+ iconColor: 'subdued',
label: i18n.translate('xpack.ingestPipelines.pipelineEditorItem.droppedStatusAriaLabel', {
defaultMessage: 'Dropped',
}),
},
skipped: {
- icon: 'dot',
+ icon: SkippedIcon,
iconColor: 'subdued',
label: i18n.translate('xpack.ingestPipelines.pipelineEditorItem.skippedStatusAriaLabel', {
defaultMessage: 'Skipped',
@@ -53,7 +54,7 @@ const processorStatusToIconMap: Record = {
},
inactive: {
icon: 'dot',
- iconColor: 'subdued',
+ iconColor: '#D3DAE6', // $euiColorLightShade
label: i18n.translate('xpack.ingestPipelines.pipelineEditorItem.inactiveStatusAriaLabel', {
defaultMessage: 'Not run',
}),
@@ -64,7 +65,7 @@ const processorStatusToIconMap: Record = {
// This is not expected and likely means we need to modify the code to support a new status
const unknownStatus = {
icon: 'dot',
- iconColor: 'subdued',
+ iconColor: '#D3DAE6', // $euiColorLightShade
label: i18n.translate('xpack.ingestPipelines.pipelineEditorItem.unknownStatusAriaLabel', {
defaultMessage: 'Unknown',
}),
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/add_processor_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/add_processor_form.tsx
new file mode 100644
index 0000000000000..5231a3d17811b
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/add_processor_form.tsx
@@ -0,0 +1,134 @@
+/*
+ * 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 { FormattedMessage } from '@kbn/i18n/react';
+import React, { FunctionComponent, useEffect } from 'react';
+import {
+ EuiButton,
+ EuiButtonEmpty,
+ EuiFlyout,
+ EuiFlyoutHeader,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
+ EuiTitle,
+ EuiFlexGroup,
+ EuiFlexItem,
+} from '@elastic/eui';
+
+import { Form, FormDataProvider, FormHook } from '../../../../../shared_imports';
+import { getProcessorDescriptor } from '../shared';
+
+import { DocumentationButton } from './documentation_button';
+import { ProcessorSettingsFields } from './processor_settings_fields';
+
+interface Fields {
+ fields: { [key: string]: any };
+}
+export interface Props {
+ isOnFailure: boolean;
+ form: FormHook;
+ onOpen: () => void;
+ esDocsBasePath: string;
+ closeFlyout: () => void;
+ handleSubmit: (shouldCloseFlyout?: boolean) => Promise;
+}
+
+const addButtonLabel = i18n.translate(
+ 'xpack.ingestPipelines.addProcessorFormOnFailureFlyout.addButtonLabel',
+ { defaultMessage: 'Add' }
+);
+
+const cancelButtonLabel = i18n.translate(
+ 'xpack.ingestPipelines.addProcesorFormOnFailureFlyout.cancelButtonLabel',
+ { defaultMessage: 'Cancel' }
+);
+
+const getFlyoutTitle = (isOnFailure: boolean) => {
+ return isOnFailure ? (
+
+ ) : (
+
+ );
+};
+
+export const AddProcessorForm: FunctionComponent = ({
+ isOnFailure,
+ onOpen,
+ form,
+ esDocsBasePath,
+ closeFlyout,
+ handleSubmit,
+}) => {
+ useEffect(
+ () => {
+ onOpen();
+ },
+ [] /* eslint-disable-line react-hooks/exhaustive-deps */
+ );
+
+ return (
+
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/documentation_button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/documentation_button.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/documentation_button.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/documentation_button.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/edit_processor_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/edit_processor_form.tsx
new file mode 100644
index 0000000000000..e449ed75b6343
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/edit_processor_form.tsx
@@ -0,0 +1,253 @@
+/*
+ * 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 { FormattedMessage } from '@kbn/i18n/react';
+import React, { FunctionComponent, useEffect, useState } from 'react';
+import {
+ EuiButton,
+ EuiButtonEmpty,
+ EuiFlyout,
+ EuiFlyoutHeader,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
+ EuiTabs,
+ EuiTab,
+ EuiTitle,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiSpacer,
+} from '@elastic/eui';
+
+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';
+import { Fields } from './processor_form.container';
+
+export interface Props {
+ isOnFailure: boolean;
+ form: FormHook;
+ onOpen: () => void;
+ esDocsBasePath: string;
+ closeFlyout: () => void;
+ resetProcessors: () => void;
+ handleSubmit: (shouldCloseFlyout?: boolean) => Promise;
+ getProcessor: () => ProcessorInternal;
+}
+
+const updateButtonLabel = i18n.translate(
+ 'xpack.ingestPipelines.processorFormFlyout.updateButtonLabel',
+ { defaultMessage: 'Update' }
+);
+
+const cancelButtonLabel = i18n.translate(
+ 'xpack.ingestPipelines.processorFormFlyout.cancelButtonLabel',
+ { defaultMessage: 'Cancel' }
+);
+
+export type TabType = 'configuration' | 'output';
+
+interface Tab {
+ id: TabType;
+ name: string;
+}
+
+const tabs: Tab[] = [
+ {
+ id: 'configuration',
+ name: i18n.translate(
+ 'xpack.ingestPipelines.settingsFormOnFailureFlyout.configurationTabTitle',
+ {
+ defaultMessage: 'Configuration',
+ }
+ ),
+ },
+ {
+ id: 'output',
+ name: i18n.translate('xpack.ingestPipelines.settingsFormOnFailureFlyout.outputTabTitle', {
+ defaultMessage: 'Output',
+ }),
+ },
+];
+
+const getFlyoutTitle = (isOnFailure: boolean) => {
+ return isOnFailure ? (
+
+ ) : (
+
+ );
+};
+
+export const EditProcessorForm: FunctionComponent = ({
+ getProcessor,
+ form,
+ isOnFailure,
+ onOpen,
+ esDocsBasePath,
+ closeFlyout,
+ handleSubmit,
+ resetProcessors,
+}) => {
+ const { testPipelineData, setCurrentTestPipelineData } = useTestPipelineContext();
+ const {
+ testOutputPerProcessor,
+ config: { selectedDocumentIndex, documents },
+ isExecutingPipeline,
+ } = testPipelineData;
+
+ const processor = getProcessor();
+
+ const processorOutput =
+ processor &&
+ testOutputPerProcessor &&
+ testOutputPerProcessor[selectedDocumentIndex][processor.id];
+
+ const updateSelectedDocument = (index: number) => {
+ setCurrentTestPipelineData({
+ type: 'updateActiveDocument',
+ payload: {
+ config: {
+ selectedDocumentIndex: index,
+ },
+ },
+ });
+ };
+
+ useEffect(
+ () => {
+ onOpen();
+ },
+ [] /* eslint-disable-line react-hooks/exhaustive-deps */
+ );
+
+ const [activeTab, setActiveTab] = useState('configuration');
+
+ 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/manage_processor_form/field_components/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/index.ts
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/field_components/index.ts
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/index.ts
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/field_components/text_editor.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/text_editor.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/field_components/text_editor.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/text_editor.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/field_components/xjson_editor.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/xjson_editor.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/field_components/xjson_editor.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/xjson_editor.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/index.ts
similarity index 71%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/index.ts
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/index.ts
index 986bd52e911bf..5a8d2522f1376 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/index.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/index.ts
@@ -5,7 +5,7 @@
*/
export {
- ManageProcessorForm,
- ManageProcessorFormOnSubmitArg,
+ ProcessorFormContainer as ProcessorForm,
+ ProcessorFormOnSubmitArg,
OnSubmitHandler,
-} from './manage_processor_form.container';
+} from './processor_form.container';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_form.container.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_form.container.tsx
new file mode 100644
index 0000000000000..332908d0756f2
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_form.container.tsx
@@ -0,0 +1,127 @@
+/*
+ * 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, useCallback, useEffect, useRef } from 'react';
+
+import { useForm, OnFormUpdateArg, FormData, useKibana } from '../../../../../shared_imports';
+import { ProcessorInternal } from '../../types';
+
+import { EditProcessorForm } from './edit_processor_form';
+import { AddProcessorForm } from './add_processor_form';
+
+export type ProcessorFormOnSubmitArg = Omit;
+
+export type OnSubmitHandler = (processor: ProcessorFormOnSubmitArg) => void;
+
+export type OnFormUpdateHandler = (form: OnFormUpdateArg) => void;
+
+export interface Fields {
+ fields: { [key: string]: any };
+}
+
+interface Props {
+ onFormUpdate: OnFormUpdateHandler;
+ onSubmit: OnSubmitHandler;
+ isOnFailure: boolean;
+ onOpen: () => void;
+ onClose: () => void;
+ processor?: ProcessorInternal;
+}
+
+export const ProcessorFormContainer: FunctionComponent = ({
+ processor,
+ onFormUpdate,
+ onSubmit,
+ onClose,
+ ...rest
+}) => {
+ const { services } = useKibana();
+
+ // We need to keep track of the processor form state if the user
+ // has made config changes, navigated between tabs (Configuration vs. Output)
+ // and has not yet submitted the form
+ const unsavedFormState = useRef();
+
+ const getProcessor = useCallback((): ProcessorInternal => {
+ let options;
+
+ if (unsavedFormState?.current) {
+ options = unsavedFormState.current;
+ } else {
+ options = processor?.options ?? {};
+ }
+
+ return { ...processor, options } as ProcessorInternal;
+ }, [processor, unsavedFormState]);
+
+ const { form } = useForm({
+ defaultValue: { fields: getProcessor().options },
+ });
+
+ const handleSubmit = useCallback(
+ async (shouldCloseFlyout: boolean = true) => {
+ const { isValid, data } = await form.submit();
+
+ if (isValid) {
+ const { type, customOptions, fields } = data as FormData;
+ const options = customOptions ? customOptions : fields;
+
+ unsavedFormState.current = options;
+
+ onSubmit({
+ type,
+ options,
+ });
+
+ if (shouldCloseFlyout) {
+ onClose();
+ }
+ }
+ },
+ [form, onClose, onSubmit]
+ );
+
+ const resetProcessors = useCallback(() => {
+ onSubmit({
+ type: processor!.type,
+ options: processor?.options || {},
+ });
+ }, [onSubmit, processor]);
+
+ useEffect(() => {
+ const subscription = form.subscribe(onFormUpdate);
+ return subscription.unsubscribe;
+
+ // TODO: Address this issue
+ // For some reason adding `form` object to the dependencies array here is causing an
+ // infinite update loop.
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [onFormUpdate]);
+
+ if (processor) {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/index.ts
new file mode 100644
index 0000000000000..3b506fc9296e3
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/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 { ProcessorOutput } from './processor_output';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.scss
new file mode 100644
index 0000000000000..e1b5eb83584ff
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.scss
@@ -0,0 +1,12 @@
+.processorOutput {
+ &__callOut {
+ &--customIcon {
+ .euiCallOutHeader {
+ align-items: center;
+ }
+ }
+ &__codeBlock > pre {
+ background: transparent;
+ }
+ }
+}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.tsx
new file mode 100644
index 0000000000000..bd0ce6ca2cd52
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.tsx
@@ -0,0 +1,240 @@
+/*
+ * 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 } from 'react';
+import { i18n } from '@kbn/i18n';
+
+import {
+ EuiAccordion,
+ EuiCallOut,
+ EuiCodeBlock,
+ EuiText,
+ EuiSpacer,
+ EuiSelect,
+} from '@elastic/eui';
+
+import { SectionLoading } from '../../../../../../shared_imports';
+import { ProcessorResult, Document } from '../../../types';
+import { ErrorIcon, ErrorIgnoredIcon, SkippedIcon } from '../../shared';
+
+import './processor_output.scss';
+
+export interface Props {
+ processorOutput?: ProcessorResult;
+ documents: Document[];
+ selectedDocumentIndex: number;
+ updateSelectedDocument: (index: number) => void;
+ isExecuting?: boolean;
+}
+
+const i18nTexts = {
+ tabDescription: i18n.translate('xpack.ingestPipelines.processorOutput.descriptionText', {
+ defaultMessage:
+ 'View how the processor affects the ingest document as it passes through the pipeline.',
+ }),
+ skippedCalloutTitle: i18n.translate('xpack.ingestPipelines.processorOutput.skippedCalloutTitle', {
+ defaultMessage: 'The processor was not run.',
+ }),
+ droppedCalloutTitle: i18n.translate('xpack.ingestPipelines.processorOutput.droppedCalloutTitle', {
+ defaultMessage: 'The document was dropped.',
+ }),
+ noOutputCalloutTitle: i18n.translate(
+ 'xpack.ingestPipelines.processorOutput.noOutputCalloutTitle',
+ {
+ defaultMessage: 'Output is not available for this processor.',
+ }
+ ),
+ processorOutputLabel: i18n.translate(
+ 'xpack.ingestPipelines.processorOutput.processorOutputCodeBlockLabel',
+ {
+ defaultMessage: 'Data out',
+ }
+ ),
+ processorErrorTitle: i18n.translate(
+ 'xpack.ingestPipelines.processorOutput.processorErrorCodeBlockLabel',
+ {
+ defaultMessage: 'There was an error',
+ }
+ ),
+ prevProcessorLabel: i18n.translate(
+ 'xpack.ingestPipelines.processorOutput.processorInputCodeBlockLabel',
+ {
+ defaultMessage: 'Data in',
+ }
+ ),
+ processorIgnoredErrorTitle: i18n.translate(
+ 'xpack.ingestPipelines.processorOutput.ignoredErrorCodeBlockLabel',
+ {
+ defaultMessage: 'There was an error that was ignored',
+ }
+ ),
+ documentsDropdownLabel: i18n.translate(
+ 'xpack.ingestPipelines.processorOutput.documentsDropdownLabel',
+ {
+ defaultMessage: 'Test data:',
+ }
+ ),
+ loadingMessage: i18n.translate('xpack.ingestPipelines.processorOutput.loadingMessage', {
+ defaultMessage: 'Loading processor output…',
+ }),
+};
+
+export const ProcessorOutput: FunctionComponent = ({
+ processorOutput,
+ documents,
+ selectedDocumentIndex,
+ updateSelectedDocument,
+ isExecuting,
+}) => {
+ if (isExecuting) {
+ return {i18nTexts.loadingMessage} ;
+ }
+
+ if (!processorOutput) {
+ return ;
+ }
+
+ const {
+ processorInput,
+ doc: currentResult,
+ ignored_error: ignoredError,
+ error,
+ status,
+ } = processorOutput!;
+
+ const NoOutputCallOut: FunctionComponent = () => (
+
+ );
+
+ const getOutputContent = () => {
+ switch (status) {
+ case 'skipped':
+ return (
+
+ );
+ case 'dropped':
+ return ;
+ case 'success':
+ if (currentResult) {
+ return (
+
+ {JSON.stringify(currentResult, null, 2)}
+
+ );
+ }
+
+ return ;
+ case 'error':
+ return (
+
+
+ {JSON.stringify(error, null, 2)}
+
+
+ );
+ case 'error_ignored':
+ return (
+
+
+ {JSON.stringify(ignoredError, null, 2)}
+
+
+ );
+ default:
+ return ;
+ }
+ };
+
+ return (
+
+
+ {i18nTexts.tabDescription}
+
+
+
+
+ {/* Documents dropdown */}
+
({
+ value: index,
+ text: i18n.translate('xpack.ingestPipelines.processorOutput.documentLabel', {
+ defaultMessage: 'Document {number}',
+ values: {
+ number: index + 1,
+ },
+ }),
+ }))}
+ value={selectedDocumentIndex}
+ onChange={(e) => {
+ updateSelectedDocument(Number(e.target.value));
+ }}
+ aria-label={i18nTexts.documentsDropdownLabel}
+ prepend={i18nTexts.documentsDropdownLabel}
+ />
+
+
+
+ {/* Data-in accordion */}
+
+ {i18nTexts.prevProcessorLabel}
+
+ }
+ >
+ <>
+
+
+
+ {/* If there is no processorInput defined (i.e., it's the first processor), we provide the sample document */}
+ {JSON.stringify(
+ processorInput ? processorInput : documents[selectedDocumentIndex],
+ null,
+ 2
+ )}
+
+ >
+
+
+
+
+ {/* Data-out content */}
+
+ {i18nTexts.processorOutputLabel}
+
+
+
+
+ {getOutputContent()}
+
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_settings_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_settings_fields.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processor_settings_fields.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_settings_fields.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/append.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/append.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/append.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/append.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/bytes.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/bytes.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/bytes.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/bytes.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/circle.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/circle.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/circle.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/circle.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/common_processor_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/common_processor_fields.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/field_name_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/field_name_field.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/field_name_field.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/field_name_field.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/ignore_missing_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/ignore_missing_field.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/ignore_missing_field.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/ignore_missing_field.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/index.ts
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/index.ts
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/index.ts
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/processor_type_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/processor_type_field.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/processor_type_field.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/processor_type_field.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/properties_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/properties_field.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/properties_field.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/properties_field.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/target_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/target_field.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/target_field.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/target_field.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/convert.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/convert.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/convert.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/convert.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/csv.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/csv.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/csv.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/csv.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/custom.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/custom.tsx
similarity index 96%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/custom.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/custom.tsx
index c2aab62cf8933..f49e77501f931 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/custom.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/custom.tsx
@@ -17,6 +17,7 @@ import {
const { emptyField, isJsonField } = fieldValidators;
import { XJsonEditor } from '../field_components';
+import { Fields } from '../processor_form.container';
import { EDITOR_PX_HEIGHT } from './shared';
const customConfig: FieldConfig = {
@@ -60,7 +61,7 @@ const customConfig: FieldConfig = {
};
interface Props {
- defaultOptions?: any;
+ defaultOptions?: Fields['fields'];
}
/**
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/date.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/date.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/date.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/date.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/date_index_name.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/date_index_name.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/date_index_name.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/date_index_name.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/dissect.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/dissect.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/dissect.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/dissect.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/dot_expander.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/dot_expander.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/dot_expander.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/dot_expander.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/drop.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/drop.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/drop.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/drop.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/enrich.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/enrich.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/enrich.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/enrich.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/fail.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/fail.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/fail.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/fail.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/foreach.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/foreach.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/foreach.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/foreach.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/geoip.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/geoip.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/geoip.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/geoip.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/grok.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/grok.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/grok.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/grok.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/gsub.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/gsub.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/gsub.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/gsub.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/html_strip.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/html_strip.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/html_strip.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/html_strip.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/index.ts
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/index.ts
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/index.ts
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/inference.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/inference.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/inference.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/inference.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/join.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/join.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/join.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/join.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/json.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/json.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/json.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/json.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/kv.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/kv.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/kv.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/kv.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/lowercase.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/lowercase.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/lowercase.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/lowercase.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/pipeline.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/pipeline.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/pipeline.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/pipeline.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/remove.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/remove.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/remove.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/remove.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/rename.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/rename.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/rename.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/rename.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/script.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/script.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/script.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/script.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/set.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/set.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/set.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/set.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/set_security_user.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/set_security_user.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/set_security_user.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/set_security_user.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/shared.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/shared.ts
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/shared.ts
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/shared.ts
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/sort.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/sort.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/sort.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/sort.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/split.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/split.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/split.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/split.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/trim.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/trim.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/trim.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/trim.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/uppercase.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/uppercase.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/uppercase.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/uppercase.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/url_decode.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/url_decode.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/url_decode.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/url_decode.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/user_agent.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/user_agent.tsx
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/user_agent.tsx
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/user_agent.tsx
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/index.ts
index 1b4b975b5305e..3f258bf279e42 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/index.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/index.ts
@@ -9,3 +9,5 @@ export {
mapProcessorTypeToDescriptor,
ProcessorType,
} from './map_processor_type_to_form';
+
+export { ErrorIcon, ErrorIgnoredIcon, SkippedIcon } from './status_icons';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx
index 95a8d35c119a6..8d9260f3c822c 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx
@@ -46,7 +46,7 @@ import {
UrlDecode,
UserAgent,
FormFieldsComponent,
-} from '../manage_processor_form/processors';
+} from '../processor_form/processors';
interface FieldDescriptor {
FieldsComponent?: FormFieldsComponent;
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/error_icon.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/error_icon.tsx
new file mode 100644
index 0000000000000..58cb56d4f768d
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/error_icon.tsx
@@ -0,0 +1,18 @@
+/*
+ * 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 } from 'react';
+
+export const ErrorIcon: FunctionComponent = () => (
+
+
+
+);
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/error_ignored_icon.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/error_ignored_icon.tsx
new file mode 100644
index 0000000000000..74ceda7687f02
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/error_ignored_icon.tsx
@@ -0,0 +1,24 @@
+/*
+ * 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 } from 'react';
+
+export const ErrorIgnoredIcon: FunctionComponent = () => (
+
+
+
+
+);
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/index.ts
new file mode 100644
index 0000000000000..9fe0871e445eb
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/index.ts
@@ -0,0 +1,9 @@
+/*
+ * 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 { ErrorIcon } from './error_icon';
+export { ErrorIgnoredIcon } from './error_ignored_icon';
+export { SkippedIcon } from './skipped_icon';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/skipped_icon.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/skipped_icon.tsx
new file mode 100644
index 0000000000000..c540bd3790fb0
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/status_icons/skipped_icon.tsx
@@ -0,0 +1,18 @@
+/*
+ * 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 } from 'react';
+
+export const SkippedIcon: FunctionComponent = () => (
+
+
+
+);
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/add_documents_button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/add_documents_button.tsx
index e3ef9a9ee5390..26492454cbcf5 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/add_documents_button.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/add_documents_button.tsx
@@ -6,6 +6,7 @@
import { i18n } from '@kbn/i18n';
import React, { FunctionComponent } from 'react';
import { EuiButtonEmpty } from '@elastic/eui';
+import { TestPipelineFlyoutTab } from './test_pipeline_flyout_tabs';
const i18nTexts = {
buttonLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.testPipeline.buttonLabel', {
@@ -14,16 +15,15 @@ const i18nTexts = {
};
interface Props {
- openTestPipelineFlyout: () => void;
+ openFlyout: (activeFlyoutTab: TestPipelineFlyoutTab) => void;
}
-export const AddDocumentsButton: FunctionComponent = ({ openTestPipelineFlyout }) => {
+export const AddDocumentsButton: FunctionComponent = ({ openFlyout }) => {
return (
openFlyout('documents')}
data-test-subj="addDocumentsButton"
- iconType="plusInCircleFilled"
>
{i18nTexts.buttonLabel}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/documents_dropdown/documents_dropdown.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/documents_dropdown/documents_dropdown.scss
new file mode 100644
index 0000000000000..5deb48a2f01a7
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/documents_dropdown/documents_dropdown.scss
@@ -0,0 +1,3 @@
+.documentsDropdownPanel {
+ min-width: 200px;
+}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/documents_dropdown/documents_dropdown.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/documents_dropdown/documents_dropdown.tsx
new file mode 100644
index 0000000000000..269a697a33c17
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/documents_dropdown/documents_dropdown.tsx
@@ -0,0 +1,138 @@
+/*
+ * 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, useState } from 'react';
+import {
+ EuiButton,
+ EuiPopover,
+ EuiButtonEmpty,
+ EuiPopoverTitle,
+ EuiSelectable,
+ EuiHorizontalRule,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiSpacer,
+} from '@elastic/eui';
+
+import { Document } from '../../../types';
+
+import { TestPipelineFlyoutTab } from '../test_pipeline_flyout_tabs';
+
+import './documents_dropdown.scss';
+
+const i18nTexts = {
+ dropdownLabel: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.testPipeline.documentsdropdown.dropdownLabel',
+ {
+ defaultMessage: 'Documents:',
+ }
+ ),
+ addDocumentsButtonLabel: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.testPipeline.documentsDropdown.buttonLabel',
+ {
+ defaultMessage: 'Add documents',
+ }
+ ),
+ popoverTitle: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.testPipeline.documentsDropdown.popoverTitle',
+ {
+ defaultMessage: 'Test documents',
+ }
+ ),
+};
+
+interface Props {
+ documents: Document[];
+ selectedDocumentIndex: number;
+ updateSelectedDocument: (index: number) => void;
+ openFlyout: (activeFlyoutTab: TestPipelineFlyoutTab) => void;
+}
+
+export const DocumentsDropdown: FunctionComponent = ({
+ documents,
+ selectedDocumentIndex,
+ updateSelectedDocument,
+ openFlyout,
+}) => {
+ const [showPopover, setShowPopover] = useState(false);
+
+ const managePipelineButton = (
+ setShowPopover((previousBool) => !previousBool)}
+ iconType="arrowDown"
+ iconSide="right"
+ >
+ {i18n.translate('xpack.ingestPipelines.pipelineEditor.testPipeline.selectedDocumentLabel', {
+ defaultMessage: 'Document {selectedDocument}',
+ values: {
+ selectedDocument: selectedDocumentIndex + 1,
+ },
+ })}
+
+ );
+
+ return (
+ setShowPopover(false)}
+ button={managePipelineButton}
+ panelPaddingSize="none"
+ withTitle
+ repositionOnScroll
+ data-test-subj="documentsDropdown"
+ panelClassName="documentsDropdownPanel"
+ >
+ ({
+ key: index.toString(),
+ checked: selectedDocumentIndex === index ? 'on' : undefined,
+ label: i18n.translate('xpack.ingestPipelines.pipelineEditor.testPipeline.documentLabel', {
+ defaultMessage: 'Document {documentNumber}',
+ values: {
+ documentNumber: index + 1,
+ },
+ }),
+ }))}
+ onChange={(newOptions) => {
+ const selectedOption = newOptions.find((option) => option.checked === 'on');
+ if (selectedOption) {
+ updateSelectedDocument(Number(selectedOption.key!));
+ }
+
+ setShowPopover(false);
+ }}
+ >
+ {(list, search) => (
+
+ {i18nTexts.popoverTitle}
+ {list}
+
+ )}
+
+
+
+
+
+
+ {
+ openFlyout('documents');
+ setShowPopover(false);
+ }}
+ data-test-subj="addDocumentsButton"
+ >
+ {i18nTexts.addDocumentsButtonLabel}
+
+
+
+
+
+
+ );
+};
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/test_pipeline/documents_dropdown/index.ts
similarity index 100%
rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/documents_dropdown/index.ts
rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/documents_dropdown/index.ts
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
index 6fd1adad54f84..9018042229590 100644
--- 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
@@ -5,7 +5,8 @@
*/
import { i18n } from '@kbn/i18n';
import React, { FunctionComponent } from 'react';
-import { EuiButton, EuiToolTip } from '@elastic/eui';
+import { EuiButton } from '@elastic/eui';
+import { TestPipelineFlyoutTab } from './test_pipeline_flyout_tabs';
const i18nTexts = {
buttonLabel: i18n.translate(
@@ -14,46 +15,15 @@ const i18nTexts = {
defaultMessage: 'View output',
}
),
- disabledButtonTooltipLabel: i18n.translate(
- 'xpack.ingestPipelines.pipelineEditor.testPipeline.outputButtonTooltipLabel',
- {
- defaultMessage: 'Add documents to view the output',
- }
- ),
};
interface Props {
- isDisabled: boolean;
- openTestPipelineFlyout: () => void;
+ openFlyout: (activeFlyoutTab: TestPipelineFlyoutTab) => void;
}
-export const TestOutputButton: FunctionComponent = ({
- isDisabled,
- openTestPipelineFlyout,
-}) => {
- if (isDisabled) {
- return (
- {i18nTexts.disabledButtonTooltipLabel}
}>
-
- {i18nTexts.buttonLabel}
-
-
- );
- }
-
+export const TestOutputButton: FunctionComponent = ({ openFlyout }) => {
return (
-
+ openFlyout('output')} data-test-subj="viewOutputButton">
{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
index eb9d9352e4b90..cec02db26729d 100644
--- 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
@@ -4,15 +4,24 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FunctionComponent, useState } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
-import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { useTestPipelineContext, usePipelineProcessorsContext } from '../../context';
-
-import { DocumentsDropdown } from '../documents_dropdown';
+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';
+import { TestPipelineFlyout } from './test_pipeline_flyout.container';
+
+const i18nTexts = {
+ testPipelineActionsLabel: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.testPipeline.testPipelineActionsLabel',
+ {
+ defaultMessage: 'Test pipeline:',
+ }
+ ),
+};
export const TestPipelineActions: FunctionComponent = () => {
const { testPipelineData, setCurrentTestPipelineData } = useTestPipelineContext();
@@ -40,35 +49,42 @@ export const TestPipelineActions: FunctionComponent = () => {
});
};
+ const openFlyout = (activeTab: TestPipelineFlyoutTab) => {
+ setOpenTestPipelineFlyout(true);
+ setActiveFlyoutTab(activeTab);
+ };
+
return (
<>
-
+
+
+
+
+ {i18nTexts.testPipelineActionsLabel}
+
+
+
+
{documents ? (
) : (
- {
- setOpenTestPipelineFlyout(true);
- setActiveFlyoutTab('documents');
- }}
- />
+
)}
-
- {
- setOpenTestPipelineFlyout(true);
- setActiveFlyoutTab('output');
- }}
- />
-
+
+ {testOutputPerProcessor && (
+
+
+
+ )}
+
{openTestPipelineFlyout && (
void;
+ processors: DeserializeResult;
+}
+
+export interface TestPipelineConfig {
+ 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 { form } = useForm({
+ schema: documentsSchema,
+ defaultValue: {
+ documents: cachedDocuments || '',
+ },
+ });
+
+ const [selectedTab, setSelectedTab] = useState(activeTab);
+
+ const [isRunningTest, setIsRunningTest] = useState(false);
+ const [testingError, setTestingError] = useState(null);
+ const [testOutput, setTestOutput] = useState(undefined);
+
+ const handleTestPipeline = useCallback(
+ async (
+ { documents, verbose }: TestPipelineConfig,
+ updateProcessorOutput?: boolean
+ ): Promise<{ isSuccessful: boolean }> => {
+ 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);
+
+ // reset the per-processor output
+ // this is needed in the scenario where the pipeline has already executed,
+ // but you modified the sample documents and there was an error on re-execution
+ setCurrentTestPipelineData({
+ type: 'updateOutputPerProcessor',
+ payload: {
+ isExecutingPipeline: false,
+ testOutputPerProcessor: undefined,
+ },
+ });
+
+ return { isSuccessful: false };
+ }
+
+ setCurrentTestPipelineData({
+ type: 'updateConfig',
+ payload: {
+ config: {
+ documents,
+ verbose,
+ },
+ },
+ });
+
+ // We sometimes need to manually refresh the per-processor output
+ // e.g., when clicking the "Refresh output" button and there have been no state changes
+ if (updateProcessorOutput) {
+ updateTestOutputPerProcessor(documents, processors);
+ }
+
+ setTestOutput(currentTestOutput);
+
+ services.notifications.toasts.addSuccess(
+ i18n.translate('xpack.ingestPipelines.testPipelineFlyout.successNotificationText', {
+ defaultMessage: 'Pipeline executed',
+ }),
+ {
+ toastLifeTimeMs: 1000,
+ }
+ );
+
+ return { isSuccessful: true };
+ },
+ [
+ processors,
+ services.api,
+ services.notifications.toasts,
+ setCurrentTestPipelineData,
+ updateTestOutputPerProcessor,
+ ]
+ );
+
+ const validateAndTestPipeline = async () => {
+ const { isValid, data } = await form.submit();
+
+ if (!isValid) {
+ return;
+ }
+
+ const { documents } = data as { documents: Document[] };
+
+ const { isSuccessful } = await handleTestPipeline({
+ documents: documents!,
+ verbose: cachedVerbose,
+ });
+
+ if (isSuccessful) {
+ setSelectedTab('output');
+ }
+ };
+
+ useEffect(() => {
+ if (cachedDocuments && activeTab === 'output') {
+ handleTestPipeline({ documents: cachedDocuments, verbose: cachedVerbose }, true);
+ }
+ // We only want to know on initial mount if
+ // there are cached documents and we are on the output tab
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return (
+
+ );
+};
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
index b26c6f536366d..46271a6bce51c 100644
--- 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
@@ -4,9 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useState, useCallback, useEffect } from 'react';
+import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
-import { i18n } from '@kbn/i18n';
import {
EuiFlyout,
@@ -17,112 +16,46 @@ import {
EuiCallOut,
} from '@elastic/eui';
-import { useKibana } from '../../../../../shared_imports';
-import { useTestPipelineContext } from '../../context';
-import { serialize } from '../../serialize';
-import { DeserializeResult } from '../../deserialize';
+import { Form, FormHook } from '../../../../../shared_imports';
import { Document } from '../../types';
import { Tabs, TestPipelineFlyoutTab, OutputTab, DocumentsTab } from './test_pipeline_flyout_tabs';
export interface Props {
- activeTab: TestPipelineFlyoutTab;
onClose: () => void;
- processors: DeserializeResult;
+ handleTestPipeline: (
+ testPipelineConfig: TestPipelineConfig,
+ refreshOutputPerProcessor?: boolean
+ ) => Promise<{ isSuccessful: boolean }>;
+ isRunningTest: boolean;
+ cachedVerbose?: boolean;
+ cachedDocuments?: Document[];
+ testOutput?: any;
+ form: FormHook;
+ validateAndTestPipeline: () => Promise;
+ selectedTab: TestPipelineFlyoutTab;
+ setSelectedTab: (selectedTa: TestPipelineFlyoutTab) => void;
+ testingError: any;
}
-export interface HandleTestPipelineArgs {
+export interface TestPipelineConfig {
documents: Document[];
verbose?: boolean;
}
export const TestPipelineFlyout: React.FunctionComponent = ({
+ handleTestPipeline,
+ isRunningTest,
+ cachedVerbose,
+ cachedDocuments,
+ testOutput,
+ form,
+ validateAndTestPipeline,
+ selectedTab,
+ setSelectedTab,
+ testingError,
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') {
@@ -138,13 +71,19 @@ export const TestPipelineFlyout: React.FunctionComponent = ({
} else {
// default to "Documents" tab
tabContent = (
-
+
);
}
@@ -163,9 +102,17 @@ export const TestPipelineFlyout: React.FunctionComponent = ({
{
+ if (nextTab === 'output') {
+ // When switching to the output tab,
+ // we automatically run the pipeline if documents are defined
+ validateAndTestPipeline();
+ } else {
+ form.reset({ defaultValue: { documents: cachedDocuments! } });
+ setSelectedTab(nextTab);
+ }
+ }}
selectedTab={selectedTab}
- getIsDisabled={(tabId) => !testOutput && tabId === 'output'}
/>
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/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
index dd12cdab0c934..b2326644340a7 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/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
@@ -10,67 +10,23 @@ import { i18n } from '@kbn/i18n';
import { EuiSpacer, EuiText, EuiButton, EuiLink } from '@elastic/eui';
-import {
- getUseField,
- Field,
- JsonEditorField,
- Form,
- useForm,
- useKibana,
-} from '../../../../../../shared_imports';
-
-import { TestPipelineContext } from '../../../context';
-import { Document } from '../../../types';
-import { DeserializeResult } from '../../../deserialize';
-import { HandleTestPipelineArgs } from '../test_pipeline_flyout';
-import { documentsSchema } from './documents_schema';
+import { getUseField, Field, JsonEditorField, useKibana } from '../../../../../../shared_imports';
const UseField = getUseField({ component: Field });
interface Props {
- handleTestPipeline: (data: HandleTestPipelineArgs) => void;
- setPerProcessorOutput: (documents: Document[] | undefined, processors: DeserializeResult) => void;
+ validateAndTestPipeline: () => Promise;
isRunningTest: boolean;
- processors: DeserializeResult;
- testPipelineData: TestPipelineContext['testPipelineData'];
+ isSubmitButtonDisabled: boolean;
}
export const DocumentsTab: React.FunctionComponent = ({
- handleTestPipeline,
+ validateAndTestPipeline,
+ isSubmitButtonDisabled,
isRunningTest,
- setPerProcessorOutput,
- processors,
- testPipelineData,
}) => {
const { services } = useKibana();
- const {
- config: { documents: cachedDocuments, verbose: cachedVerbose },
- } = testPipelineData;
-
- const testPipeline = async () => {
- const { isValid, data } = await form.submit();
-
- if (!isValid) {
- return;
- }
-
- const { documents } = data as { documents: Document[] };
-
- await handleTestPipeline({ documents: documents!, verbose: 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 || '',
- },
- });
-
return (
@@ -100,53 +56,46 @@ export const DocumentsTab: React.FunctionComponent = ({
-
+
+ {isRunningTest ? (
+
+ ) : (
+
+ )}
+
);
};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_output.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_output.tsx
index 926bab6da993c..db6a020e307a5 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_output.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_output.tsx
@@ -18,10 +18,13 @@ import {
} from '@elastic/eui';
import { Document } from '../../../types';
-import { HandleTestPipelineArgs } from '../test_pipeline_flyout';
+import { TestPipelineConfig } from '../test_pipeline_flyout.container';
interface Props {
- handleTestPipeline: (data: HandleTestPipelineArgs) => void;
+ handleTestPipeline: (
+ testPipelineConfig: TestPipelineConfig,
+ refreshOutputPerProcessor?: boolean
+ ) => Promise<{ isSuccessful: boolean }>;
isRunningTest: boolean;
cachedVerbose?: boolean;
cachedDocuments: Document[];
@@ -37,12 +40,6 @@ export const OutputTab: React.FunctionComponent = ({
}) => {
const [isVerboseEnabled, setIsVerboseEnabled] = useState(Boolean(cachedVerbose));
- const onEnableVerbose = (isVerbose: boolean) => {
- setIsVerboseEnabled(isVerbose);
-
- handleTestPipeline({ documents: cachedDocuments!, verbose: isVerbose });
- };
-
let content: React.ReactNode | undefined;
if (isRunningTest) {
@@ -78,15 +75,23 @@ export const OutputTab: React.FunctionComponent = ({
/>
}
checked={isVerboseEnabled}
- onChange={(e) => onEnableVerbose(e.target.checked)}
data-test-subj="verboseOutputToggle"
+ onChange={async (e) => {
+ const isVerbose = e.target.checked;
+ setIsVerboseEnabled(isVerbose);
+
+ await handleTestPipeline({ documents: cachedDocuments!, verbose: isVerbose });
+ }}
/>
- handleTestPipeline({ documents: cachedDocuments!, verbose: isVerboseEnabled })
+ onClick={async () =>
+ await handleTestPipeline(
+ { documents: cachedDocuments!, verbose: isVerboseEnabled },
+ true
+ )
}
iconType="refresh"
data-test-subj="refreshOutputButton"
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/test_pipeline_tabs.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/test_pipeline_tabs.tsx
index abfb86c2afda1..b13fb2df90984 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/test_pipeline_tabs.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/test_pipeline_tabs.tsx
@@ -13,14 +13,9 @@ export type TestPipelineFlyoutTab = 'documents' | 'output';
interface Props {
onTabChange: (tab: TestPipelineFlyoutTab) => void;
selectedTab: TestPipelineFlyoutTab;
- getIsDisabled: (tab: TestPipelineFlyoutTab) => boolean;
}
-export const Tabs: React.FunctionComponent = ({
- onTabChange,
- selectedTab,
- getIsDisabled,
-}) => {
+export const Tabs: React.FunctionComponent = ({ onTabChange, selectedTab }) => {
const tabs: Array<{
id: TestPipelineFlyoutTab;
name: React.ReactNode;
@@ -29,8 +24,8 @@ export const Tabs: React.FunctionComponent = ({
id: 'documents',
name: (
),
},
@@ -49,7 +44,6 @@ export const Tabs: React.FunctionComponent = ({
onClick={() => onTabChange(tab.id)}
isSelected={tab.id === selectedTab}
key={tab.id}
- disabled={getIsDisabled(tab.id)}
data-test-subj={tab.id.toLowerCase() + 'Tab'}
>
{tab.name}
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 8c59d484acd08..6595437c01810 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
@@ -38,7 +38,7 @@ import { OnActionHandler } from '../components/processors_tree';
import {
ProcessorRemoveModal,
PipelineProcessorsItemTooltip,
- ManageProcessorForm,
+ ProcessorForm,
OnSubmitHandler,
} from '../components';
@@ -159,12 +159,12 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({
selector: mode.arg.selector,
},
});
+
break;
default:
}
- setMode({ id: 'idle' });
},
- [processorsDispatch, mode, setMode]
+ [processorsDispatch, mode]
);
const onCloseSettingsForm = useCallback(() => {
@@ -208,8 +208,8 @@ 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
+ // Make a request to the simulate API and update the processor output
+ // whenever the documents or processorsState changes (e.g., on move, update, delete)
useEffect(() => {
updateTestOutputPerProcessor(documents, processorsState);
}, [documents, processorsState, updateTestOutputPerProcessor]);
@@ -233,7 +233,7 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({
)}
{mode.id === 'managingProcessor' || mode.id === 'creatingProcessor' ? (
- {
+ const previousProcessorIndex = processorIndex - count;
+
+ if (previousProcessorIndex >= 0) {
+ const processorResult = document.processor_results[previousProcessorIndex];
+
+ if (!processorResult.doc) {
+ const newCount = count + 1;
+ return getProcessorInput(processorIndex, document, newCount);
+ }
+
+ return processorResult.doc;
+ }
+
+ return undefined;
+};
+
/**
* This function takes the verbose response of the simulate API
* and maps the results to each processor in the pipeline by the "tag" field
@@ -81,11 +114,9 @@ export const deserializeVerboseTestOutput = (
const result = { ...currentResult };
const resultId = result.tag;
+ // We skip index 0, as the first processor will not have a previous result
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];
+ result.processorInput = getProcessorInput(index, doc);
}
// The tag is added programatically as a way to map
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 71b2e2fa8f7f1..c462b19c79327 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,9 +14,4 @@ export { OnUpdateHandlerArg, OnUpdateHandler } from './types';
export { SerializeResult } from './serialize';
-export {
- LoadFromJsonButton,
- OnDoneLoadJsonHandler,
- TestPipelineActions,
- DocumentsDropdown,
-} from './components';
+export { LoadFromJsonButton, OnDoneLoadJsonHandler, TestPipelineActions } from './components';
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 5229f5eb0bb21..42201b3102c28 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
@@ -94,7 +94,7 @@ export interface ProcessorResult {
tag: string;
ignored_error?: any;
error?: any;
- prevProcessorResult?: ProcessorResult;
+ processorInput?: Document;
[key: string]: any;
}
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 0977e99fa0c3c..68f6bc166cd1d 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -9497,9 +9497,6 @@
"xpack.ingestPipelines.requestFlyout.descriptionText": "このElasticsearchリクエストは、このパイプラインを作成または更新します。",
"xpack.ingestPipelines.requestFlyout.namedTitle": "「{name}」のリクエスト",
"xpack.ingestPipelines.requestFlyout.unnamedTitle": "リクエスト",
- "xpack.ingestPipelines.settingsFormOnFailureFlyout.addButtonLabel": "追加",
- "xpack.ingestPipelines.settingsFormOnFailureFlyout.cancelButtonLabel": "キャンセル",
- "xpack.ingestPipelines.settingsFormOnFailureFlyout.updateButtonLabel": "更新",
"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 7f8f2a98abae3..cb43cefdc3655 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -9503,9 +9503,6 @@
"xpack.ingestPipelines.requestFlyout.descriptionText": "此 Elasticsearch 请求将创建或更新管道。",
"xpack.ingestPipelines.requestFlyout.namedTitle": "对“{name}”的请求",
"xpack.ingestPipelines.requestFlyout.unnamedTitle": "请求",
- "xpack.ingestPipelines.settingsFormOnFailureFlyout.addButtonLabel": "添加",
- "xpack.ingestPipelines.settingsFormOnFailureFlyout.cancelButtonLabel": "取消",
- "xpack.ingestPipelines.settingsFormOnFailureFlyout.updateButtonLabel": "更新",
"xpack.ingestPipelines.tabs.outputTabTitle": "输出",
"xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsFieldLabel": "文档",
"xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsJsonError": "文档 JSON 无效。",
From 6dd558e59cc85ebf3d49c0278c2372aa978f276b Mon Sep 17 00:00:00 2001
From: Ryland Herrick
Date: Tue, 15 Sep 2020 15:30:41 -0500
Subject: [PATCH 3/9] [Security Solution][Detections] Integration test for
Editing a Rule (#77090)
* Add cypress test around editing a detection rule
Right now this just navigates around and verifies that the form is
correctly repopulated; next step will be to modify/asset some changes.
* Add assertions for editing a rule
We already were asserting on the population of the Edit form after
creation; this additionally makes modifications, saves them, and asserts
the resulting values on the Rule Details page.
* Remove unused imports
* Inline our cypress expectations
So that expectation failures are less obfuscated, the decision was
previously made to abstract user navigation into functions, but to leave
expectations directly within the test body.
* Dynamically assert Rule Details based on titles
Rule Details are unfortunately unstructured: they're an array of s
and s without any hierarchy. To address this, tests
were previously hardcoding the order of these fields, and assertions
were performed by querying for all s and then indexing with the
hardcoded number (e.g. ABOUT_FALSE_POSITIVES).
However, in addition to being unstructured, these fields are also
_dynamic_, and will be present/absent depending on the data of the given
rule. Thus, we started needing multiple orderings for the different
combinations of rule fields/rule types.
In the absence of refactoring how we build rule details, I'm introducing
a simple helper function to fetch the relevant by the corresponding
s text. This should be more robust to change and more declarative.
* Fix bad merge conflict
Lots of these variables no longer exist upstream and this new test
needed to be refactored.
Co-authored-by: Elastic Machine
---
.../alerts_detection_rules_custom.spec.ts | 194 ++++++++++++++----
.../alerts_detection_rules_eql.spec.ts | 64 +++---
.../alerts_detection_rules_ml.spec.ts | 81 ++++----
.../alerts_detection_rules_override.spec.ts | 112 +++++-----
.../alerts_detection_rules_threshold.spec.ts | 77 +++----
.../security_solution/cypress/objects/rule.ts | 25 +++
.../cypress/screens/alerts_detection_rules.ts | 2 +
.../cypress/screens/create_new_rule.ts | 29 +++
.../cypress/screens/edit_rule.ts | 7 +
.../cypress/screens/rule_details.ts | 61 ++----
.../cypress/tasks/alerts_detection_rules.ts | 6 +
.../cypress/tasks/create_new_rule.ts | 67 +++---
.../cypress/tasks/edit_rule.ts | 12 ++
.../rules/schedule_item_form/index.tsx | 2 +
.../detection_engine/rules/all/columns.tsx | 1 +
.../detection_engine/rules/edit/index.tsx | 5 +
.../es_archives/custom_rules/data.json.gz | Bin 2885 -> 2975 bytes
17 files changed, 443 insertions(+), 302 deletions(-)
create mode 100644 x-pack/plugins/security_solution/cypress/screens/edit_rule.ts
create mode 100644 x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts
index d9d9fde8fc8cc..17ff1dad79960 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { newRule } from '../objects/rule';
+import { newRule, existingRule } from '../objects/rule';
import {
CUSTOM_RULES_BTN,
@@ -16,26 +16,16 @@ import {
SHOWING_RULES_TEXT,
} from '../screens/alerts_detection_rules';
import {
- ABOUT_FALSE_POSITIVES,
ABOUT_INVESTIGATION_NOTES,
- ABOUT_MITRE,
- ABOUT_RISK,
ABOUT_RULE_DESCRIPTION,
- ABOUT_SEVERITY,
- ABOUT_STEP,
- ABOUT_TAGS,
- ABOUT_URLS,
- DEFINITION_CUSTOM_QUERY,
- DEFINITION_INDEX_PATTERNS,
- DEFINITION_TIMELINE,
- DEFINITION_STEP,
INVESTIGATION_NOTES_MARKDOWN,
INVESTIGATION_NOTES_TOGGLE,
RULE_ABOUT_DETAILS_HEADER_TOGGLE,
RULE_NAME_HEADER,
- SCHEDULE_LOOPBACK,
- SCHEDULE_RUNS,
- SCHEDULE_STEP,
+ getDescriptionForTitle,
+ ABOUT_DETAILS,
+ DEFINITION_DETAILS,
+ SCHEDULE_DETAILS,
} from '../screens/rule_details';
import {
@@ -53,18 +43,38 @@ import {
selectNumberOfRules,
waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded,
waitForRulesToBeLoaded,
+ editFirstRule,
} from '../tasks/alerts_detection_rules';
import {
createAndActivateRule,
fillAboutRuleAndContinue,
fillDefineCustomRuleWithImportedQueryAndContinue,
- expectDefineFormToRepopulateAndContinue,
- expectAboutFormToRepopulateAndContinue,
+ goToAboutStepTab,
+ goToScheduleStepTab,
+ goToActionsStepTab,
+ fillAboutRule,
} from '../tasks/create_new_rule';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../tasks/login';
import { DETECTIONS_URL } from '../urls/navigation';
+import {
+ ACTIONS_THROTTLE_INPUT,
+ CUSTOM_QUERY_INPUT,
+ DEFINE_INDEX_INPUT,
+ RULE_NAME_INPUT,
+ RULE_DESCRIPTION_INPUT,
+ TAGS_FIELD,
+ SEVERITY_DROPDOWN,
+ RISK_INPUT,
+ SCHEDULE_INTERVAL_AMOUNT_INPUT,
+ SCHEDULE_INTERVAL_UNITS_INPUT,
+ DEFINE_EDIT_BUTTON,
+ DEFINE_CONTINUE_BUTTON,
+ ABOUT_EDIT_BUTTON,
+ ABOUT_CONTINUE_BTN,
+} from '../screens/create_new_rule';
+import { saveEditedRule } from '../tasks/edit_rule';
describe('Detection rules, custom', () => {
before(() => {
@@ -84,8 +94,19 @@ describe('Detection rules, custom', () => {
goToCreateNewRule();
fillDefineCustomRuleWithImportedQueryAndContinue(newRule);
fillAboutRuleAndContinue(newRule);
- expectDefineFormToRepopulateAndContinue(newRule);
- expectAboutFormToRepopulateAndContinue(newRule);
+
+ // expect define step to repopulate
+ cy.get(DEFINE_EDIT_BUTTON).click();
+ cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', newRule.customQuery);
+ cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true });
+ cy.get(DEFINE_CONTINUE_BUTTON).should('not.exist');
+
+ // expect about step to populate
+ cy.get(ABOUT_EDIT_BUTTON).click();
+ cy.get(RULE_NAME_INPUT).invoke('val').should('eq', newRule.name);
+ cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true });
+ cy.get(ABOUT_CONTINUE_BTN).should('not.exist');
+
createAndActivateRule();
cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)');
@@ -142,32 +163,35 @@ describe('Detection rules, custom', () => {
cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newRule.name} Beta`);
cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newRule.description);
- cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', newRule.severity);
- cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', newRule.riskScore);
- cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_FALSE_POSITIVES)
- .invoke('text')
- .should('eql', expectedFalsePositives);
- cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre);
- cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags);
+ cy.get(ABOUT_DETAILS).within(() => {
+ getDescriptionForTitle('Severity').invoke('text').should('eql', newRule.severity);
+ getDescriptionForTitle('Risk score').invoke('text').should('eql', newRule.riskScore);
+ getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls);
+ getDescriptionForTitle('False positive examples')
+ .invoke('text')
+ .should('eql', expectedFalsePositives);
+ getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre);
+ getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags);
+ });
cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN);
- cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => {
- cy.wrap(patterns).each((pattern, index) => {
- cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]);
- });
+ cy.get(DEFINITION_DETAILS).within(() => {
+ getDescriptionForTitle('Index patterns')
+ .invoke('text')
+ .should('eql', expectedIndexPatterns.join(''));
+ getDescriptionForTitle('Custom query')
+ .invoke('text')
+ .should('eql', `${newRule.customQuery} `);
+ getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query');
+ getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None');
+ });
+
+ cy.get(SCHEDULE_DETAILS).within(() => {
+ getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m');
+ getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m');
});
- cy.get(DEFINITION_STEP)
- .eq(DEFINITION_CUSTOM_QUERY)
- .invoke('text')
- .should('eql', `${newRule.customQuery} `);
- cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None');
-
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m');
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m');
});
});
@@ -233,4 +257,94 @@ describe('Deletes custom rules', () => {
.should('eql', `Custom rules (${expectedNumberOfRulesAfterDeletion})`);
});
});
+
+ it('Allows a rule to be edited', () => {
+ editFirstRule();
+
+ // expect define step to populate
+ cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', existingRule.customQuery);
+ if (existingRule.index && existingRule.index.length > 0) {
+ cy.get(DEFINE_INDEX_INPUT).invoke('text').should('eq', existingRule.index.join(''));
+ }
+
+ goToAboutStepTab();
+
+ // expect about step to populate
+ cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name);
+ cy.get(RULE_DESCRIPTION_INPUT).invoke('text').should('eql', existingRule.description);
+ cy.get(TAGS_FIELD).invoke('text').should('eql', existingRule.tags.join(''));
+
+ cy.get(SEVERITY_DROPDOWN).invoke('text').should('eql', existingRule.severity);
+ cy.get(RISK_INPUT).invoke('val').should('eql', existingRule.riskScore);
+
+ goToScheduleStepTab();
+
+ // expect schedule step to populate
+ const intervalParts = existingRule.interval && existingRule.interval.match(/[0-9]+|[a-zA-Z]+/g);
+ if (intervalParts) {
+ const [amount, unit] = intervalParts;
+ cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', amount);
+ cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', unit);
+ } else {
+ throw new Error('Cannot assert scheduling info on a rule without an interval');
+ }
+
+ goToActionsStepTab();
+
+ cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions');
+
+ goToAboutStepTab();
+
+ const editedRule = {
+ ...existingRule,
+ severity: 'Medium',
+ description: 'Edited Rule description',
+ };
+
+ fillAboutRule(editedRule);
+ saveEditedRule();
+
+ const expectedTags = editedRule.tags.join('');
+ const expectedIndexPatterns =
+ editedRule.index && editedRule.index.length
+ ? editedRule.index
+ : [
+ 'apm-*-transaction*',
+ 'auditbeat-*',
+ 'endgame-*',
+ 'filebeat-*',
+ 'logs-*',
+ 'packetbeat-*',
+ 'winlogbeat-*',
+ ];
+
+ cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${editedRule.name} Beta`);
+
+ cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', editedRule.description);
+ cy.get(ABOUT_DETAILS).within(() => {
+ getDescriptionForTitle('Severity').invoke('text').should('eql', editedRule.severity);
+ getDescriptionForTitle('Risk score').invoke('text').should('eql', editedRule.riskScore);
+ getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags);
+ });
+
+ cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
+ cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', editedRule.note);
+
+ cy.get(DEFINITION_DETAILS).within(() => {
+ getDescriptionForTitle('Index patterns')
+ .invoke('text')
+ .should('eql', expectedIndexPatterns.join(''));
+ getDescriptionForTitle('Custom query')
+ .invoke('text')
+ .should('eql', `${editedRule.customQuery} `);
+ getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query');
+ getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None');
+ });
+
+ if (editedRule.interval) {
+ cy.get(SCHEDULE_DETAILS).within(() => {
+ getDescriptionForTitle('Runs every').invoke('text').should('eql', editedRule.interval);
+ });
+ }
+ });
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts
index c65cd8406099a..76871929fe050 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts
@@ -15,26 +15,16 @@ import {
SEVERITY,
} from '../screens/alerts_detection_rules';
import {
- ABOUT_FALSE_POSITIVES,
+ ABOUT_DETAILS,
ABOUT_INVESTIGATION_NOTES,
- ABOUT_MITRE,
- ABOUT_RISK,
ABOUT_RULE_DESCRIPTION,
- ABOUT_SEVERITY,
- ABOUT_STEP,
- ABOUT_TAGS,
- ABOUT_URLS,
- DEFINITION_CUSTOM_QUERY,
- DEFINITION_INDEX_PATTERNS,
- DEFINITION_TIMELINE,
- DEFINITION_STEP,
+ DEFINITION_DETAILS,
+ getDescriptionForTitle,
INVESTIGATION_NOTES_MARKDOWN,
INVESTIGATION_NOTES_TOGGLE,
RULE_ABOUT_DETAILS_HEADER_TOGGLE,
RULE_NAME_HEADER,
- SCHEDULE_LOOPBACK,
- SCHEDULE_RUNS,
- SCHEDULE_STEP,
+ SCHEDULE_DETAILS,
} from '../screens/rule_details';
import {
@@ -136,32 +126,34 @@ describe('Detection rules, EQL', () => {
cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${eqlRule.name} Beta`);
cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', eqlRule.description);
- cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', eqlRule.severity);
- cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', eqlRule.riskScore);
- cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_FALSE_POSITIVES)
- .invoke('text')
- .should('eql', expectedFalsePositives);
- cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre);
- cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags);
+ cy.get(ABOUT_DETAILS).within(() => {
+ getDescriptionForTitle('Severity').invoke('text').should('eql', eqlRule.severity);
+ getDescriptionForTitle('Risk score').invoke('text').should('eql', eqlRule.riskScore);
+ getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls);
+ getDescriptionForTitle('False positive examples')
+ .invoke('text')
+ .should('eql', expectedFalsePositives);
+ getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre);
+ getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags);
+ });
cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN);
- cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => {
- cy.wrap(patterns).each((pattern, index) => {
- cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]);
- });
+ cy.get(DEFINITION_DETAILS).within(() => {
+ getDescriptionForTitle('Index patterns')
+ .invoke('text')
+ .should('eql', expectedIndexPatterns.join(''));
+ getDescriptionForTitle('Custom query')
+ .invoke('text')
+ .should('eql', `${eqlRule.customQuery} `);
+ getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Event Correlation');
+ getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None');
+ });
+
+ cy.get(SCHEDULE_DETAILS).within(() => {
+ getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m');
+ getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m');
});
- cy.get(DEFINITION_STEP)
- .eq(DEFINITION_CUSTOM_QUERY)
- .invoke('text')
- .should('eql', `${eqlRule.customQuery} `);
- cy.get(DEFINITION_STEP).eq(2).invoke('text').should('eql', 'Event Correlation');
- cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None');
-
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m');
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m');
});
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts
index b6b30ef550eb1..47e49d48e2aec 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts
@@ -16,24 +16,14 @@ import {
SEVERITY,
} from '../screens/alerts_detection_rules';
import {
- ABOUT_FALSE_POSITIVES,
- ABOUT_MITRE,
- ABOUT_RISK,
ABOUT_RULE_DESCRIPTION,
- ABOUT_SEVERITY,
- ABOUT_STEP,
- ABOUT_TAGS,
- ABOUT_URLS,
- ANOMALY_SCORE,
- DEFINITION_TIMELINE,
- DEFINITION_STEP,
MACHINE_LEARNING_JOB_ID,
MACHINE_LEARNING_JOB_STATUS,
RULE_NAME_HEADER,
- SCHEDULE_LOOPBACK,
- SCHEDULE_RUNS,
- SCHEDULE_STEP,
- RULE_TYPE,
+ getDescriptionForTitle,
+ ABOUT_DETAILS,
+ DEFINITION_DETAILS,
+ SCHEDULE_DETAILS,
} from '../screens/rule_details';
import {
@@ -126,36 +116,37 @@ describe('Detection rules, machine learning', () => {
cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${machineLearningRule.name} Beta`);
cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', machineLearningRule.description);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_SEVERITY)
- .invoke('text')
- .should('eql', machineLearningRule.severity);
- cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', machineLearningRule.riskScore);
- cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_FALSE_POSITIVES)
- .invoke('text')
- .should('eql', expectedFalsePositives);
- cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre);
- cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags);
-
- cy.get(DEFINITION_STEP).eq(RULE_TYPE).invoke('text').should('eql', 'Machine Learning');
- cy.get(DEFINITION_STEP)
- .eq(ANOMALY_SCORE)
- .invoke('text')
- .should('eql', machineLearningRule.anomalyScoreThreshold);
- cy.get(DEFINITION_STEP)
- .get(MACHINE_LEARNING_JOB_STATUS)
- .invoke('text')
- .should('eql', 'Stopped');
- cy.get(DEFINITION_STEP)
- .get(MACHINE_LEARNING_JOB_ID)
- .invoke('text')
- .should('eql', machineLearningRule.machineLearningJob);
-
- cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None');
-
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m');
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m');
+ cy.get(ABOUT_DETAILS).within(() => {
+ getDescriptionForTitle('Severity').invoke('text').should('eql', machineLearningRule.severity);
+ getDescriptionForTitle('Risk score')
+ .invoke('text')
+ .should('eql', machineLearningRule.riskScore);
+ getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls);
+ getDescriptionForTitle('False positive examples')
+ .invoke('text')
+ .should('eql', expectedFalsePositives);
+ getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre);
+ getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags);
+ });
+
+ cy.get(DEFINITION_DETAILS).within(() => {
+ getDescriptionForTitle('Anomaly score')
+ .invoke('text')
+ .should('eql', machineLearningRule.anomalyScoreThreshold);
+ getDescriptionForTitle('Anomaly score')
+ .invoke('text')
+ .should('eql', machineLearningRule.anomalyScoreThreshold);
+ getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Machine Learning');
+ getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None');
+ cy.get(MACHINE_LEARNING_JOB_STATUS).invoke('text').should('eql', 'Stopped');
+ cy.get(MACHINE_LEARNING_JOB_ID)
+ .invoke('text')
+ .should('eql', machineLearningRule.machineLearningJob);
+ });
+
+ cy.get(SCHEDULE_DETAILS).within(() => {
+ getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m');
+ getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m');
+ });
});
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts
index e3526c63e2310..4edf5e1866087 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts
@@ -16,29 +16,17 @@ import {
} from '../screens/alerts_detection_rules';
import {
ABOUT_INVESTIGATION_NOTES,
- ABOUT_OVERRIDE_FALSE_POSITIVES,
- ABOUT_OVERRIDE_MITRE,
- ABOUT_OVERRIDE_NAME_OVERRIDE,
- ABOUT_OVERRIDE_RISK,
- ABOUT_OVERRIDE_RISK_OVERRIDE,
- ABOUT_OVERRIDE_SEVERITY_OVERRIDE,
- ABOUT_OVERRIDE_TAGS,
- ABOUT_OVERRIDE_TIMESTAMP_OVERRIDE,
- ABOUT_OVERRIDE_URLS,
ABOUT_RULE_DESCRIPTION,
- ABOUT_SEVERITY,
- ABOUT_STEP,
- DEFINITION_CUSTOM_QUERY,
- DEFINITION_INDEX_PATTERNS,
- DEFINITION_TIMELINE,
- DEFINITION_STEP,
INVESTIGATION_NOTES_MARKDOWN,
INVESTIGATION_NOTES_TOGGLE,
RULE_ABOUT_DETAILS_HEADER_TOGGLE,
RULE_NAME_HEADER,
- SCHEDULE_LOOPBACK,
- SCHEDULE_RUNS,
- SCHEDULE_STEP,
+ ABOUT_DETAILS,
+ getDescriptionForTitle,
+ DEFINITION_DETAILS,
+ SCHEDULE_DETAILS,
+ DETAILS_TITLE,
+ DETAILS_DESCRIPTION,
} from '../screens/rule_details';
import {
@@ -141,56 +129,56 @@ describe('Detection rules, override', () => {
const expectedOverrideSeverities = ['Low', 'Medium', 'High', 'Critical'];
- cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', newOverrideRule.severity);
- newOverrideRule.severityOverride.forEach((severity, i) => {
- cy.get(ABOUT_STEP)
- .eq(ABOUT_OVERRIDE_SEVERITY_OVERRIDE + i)
+ cy.get(ABOUT_DETAILS).within(() => {
+ getDescriptionForTitle('Severity').invoke('text').should('eql', newOverrideRule.severity);
+ getDescriptionForTitle('Risk score').invoke('text').should('eql', newOverrideRule.riskScore);
+ getDescriptionForTitle('Risk score override')
.invoke('text')
- .should(
- 'eql',
- `${severity.sourceField}:${severity.sourceValue}${expectedOverrideSeverities[i]}`
- );
+ .should('eql', `${newOverrideRule.riskOverride}signal.rule.risk_score`);
+ getDescriptionForTitle('Rule name override')
+ .invoke('text')
+ .should('eql', newOverrideRule.nameOverride);
+ getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls);
+ getDescriptionForTitle('False positive examples')
+ .invoke('text')
+ .should('eql', expectedFalsePositives);
+ getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre);
+ getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags);
+ getDescriptionForTitle('Timestamp override')
+ .invoke('text')
+ .should('eql', newOverrideRule.timestampOverride);
+ cy.contains(DETAILS_TITLE, 'Severity override')
+ .invoke('index', DETAILS_TITLE) // get index relative to other titles, not all siblings
+ .then((severityOverrideIndex) => {
+ newOverrideRule.severityOverride.forEach((severity, i) => {
+ cy.get(DETAILS_DESCRIPTION)
+ .eq(severityOverrideIndex + i)
+ .invoke('text')
+ .should(
+ 'eql',
+ `${severity.sourceField}:${severity.sourceValue}${expectedOverrideSeverities[i]}`
+ );
+ });
+ });
});
- cy.get(ABOUT_STEP)
- .eq(ABOUT_OVERRIDE_RISK)
- .invoke('text')
- .should('eql', newOverrideRule.riskScore);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_OVERRIDE_RISK_OVERRIDE)
- .invoke('text')
- .should('eql', `${newOverrideRule.riskOverride}signal.rule.risk_score`);
- cy.get(ABOUT_STEP).eq(ABOUT_OVERRIDE_URLS).invoke('text').should('eql', expectedUrls);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_OVERRIDE_FALSE_POSITIVES)
- .invoke('text')
- .should('eql', expectedFalsePositives);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_OVERRIDE_NAME_OVERRIDE)
- .invoke('text')
- .should('eql', newOverrideRule.nameOverride);
- cy.get(ABOUT_STEP).eq(ABOUT_OVERRIDE_MITRE).invoke('text').should('eql', expectedMitre);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_OVERRIDE_TIMESTAMP_OVERRIDE)
- .invoke('text')
- .should('eql', newOverrideRule.timestampOverride);
- cy.get(ABOUT_STEP).eq(ABOUT_OVERRIDE_TAGS).invoke('text').should('eql', expectedTags);
-
cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN);
- cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => {
- cy.wrap(patterns).each((pattern, index) => {
- cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]);
- });
+ cy.get(DEFINITION_DETAILS).within(() => {
+ getDescriptionForTitle('Index patterns')
+ .invoke('text')
+ .should('eql', expectedIndexPatterns.join(''));
+ getDescriptionForTitle('Custom query')
+ .invoke('text')
+ .should('eql', `${newOverrideRule.customQuery} `);
+ getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query');
+ getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None');
+ });
+
+ cy.get(SCHEDULE_DETAILS).within(() => {
+ getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m');
+ getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m');
});
- cy.get(DEFINITION_STEP)
- .eq(DEFINITION_CUSTOM_QUERY)
- .invoke('text')
- .should('eql', `${newOverrideRule.customQuery} `);
- cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None');
-
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m');
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m');
});
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts
index 10f9ebb5623df..00175ed3baeb8 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts
@@ -15,27 +15,16 @@ import {
SEVERITY,
} from '../screens/alerts_detection_rules';
import {
- ABOUT_FALSE_POSITIVES,
ABOUT_INVESTIGATION_NOTES,
- ABOUT_MITRE,
- ABOUT_RISK,
ABOUT_RULE_DESCRIPTION,
- ABOUT_SEVERITY,
- ABOUT_STEP,
- ABOUT_TAGS,
- ABOUT_URLS,
- DEFINITION_CUSTOM_QUERY,
- DEFINITION_INDEX_PATTERNS,
- DEFINITION_THRESHOLD,
- DEFINITION_TIMELINE,
- DEFINITION_STEP,
INVESTIGATION_NOTES_MARKDOWN,
INVESTIGATION_NOTES_TOGGLE,
RULE_ABOUT_DETAILS_HEADER_TOGGLE,
RULE_NAME_HEADER,
- SCHEDULE_LOOPBACK,
- SCHEDULE_RUNS,
- SCHEDULE_STEP,
+ getDescriptionForTitle,
+ ABOUT_DETAILS,
+ DEFINITION_DETAILS,
+ SCHEDULE_DETAILS,
} from '../screens/rule_details';
import {
@@ -137,38 +126,40 @@ describe('Detection rules, threshold', () => {
cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newThresholdRule.name} Beta`);
cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newThresholdRule.description);
- cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', newThresholdRule.severity);
- cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', newThresholdRule.riskScore);
- cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_FALSE_POSITIVES)
- .invoke('text')
- .should('eql', expectedFalsePositives);
- cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre);
- cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags);
+ cy.get(ABOUT_DETAILS).within(() => {
+ getDescriptionForTitle('Severity').invoke('text').should('eql', newThresholdRule.severity);
+ getDescriptionForTitle('Risk score').invoke('text').should('eql', newThresholdRule.riskScore);
+ getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls);
+ getDescriptionForTitle('False positive examples')
+ .invoke('text')
+ .should('eql', expectedFalsePositives);
+ getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre);
+ getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags);
+ });
cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN);
- cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => {
- cy.wrap(patterns).each((pattern, index) => {
- cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]);
- });
+ cy.get(DEFINITION_DETAILS).within(() => {
+ getDescriptionForTitle('Index patterns')
+ .invoke('text')
+ .should('eql', expectedIndexPatterns.join(''));
+ getDescriptionForTitle('Custom query')
+ .invoke('text')
+ .should('eql', `${newThresholdRule.customQuery} `);
+ getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Threshold');
+ getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None');
+ getDescriptionForTitle('Threshold')
+ .invoke('text')
+ .should(
+ 'eql',
+ `Results aggregated by ${newThresholdRule.thresholdField} >= ${newThresholdRule.threshold}`
+ );
+ });
+
+ cy.get(SCHEDULE_DETAILS).within(() => {
+ getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m');
+ getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m');
});
- cy.get(DEFINITION_STEP)
- .eq(DEFINITION_CUSTOM_QUERY)
- .invoke('text')
- .should('eql', `${newThresholdRule.customQuery} `);
- cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None');
- cy.get(DEFINITION_STEP)
- .eq(DEFINITION_THRESHOLD)
- .invoke('text')
- .should(
- 'eql',
- `Results aggregated by ${newThresholdRule.thresholdField} >= ${newThresholdRule.threshold}`
- );
-
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m');
- cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m');
});
});
diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts
index 0624606fe8481..2a5c60815f450 100644
--- a/x-pack/plugins/security_solution/cypress/objects/rule.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts
@@ -27,6 +27,8 @@ export interface CustomRule {
customQuery: string;
name: string;
description: string;
+ index?: string[];
+ interval?: string;
severity: string;
riskScore: string;
tags: string[];
@@ -109,6 +111,29 @@ export const newRule: CustomRule = {
timelineId: '0162c130-78be-11ea-9718-118a926974a4',
};
+export const existingRule: CustomRule = {
+ customQuery: 'host.name:*',
+ name: 'Rule 1',
+ description: 'Description for Rule 1',
+ index: [
+ 'apm-*-transaction*',
+ 'auditbeat-*',
+ 'endgame-*',
+ 'filebeat-*',
+ 'packetbeat-*',
+ 'winlogbeat-*',
+ ],
+ interval: '4m',
+ severity: 'High',
+ riskScore: '19',
+ tags: ['rule1'],
+ referenceUrls: [],
+ falsePositivesExamples: [],
+ mitre: [],
+ note: 'This is my note',
+ timelineId: '',
+};
+
export const newOverrideRule: OverrideRule = {
customQuery: 'host.name:*',
name: 'New Rule Test',
diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts
index a41b8296f83e4..14f5383939a94 100644
--- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts
@@ -14,6 +14,8 @@ export const CUSTOM_RULES_BTN = '[data-test-subj="show-custom-rules-filter-butto
export const DELETE_RULE_ACTION_BTN = '[data-test-subj="deleteRuleAction"]';
+export const EDIT_RULE_ACTION_BTN = '[data-test-subj="editRuleAction"]';
+
export const DELETE_RULE_BULK_BTN = '[data-test-subj="deleteRuleBulk"]';
export const ELASTIC_RULES_BTN = '[data-test-subj="show-elastic-rules-filter-button"]';
diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts
index 1c25ed88c3bee..dda371126d5aa 100644
--- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts
@@ -8,6 +8,13 @@ export const ABOUT_CONTINUE_BTN = '[data-test-subj="about-continue"]';
export const ABOUT_EDIT_BUTTON = '[data-test-subj="edit-about-rule"]';
+export const ABOUT_EDIT_TAB = '[data-test-subj="edit-rule-about-tab"]';
+
+export const ACTIONS_EDIT_TAB = '[data-test-subj="edit-rule-actions-tab"]';
+
+export const ACTIONS_THROTTLE_INPUT =
+ '[data-test-subj="stepRuleActions"] [data-test-subj="select"]';
+
export const ADD_FALSE_POSITIVE_BTN =
'[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text';
@@ -30,6 +37,11 @@ export const DEFINE_CONTINUE_BUTTON = '[data-test-subj="define-continue"]';
export const DEFINE_EDIT_BUTTON = '[data-test-subj="edit-define-rule"]';
+export const DEFINE_EDIT_TAB = '[data-test-subj="edit-rule-define-tab"]';
+
+export const DEFINE_INDEX_INPUT =
+ '[data-test-subj="detectionEngineStepDefineRuleIndices"] [data-test-subj="input"]';
+
export const EQL_TYPE = '[data-test-subj="eqlRuleType"]';
export const EQL_QUERY_INPUT = '[data-test-subj="eqlQueryBarTextInput"]';
@@ -81,6 +93,20 @@ export const RULE_TIMESTAMP_OVERRIDE =
export const SCHEDULE_CONTINUE_BUTTON = '[data-test-subj="schedule-continue"]';
+export const SCHEDULE_EDIT_TAB = '[data-test-subj="edit-rule-schedule-tab"]';
+
+export const SCHEDULE_INTERVAL_AMOUNT_INPUT =
+ '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="schedule-amount-input"]';
+
+export const SCHEDULE_INTERVAL_UNITS_INPUT =
+ '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="schedule-units-input"]';
+
+export const SCHEDULE_LOOKBACK_AMOUNT_INPUT =
+ '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-amount-input"]';
+
+export const SCHEDULE_LOOKBACK_UNITS_INPUT =
+ '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-units-input"]';
+
export const SEVERITY_DROPDOWN =
'[data-test-subj="detectionEngineStepAboutRuleSeverity"] [data-test-subj="select"]';
@@ -88,6 +114,9 @@ export const SEVERITY_MAPPING_OVERRIDE_OPTION = '#severity-mapping-override';
export const SEVERITY_OVERRIDE_ROW = '[data-test-subj="severityOverrideRow"]';
+export const TAGS_FIELD =
+ '[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxInput"]';
+
export const TAGS_INPUT =
'[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxSearchInput"]';
diff --git a/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts b/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts
new file mode 100644
index 0000000000000..1bf0ff34ebd94
--- /dev/null
+++ b/x-pack/plugins/security_solution/cypress/screens/edit_rule.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 const EDIT_SUBMIT_BUTTON = '[data-test-subj="ruleEditSubmitButton"]';
diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts
index b221709966943..98fc7b06a9908 100644
--- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts
@@ -4,55 +4,21 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export const ABOUT_FALSE_POSITIVES = 3;
+export const getDescriptionForTitle = (title: string) =>
+ cy.get(DETAILS_TITLE).contains(title).next(DETAILS_DESCRIPTION);
-export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]';
-
-export const ABOUT_MITRE = 4;
-
-export const ABOUT_OVERRIDE_FALSE_POSITIVES = 8;
-
-export const ABOUT_OVERRIDE_MITRE = 10;
-
-export const ABOUT_OVERRIDE_NAME_OVERRIDE = 9;
-
-export const ABOUT_OVERRIDE_RISK = 5;
+export const DETAILS_DESCRIPTION = '.euiDescriptionList__description';
+export const DETAILS_TITLE = '.euiDescriptionList__title';
-export const ABOUT_OVERRIDE_RISK_OVERRIDE = 6;
-
-export const ABOUT_OVERRIDE_SEVERITY_OVERRIDE = 1;
-
-export const ABOUT_OVERRIDE_TAGS = 12;
-
-export const ABOUT_OVERRIDE_TIMESTAMP_OVERRIDE = 11;
-
-export const ABOUT_OVERRIDE_URLS = 7;
+export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]';
export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]';
-export const ABOUT_RISK = 1;
+export const ABOUT_DETAILS =
+ '[data-test-subj="aboutRule"] [data-test-subj="listItemColumnStepRuleDescription"]';
-export const ABOUT_SEVERITY = 0;
-
-export const ABOUT_STEP = '[data-test-subj="aboutRule"] .euiDescriptionList__description';
-
-export const ABOUT_TAGS = 5;
-
-export const ABOUT_URLS = 2;
-
-export const ANOMALY_SCORE = 1;
-
-export const DEFINITION_CUSTOM_QUERY = 1;
-
-export const DEFINITION_THRESHOLD = 4;
-
-export const DEFINITION_TIMELINE = 3;
-
-export const DEFINITION_INDEX_PATTERNS =
- '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description .euiBadge__text';
-
-export const DEFINITION_STEP =
- '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description';
+export const DEFINITION_DETAILS =
+ '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"]';
export const INVESTIGATION_NOTES_MARKDOWN = 'test markdown';
@@ -60,16 +26,13 @@ export const INVESTIGATION_NOTES_TOGGLE = 1;
export const MACHINE_LEARNING_JOB_ID = '[data-test-subj="machineLearningJobId"]';
-export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobStatus" ]';
+export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobStatus"]';
export const RULE_ABOUT_DETAILS_HEADER_TOGGLE = '[data-test-subj="stepAboutDetailsToggle"]';
export const RULE_NAME_HEADER = '[data-test-subj="header-page-title"]';
-export const RULE_TYPE = 0;
+export const SCHEDULE_DETAILS =
+ '[data-test-subj=schedule] [data-test-subj="listItemColumnStepRuleDescription"]';
export const SCHEDULE_STEP = '[data-test-subj="schedule"] .euiDescriptionList__description';
-
-export const SCHEDULE_RUNS = 0;
-
-export const SCHEDULE_LOOPBACK = 1;
diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts
index 5ec5bb97250db..c530594508f95 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts
@@ -24,6 +24,7 @@ import {
SORT_RULES_BTN,
THREE_HUNDRED_ROWS,
EXPORT_ACTION_BTN,
+ EDIT_RULE_ACTION_BTN,
} from '../screens/alerts_detection_rules';
export const activateRule = (rulePosition: number) => {
@@ -35,6 +36,11 @@ export const changeToThreeHundredRowsPerPage = () => {
cy.get(THREE_HUNDRED_ROWS).click();
};
+export const editFirstRule = () => {
+ cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true });
+ cy.get(EDIT_RULE_ACTION_BTN).click();
+};
+
export const deleteFirstRule = () => {
cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true });
cy.get(DELETE_RULE_ACTION_BTN).click();
diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
index f26a77171462c..0daff52de7063 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
@@ -13,14 +13,17 @@ import {
} from '../objects/rule';
import {
ABOUT_CONTINUE_BTN,
- ANOMALY_THRESHOLD_INPUT,
+ ABOUT_EDIT_TAB,
+ ACTIONS_EDIT_TAB,
ADD_FALSE_POSITIVE_BTN,
ADD_REFERENCE_URL_BTN,
ADVANCED_SETTINGS_BTN,
+ ANOMALY_THRESHOLD_INPUT,
COMBO_BOX_INPUT,
CREATE_AND_ACTIVATE_BTN,
CUSTOM_QUERY_INPUT,
DEFINE_CONTINUE_BUTTON,
+ DEFINE_EDIT_TAB,
FALSE_POSITIVES_INPUT,
IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK,
INPUT,
@@ -32,8 +35,8 @@ import {
MITRE_TACTIC,
MITRE_TACTIC_DROPDOWN,
MITRE_TECHNIQUES_INPUT,
- RISK_INPUT,
REFERENCE_URLS_INPUT,
+ RISK_INPUT,
RISK_MAPPING_OVERRIDE_OPTION,
RISK_OVERRIDE,
RULE_DESCRIPTION_INPUT,
@@ -41,6 +44,7 @@ import {
RULE_NAME_OVERRIDE,
RULE_TIMESTAMP_OVERRIDE,
SCHEDULE_CONTINUE_BUTTON,
+ SCHEDULE_EDIT_TAB,
SEVERITY_DROPDOWN,
SEVERITY_MAPPING_OVERRIDE_OPTION,
SEVERITY_OVERRIDE_ROW,
@@ -48,8 +52,6 @@ import {
THRESHOLD_FIELD_SELECTION,
THRESHOLD_INPUT_AREA,
THRESHOLD_TYPE,
- DEFINE_EDIT_BUTTON,
- ABOUT_EDIT_BUTTON,
EQL_TYPE,
EQL_QUERY_INPUT,
} from '../screens/create_new_rule';
@@ -61,11 +63,9 @@ export const createAndActivateRule = () => {
cy.get(CREATE_AND_ACTIVATE_BTN).should('not.exist');
};
-export const fillAboutRuleAndContinue = (
- rule: CustomRule | MachineLearningRule | ThresholdRule
-) => {
- cy.get(RULE_NAME_INPUT).type(rule.name, { force: true });
- cy.get(RULE_DESCRIPTION_INPUT).type(rule.description, { force: true });
+export const fillAboutRule = (rule: CustomRule | MachineLearningRule | ThresholdRule) => {
+ cy.get(RULE_NAME_INPUT).clear({ force: true }).type(rule.name, { force: true });
+ cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(rule.description, { force: true });
cy.get(SEVERITY_DROPDOWN).click({ force: true });
cy.get(`#${rule.severity.toLowerCase()}`).click();
@@ -79,12 +79,15 @@ export const fillAboutRuleAndContinue = (
cy.get(ADVANCED_SETTINGS_BTN).click({ force: true });
rule.referenceUrls.forEach((url, index) => {
- cy.get(REFERENCE_URLS_INPUT).eq(index).type(url, { force: true });
+ cy.get(REFERENCE_URLS_INPUT).eq(index).clear({ force: true }).type(url, { force: true });
cy.get(ADD_REFERENCE_URL_BTN).click({ force: true });
});
rule.falsePositivesExamples.forEach((falsePositive, index) => {
- cy.get(FALSE_POSITIVES_INPUT).eq(index).type(falsePositive, { force: true });
+ cy.get(FALSE_POSITIVES_INPUT)
+ .eq(index)
+ .clear({ force: true })
+ .type(falsePositive, { force: true });
cy.get(ADD_FALSE_POSITIVE_BTN).click({ force: true });
});
@@ -93,14 +96,22 @@ export const fillAboutRuleAndContinue = (
cy.contains(MITRE_TACTIC, mitre.tactic).click();
mitre.techniques.forEach((technique) => {
- cy.get(MITRE_TECHNIQUES_INPUT).eq(index).type(`${technique}{enter}`, { force: true });
+ cy.get(MITRE_TECHNIQUES_INPUT)
+ .eq(index)
+ .clear({ force: true })
+ .type(`${technique}{enter}`, { force: true });
});
cy.get(MITRE_BTN).click({ force: true });
});
- cy.get(INVESTIGATION_NOTES_TEXTAREA).type(rule.note, { force: true });
+ cy.get(INVESTIGATION_NOTES_TEXTAREA).clear({ force: true }).type(rule.note, { force: true });
+};
+export const fillAboutRuleAndContinue = (
+ rule: CustomRule | MachineLearningRule | ThresholdRule
+) => {
+ fillAboutRule(rule);
cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true });
};
@@ -179,20 +190,6 @@ export const fillDefineCustomRuleWithImportedQueryAndContinue = (
cy.get(CUSTOM_QUERY_INPUT).should('not.exist');
};
-export const expectDefineFormToRepopulateAndContinue = (rule: CustomRule) => {
- cy.get(DEFINE_EDIT_BUTTON).click();
- cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', rule.customQuery);
- cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true });
- cy.get(DEFINE_CONTINUE_BUTTON).should('not.exist');
-};
-
-export const expectAboutFormToRepopulateAndContinue = (rule: CustomRule) => {
- cy.get(ABOUT_EDIT_BUTTON).click();
- cy.get(RULE_NAME_INPUT).invoke('val').should('eq', rule.name);
- cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true });
- cy.get(ABOUT_CONTINUE_BTN).should('not.exist');
-};
-
export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => {
const thresholdField = 0;
const threshold = 1;
@@ -230,6 +227,22 @@ export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRu
cy.get(MACHINE_LEARNING_DROPDOWN).should('not.exist');
};
+export const goToDefineStepTab = () => {
+ cy.get(DEFINE_EDIT_TAB).click({ force: true });
+};
+
+export const goToAboutStepTab = () => {
+ cy.get(ABOUT_EDIT_TAB).click({ force: true });
+};
+
+export const goToScheduleStepTab = () => {
+ cy.get(SCHEDULE_EDIT_TAB).click({ force: true });
+};
+
+export const goToActionsStepTab = () => {
+ cy.get(ACTIONS_EDIT_TAB).click({ force: true });
+};
+
export const selectMachineLearningRuleType = () => {
cy.get(MACHINE_LEARNING_TYPE).click({ force: true });
};
diff --git a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts
new file mode 100644
index 0000000000000..690a36058ec33
--- /dev/null
+++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts
@@ -0,0 +1,12 @@
+/*
+ * 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 { EDIT_SUBMIT_BUTTON } from '../screens/edit_rule';
+
+export const saveEditedRule = () => {
+ cy.get(EDIT_SUBMIT_BUTTON).should('exist').click({ force: true });
+ cy.get(EDIT_SUBMIT_BUTTON).should('not.exist');
+};
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx
index bb33767f4f5d5..867be1c1270e8 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx
@@ -145,6 +145,7 @@ export const ScheduleItem = ({
void
) => [
{
+ 'data-test-subj': 'editRuleAction',
description: i18n.EDIT_RULE_SETTINGS,
icon: 'controlsHorizontal',
name: i18n.EDIT_RULE_SETTINGS,
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx
index 5f4fd59669103..e2772af72da06 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx
@@ -118,6 +118,7 @@ const EditRulePageComponent: FC = () => {
const tabs = useMemo(
() => [
{
+ 'data-test-subj': 'edit-rule-define-tab',
id: RuleStep.defineRule,
name: ruleI18n.DEFINITION,
disabled: rule?.immutable,
@@ -140,6 +141,7 @@ const EditRulePageComponent: FC = () => {
),
},
{
+ 'data-test-subj': 'edit-rule-about-tab',
id: RuleStep.aboutRule,
name: ruleI18n.ABOUT,
disabled: rule?.immutable,
@@ -163,6 +165,7 @@ const EditRulePageComponent: FC = () => {
),
},
{
+ 'data-test-subj': 'edit-rule-schedule-tab',
id: RuleStep.scheduleRule,
name: ruleI18n.SCHEDULE,
disabled: rule?.immutable,
@@ -185,6 +188,7 @@ const EditRulePageComponent: FC = () => {
),
},
{
+ 'data-test-subj': 'edit-rule-actions-tab',
id: RuleStep.ruleActions,
name: ruleI18n.ACTIONS,
content: (
@@ -387,6 +391,7 @@ const EditRulePageComponent: FC = () => {
QOZ*BnXTWNFZHW&VWe+6$owN33eK|XhY
zJ^kRgT(z@
zOy+S{c4TSDMK|ECCh#I(e~^${kDe-si}cX)+<+GTeqZ>zjfcT=l=uKq1KkJXD^X9T+%_`U3`4KLSkNfhWX7L5#x^fLIbh$i(
zSLf1ULk&(w^=ncOFN~qvb>XlvTU=aUU0MMcct+b*Oli?1z9F>dliB4_wdMB?-RkAV
zkvH>-Q*c$COR(ma#p;FW*rUP(Ra|mp4~r7qy|oSNrkR+QB19UTgbM}!dF31YA9J=&aUE4}YktjWS
zg15u`!h}9@gB4GH$qX8|h_DrE6eeb?in!RMO5CoXiy^zajFCPfj|?}nT-!6(b&o2+
zMy?qlGl6at5r2`@f`jIrHF@(nz;W415~2-QQ)&gy=8VFj?}cHaQL6NtJ5Q2o^jBtl
z*9()j-Juoa=*L;)lA=r_FAB#|cz*BHViGZHhr?iEpvVrhJtoOAY1^R}glufL`K<*yOo?xWtJz!TFx!#pm!kdTltarQw&jxD!{60*trbe!4ZJmBHUzJ?
zs>!wi(fI}|ii7*&6v_vIJ|sA@$)>KpRuA{IV53X0z{`waG|Ml(LpZfayY8tGH@50KcN`8$ZoP_J
z((8X|W??Hxf{jz^k|LixA2^iO3fY?QomN*fd#m9ovEhAy#i<-816AckC__$F!5gjX
zc|KAy3Q`_Otg6B{@Xh>&kQz7vsq2Nv-c=cpFtl8AD`XFb-i(6Qvw{fO
z)-rKHi8k&+!Iucnf_frk&bm#Yi;s0~zGy<@5v>?~$u#@M
z?4EkQUvEmq_eB|+>uaOhc7o4f0s#wn^$nOHsz*ytEb}G`C>A%V!x*yjM3I3ZA+Se$
z__9RF({eYw;CQ@>R)-YK^qxtn9{a|I+o30UlF**zpePFIKQX;`Mvg}OhEb%#N68Uw
zgLw4Z?u?&T^=rb$XIA_Y$c4Ai@HxmazzXJ!LWi1-uw7;ii;qn
z|K0;}G0#I*k>r0H@r`-B3i0Gp!w>)zB|0`hoWwwEsEnc;kUnC-Q~l{04?pXr#%;A!
zwSv;{qSLdWqXc24doj7bJtMb+u2#0qnRzALO7qEK&zPfAL%AHDbmp>;>)hFyI5fOP
z!|F{21FteO&YLwuRB^R2)l6GFop^&YFE|bh;^jiEAIZg|u;rKOuwkj$yjmFEtc^jT
zcYRssJCbOSuDdh~ZS8n)>OdeA%csK05ig0u(!`rcUMaX-I6^II8s%lZQ}Ao;?s5zi
zl}^l?G|hffcG~TK>QaM#c$Jk^{&QYx@Grj9*y@)1=u3=uy2eOdQamd8JGeR6%3r_X
zvVhujh%9RJtWOU&)DP$KxnFW`zBdrFig~yEdT_yj00D4B
zM_N|;p7#d!R`&h}y*2>caPNBqPSgwp#UzG6ue>+daY^uU5WoLR0{Rg+R^X*K1ON#y
zF$qaRbcVPlH7_RCDnp%i7zKGw76Ih&K7hSvF67yXZNwF^+Ey0!f@mh`szvswm~4Gr
zO0QD1%J@INRhdaPb;AZ?)K8IRM?Oh5+{deHBnVc{d`FuL&l?qxJ}RLdZZ&DqiFB&6
z;E`Qj5Lf|%%&wm5^dR~mh)W12>6%~1j
z`z`L=Uran}6nhgV7WJ{j!J?q57{2>y>BETgg5Ng%Sj6GgJOC`*_>Iqp5C^o
zk_?;kctetbAXTEl3Zn}G$3THl$D#tr5JiWgfFS^q@L7`#C%)6+7oBRW&iDD!>8Lj;
zjc)_(n)J2hq=p7PrG3=AnqjwmDs{a{K`<{G9^n=(F)*9u#;`I}PtN)kQ|!pSqNI@H
zX^$({@ytWlV+Yx?!S%0n(!HGa&66PD4vW=}5wxLGghs`gmu6F?fosFgNUD_lS>!dP
z7PipQKpc*WT_EGyVyGVa$CYK-U5xqpRampd`gnOXy_9+N+zhVz;t+|I0;iuW-DviY
zBpIYU2lG&2RppcZa+1&eUW!%>A%A=&9MXNi5)L1lZ~&a4D5^ovss^NSmZ5O;tf~`+
z23Qh3YiZO$Y*^M;!r}D^2Swxz0F^kNkhN7jfkX%xq6!=jp&&w-iE8&uIJ_Lh?>*tL
z^5`ndi*GqMt%3nj5e$k7)tNys1Bs|G2uhfdGz5si8HNtj^9PGg+9g2Z{n94|aa(w1YW9}*ys+OBR4f{tr1N4>gKQ;uhi`s@lg2n19@Z36tz4m~1Y3r$K>xNg7inzX=Yjlld0
z29oz5-X08my+U!fnE&!C6z>_AyysgJj|L<3u+8(T1Q=crHF_-5Wd;!h8A;(WJ#Qmf
zSER3CGs`AlkT_Ca|c;~!sX8K#?dDpz|ohX5O%j*sMF=u`C`+YFy1u4rf=DdzM
VkQLM)fjOId&foFMcHvn-004vgz_(-RVC{TO3U06)
zCnf*z2ma`gq!zn0jPl4JreYE(n1m<-(k1?lN~&O*8o)YK2qk7ZNYc+IbeeP(hmMJT
zdxuR7uRw}
zjw@AEKJ4zgv-VjpY^q+B&ByKLr1|Hc_g8s3@oAVWmAaTkj+Jd;ab3pVqB_ylN?k~C
zM9F@Fz>WkYAOKmA)D8rBVB~=&Dynh0p+jM|MHuz5>Cyz3D2!>Q!iyui@4CFg<~Xg_
zkYk@k*ta;O+i~}?mzrxUc4-b~rH*ijJ+@*+yp&L8h0Z83rj6ZrlXNbPfH{$KLsuoQ
z$WEF%ZbZXJ+Ju3Zi1LGpAgh3_3UfTZ)I>e^m5?D#cJvfqPxwQKLhQ2zFaB5=G-(kN
zCo(xK!o4c$lRj74{R+Mqy1B~;TSK~KxQXMtL4R4dRS7Qk`!VjPPz_@m&Q`VH;c0tG
z)_e{Euv$qYyar=NtWIWmpDt&HynQPS3rU3aBCh@(*)9pCw_
zgeSt%rwD}FICi7eWsK96@wS72MPg!_`Bi=Ggw&=Xi7*@DFI%LZI`m^GjzcImnUaD2{K
zzNaO_D&Ee6eMtwi;X>I2nNYzR5L6o(0)!L`x}quk8zdSpM+Vfg-Y3HSb%;~w)_l#?
zRXQJTQdddsl|%B+hcz?Eojb{=&SYn!&&&{H6^Na9MDBa?FKY_gY{Yj0?m%1Y;&;W%
zv9Iu!eMJ|61TPoTzI{plzkT`g&(lK^zNOhtKd*{F1Oj$P0}Njhdn|@o4L;1+aY;Q+
z{Ed!OEu*kxNryq`;V2)?6A5RX;)@lw6#ZfiFz%;me$_KQYwe!X4{_oxyLaTuXdxXw
z8A%nnlApcG+wPeW*EZwMz+pN5i3T*cTtzPJ^`9)Wh{Muw;*h(fIHZq<2BoD!?oIes
ztBaYv*6{Si@ZQ1V3;;A}7?OfC1PlYd(z+h!BPXM*=AkMY26_b_Jv?GUL|w}F?{Jo1
zeSDF1u8y#JJcy#uF?rC)c>G<%qkCZ=j&M62krjj3kM744_$Gk>ti=*MFxgbby$pDXP&yCR2#QJ@y2;2K}8Xd5+^`5ZYfX_
zfetQT?2k8^?BhGxirAMhCw7EHqGRP3q!408!z&&=`%%6Y%pKRodx{8hztynCcm9A4
z{+m1I5rCWH1-a99?t;hAR}fvPzvuQxBbT02v+6{9imZvUn)|I$l1c=3Hk;b4Sz~
z2EXTuJS6bwp3YsEC>y#aLS*FH*v6r=?Di7IK2gti>&>Y6u4skk*3zi%JHbaVK~RK}
z@d`{Z*P~;lmU$I3j>T2x_=Vj()ir3V7(O9Bd|oQ#VYzEwa5P#(s{;;ZcF*KgPkiI4
z-{xm^n$lhbNKxeMpOW1>W6z}Fsa@p4hv}hgjd=WI?u{N-^-IFWM^^kC$Yt=3kP}FB
z0_^lipnU+yp&~+lfY84q{t)2c34n7gD=3eU$d4ueCg7QQ$nVc_@N;l{J#BX%a3qVm
ztN{JZ@cus|II>Nd`$+I{d^*QPRFkf^KLzm53XJYIx&dTn9L5Gg7j8)Bde
zh}edpTQ=g41WH_gmMNj1_1faPTBMw{obL6pj<2;$_IOrstiiw
zV5|lub~f|mTHLV9^Lo1w)>@tU2ALyV8bQB^FjRhmjwJH15uXLR|Ehl
zFA0rkN_2txeQsVtqE&%A?I>n>peYam@)W?{G8gjb#Ma`9L~RQTdqy;~Of9>^V!HKp
zIK4{oBICb(tLmrO)HNGOP(Q?u8;3OAaJN_2C}Rs}zM;)Y5DW|08kTUhrA0foxXRd;
zU0s$%8NroZJ=5vC=pB}nP(rgeXWV(=&>HO9oEFzhfqXozv6ip;=s2jPG7X(&4vsD=eWk5+i>gldZ@QpdCzAkQjy&vY
zHn=#qXy@$aqJO{`*ezDuHftfTi0q0tElqCpQ&JnWhiau1PU4`UHi?7xddgr}>_Cmw
zW&>k4+^@{b{%j;o&!d{F)JOBZn=?%^j{EHVLK$GCQUKP$+>a+eB*~!W0m>s?H1wbJ
zw~tsr;qaMo_+beL2y9(9Y<^a?5s$NM9q_ZNMFk#Usr;;Eat9%OCLG=>
z;h-zPhDcB13C&!@6Iek|poR=2gk%LF^92cq=Y#ms6AlZHZiteizB(9CblK*paGlwd
z2#6}WfRRcB)x?m>z_u+&e%4@sA;YpKWI9!Cvp{FE;g+J=G;B5-bspo?b*xM){n0G&
zRC7K;)-HwGu;p8JkF|YwKJ3=jn!Z0dtxwLOhvC3b#?|6c>!zknJwIr)3%z!?L?on2
z<=NbAgZ*YhnaSm9Tj|MIi+n6QOmwIts7by+QkYc2VlPk*Ute(JAYH1_S9E53dgfK3|&nd}-nXzBIuP
z+md9cP>^KB
Date: Tue, 15 Sep 2020 23:48:12 +0300
Subject: [PATCH 4/9] [Security Solutions][Cases - Timeline] Fix bug when
adding a timeline to a case (#76967)
Co-authored-by: Gloria Hornero
Co-authored-by: Elastic Machine
---
.../cypress/integration/cases.spec.ts | 4 +-
.../timeline_attach_to_case.spec.ts | 76 +
.../security_solution/cypress/objects/case.ts | 7 +-
.../cypress/objects/timeline.ts | 4 +
.../cypress/screens/all_cases.ts | 6 +
.../cypress/screens/create_new_case.ts | 3 +-
.../cypress/screens/timeline.ts | 11 +
.../cypress/tasks/create_new_case.ts | 12 +
.../security_solution/cypress/tasks/login.ts | 9 +
.../cypress/tasks/timeline.ts | 28 +
.../public/cases/components/__mock__/form.ts | 7 +
.../components/add_comment/index.test.tsx | 9 +-
.../cases/components/add_comment/index.tsx | 26 +-
.../cases/components/create/index.test.tsx | 15 +-
.../public/cases/components/create/index.tsx | 22 +-
.../user_action_tree/index.test.tsx | 69 +-
.../user_action_tree/user_action_markdown.tsx | 21 +-
.../insert_timeline_popover/index.test.tsx | 55 +-
.../insert_timeline_popover/index.tsx | 23 +-
.../use_insert_timeline.tsx | 40 +-
.../case_and_timeline/data.json.gz | Bin 0 -> 3687 bytes
.../case_and_timeline/mappings.json | 2616 +++++++++++++++++
22 files changed, 2941 insertions(+), 122 deletions(-)
create mode 100644 x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts
create mode 100644 x-pack/test/security_solution_cypress/es_archives/case_and_timeline/data.json.gz
create mode 100644 x-pack/test/security_solution_cypress/es_archives/case_and_timeline/mappings.json
diff --git a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts
index 9438c28f05fef..6194d6892d799 100644
--- a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts
@@ -40,7 +40,7 @@ import { TIMELINE_DESCRIPTION, TIMELINE_QUERY, TIMELINE_TITLE } from '../screens
import { goToCaseDetails, goToCreateNewCase } from '../tasks/all_cases';
import { openCaseTimeline } from '../tasks/case_details';
-import { backToCases, createNewCase } from '../tasks/create_new_case';
+import { backToCases, createNewCaseWithTimeline } from '../tasks/create_new_case';
import { loginAndWaitForPageWithoutDateRange } from '../tasks/login';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
@@ -58,7 +58,7 @@ describe('Cases', () => {
it('Creates a new case with timeline and opens the timeline', () => {
loginAndWaitForPageWithoutDateRange(CASES_URL);
goToCreateNewCase();
- createNewCase(case1);
+ createNewCaseWithTimeline(case1);
backToCases();
cy.get(ALL_CASES_PAGE_TITLE).should('have.text', 'Cases Beta');
diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts
new file mode 100644
index 0000000000000..6af4d174b9583
--- /dev/null
+++ b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts
@@ -0,0 +1,76 @@
+/*
+ * 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 { loginAndWaitForTimeline } from '../tasks/login';
+import {
+ attachTimelineToNewCase,
+ attachTimelineToExistingCase,
+ addNewCase,
+ selectCase,
+} from '../tasks/timeline';
+import { DESCRIPTION_INPUT } from '../screens/create_new_case';
+import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
+import { caseTimeline, TIMELINE_CASE_ID } from '../objects/case';
+
+describe('attach timeline to case', () => {
+ beforeEach(() => {
+ loginAndWaitForTimeline(caseTimeline.id);
+ });
+ context('without cases created', () => {
+ before(() => {
+ esArchiverLoad('timeline');
+ });
+
+ after(() => {
+ esArchiverUnload('timeline');
+ });
+
+ it('attach timeline to a new case', () => {
+ attachTimelineToNewCase();
+
+ cy.location('origin').then((origin) => {
+ cy.get(DESCRIPTION_INPUT).should(
+ 'have.text',
+ `[${caseTimeline.title}](${origin}/app/security/timelines?timeline=(id:'${caseTimeline.id}',isOpen:!t))`
+ );
+ });
+ });
+
+ it('attach timeline to an existing case with no case', () => {
+ attachTimelineToExistingCase();
+ addNewCase();
+
+ cy.location('origin').then((origin) => {
+ cy.get(DESCRIPTION_INPUT).should(
+ 'have.text',
+ `[${caseTimeline.title}](${origin}/app/security/timelines?timeline=(id:'${caseTimeline.id}',isOpen:!t))`
+ );
+ });
+ });
+ });
+
+ context('with cases created', () => {
+ before(() => {
+ esArchiverLoad('case_and_timeline');
+ });
+
+ after(() => {
+ esArchiverUnload('case_and_timeline');
+ });
+
+ it('attach timeline to an existing case', () => {
+ attachTimelineToExistingCase();
+ selectCase(TIMELINE_CASE_ID);
+
+ cy.location('origin').then((origin) => {
+ cy.get(DESCRIPTION_INPUT).should(
+ 'have.text',
+ `[${caseTimeline.title}](${origin}/app/security/timelines?timeline=(id:'${caseTimeline.id}',isOpen:!t))`
+ );
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/cypress/objects/case.ts b/x-pack/plugins/security_solution/cypress/objects/case.ts
index 12d3f925169af..084df31a604a3 100644
--- a/x-pack/plugins/security_solution/cypress/objects/case.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/case.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { Timeline } from './timeline';
+import { Timeline, TimelineWithId } from './timeline';
export interface TestCase {
name: string;
@@ -21,10 +21,11 @@ export interface Connector {
password: string;
}
-const caseTimeline: Timeline = {
+export const caseTimeline: TimelineWithId = {
title: 'SIEM test',
description: 'description',
query: 'host.name:*',
+ id: '0162c130-78be-11ea-9718-118a926974a4',
};
export const case1: TestCase = {
@@ -41,3 +42,5 @@ export const serviceNowConnector: Connector = {
username: 'Username Name',
password: 'password',
};
+
+export const TIMELINE_CASE_ID = '68248e00-f689-11ea-9ab2-59238b522856';
diff --git a/x-pack/plugins/security_solution/cypress/objects/timeline.ts b/x-pack/plugins/security_solution/cypress/objects/timeline.ts
index 060a1376b46ce..ff7e80e5661ad 100644
--- a/x-pack/plugins/security_solution/cypress/objects/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/timeline.ts
@@ -9,3 +9,7 @@ export interface Timeline {
description: string;
query: string;
}
+
+export interface TimelineWithId extends Timeline {
+ id: string;
+}
diff --git a/x-pack/plugins/security_solution/cypress/screens/all_cases.ts b/x-pack/plugins/security_solution/cypress/screens/all_cases.ts
index 4fa6b69eea7c3..dc0e764744f84 100644
--- a/x-pack/plugins/security_solution/cypress/screens/all_cases.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/all_cases.ts
@@ -4,6 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
+export const ALL_CASES_CASE = (id: string) => {
+ return `[data-test-subj="cases-table-row-${id}"]`;
+};
+
export const ALL_CASES_CLOSE_ACTION = '[data-test-subj="action-close"]';
export const ALL_CASES_CLOSED_CASES_COUNT = '[data-test-subj="closed-case-count"]';
@@ -14,6 +18,8 @@ export const ALL_CASES_COMMENTS_COUNT = '[data-test-subj="case-table-column-comm
export const ALL_CASES_CREATE_NEW_CASE_BTN = '[data-test-subj="createNewCaseBtn"]';
+export const ALL_CASES_CREATE_NEW_CASE_TABLE_BTN = '[data-test-subj="cases-table-add-case"]';
+
export const ALL_CASES_DELETE_ACTION = '[data-test-subj="action-delete"]';
export const ALL_CASES_NAME = '[data-test-subj="case-details-link"]';
diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_case.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_case.ts
index 6e2beb78fff19..9431c054d96a4 100644
--- a/x-pack/plugins/security_solution/cypress/screens/create_new_case.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/create_new_case.ts
@@ -6,8 +6,7 @@
export const BACK_TO_CASES_BTN = '[data-test-subj="backToCases"]';
-export const DESCRIPTION_INPUT =
- '[data-test-subj="caseDescription"] [data-test-subj="textAreaInput"]';
+export const DESCRIPTION_INPUT = '[data-test-subj="textAreaInput"]';
export const INSERT_TIMELINE_BTN = '[data-test-subj="insert-timeline-button"]';
diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts
index fd41cd63fc090..bcb64fc947feb 100644
--- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts
@@ -4,8 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/
+export const ATTACH_TIMELINE_TO_NEW_CASE_ICON = '[data-test-subj="attach-timeline-case"]';
+
+export const ATTACH_TIMELINE_TO_EXISTING_CASE_ICON =
+ '[data-test-subj="attach-timeline-existing-case"]';
+
export const BULK_ACTIONS = '[data-test-subj="utility-bar-action-button"]';
+export const CASE = (id: string) => {
+ return `[data-test-subj="cases-table-row-${id}"]`;
+};
+
export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]';
export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]';
@@ -25,6 +34,8 @@ export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name
export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]';
+export const OPEN_TIMELINE_ICON = '[data-test-subj="open-timeline-button"]';
+
export const PIN_EVENT = '[data-test-subj="pin"]';
export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]';
diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_case.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_case.ts
index b2cde23a8dce2..1d5d240c5c53d 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/create_new_case.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_case.ts
@@ -29,6 +29,18 @@ export const createNewCase = (newCase: TestCase) => {
});
cy.get(DESCRIPTION_INPUT).type(`${newCase.description} `, { force: true });
+ cy.get(SUBMIT_BTN).click({ force: true });
+ cy.get(LOADING_SPINNER).should('exist');
+ cy.get(LOADING_SPINNER).should('not.exist');
+};
+
+export const createNewCaseWithTimeline = (newCase: TestCase) => {
+ cy.get(TITLE_INPUT).type(newCase.name, { force: true });
+ newCase.tags.forEach((tag) => {
+ cy.get(TAGS_INPUT).type(`${tag}{enter}`, { force: true });
+ });
+ cy.get(DESCRIPTION_INPUT).type(`${newCase.description} `, { force: true });
+
cy.get(INSERT_TIMELINE_BTN).click({ force: true });
cy.get(TIMELINE_SEARCHBOX).type(`${newCase.timeline.title}{enter}`);
cy.get(TIMELINE).should('be.visible');
diff --git a/x-pack/plugins/security_solution/cypress/tasks/login.ts b/x-pack/plugins/security_solution/cypress/tasks/login.ts
index ca23a1defd4f5..65f821ec5bfb7 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/login.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/login.ts
@@ -5,6 +5,7 @@
*/
import * as yaml from 'js-yaml';
+import { TIMELINE_FLYOUT_BODY } from '../screens/timeline';
/**
* Credentials in the `kibana.dev.yml` config file will be used to authenticate
@@ -143,3 +144,11 @@ export const loginAndWaitForPageWithoutDateRange = (url: string) => {
cy.visit(url);
cy.get('[data-test-subj="headerGlobalNav"]', { timeout: 120000 });
};
+
+export const loginAndWaitForTimeline = (timelineId: string) => {
+ login();
+ cy.viewport('macbook-15');
+ cy.visit(`/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t)`);
+ cy.get('[data-test-subj="headerGlobalNav"]');
+ cy.get(TIMELINE_FLYOUT_BODY).should('be.visible');
+};
diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
index 6fb8bb5e29ae5..cd8b197fc4dec 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { ALL_CASES_CREATE_NEW_CASE_TABLE_BTN } from '../screens/all_cases';
import {
BULK_ACTIONS,
CLOSE_TIMELINE_BTN,
@@ -28,6 +29,10 @@ import {
TOGGLE_TIMELINE_EXPAND_EVENT,
REMOVE_COLUMN,
RESET_FIELDS,
+ ATTACH_TIMELINE_TO_NEW_CASE_ICON,
+ OPEN_TIMELINE_ICON,
+ ATTACH_TIMELINE_TO_EXISTING_CASE_ICON,
+ CASE,
} from '../screens/timeline';
import { drag, drop } from '../tasks/common';
@@ -44,6 +49,20 @@ export const addNameToTimeline = (name: string) => {
cy.get(TIMELINE_TITLE).should('have.attr', 'value', name);
};
+export const addNewCase = () => {
+ cy.get(ALL_CASES_CREATE_NEW_CASE_TABLE_BTN).click();
+};
+
+export const attachTimelineToNewCase = () => {
+ cy.get(TIMELINE_SETTINGS_ICON).click({ force: true });
+ cy.get(ATTACH_TIMELINE_TO_NEW_CASE_ICON).click({ force: true });
+};
+
+export const attachTimelineToExistingCase = () => {
+ cy.get(TIMELINE_SETTINGS_ICON).click({ force: true });
+ cy.get(ATTACH_TIMELINE_TO_EXISTING_CASE_ICON).click({ force: true });
+};
+
export const checkIdToggleField = () => {
cy.get(ID_HEADER_FIELD).should('not.exist');
@@ -85,6 +104,11 @@ export const openTimelineInspectButton = () => {
cy.get(TIMELINE_INSPECT_BUTTON).trigger('click', { force: true });
};
+export const openTimelineFromSettings = () => {
+ cy.get(TIMELINE_SETTINGS_ICON).click({ force: true });
+ cy.get(OPEN_TIMELINE_ICON).click({ force: true });
+};
+
export const openTimelineSettings = () => {
cy.get(TIMELINE_SETTINGS_ICON).trigger('click', { force: true });
};
@@ -132,6 +156,10 @@ export const resetFields = () => {
cy.get(RESET_FIELDS).click({ force: true });
};
+export const selectCase = (caseId: string) => {
+ cy.get(CASE(caseId)).click();
+};
+
export const waitForTimelinesPanelToBeLoaded = () => {
cy.get(TIMELINES_TABLE).should('exist');
};
diff --git a/x-pack/plugins/security_solution/public/cases/components/__mock__/form.ts b/x-pack/plugins/security_solution/public/cases/components/__mock__/form.ts
index 96c1217577ff2..87f8f46affb52 100644
--- a/x-pack/plugins/security_solution/public/cases/components/__mock__/form.ts
+++ b/x-pack/plugins/security_solution/public/cases/components/__mock__/form.ts
@@ -4,9 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form';
+import { useFormData } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data';
+
jest.mock(
'../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'
);
+jest.mock(
+ '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data'
+);
+
export const mockFormHook = {
isSubmitted: false,
isSubmitting: false,
@@ -41,3 +47,4 @@ export const getFormMock = (sampleData: any) => ({
});
export const useFormMock = useForm as jest.Mock;
+export const useFormDataMock = useFormData as jest.Mock;
diff --git a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx
index f697ce443f2c5..a800bd690f710 100644
--- a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx
@@ -15,6 +15,7 @@ import { Router, routeData, mockHistory, mockLocation } from '../__mock__/router
import { useInsertTimeline } from '../../../timelines/components/timeline/insert_timeline_popover/use_insert_timeline';
import { usePostComment } from '../../containers/use_post_comment';
import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form';
+import { useFormData } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data';
// we don't have the types for waitFor just yet, so using "as waitFor" until when we do
import { wait as waitFor } from '@testing-library/react';
@@ -23,10 +24,15 @@ jest.mock(
'../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'
);
+jest.mock(
+ '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data'
+);
+
jest.mock('../../../timelines/components/timeline/insert_timeline_popover/use_insert_timeline');
jest.mock('../../containers/use_post_comment');
-export const useFormMock = useForm as jest.Mock;
+const useFormMock = useForm as jest.Mock;
+const useFormDataMock = useFormData as jest.Mock;
const useInsertTimelineMock = useInsertTimeline as jest.Mock;
const usePostCommentMock = usePostComment as jest.Mock;
@@ -73,6 +79,7 @@ describe('AddComment ', () => {
useInsertTimelineMock.mockImplementation(() => defaultInsertTimeline);
usePostCommentMock.mockImplementation(() => defaultPostCommment);
useFormMock.mockImplementation(() => ({ form: formHookMock }));
+ useFormDataMock.mockImplementation(() => [{ comment: sampleData.comment }]);
jest.spyOn(routeData, 'useLocation').mockReturnValue(mockLocation);
});
diff --git a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx
index 87bd7bb247056..ef13c87a92dbb 100644
--- a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx
+++ b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx
@@ -14,7 +14,7 @@ import { Case } from '../../containers/types';
import { MarkdownEditorForm } from '../../../common/components/markdown_editor/form';
import { InsertTimelinePopover } from '../../../timelines/components/timeline/insert_timeline_popover';
import { useInsertTimeline } from '../../../timelines/components/timeline/insert_timeline_popover/use_insert_timeline';
-import { Form, useForm, UseField } from '../../../shared_imports';
+import { Form, useForm, UseField, useFormData } from '../../../shared_imports';
import * as i18n from './translations';
import { schema } from './schema';
@@ -46,23 +46,31 @@ export const AddComment = React.memo(
forwardRef(
({ caseId, disabled, showLoading = true, onCommentPosted, onCommentSaving }, ref) => {
const { isLoading, postComment } = usePostComment(caseId);
+
const { form } = useForm({
defaultValue: initialCommentValue,
options: { stripEmptyFields: false },
schema,
});
- const { getFormData, setFieldValue, reset, submit } = form;
- const { handleCursorChange, handleOnTimelineChange } = useInsertTimeline(
- form,
- 'comment'
+
+ const fieldName = 'comment';
+ const { setFieldValue, reset, submit } = form;
+ const [{ comment }] = useFormData({ form, watch: [fieldName] });
+
+ const onCommentChange = useCallback((newValue) => setFieldValue(fieldName, newValue), [
+ setFieldValue,
+ ]);
+
+ const { handleCursorChange, handleOnTimelineChange } = useInsertTimeline(
+ comment,
+ onCommentChange
);
const addQuote = useCallback(
(quote) => {
- const { comment } = getFormData();
- setFieldValue('comment', `${comment}${comment.length > 0 ? '\n\n' : ''}${quote}`);
+ setFieldValue(fieldName, `${comment}${comment.length > 0 ? '\n\n' : ''}${quote}`);
},
- [getFormData, setFieldValue]
+ [comment, setFieldValue]
);
useImperativeHandle(ref, () => ({
@@ -87,7 +95,7 @@ export const AddComment = React.memo(
{isLoading && showLoading && }