From 9c53b777cea09f30c48e14774e2c89508371390b Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:14:09 -0700 Subject: [PATCH] [Part 2] Simplify detector creation UX (#738) (#739) * removed review page; enhanced alerts page Signed-off-by: Amardeepsingh Siglani * updated tests Signed-off-by: Amardeepsingh Siglani * updated trigger details subheading to use selected data Signed-off-by: Amardeepsingh Siglani * updated define detector, overview pages Signed-off-by: Amardeepsingh Siglani * reverting cypress workflow Signed-off-by: Amardeepsingh Siglani * updated field mapping texts Signed-off-by: Amardeepsingh Siglani --------- Signed-off-by: Amardeepsingh Siglani (cherry picked from commit 8a7bf7dc12af912f1ac5e4c59104d42043256eb3) Co-authored-by: Amardeepsingh Siglani --- cypress/integration/1_detectors.spec.js | 55 +- .../AlertCondition/AlertConditionPanel.tsx | 319 +-- .../AlertConditionPanel.test.tsx.snap | 2034 +++++++++-------- .../containers/ConfigureAlerts.tsx | 46 +- .../ConfigureAlerts/utils/helpers.ts | 4 +- .../FieldMappingsTable.tsx | 11 +- .../containers/ConfigureFieldMapping.tsx | 348 +-- .../DetectionRules/DetectionRules.tsx | 3 - .../components/DetectorType/DetectorType.tsx | 15 +- .../containers/DefineDetector.tsx | 36 +- .../containers/ReviewAndCreate.tsx | 72 - .../containers/CreateDetector.tsx | 34 +- public/pages/CreateDetector/models/types.ts | 14 +- .../pages/CreateDetector/utils/constants.ts | 6 +- .../UpdateAlertConditions.tsx | 4 +- .../UpdateAlertConditions.test.tsx.snap | 1 + .../containers/Detectors/Detectors.tsx | 13 +- .../EditFieldMappings.test.tsx.snap | 101 +- .../GettingStarted/GettingStartedPopup.tsx | 20 - .../Overview/components/Widgets/Summary.tsx | 47 +- .../Overview/containers/Overview/Overview.tsx | 16 +- types/Detector.ts | 4 +- 22 files changed, 1658 insertions(+), 1545 deletions(-) delete mode 100644 public/pages/CreateDetector/components/ReviewAndCreate/containers/ReviewAndCreate.tsx diff --git a/cypress/integration/1_detectors.spec.js b/cypress/integration/1_detectors.spec.js index 71aed54df..cf68e69ae 100644 --- a/cypress/integration/1_detectors.spec.js +++ b/cypress/integration/1_detectors.spec.js @@ -128,7 +128,7 @@ const createDetector = (detectorName, dataSource, expectFailure) => { .click({ force: true, timeout: 5000 }) .then(() => cy.contains('.euiTable .euiTableRow', 'Dns')); - cy.getElementByText('.euiAccordion .euiTitle', 'Configure field mapping - optional'); + cy.getElementByText('.euiAccordion .euiTitle', 'Field mapping - optional'); cy.get('[aria-controls="mappedTitleFieldsAccordion"]').then(($btn) => { // first check if the accordion is expanded, if not than expand the accordion if ($btn && $btn[0] && $btn[0].getAttribute('aria-expanded') === 'false') { @@ -140,8 +140,9 @@ const createDetector = (detectorName, dataSource, expectFailure) => { getNextButton().click({ force: true }); // TEST ALERTS PAGE + // Open the trigger details accordion + cy.get('[data-test-subj="trigger-details-btn"]').click({ force: true }); cy.getElementByText('.euiTitle.euiTitle--medium', 'Set up alert triggers'); - cy.getInputByPlaceholder('Enter a name to describe the alert condition').type('test_trigger'); cy.getElementByTestSubject('alert-tags-combo-box') .type(`attack.defense_evasion{enter}`) .find('input') @@ -150,27 +151,8 @@ const createDetector = (detectorName, dataSource, expectFailure) => { cy.getFieldByLabel('Specify alert severity').selectComboboxItem('1 (Highest)'); - // go to review page - getNextButton().click({ force: true }); - - // TEST REVIEW AND CREATE PAGE - cy.getElementByText('.euiTitle', 'Review and create'); - cy.getElementByText('.euiTitle', 'Detector details'); - cy.getElementByText('.euiTitle', 'Field mapping'); - cy.getElementByText('.euiTitle', 'Alert triggers'); - - cy.validateDetailsItem('Detector name', detectorName); - cy.validateDetailsItem('Description', '-'); - cy.validateDetailsItem('Detector schedule', 'Every 1 minute'); - cy.validateDetailsItem('Detection rules', '14'); - cy.validateDetailsItem('Created at', '-'); - cy.validateDetailsItem('Last updated time', '-'); - cy.validateDetailsItem('Detector dashboard', 'Not available for this log type'); - - validateAlertPanel('test_trigger'); - - cy.intercept('POST', '/mappings').as('createMappingsRequest'); - cy.intercept('POST', '/detectors').as('createDetectorRequest'); + cy.intercept('POST', '/_plugins/_security_analytics/mappings').as('createMappingsRequest'); + cy.intercept('POST', '/_plugins/_security_analytics/detectors').as('createDetectorRequest'); // create the detector cy.getElementByText('button', 'Create').click({ force: true }); @@ -198,7 +180,7 @@ const createDetector = (detectorName, dataSource, expectFailure) => { cy.wait(5000); // waiting for the page to be reloaded after pushing detector id into route cy.getElementByText('button.euiTab', 'Alert triggers').should('be.visible').click(); - validateAlertPanel('test_trigger'); + validateAlertPanel('Trigger 1'); }); }); }); @@ -238,7 +220,7 @@ describe('Detectors', () => { describe('...should validate form fields', () => { beforeEach(() => { - cy.intercept('/detectors/_search').as('detectorsSearch'); + cy.intercept('/_plugins/_security_analytics/detectors/_search').as('detectorsSearch'); // Visit Detectors page before any test cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/detectors`); @@ -341,28 +323,21 @@ describe('Detectors', () => { it('...should validate alerts page', () => { fillDetailsForm(detectorName, cypressIndexDns); getNextButton().click({ force: true }); - getTriggerNameField().should('be.empty'); - - getTriggerNameField().focus().blur(); - getTriggerNameField() - .parents('.euiFormRow__fieldWrapper') - .find('.euiFormErrorText') - .contains('Enter a name.'); - - getTriggerNameField().type('Trigger name').focus().blur(); - + // Open the trigger details accordion + cy.get('[data-test-subj="trigger-details-btn"]').click({ force: true }); + getTriggerNameField().should('have.value', 'Trigger 1'); getTriggerNameField() .parents('.euiFormRow__fieldWrapper') .find('.euiFormErrorText') .should('not.exist'); - getNextButton().should('be.enabled'); + getCreateDetectorButton().should('be.enabled'); getTriggerNameField().type('{selectall}').type('{backspace}').focus().blur(); - getNextButton().should('be.disabled'); + getCreateDetectorButton().should('be.disabled'); cy.getButtonByText('Remove').click({ force: true }); - getNextButton().should('be.enabled'); + getCreateDetectorButton().should('be.enabled'); }); it('...should show mappings warning', () => { @@ -381,7 +356,7 @@ describe('Detectors', () => { describe('...validate create detector flow', () => { beforeEach(() => { - cy.intercept('/detectors/_search').as('detectorsSearch'); + cy.intercept('/_plugins/_security_analytics/detectors/_search').as('detectorsSearch'); // Visit Detectors page before any test cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/detectors`); @@ -399,7 +374,7 @@ describe('Detectors', () => { }); it('...basic details can be edited', () => { - cy.intercept('GET', '/indices').as('getIndices'); + cy.intercept('GET', '/_plugins/_security_analytics/indices').as('getIndices'); openDetectorDetails(detectorName); editDetectorDetails(detectorName, 'Detector details'); diff --git a/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx b/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx index 79939763b..9025bd3e9 100644 --- a/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx +++ b/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx @@ -18,7 +18,6 @@ import { EuiText, EuiTextArea, } from '@elastic/eui'; -import { Detector } from '../../../../../../../models/interfaces'; import { AlertCondition } from '../../../../../../../models/interfaces'; import { createSelectedOptions, @@ -31,6 +30,7 @@ import { NotificationChannelOption, NotificationChannelTypeOptions } from '../.. import { NOTIFICATIONS_HREF } from '../../../../../../utils/constants'; import { getNameErrorMessage, validateName } from '../../../../../../utils/validation'; import { NotificationsCallOut } from '../../../../../../components/NotificationsCallOut'; +import { Detector } from '../../../../../../../types'; interface AlertConditionPanelProps extends RouteComponentProps { alertCondition: AlertCondition; @@ -299,172 +299,213 @@ export default class AlertConditionPanel extends Component< }); } + const triggerDetailsSubheading = ` + ${ + selectedNames.length === 1 + ? '1 rule' + : `${!selectedNames.length ? 'All' : selectedNames.length} rules` + }, + ${ + ruleSeverityLevels.length === 1 + ? '1 severity' + : `${!ruleSeverityLevels.length ? 'All' : ruleSeverityLevels.length} severities` + }, + ${tags.length === 1 ? '1 tag' : `${!tags.length ? 'All' : tags.length} tags`} + `; + return (
- -

Trigger name

- + + Trigger details and condition + + {triggerDetailsSubheading} + +
} - isInvalid={nameFieldTouched && nameIsInvalid} - error={getNameErrorMessage(name, nameIsInvalid, nameFieldTouched)} > - - - - - -

