From d33c233244a78eb1926ab1bffdb5d640309f05b6 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 9 Aug 2021 14:59:44 -0400 Subject: [PATCH] adds status filter changes to rule details page --- .../detection_engine/rules/details/index.tsx | 67 +++++++++++++++++-- .../test/security_solution_cypress/runner.ts | 2 +- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 233189a3e8be9..b654d02dd6997 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -23,7 +23,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { noop } from 'lodash/fp'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { useDispatch } from 'react-redux'; +import { connect, ConnectedProps, useDispatch } from 'react-redux'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; import { @@ -31,6 +31,7 @@ import { ExceptionListIdentifiers, } from '@kbn/securitysolution-io-ts-list-types'; +import { Dispatch } from 'redux'; import { isTab } from '../../../../../../../timelines/public'; import { useDeepEqualSelector, @@ -63,6 +64,8 @@ import { StepDefineRule } from '../../../../components/rules/step_define_rule'; import { StepScheduleRule } from '../../../../components/rules/step_schedule_rule'; import { buildAlertsRuleIdFilter, + buildAlertStatusFilter, + buildAlertStatusFilterRuleRegistry, buildShowBuildingBlockFilter, buildShowBuildingBlockFilterRuleRegistry, buildThreatMatchFilter, @@ -98,7 +101,7 @@ import { resetKeyboardFocus, showGlobalFilters, } from '../../../../../timelines/components/timeline/helpers'; -import { timelineSelectors } from '../../../../../timelines/store/timeline'; +import { timelineActions, timelineSelectors } from '../../../../../timelines/store/timeline'; import { timelineDefaults } from '../../../../../timelines/store/timeline/defaults'; import { useSourcererScope } from '../../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../../common/store/sourcerer/model'; @@ -118,6 +121,11 @@ import { MissingPrivilegesCallOut } from '../../../../components/callouts/missin import { useRuleWithFallback } from '../../../../containers/detection_engine/rules/use_rule_with_fallback'; import { BadgeOptions } from '../../../../../common/components/header_page/types'; import { AlertsStackByField } from '../../../../components/alerts_kpis/common/types'; +import { Status } from '../../../../../../common/detection_engine/schemas/common/schemas'; +import { + AlertsTableFilterGroup, + FILTER_OPEN, +} from '../../../../components/alerts_table/alerts_filter_group'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -155,7 +163,13 @@ const ruleDetailTabs = [ }, ]; -const RuleDetailsPageComponent = () => { +type DetectionEngineComponentProps = PropsFromRedux; + +const RuleDetailsPageComponent: React.FC = ({ + clearEventsDeleted, + clearEventsLoading, + clearSelected, +}) => { const { navigateToApp } = useKibana().services.application; const dispatch = useDispatch(); const containerElement = useRef(null); @@ -226,6 +240,7 @@ const RuleDetailsPageComponent = () => { const mlCapabilities = useMlCapabilities(); const { formatUrl } = useFormatUrl(SecurityPageName.rules); const { globalFullScreen } = useGlobalFullScreen(); + const [filterGroup, setFilterGroup] = useState(FILTER_OPEN); // TODO: Once we are past experimental phase this code should be removed const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled'); @@ -315,6 +330,18 @@ const RuleDetailsPageComponent = () => { [rule, ruleLoading] ); + // Callback for when open/closed filter changes + const onFilterGroupChangedCallback = useCallback( + (newFilterGroup: Status) => { + const timelineId = TimelineId.detectionsPage; + clearEventsLoading!({ id: timelineId }); + clearEventsDeleted!({ id: timelineId }); + clearSelected!({ id: timelineId }); + setFilterGroup(newFilterGroup); + }, + [clearEventsLoading, clearEventsDeleted, clearSelected, setFilterGroup] + ); + // Set showBuildingBlockAlerts if rule is a Building Block Rule otherwise we won't show alerts useEffect(() => { setShowBuildingBlockAlerts(rule?.building_block_type != null); @@ -324,11 +351,23 @@ const RuleDetailsPageComponent = () => { () => [ ...buildAlertsRuleIdFilter(ruleId), ...(ruleRegistryEnabled - ? buildShowBuildingBlockFilterRuleRegistry(showBuildingBlockAlerts) // TODO: Once we are past experimental phase this code should be removed - : buildShowBuildingBlockFilter(showBuildingBlockAlerts)), + ? [ + ...buildShowBuildingBlockFilterRuleRegistry(showBuildingBlockAlerts), // TODO: Once we are past experimental phase this code should be removed + ...buildAlertStatusFilterRuleRegistry(filterGroup), + ] + : [ + ...buildShowBuildingBlockFilter(showBuildingBlockAlerts), + ...buildAlertStatusFilter(filterGroup), + ]), ...buildThreatMatchFilter(showOnlyThreatIndicatorAlerts), ], - [ruleId, ruleRegistryEnabled, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts] + [ + ruleId, + ruleRegistryEnabled, + showBuildingBlockAlerts, + showOnlyThreatIndicatorAlerts, + filterGroup, + ] ); const alertMergedFilters = useMemo(() => [...alertDefaultFilters, ...filters], [ @@ -705,6 +744,8 @@ const RuleDetailsPageComponent = () => { {ruleDetailTab === RuleDetailTabs.alerts && ( <> + + { ); }; +const mapDispatchToProps = (dispatch: Dispatch) => ({ + clearSelected: ({ id }: { id: string }) => dispatch(timelineActions.clearSelected({ id })), + clearEventsLoading: ({ id }: { id: string }) => + dispatch(timelineActions.clearEventsLoading({ id })), + clearEventsDeleted: ({ id }: { id: string }) => + dispatch(timelineActions.clearEventsDeleted({ id })), +}); + +const connector = connect(null, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + RuleDetailsPageComponent.displayName = 'RuleDetailsPageComponent'; -export const RuleDetailsPage = React.memo(RuleDetailsPageComponent); +export const RuleDetailsPage = connector(React.memo(RuleDetailsPageComponent)); RuleDetailsPage.displayName = 'RuleDetailsPage'; diff --git a/x-pack/test/security_solution_cypress/runner.ts b/x-pack/test/security_solution_cypress/runner.ts index 10a754d9bc53f..0aad166c14b78 100644 --- a/x-pack/test/security_solution_cypress/runner.ts +++ b/x-pack/test/security_solution_cypress/runner.ts @@ -22,7 +22,7 @@ export async function SecuritySolutionCypressCliTestRunner({ getService }: FtrPr await withProcRunner(log, async (procs) => { await procs.run('cypress', { cmd: 'yarn', - args: ['cypress:run'], + args: ['cypress:open'], cwd: resolve(__dirname, '../../plugins/security_solution'), env: { FORCE_COLOR: '1',