[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
This commit is contained in:
Yara Tercero 2020-08-27 20:00:42 -04:00 committed by GitHub
parent 64311d306f
commit 69599e0b49
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 92 additions and 18 deletions

View file

@ -50,6 +50,10 @@ const utilityBar = (refetch: inputsModel.Refetch, totalCount: number) => (
<div data-test-subj="mock-utility-bar" />
);
const exceptionsModal = (refetch: inputsModel.Refetch) => (
<div data-test-subj="mock-exceptions-modal" />
);
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(
<TestProviders>
<MockedProvider mocks={mockEventViewerResponse} addTypename={false}>
<EventsViewer
{...eventsViewerDefaultProps}
exceptionsModal={exceptionsModal}
graphEventId=""
/>
</MockedProvider>
</TestProviders>
);
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(
<TestProviders>
<MockedProvider mocks={mockEventViewerResponse} addTypename={false}>
<EventsViewer {...eventsViewerDefaultProps} graphEventId="" />
</MockedProvider>
</TestProviders>
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find(`[data-test-subj="mock-exceptions-modal"]`).exists()).toBeFalsy();
});
});
});
});

View file

@ -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<Props> = ({
@ -134,6 +135,7 @@ const EventsViewerComponent: React.FC<Props> = ({
toggleColumn,
utilityBar,
graphEventId,
exceptionsModal,
}) => {
const { globalFullScreen } = useFullScreen();
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
@ -259,6 +261,7 @@ const EventsViewerComponent: React.FC<Props> = ({
</HeaderFilterGroupWrapper>
)}
</HeaderSection>
{exceptionsModal && exceptionsModal(refetch)}
{utilityBar && !resolverIsShowing(graphEventId) && (
<UtilityBar>{utilityBar?.(refetch, totalCountMinusDeleted)}</UtilityBar>
)}
@ -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
);

View file

@ -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<Props> = ({
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<Props> = ({
toggleColumn={toggleColumn}
utilityBar={utilityBar}
graphEventId={graphEventId}
exceptionsModal={exceptionsModal}
/>
</InspectButtonContainer>
</FullScreenContainer>
@ -220,6 +223,7 @@ type PropsFromRedux = ConnectedProps<typeof connector>;
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
)
);

View file

@ -228,7 +228,7 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
exceptionListType,
alertData,
}: AddExceptionModalBaseProps) => {
if (alertData !== null && alertData !== undefined) {
if (alertData != null) {
setShouldShowAddExceptionModal(true);
setAddExceptionModalState({
ruleName,
@ -441,9 +441,43 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
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 (
<AddExceptionModal
ruleName={addExceptionModalState.ruleName}
ruleId={addExceptionModalState.ruleId}
ruleIndices={addExceptionModalState.ruleIndices}
exceptionListType={addExceptionModalState.exceptionListType}
alertData={addExceptionModalState.alertData}
onCancel={onAddExceptionCancel}
onConfirm={onAddExceptionConfirm(refetchQuery)}
alertStatus={filterGroup}
/>
);
} else {
return <></>;
}
},
[
addExceptionModalState,
filterGroup,
onAddExceptionCancel,
onAddExceptionConfirm,
shouldShowAddExceptionModal,
]
);
if (loading || indexPatternsLoading || isEmpty(signalsIndex)) {
return (
@ -465,19 +499,8 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
id={timelineId}
start={from}
utilityBar={utilityBarCallback}
exceptionsModal={exceptionModalCallback}
/>
{shouldShowAddExceptionModal === true && addExceptionModalState.alertData !== null && (
<AddExceptionModal
ruleName={addExceptionModalState.ruleName}
ruleId={addExceptionModalState.ruleId}
ruleIndices={addExceptionModalState.ruleIndices}
exceptionListType={addExceptionModalState.exceptionListType}
alertData={addExceptionModalState.alertData}
onCancel={onAddExceptionCancel}
onConfirm={onAddExceptionConfirm}
alertStatus={filterGroup}
/>
)}
</>
);
};