If a detection rule matches

-
- + +

Trigger name

+ + } + isInvalid={nameFieldTouched && nameIsInvalid} + error={getNameErrorMessage(name, nameIsInvalid, nameFieldTouched)} + > + +
+ + + +

If a detection rule matches

+
+ - -

Rule names

- - } - > - -
- + +

Rule names

+ + } + > + +
+ - -

Rule Severities

- - } - > - -
- + +

Rule Severities

+ + } + > + +
+ - -

Tags

- - } - > - -
- - + +

Tags

+ + } + > + +
+ + + -

Alert and notify

+

Notification

- -

Specify alert severity

- + + Notification + + {`Configure notification to receive alerts when the trigger condition is met.`} + + } > - +

Specify alert severity

+ } - onChange={this.onAlertSeverityChange} - singleSelection={{ asPlainText: true }} - isClearable={false} - data-test-subj={'security-levels-combo-box'} - /> -
+ > + + - + - - - -

Select channel to notify

- - } - > - []} - selectedOptions={ - selectedNotificationChannelOption as EuiComboBoxOptionOption[] + + + +

Select channel to notify

+ } - onChange={this.onNotificationChannelsChange} - singleSelection={{ asPlainText: true }} - onBlur={refreshNotificationChannels} + > + []} + selectedOptions={ + selectedNotificationChannelOption as EuiComboBoxOptionOption[] + } + onChange={this.onNotificationChannelsChange} + singleSelection={{ asPlainText: true }} + onBlur={refreshNotificationChannels} + isDisabled={!hasNotificationPlugin} + /> +
+
+ + -
-
- - - Manage channels - - -
- - {!hasNotificationPlugin && ( - <> - - - - )} - - + > + Manage channels + + + + + {!hasNotificationPlugin && ( + <> + + + + )} + + + -

Show notify message

+

Notification message

} - paddingSize={'none'} + paddingSize={'l'} initialIsOpen={false} > -
-
- -
-
-
-
- -
-
-
-
-
-
-

- If a detection rule matches -

-
-
-
- -
-
- -
-
-
-
-
- + +
- -
-
-
- -
-
-