From 69599e0b49ef44f174247adaa49f74bafc1c8735 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Thu, 27 Aug 2020 20:00:42 -0400 Subject: [PATCH] [Security Solution][Exceptions] - Fix bug of alerts not updating after closure from exceptions modal (#76145) ## Summary Fixes bug found in 7.9. **Current behavior:** - Go to alerts page - Select action to add exception item - Select checkbox to close all alerts matching exception - Click `Add exception` - Note that the alerts table does not update until after you refresh or toggle between `Closed` and `Open` **New behavior:** - Go to alerts page - Select action to add exception item - Select checkbox to close all alerts matching exception - Click `Add exception` - Note that the alerts table updates --- .../events_viewer/events_viewer.test.tsx | 42 ++++++++++++++ .../events_viewer/events_viewer.tsx | 6 +- .../common/components/events_viewer/index.tsx | 7 ++- .../components/alerts_table/index.tsx | 55 +++++++++++++------ 4 files changed, 92 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx index 833688ae57993..6f77d15913d07 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx @@ -50,6 +50,10 @@ const utilityBar = (refetch: inputsModel.Refetch, totalCount: number) => (
); +const exceptionsModal = (refetch: inputsModel.Refetch) => ( +
+); + const eventsViewerDefaultProps = { browserFields: {}, columns: [], @@ -460,4 +464,42 @@ describe('EventsViewer', () => { }); }); }); + + describe('exceptions modal', () => { + test('it renders exception modal if "exceptionsModal" callback exists', async () => { + const wrapper = mount( + + + + + + ); + + await waitFor(() => { + wrapper.update(); + + expect(wrapper.find(`[data-test-subj="mock-exceptions-modal"]`).exists()).toBeTruthy(); + }); + }); + + test('it does not render exception modal if "exceptionModal" callback does not exist', async () => { + const wrapper = mount( + + + + + + ); + + await waitFor(() => { + wrapper.update(); + + expect(wrapper.find(`[data-test-subj="mock-exceptions-modal"]`).exists()).toBeFalsy(); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 436386077e725..ebda64efabf65 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -109,6 +109,7 @@ interface Props { utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode; // If truthy, the graph viewer (Resolver) is showing graphEventId: string | undefined; + exceptionsModal?: (refetch: inputsModel.Refetch) => React.ReactNode; } const EventsViewerComponent: React.FC = ({ @@ -134,6 +135,7 @@ const EventsViewerComponent: React.FC = ({ toggleColumn, utilityBar, graphEventId, + exceptionsModal, }) => { const { globalFullScreen } = useFullScreen(); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; @@ -259,6 +261,7 @@ const EventsViewerComponent: React.FC = ({ )} + {exceptionsModal && exceptionsModal(refetch)} {utilityBar && !resolverIsShowing(graphEventId) && ( {utilityBar?.(refetch, totalCountMinusDeleted)} )} @@ -335,5 +338,6 @@ export const EventsViewer = React.memo( prevProps.start === nextProps.start && prevProps.sort === nextProps.sort && prevProps.utilityBar === nextProps.utilityBar && - prevProps.graphEventId === nextProps.graphEventId + prevProps.graphEventId === nextProps.graphEventId && + prevProps.exceptionsModal === nextProps.exceptionsModal ); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index e4520dab4626a..ec56a3a1bd8d3 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -43,6 +43,7 @@ export interface OwnProps { headerFilterGroup?: React.ReactNode; pageFilters?: Filter[]; utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode; + exceptionsModal?: (refetch: inputsModel.Refetch) => React.ReactNode; } type Props = OwnProps & PropsFromRedux; @@ -74,6 +75,7 @@ const StatefulEventsViewerComponent: React.FC = ({ utilityBar, // If truthy, the graph viewer (Resolver) is showing graphEventId, + exceptionsModal, }) => { const [ { docValueFields, browserFields, indexPatterns, isLoading: isLoadingIndexPattern }, @@ -156,6 +158,7 @@ const StatefulEventsViewerComponent: React.FC = ({ toggleColumn={toggleColumn} utilityBar={utilityBar} graphEventId={graphEventId} + exceptionsModal={exceptionsModal} /> @@ -220,6 +223,7 @@ type PropsFromRedux = ConnectedProps; export const StatefulEventsViewer = connector( React.memo( StatefulEventsViewerComponent, + // eslint-disable-next-line complexity (prevProps, nextProps) => prevProps.id === nextProps.id && deepEqual(prevProps.columns, nextProps.columns) && @@ -240,6 +244,7 @@ export const StatefulEventsViewer = connector( prevProps.showCheckboxes === nextProps.showCheckboxes && prevProps.start === nextProps.start && prevProps.utilityBar === nextProps.utilityBar && - prevProps.graphEventId === nextProps.graphEventId + prevProps.graphEventId === nextProps.graphEventId && + prevProps.exceptionsModal === nextProps.exceptionsModal ) ); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 07e69d850f173..854565ace9b4b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -228,7 +228,7 @@ export const AlertsTableComponent: React.FC = ({ exceptionListType, alertData, }: AddExceptionModalBaseProps) => { - if (alertData !== null && alertData !== undefined) { + if (alertData != null) { setShouldShowAddExceptionModal(true); setAddExceptionModalState({ ruleName, @@ -441,9 +441,43 @@ export const AlertsTableComponent: React.FC = ({ closeAddExceptionModal(); }, [closeAddExceptionModal]); - const onAddExceptionConfirm = useCallback(() => closeAddExceptionModal(), [ - closeAddExceptionModal, - ]); + const onAddExceptionConfirm = useCallback( + (refetch: inputsModel.Refetch) => (): void => { + refetch(); + closeAddExceptionModal(); + }, + [closeAddExceptionModal] + ); + + // Callback for creating the AddExceptionModal and allowing it + // access to the refetchQuery to update the page + const exceptionModalCallback = useCallback( + (refetchQuery: inputsModel.Refetch) => { + if (shouldShowAddExceptionModal) { + return ( + + ); + } else { + return <>; + } + }, + [ + addExceptionModalState, + filterGroup, + onAddExceptionCancel, + onAddExceptionConfirm, + shouldShowAddExceptionModal, + ] + ); if (loading || indexPatternsLoading || isEmpty(signalsIndex)) { return ( @@ -465,19 +499,8 @@ export const AlertsTableComponent: React.FC = ({ id={timelineId} start={from} utilityBar={utilityBarCallback} + exceptionsModal={exceptionModalCallback} /> - {shouldShowAddExceptionModal === true && addExceptionModalState.alertData !== null && ( - - )} ); };