[Security Solution][Tech debt] T-Grid cleanup (#138581)

* [Security Solution][Tech debt] T-Grid cleanup: removed unused logic

* Renamed folder to correspond the logic inside

* Fixed types for isEventViewer

* Fixed tests, corrected names

* Fixed due to comments
This commit is contained in:
Yuliia Naumenko 2022-08-23 14:21:37 -07:00 committed by GitHub
parent 908a01b5a6
commit fe646b297d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 259 additions and 1134 deletions

View file

@ -61,7 +61,6 @@ export interface Props {
onRuleChange?: () => void;
renderCellValue: (props: CellValueElementProps) => React.ReactNode;
rowRenderers: RowRenderer[];
utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode;
additionalFilters?: React.ReactNode;
hasAlertsCrud?: boolean;
unit?: (n: number) => string;
@ -86,7 +85,6 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
rowRenderers,
start,
scopeId,
utilityBar,
additionalFilters,
hasAlertsCrud = false,
unit,

View file

@ -0,0 +1,80 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { AdditionalFiltersAction } from '.';
import { TestProviders } from '../../../../common/mock/test_providers';
jest.useFakeTimers();
jest.mock('../../../../common/lib/kibana');
describe('AdditionalFiltersAction', () => {
describe('UtilityBarAdditionalFiltersContent', () => {
test('does not show the showBuildingBlockAlerts checked if the showBuildingBlockAlerts is false', async () => {
const onShowBuildingBlockAlertsChanged = jest.fn();
render(
<TestProviders>
<AdditionalFiltersAction
onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChanged}
areEventsLoading={false}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
showBuildingBlockAlerts={false}
showOnlyThreatIndicatorAlerts={false}
/>
</TestProviders>
);
// click the filters button to popup the checkbox to make it visible
const additionalFiltersButton = screen.findByTestId('additionalFilters-popover');
fireEvent.click(await additionalFiltersButton);
// The check box should be false
expect(await screen.findByTestId('showBuildingBlockAlertsCheckbox')).not.toBeChecked();
});
test('does not show the showOnlyThreatIndicatorAlerts checked if the showOnlyThreatIndicatorAlerts is true', async () => {
render(
<TestProviders>
<AdditionalFiltersAction
onShowBuildingBlockAlertsChanged={jest.fn()}
areEventsLoading={false}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
showBuildingBlockAlerts={false}
showOnlyThreatIndicatorAlerts={true}
/>
</TestProviders>
);
// click the filters button to popup the checkbox to make it visible
const additionalFiltersButton = screen.findByTestId('additionalFilters-popover');
fireEvent.click(await additionalFiltersButton);
expect(await screen.findByTestId('showOnlyThreatIndicatorAlertsCheckbox')).toBeChecked();
});
test('does show the showBuildingBlockAlerts checked if the showBuildingBlockAlerts is true', async () => {
const onShowBuildingBlockAlertsChanged = jest.fn();
render(
<TestProviders>
<AdditionalFiltersAction
onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChanged}
areEventsLoading={false}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
showBuildingBlockAlerts={true}
showOnlyThreatIndicatorAlerts={false}
/>
</TestProviders>
);
// click the filters button to popup the checkbox to make it visible
const additionalFiltersButton = screen.findByTestId('additionalFilters-popover');
fireEvent.click(await additionalFiltersButton);
// The check box should be true
expect(await screen.findByTestId('showBuildingBlockAlertsCheckbox')).toBeChecked();
});
});
});

View file

@ -0,0 +1,94 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useCallback } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiCheckbox } from '@elastic/eui';
import styled from 'styled-components';
import { UtilityBarAction } from '../../../../common/components/utility_bar';
import * as i18n from './translations';
const UtilityBarFlexGroup = styled(EuiFlexGroup)`
min-width: 175px;
`;
const AdditionalFiltersItem = styled(EuiFlexItem)`
padding: ${({ theme }) => theme.eui.euiSizeS};
`;
const BuildingBlockContainer = styled(AdditionalFiltersItem)`
background: ${({ theme }) => theme.eui.euiColorHighlight};
`;
export const AdditionalFiltersAction = ({
areEventsLoading,
onShowBuildingBlockAlertsChanged,
showBuildingBlockAlerts,
onShowOnlyThreatIndicatorAlertsChanged,
showOnlyThreatIndicatorAlerts,
}: {
areEventsLoading: boolean;
onShowBuildingBlockAlertsChanged: (showBuildingBlockAlerts: boolean) => void;
showBuildingBlockAlerts: boolean;
onShowOnlyThreatIndicatorAlertsChanged: (showOnlyThreatIndicatorAlerts: boolean) => void;
showOnlyThreatIndicatorAlerts: boolean;
}) => {
const UtilityBarAdditionalFiltersContent = useCallback(
(closePopover: () => void) => (
<UtilityBarFlexGroup direction="column" gutterSize="none">
<BuildingBlockContainer>
<EuiCheckbox
id="showBuildingBlockAlertsCheckbox"
aria-label="showBuildingBlockAlerts"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
closePopover();
onShowBuildingBlockAlertsChanged(e.target.checked);
}}
checked={showBuildingBlockAlerts}
color="text"
data-test-subj="showBuildingBlockAlertsCheckbox"
label={i18n.ADDITIONAL_FILTERS_ACTIONS_SHOW_BUILDING_BLOCK}
/>
</BuildingBlockContainer>
<AdditionalFiltersItem>
<EuiCheckbox
id="showOnlyThreatIndicatorAlertsCheckbox"
aria-label="showOnlyThreatIndicatorAlerts"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
closePopover();
onShowOnlyThreatIndicatorAlertsChanged(e.target.checked);
}}
checked={showOnlyThreatIndicatorAlerts}
color="text"
data-test-subj="showOnlyThreatIndicatorAlertsCheckbox"
label={i18n.ADDITIONAL_FILTERS_ACTIONS_SHOW_ONLY_THREAT_INDICATOR_ALERTS}
/>
</AdditionalFiltersItem>
</UtilityBarFlexGroup>
),
[
onShowBuildingBlockAlertsChanged,
onShowOnlyThreatIndicatorAlertsChanged,
showBuildingBlockAlerts,
showOnlyThreatIndicatorAlerts,
]
);
return (
<UtilityBarAction
dataTestSubj="additionalFilters"
disabled={areEventsLoading}
iconType="arrowDown"
iconSide="right"
ownFocus
popoverContent={UtilityBarAdditionalFiltersContent}
>
{i18n.ADDITIONAL_FILTERS_ACTIONS}
</UtilityBarAction>
);
};

View file

@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
export const ADDITIONAL_FILTERS_ACTIONS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersTitle',
{
defaultMessage: 'Additional filters',
}
);
export const ADDITIONAL_FILTERS_ACTIONS_SHOW_BUILDING_BLOCK = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showBuildingBlockTitle',
{
defaultMessage: 'Include building block alerts',
}
);
export const ADDITIONAL_FILTERS_ACTIONS_SHOW_ONLY_THREAT_INDICATOR_ALERTS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showOnlyThreatIndicatorAlerts',
{
defaultMessage: 'Show only threat indicator alerts',
}
);
export const TAKE_ACTION = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.takeActionTitle',
{
defaultMessage: 'Take action',
}
);

View file

@ -1,419 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { shallow, mount } from 'enzyme';
import type { AlertsUtilityBarProps } from '.';
import { AlertsUtilityBar } from '.';
import { TestProviders } from '../../../../common/mock/test_providers';
jest.useFakeTimers();
jest.mock('../../../../common/lib/kibana');
describe('AlertsUtilityBar', () => {
test('renders correctly', () => {
const wrapper = shallow(
<AlertsUtilityBar
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={jest.fn()}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={false}
showClearSelection={true}
showOnlyThreatIndicatorAlerts={false}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
);
expect(wrapper.find('[dataTestSubj="alertActionPopover"]')).toBeTruthy();
});
describe('UtilityBarAdditionalFiltersContent', () => {
test('does not show the showBuildingBlockAlerts checked if the showBuildingBlockAlerts is false', () => {
const onShowBuildingBlockAlertsChanged = jest.fn();
const wrapper = mount(
<TestProviders>
<AlertsUtilityBar
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChanged}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={false} // Does not show showBuildingBlockAlerts checked if this is false
showClearSelection={true}
showOnlyThreatIndicatorAlerts={false}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
</TestProviders>
);
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// The check box should be false
expect(
wrapper
.find('[data-test-subj="showBuildingBlockAlertsCheckbox"] input')
.first()
.prop('checked')
).toEqual(false);
});
test('does not show the showOnlyThreatIndicatorAlerts checked if the showThreatMatchOnly is false', () => {
const wrapper = mount(
<TestProviders>
<AlertsUtilityBar
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={jest.fn()}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={false} // Does not show showBuildingBlockAlerts checked if this is false
showClearSelection={true}
showOnlyThreatIndicatorAlerts={false}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
</TestProviders>
);
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// The check box should be false
expect(
wrapper
.find('[data-test-subj="showOnlyThreatIndicatorAlertsCheckbox"] input')
.first()
.prop('checked')
).toEqual(false);
});
test('does show the showBuildingBlockAlerts checked if the showBuildingBlockAlerts is true', () => {
const onShowBuildingBlockAlertsChanged = jest.fn();
const wrapper = mount(
<TestProviders>
<AlertsUtilityBar
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChanged}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={true} // Does show showBuildingBlockAlerts checked if this is true
showClearSelection={true}
showOnlyThreatIndicatorAlerts={false}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
</TestProviders>
);
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// The check box should be true
expect(
wrapper
.find('[data-test-subj="showBuildingBlockAlertsCheckbox"] input')
.first()
.prop('checked')
).toEqual(true);
});
test('does show the showOnlyThreatIndicatorAlerts checked if the showOnlyThreatIndicatorAlerts is true', () => {
const wrapper = mount(
<TestProviders>
<AlertsUtilityBar
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={jest.fn()}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={true} // Does show showBuildingBlockAlerts checked if this is true
showClearSelection={true}
showOnlyThreatIndicatorAlerts={true}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
</TestProviders>
);
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// The check box should be true
expect(
wrapper
.find('[data-test-subj="showOnlyThreatIndicatorAlertsCheckbox"] input')
.first()
.prop('checked')
).toEqual(true);
});
test('calls the onShowBuildingBlockAlertsChanged when the check box is clicked', () => {
const onShowBuildingBlockAlertsChanged = jest.fn();
const wrapper = mount(
<TestProviders>
<AlertsUtilityBar
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChanged}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={false}
showClearSelection={true}
showOnlyThreatIndicatorAlerts={false}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
</TestProviders>
);
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// check the box
wrapper
.find('[data-test-subj="showBuildingBlockAlertsCheckbox"] input')
.first()
.simulate('change', { target: { checked: true } });
// Make sure our callback is called
expect(onShowBuildingBlockAlertsChanged).toHaveBeenCalled();
});
test('calls the onShowOnlyThreatIndicatorAlertsChanged when the check box is clicked', () => {
const onShowOnlyThreatIndicatorAlertsChanged = jest.fn();
const wrapper = mount(
<TestProviders>
<AlertsUtilityBar
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={jest.fn()}
onShowOnlyThreatIndicatorAlertsChanged={onShowOnlyThreatIndicatorAlertsChanged}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={false}
showClearSelection={true}
showOnlyThreatIndicatorAlerts={false}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
</TestProviders>
);
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// check the box
wrapper
.find('[data-test-subj="showOnlyThreatIndicatorAlertsCheckbox"] input')
.first()
.simulate('change', { target: { checked: true } });
// Make sure our callback is called
expect(onShowOnlyThreatIndicatorAlertsChanged).toHaveBeenCalled();
});
test('can update showBuildingBlockAlerts from false to true', () => {
const Proxy = (props: AlertsUtilityBarProps) => (
<TestProviders>
<AlertsUtilityBar
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={jest.fn()}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={props.showBuildingBlockAlerts}
showClearSelection={true}
showOnlyThreatIndicatorAlerts={false}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
</TestProviders>
);
const wrapper = mount(
<Proxy
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={jest.fn()}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={false}
showClearSelection={true}
showOnlyThreatIndicatorAlerts={false}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
);
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// The check box should false now since we initially set the showBuildingBlockAlerts to false
expect(
wrapper
.find('[data-test-subj="showBuildingBlockAlertsCheckbox"] input')
.first()
.prop('checked')
).toEqual(false);
wrapper.setProps({ showBuildingBlockAlerts: true });
wrapper.update();
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// The check box should be true now since we changed the showBuildingBlockAlerts from false to true
expect(
wrapper
.find('[data-test-subj="showBuildingBlockAlertsCheckbox"] input')
.first()
.prop('checked')
).toEqual(true);
});
test('can update showOnlyThreatIndicatorAlerts from false to true', () => {
const Proxy = (props: AlertsUtilityBarProps) => (
<TestProviders>
<AlertsUtilityBar
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={jest.fn()}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={false}
showClearSelection={true}
showOnlyThreatIndicatorAlerts={props.showOnlyThreatIndicatorAlerts}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
</TestProviders>
);
const wrapper = mount(
<Proxy
areEventsLoading={false}
clearSelection={jest.fn()}
currentFilter="closed"
hasIndexMaintenance={true}
hasIndexWrite={true}
onShowBuildingBlockAlertsChanged={jest.fn()}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
selectAll={jest.fn()}
selectedEventIds={{}}
showBuildingBlockAlerts={false}
showClearSelection={true}
showOnlyThreatIndicatorAlerts={false}
totalCount={100}
updateAlertsStatus={jest.fn()}
/>
);
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// The check box should false now since we initially set the showBuildingBlockAlerts to false
expect(
wrapper
.find('[data-test-subj="showOnlyThreatIndicatorAlertsCheckbox"] input')
.first()
.prop('checked')
).toEqual(false);
wrapper.setProps({ showOnlyThreatIndicatorAlerts: true });
wrapper.update();
// click the filters button to popup the checkbox to make it visible
wrapper
.find('[data-test-subj="additionalFilters"] button')
.first()
.simulate('click')
.update();
// The check box should be true now since we changed the showBuildingBlockAlerts from false to true
expect(
wrapper
.find('[data-test-subj="showOnlyThreatIndicatorAlertsCheckbox"] input')
.first()
.prop('checked')
).toEqual(true);
});
});
});

View file

@ -1,284 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { isEmpty } from 'lodash/fp';
import React, { useCallback } from 'react';
import numeral from '@elastic/numeral';
import { EuiFlexGroup, EuiFlexItem, EuiCheckbox } from '@elastic/eui';
import styled from 'styled-components';
import type { Status } from '../../../../../common/detection_engine/schemas/common/schemas';
import { Link } from '../../../../common/components/link_icon';
import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants';
import {
UtilityBar,
UtilityBarAction,
UtilityBarGroup,
UtilityBarSection,
UtilityBarSpacer,
UtilityBarText,
} from '../../../../common/components/utility_bar';
import * as i18n from './translations';
import { useUiSetting$ } from '../../../../common/lib/kibana';
import type { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline';
import type { UpdateAlertsStatus } from '../types';
import { FILTER_CLOSED, FILTER_ACKNOWLEDGED, FILTER_OPEN } from '../alerts_filter_group';
export interface AlertsUtilityBarProps {
areEventsLoading: boolean;
clearSelection: () => void;
currentFilter: Status;
hasIndexMaintenance: boolean;
hasIndexWrite: boolean;
onShowBuildingBlockAlertsChanged: (showBuildingBlockAlerts: boolean) => void;
onShowOnlyThreatIndicatorAlertsChanged: (showOnlyThreatIndicatorAlerts: boolean) => void;
selectAll: () => void;
selectedEventIds: Readonly<Record<string, TimelineNonEcsData[]>>;
showBuildingBlockAlerts: boolean;
showClearSelection: boolean;
showOnlyThreatIndicatorAlerts: boolean;
totalCount: number;
updateAlertsStatus: UpdateAlertsStatus;
}
const UtilityBarFlexGroup = styled(EuiFlexGroup)`
min-width: 175px;
`;
const AdditionalFiltersItem = styled(EuiFlexItem)`
padding: ${({ theme }) => theme.eui.euiSizeS};
`;
const BuildingBlockContainer = styled(AdditionalFiltersItem)`
background: ${({ theme }) => theme.eui.euiColorHighlight};
`;
const AlertsUtilityBarComponent: React.FC<AlertsUtilityBarProps> = ({
areEventsLoading,
clearSelection,
currentFilter,
hasIndexMaintenance,
hasIndexWrite,
onShowBuildingBlockAlertsChanged,
onShowOnlyThreatIndicatorAlertsChanged,
selectAll,
selectedEventIds,
showBuildingBlockAlerts,
showClearSelection,
showOnlyThreatIndicatorAlerts,
totalCount,
updateAlertsStatus,
}) => {
const [defaultNumberFormat] = useUiSetting$<string>(DEFAULT_NUMBER_FORMAT);
const handleUpdateStatus = useCallback(
async (selectedStatus: Status) => {
await updateAlertsStatus({
alertIds: Object.keys(selectedEventIds),
status: currentFilter,
selectedStatus,
});
},
[currentFilter, selectedEventIds, updateAlertsStatus]
);
const formattedTotalCount = numeral(totalCount).format(defaultNumberFormat);
const formattedSelectedEventsCount = numeral(Object.keys(selectedEventIds).length).format(
defaultNumberFormat
);
const UtilityBarPopoverContent = (closePopover: () => void) => (
<UtilityBarFlexGroup direction="column">
{currentFilter !== FILTER_OPEN && (
<EuiFlexItem>
<Link
aria-label="openSelectedAlerts"
onClick={() => {
closePopover();
handleUpdateStatus('open');
}}
color="text"
data-test-subj="openSelectedAlertsButton"
>
{i18n.BATCH_ACTION_OPEN_SELECTED}
</Link>
</EuiFlexItem>
)}
{currentFilter !== FILTER_CLOSED && (
<EuiFlexItem>
<Link
aria-label="closeSelectedAlerts"
onClick={() => {
closePopover();
handleUpdateStatus('closed');
}}
color="text"
data-test-subj="closeSelectedAlertsButton"
>
{i18n.BATCH_ACTION_CLOSE_SELECTED}
</Link>
</EuiFlexItem>
)}
{currentFilter !== FILTER_ACKNOWLEDGED && (
<EuiFlexItem>
<Link
aria-label="markSelectedAlertsAcknowledged"
onClick={() => {
closePopover();
handleUpdateStatus('acknowledged');
}}
color="text"
data-test-subj="markSelectedAlertsAcknowledgedButton"
>
{i18n.BATCH_ACTION_ACKNOWLEDGED_SELECTED}
</Link>
</EuiFlexItem>
)}
</UtilityBarFlexGroup>
);
const handleSelectAllAlertsClick = useCallback(() => {
if (!showClearSelection) {
selectAll();
} else {
clearSelection();
}
}, [clearSelection, selectAll, showClearSelection]);
return (
<>
<UtilityBar>
<UtilityBarSection grow={true}>
<UtilityBarGroup>
<UtilityBarText dataTestSubj="showingAlerts">
{i18n.SHOWING_ALERTS(formattedTotalCount, totalCount)}
</UtilityBarText>
</UtilityBarGroup>
<UtilityBarGroup grow={true}>
{hasIndexWrite && hasIndexMaintenance && (
<>
<UtilityBarText dataTestSubj="selectedAlerts">
{i18n.SELECTED_ALERTS(
showClearSelection ? formattedTotalCount : formattedSelectedEventsCount,
showClearSelection ? totalCount : Object.keys(selectedEventIds).length
)}
</UtilityBarText>
<UtilityBarAction
dataTestSubj="alertActionPopover"
disabled={
areEventsLoading || (isEmpty(selectedEventIds) && showClearSelection === false)
}
iconType="arrowDown"
iconSide="right"
ownFocus={false}
popoverContent={UtilityBarPopoverContent}
>
{i18n.TAKE_ACTION}
</UtilityBarAction>
<UtilityBarAction
aria-label="selectAllAlerts"
dataTestSubj="selectAllAlertsButton"
iconType={showClearSelection ? 'cross' : 'pagesSelect'}
onClick={handleSelectAllAlertsClick}
>
{showClearSelection
? i18n.CLEAR_SELECTION
: i18n.SELECT_ALL_ALERTS(formattedTotalCount, totalCount)}
</UtilityBarAction>
</>
)}
<UtilityBarSpacer />
<AditionalFiltersAction
areEventsLoading={areEventsLoading}
onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChanged}
showBuildingBlockAlerts={showBuildingBlockAlerts}
onShowOnlyThreatIndicatorAlertsChanged={onShowOnlyThreatIndicatorAlertsChanged}
showOnlyThreatIndicatorAlerts={showOnlyThreatIndicatorAlerts}
/>
</UtilityBarGroup>
</UtilityBarSection>
</UtilityBar>
</>
);
};
export const AlertsUtilityBar = React.memo(
AlertsUtilityBarComponent,
(prevProps, nextProps) =>
prevProps.areEventsLoading === nextProps.areEventsLoading &&
prevProps.selectedEventIds === nextProps.selectedEventIds &&
prevProps.totalCount === nextProps.totalCount &&
prevProps.showClearSelection === nextProps.showClearSelection &&
prevProps.showBuildingBlockAlerts === nextProps.showBuildingBlockAlerts &&
prevProps.showOnlyThreatIndicatorAlerts === nextProps.showOnlyThreatIndicatorAlerts
);
export const AditionalFiltersAction = ({
areEventsLoading,
onShowBuildingBlockAlertsChanged,
showBuildingBlockAlerts,
onShowOnlyThreatIndicatorAlertsChanged,
showOnlyThreatIndicatorAlerts,
}: {
areEventsLoading: boolean;
onShowBuildingBlockAlertsChanged: (showBuildingBlockAlerts: boolean) => void;
showBuildingBlockAlerts: boolean;
onShowOnlyThreatIndicatorAlertsChanged: (showOnlyThreatIndicatorAlerts: boolean) => void;
showOnlyThreatIndicatorAlerts: boolean;
}) => {
const UtilityBarAdditionalFiltersContent = (closePopover: () => void) => (
<UtilityBarFlexGroup direction="column" gutterSize="none">
<BuildingBlockContainer>
<EuiCheckbox
id="showBuildingBlockAlertsCheckbox"
aria-label="showBuildingBlockAlerts"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
closePopover();
onShowBuildingBlockAlertsChanged(e.target.checked);
}}
checked={showBuildingBlockAlerts}
color="text"
data-test-subj="showBuildingBlockAlertsCheckbox"
label={i18n.ADDITIONAL_FILTERS_ACTIONS_SHOW_BUILDING_BLOCK}
/>
</BuildingBlockContainer>
<AdditionalFiltersItem>
<EuiCheckbox
id="showOnlyThreatIndicatorAlertsCheckbox"
aria-label="showOnlyThreatIndicatorAlerts"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
closePopover();
onShowOnlyThreatIndicatorAlertsChanged(e.target.checked);
}}
checked={showOnlyThreatIndicatorAlerts}
color="text"
data-test-subj="showOnlyThreatIndicatorAlertsCheckbox"
label={i18n.ADDITIONAL_FILTERS_ACTIONS_SHOW_ONLY_THREAT_INDICATOR_ALERTS}
/>
</AdditionalFiltersItem>
</UtilityBarFlexGroup>
);
return (
<UtilityBarAction
dataTestSubj="additionalFilters"
disabled={areEventsLoading}
iconType="arrowDown"
iconSide="right"
ownFocus={true}
popoverContent={UtilityBarAdditionalFiltersContent}
>
{i18n.ADDITIONAL_FILTERS_ACTIONS}
</UtilityBarAction>
);
};

View file

@ -1,85 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
export const SHOWING_ALERTS = (totalAlertsFormatted: string, totalAlerts: number) =>
i18n.translate('xpack.securitySolution.detectionEngine.alerts.utilityBar.showingAlertsTitle', {
values: { totalAlertsFormatted, totalAlerts },
defaultMessage:
'Showing {totalAlertsFormatted} {totalAlerts, plural, =1 {alert} other {alerts}}',
});
export const SELECTED_ALERTS = (selectedAlertsFormatted: string, selectedAlerts: number) =>
i18n.translate('xpack.securitySolution.detectionEngine.alerts.utilityBar.selectedAlertsTitle', {
values: { selectedAlertsFormatted, selectedAlerts },
defaultMessage:
'Selected {selectedAlertsFormatted} {selectedAlerts, plural, =1 {alert} other {alerts}}',
});
export const SELECT_ALL_ALERTS = (totalAlertsFormatted: string, totalAlerts: number) =>
i18n.translate('xpack.securitySolution.detectionEngine.alerts.utilityBar.selectAllAlertsTitle', {
values: { totalAlertsFormatted, totalAlerts },
defaultMessage:
'Select all {totalAlertsFormatted} {totalAlerts, plural, =1 {alert} other {alerts}}',
});
export const ADDITIONAL_FILTERS_ACTIONS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersTitle',
{
defaultMessage: 'Additional filters',
}
);
export const ADDITIONAL_FILTERS_ACTIONS_SHOW_BUILDING_BLOCK = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showBuildingBlockTitle',
{
defaultMessage: 'Include building block alerts',
}
);
export const ADDITIONAL_FILTERS_ACTIONS_SHOW_ONLY_THREAT_INDICATOR_ALERTS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showOnlyThreatIndicatorAlerts',
{
defaultMessage: 'Show only threat indicator alerts',
}
);
export const CLEAR_SELECTION = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.clearSelectionTitle',
{
defaultMessage: 'Clear selection',
}
);
export const TAKE_ACTION = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.takeActionTitle',
{
defaultMessage: 'Take action',
}
);
export const BATCH_ACTION_OPEN_SELECTED = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.openSelectedTitle',
{
defaultMessage: 'Open selected',
}
);
export const BATCH_ACTION_CLOSE_SELECTED = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.closeSelectedTitle',
{
defaultMessage: 'Close selected',
}
);
export const BATCH_ACTION_ACKNOWLEDGED_SELECTED = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.acknowledgedSelectedTitle',
{
defaultMessage: 'Mark as acknowledged',
}
);

View file

@ -33,13 +33,11 @@ describe('AlertsTableComponent', () => {
loadingEventIds={[]}
selectedEventIds={{}}
isSelectAllChecked={false}
clearSelected={jest.fn()}
setEventsLoading={jest.fn()}
setEventsDeleted={jest.fn()}
showBuildingBlockAlerts={false}
onShowBuildingBlockAlertsChanged={jest.fn()}
showOnlyThreatIndicatorAlerts={false}
onShowOnlyThreatIndicatorAlertsChanged={jest.fn()}
dispatch={jest.fn()}
/>
</TestProviders>
);

View file

@ -6,22 +6,15 @@
*/
import { isEmpty } from 'lodash/fp';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';
import type { ConnectedProps } from 'react-redux';
import { connect, useDispatch } from 'react-redux';
import type { Dispatch } from 'redux';
import type { Filter } from '@kbn/es-query';
import { getEsQueryConfig } from '@kbn/data-plugin/common';
import type { Status } from '../../../../common/detection_engine/schemas/common/schemas';
import type { RowRendererId, TimelineIdLiteral } from '../../../../common/types/timeline';
import { StatefulEventsViewer } from '../../../common/components/events_viewer';
import {
displayErrorToast,
displaySuccessToast,
useStateToaster,
} from '../../../common/components/toasters';
import { useSourcererDataView } from '../../../common/containers/sourcerer';
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query';
import { defaultCellActions } from '../../../common/lib/cell_actions/default_cell_actions';
@ -29,7 +22,6 @@ import { useKibana } from '../../../common/lib/kibana';
import type { inputsModel, State } from '../../../common/store';
import { inputsSelectors } from '../../../common/store';
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
import * as i18nCommon from '../../../common/translations';
import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns';
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
@ -38,8 +30,7 @@ import { timelineActions, timelineSelectors } from '../../../timelines/store/tim
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
import type { TimelineModel } from '../../../timelines/store/timeline/model';
import { columns, RenderCellValue } from '../../configurations/security_solution_detections';
import { updateAlertStatusAction } from './actions';
import { AditionalFiltersAction, AlertsUtilityBar } from './alerts_utility_bar';
import { AdditionalFiltersAction } from './additional_filters_action';
import {
alertsDefaultModel,
buildAlertStatusFilter,
@ -47,13 +38,6 @@ import {
} from './default_config';
import { buildTimeRangeFilter } from './helpers';
import * as i18n from './translations';
import type {
SetEventsDeletedProps,
SetEventsLoadingProps,
UpdateAlertsStatusCallback,
UpdateAlertsStatusProps,
} from './types';
interface OwnProps {
defaultFilters?: Filter[];
from: string;
@ -73,7 +57,6 @@ interface OwnProps {
type AlertsTableComponentProps = OwnProps & PropsFromRedux;
export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
clearSelected,
defaultFilters,
from,
globalFilters,
@ -86,9 +69,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
onRuleChange,
onShowBuildingBlockAlertsChanged,
onShowOnlyThreatIndicatorAlertsChanged,
selectedEventIds,
setEventsDeleted,
setEventsLoading,
showBuildingBlockAlerts,
showOnlyThreatIndicatorAlerts,
timelineId,
@ -96,15 +76,12 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
filterGroup = 'open',
}) => {
const dispatch = useDispatch();
const [showClearSelectionAction, setShowClearSelectionAction] = useState(false);
const {
browserFields,
indexPattern: indexPatterns,
selectedPatterns,
} = useSourcererDataView(SourcererScopeName.detections);
const kibana = useKibana();
const [, dispatchToaster] = useStateToaster();
const { addWarning } = useAppToasts();
const ACTION_BUTTON_COUNT = 5;
const getGlobalQuery = useCallback(
@ -123,7 +100,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
],
kqlQuery: globalQuery,
kqlMode: globalQuery.language,
isEventViewer: true,
});
}
return null;
@ -140,66 +116,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
endDate: to,
});
const setEventsLoadingCallback = useCallback(
({ eventIds, isLoading }: SetEventsLoadingProps) => {
setEventsLoading({ id: timelineId, eventIds, isLoading });
},
[setEventsLoading, timelineId]
);
const setEventsDeletedCallback = useCallback(
({ eventIds, isDeleted }: SetEventsDeletedProps) => {
setEventsDeleted({ id: timelineId, eventIds, isDeleted });
},
[setEventsDeleted, timelineId]
);
const onAlertStatusUpdateSuccess = useCallback(
(updated: number, conflicts: number, status: Status) => {
if (conflicts > 0) {
// Partial failure
addWarning({
title: i18nCommon.UPDATE_ALERT_STATUS_FAILED(conflicts),
text: i18nCommon.UPDATE_ALERT_STATUS_FAILED_DETAILED(updated, conflicts),
});
} else {
let title = '';
switch (status) {
case 'closed':
title = i18n.CLOSED_ALERT_SUCCESS_TOAST(updated);
break;
case 'open':
title = i18n.OPENED_ALERT_SUCCESS_TOAST(updated);
break;
case 'acknowledged':
case 'in-progress':
title = i18n.ACKNOWLEDGED_ALERT_SUCCESS_TOAST(updated);
}
displaySuccessToast(title, dispatchToaster);
}
},
[addWarning, dispatchToaster]
);
const onAlertStatusUpdateFailure = useCallback(
(status: Status, error: Error) => {
let title = '';
switch (status) {
case 'closed':
title = i18n.CLOSED_ALERT_FAILED_TOAST;
break;
case 'open':
title = i18n.OPENED_ALERT_FAILED_TOAST;
break;
case 'acknowledged':
case 'in-progress':
title = i18n.ACKNOWLEDGED_ALERT_FAILED_TOAST;
}
displayErrorToast(title, [error.message], dispatchToaster);
},
[dispatchToaster]
);
// Catches state change isSelectAllChecked->false upon user selection change to reset utility bar
useEffect(() => {
if (isSelectAllChecked) {
@ -209,107 +125,12 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
selectAll: false,
})
);
} else {
setShowClearSelectionAction(false);
}
}, [dispatch, isSelectAllChecked, timelineId]);
// Callback for clearing entire selection from utility bar
const clearSelectionCallback = useCallback(() => {
clearSelected({ id: timelineId });
dispatch(
timelineActions.setTGridSelectAll({
id: timelineId,
selectAll: false,
})
);
setShowClearSelectionAction(false);
}, [clearSelected, dispatch, timelineId]);
// Callback for selecting all events on all pages from utility bar
// Dispatches to stateful_body's selectAll via TimelineTypeContext props
// as scope of response data required to actually set selectedEvents
const selectAllOnAllPagesCallback = useCallback(() => {
dispatch(
timelineActions.setTGridSelectAll({
id: timelineId,
selectAll: true,
})
);
setShowClearSelectionAction(true);
}, [dispatch, timelineId]);
const updateAlertsStatusCallback: UpdateAlertsStatusCallback = useCallback(
async (
refetchQuery: inputsModel.Refetch,
{ status, selectedStatus }: UpdateAlertsStatusProps
) => {
await updateAlertStatusAction({
query: showClearSelectionAction
? getGlobalQuery(buildAlertStatusFilter(status))?.filterQuery
: undefined,
alertIds: Object.keys(selectedEventIds),
selectedStatus,
setEventsDeleted: setEventsDeletedCallback,
setEventsLoading: setEventsLoadingCallback,
onAlertStatusUpdateSuccess,
onAlertStatusUpdateFailure,
});
refetchQuery();
},
[
getGlobalQuery,
selectedEventIds,
setEventsDeletedCallback,
setEventsLoadingCallback,
showClearSelectionAction,
onAlertStatusUpdateSuccess,
onAlertStatusUpdateFailure,
]
);
// Callback for creating the AlertsUtilityBar which receives totalCount from EventsViewer component
const utilityBarCallback = useCallback(
(refetchQuery: inputsModel.Refetch, totalCount: number) => {
return (
<AlertsUtilityBar
areEventsLoading={loadingEventIds.length > 0}
clearSelection={clearSelectionCallback}
currentFilter={filterGroup}
hasIndexMaintenance={hasIndexMaintenance}
hasIndexWrite={hasIndexWrite}
onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChanged}
onShowOnlyThreatIndicatorAlertsChanged={onShowOnlyThreatIndicatorAlertsChanged}
selectAll={selectAllOnAllPagesCallback}
selectedEventIds={selectedEventIds}
showBuildingBlockAlerts={showBuildingBlockAlerts}
showClearSelection={showClearSelectionAction}
showOnlyThreatIndicatorAlerts={showOnlyThreatIndicatorAlerts}
totalCount={totalCount}
updateAlertsStatus={updateAlertsStatusCallback.bind(null, refetchQuery)}
/>
);
},
[
clearSelectionCallback,
filterGroup,
hasIndexMaintenance,
hasIndexWrite,
loadingEventIds.length,
onShowBuildingBlockAlertsChanged,
onShowOnlyThreatIndicatorAlertsChanged,
selectAllOnAllPagesCallback,
selectedEventIds,
showBuildingBlockAlerts,
showClearSelectionAction,
showOnlyThreatIndicatorAlerts,
updateAlertsStatusCallback,
]
);
const additionalFiltersComponent = useMemo(
() => (
<AditionalFiltersAction
<AdditionalFiltersAction
areEventsLoading={loadingEventIds.length > 0}
onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChanged}
showBuildingBlockAlerts={showBuildingBlockAlerts}
@ -387,7 +208,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
rowRenderers={defaultRowRenderers}
scopeId={SourcererScopeName.detections}
start={from}
utilityBar={utilityBarCallback}
/>
);
};
@ -414,29 +234,7 @@ const makeMapStateToProps = () => {
return mapStateToProps;
};
const mapDispatchToProps = (dispatch: Dispatch) => ({
clearSelected: ({ id }: { id: string }) => dispatch(timelineActions.clearSelected({ id })),
setEventsLoading: ({
id,
eventIds,
isLoading,
}: {
id: string;
eventIds: string[];
isLoading: boolean;
}) => dispatch(timelineActions.setEventsLoading({ id, eventIds, isLoading })),
setEventsDeleted: ({
id,
eventIds,
isDeleted,
}: {
id: string;
eventIds: string[];
isDeleted: boolean;
}) => dispatch(timelineActions.setEventsDeleted({ id, eventIds, isDeleted })),
});
const connector = connect(makeMapStateToProps, mapDispatchToProps);
const connector = connect(makeMapStateToProps);
type PropsFromRedux = ConnectedProps<typeof connector>;

View file

@ -10,7 +10,7 @@ import { EuiButton, EuiContextMenuPanel, EuiPopover } from '@elastic/eui';
import type { ExceptionListType } from '@kbn/securitysolution-io-ts-list-types';
import { useResponderActionItem } from '../endpoint_responder';
import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
import { TAKE_ACTION } from '../alerts_table/alerts_utility_bar/translations';
import { TAKE_ACTION } from '../alerts_table/additional_filters_action/translations';
import { useExceptionActions } from '../alerts_table/timeline_actions/use_add_exception_actions';
import { useAlertsActions } from '../alerts_table/timeline_actions/use_alerts_actions';
import { useInvestigateInTimeline } from '../alerts_table/timeline_actions/use_investigate_in_timeline';

View file

@ -84,11 +84,8 @@ const GraphOverlayComponent: React.FC<GraphOverlayProps> = ({
const { timelineFullScreen } = useTimelineFullScreen();
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const graphEventId = useDeepEqualSelector(
(state) => (getTimeline(state, timelineId) ?? timelineDefaults).graphEventId
);
const sessionViewConfig = useDeepEqualSelector(
(state) => (getTimeline(state, timelineId) ?? timelineDefaults).sessionViewConfig
const { graphEventId, sessionViewConfig } = useDeepEqualSelector(
(state) => getTimeline(state, timelineId) ?? timelineDefaults
);
const fullScreen = useMemo(

View file

@ -225,7 +225,7 @@ describe('Combined Queries', () => {
ignoreFilterIfFieldNotInIndex: true,
dateFormatTZ: 'America/New_York',
};
test('No Data Provider & No kqlQuery & and isEventViewer is false', () => {
test('No Data Provider & No kqlQuery', () => {
expect(
combineQueries({
config,
@ -239,26 +239,7 @@ describe('Combined Queries', () => {
).toBeNull();
});
test('No Data Provider & No kqlQuery & isEventViewer is true', () => {
const isEventViewer = true;
expect(
combineQueries({
config,
dataProviders: [],
indexPattern: mockIndexPattern,
browserFields: mockBrowserFields,
filters: [],
kqlQuery: { query: '', language: 'kuery' },
kqlMode: 'search',
isEventViewer,
})
).toEqual({
filterQuery: '{"bool":{"must":[],"filter":[],"should":[],"must_not":[]}}',
});
});
test('No Data Provider & No kqlQuery & with Filters', () => {
const isEventViewer = true;
expect(
combineQueries({
config,
@ -293,7 +274,6 @@ describe('Combined Queries', () => {
],
kqlQuery: { query: '', language: 'kuery' },
kqlMode: 'search',
isEventViewer,
})
).toEqual({
filterQuery:

View file

@ -141,7 +141,6 @@ interface CombineQueries {
filters: Filter[];
kqlQuery: Query;
kqlMode: string;
isEventViewer?: boolean;
}
export const combineQueries = ({
@ -152,23 +151,10 @@ export const combineQueries = ({
filters = [],
kqlQuery,
kqlMode,
isEventViewer,
}: CombineQueries): { filterQuery: string | undefined; kqlError: Error | undefined } | null => {
const kuery: Query = { query: '', language: kqlQuery.language };
if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEmpty(filters) && !isEventViewer) {
if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEmpty(filters)) {
return null;
} else if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEventViewer) {
const [filterQuery, kqlError] = convertToBuildEsQuery({
config,
queries: [kuery],
indexPattern,
filters,
});
return {
filterQuery,
kqlError,
};
} else if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && !isEmpty(filters)) {
const [filterQuery, kqlError] = convertToBuildEsQuery({
config,
@ -229,15 +215,6 @@ export const combineQueries = ({
};
};
export const buildCombinedQuery = (combineQueriesParams: CombineQueries) => {
const combinedQuery = combineQueries(combineQueriesParams);
return combinedQuery?.filterQuery
? {
filterQuery: combinedQuery.filterQuery,
}
: null;
};
export const buildTimeRangeFilter = (from: string, to: string): Filter =>
({
range: {

View file

@ -41,9 +41,6 @@ jest.mock('../helpers', () => {
filter: [],
},
}),
buildCombinedQuery: () => ({
filterQuery: '{"bool":{"must":[],"filter":[]}}',
}),
};
});
const defaultProps: TGridIntegratedProps = tGridIntegratedProps;

View file

@ -39,7 +39,7 @@ import type {
import { useDeepEqualSelector } from '../../../hooks/use_selector';
import { defaultHeaders } from '../body/column_headers/default_headers';
import { buildCombinedQuery, getCombinedFilterQuery, resolverIsShowing } from '../helpers';
import { getCombinedFilterQuery, resolverIsShowing } from '../helpers';
import { tGridActions, tGridSelectors } from '../../../store/t_grid';
import { useTimelineEvents, InspectResponse, Refetch } from '../../../container';
import { StatefulBody } from '../body';
@ -194,26 +194,32 @@ const TGridIntegratedComponent: React.FC<TGridIntegratedProps> = ({
}, [dispatch, id, isQueryLoading]);
const justTitle = useMemo(() => <TitleText data-test-subj="title">{title}</TitleText>, [title]);
const esQueryConfig = getEsQueryConfig(uiSettings);
const combinedQueries = buildCombinedQuery({
config: getEsQueryConfig(uiSettings),
dataProviders,
indexPattern,
browserFields,
filters,
kqlQuery: query,
kqlMode,
isEventViewer: true,
});
const filterQuery = useMemo(
() =>
getCombinedFilterQuery({
config: esQueryConfig,
browserFields,
dataProviders,
filters,
from: start,
indexPattern,
kqlMode,
kqlQuery: query,
to: end,
}),
[esQueryConfig, dataProviders, indexPattern, browserFields, filters, start, end, query, kqlMode]
);
const canQueryTimeline = useMemo(
() =>
combinedQueries != null &&
filterQuery != null &&
isLoadingIndexPattern != null &&
!isLoadingIndexPattern &&
!isEmpty(start) &&
!isEmpty(end),
[isLoadingIndexPattern, combinedQueries, start, end]
[isLoadingIndexPattern, filterQuery, start, end]
);
const fields = useMemo(
@ -241,7 +247,7 @@ const TGridIntegratedComponent: React.FC<TGridIntegratedProps> = ({
endDate: end,
entityType,
fields,
filterQuery: combinedQueries?.filterQuery,
filterQuery,
id,
indexNames,
limit: itemsPerPage,
@ -251,23 +257,6 @@ const TGridIntegratedComponent: React.FC<TGridIntegratedProps> = ({
startDate: start,
});
const filterQuery = useMemo(
() =>
getCombinedFilterQuery({
config: getEsQueryConfig(uiSettings),
browserFields,
dataProviders,
filters,
from: start,
indexPattern,
isEventViewer: true,
kqlMode,
kqlQuery: query,
to: end,
}),
[uiSettings, dataProviders, indexPattern, browserFields, filters, start, end, query, kqlMode]
);
const totalCountMinusDeleted = useMemo(
() => (totalCount > 0 ? totalCount - deletedEventIds.length : 0),
[deletedEventIds.length, totalCount]

View file

@ -30,7 +30,7 @@ import type {
} from '../../../../common/types/timeline';
import { useDeepEqualSelector } from '../../../hooks/use_selector';
import { defaultHeaders } from '../body/column_headers/default_headers';
import { combineQueries, getCombinedFilterQuery } from '../helpers';
import { getCombinedFilterQuery } from '../helpers';
import { tGridActions, tGridSelectors } from '../../../store/t_grid';
import type { State } from '../../../store/t_grid';
import { useTimelineEvents } from '../../../container';
@ -170,25 +170,32 @@ const TGridStandaloneComponent: React.FC<TGridStandaloneProps> = ({
}, [dispatch, isQueryLoading]);
const justTitle = useMemo(() => <TitleText data-test-subj="title">{title}</TitleText>, [title]);
const esQueryConfig = getEsQueryConfig(uiSettings);
const combinedQueries = useMemo(
const filterQuery = useMemo(
() =>
combineQueries({
config: getEsQueryConfig(uiSettings),
dataProviders: EMPTY_DATA_PROVIDERS,
indexPattern: indexPatterns,
getCombinedFilterQuery({
config: esQueryConfig,
browserFields,
dataProviders: EMPTY_DATA_PROVIDERS,
filters,
kqlQuery: query,
from: start,
indexPattern: indexPatterns,
kqlMode: 'search',
isEventViewer: true,
kqlQuery: query,
to: end,
}),
[uiSettings, indexPatterns, browserFields, filters, query]
[esQueryConfig, indexPatterns, browserFields, filters, start, end, query]
);
const canQueryTimeline = useMemo(
() => !indexPatternsLoading && combinedQueries != null && !isEmpty(start) && !isEmpty(end),
[indexPatternsLoading, combinedQueries, start, end]
() =>
filterQuery != null &&
indexPatternsLoading != null &&
!indexPatternsLoading &&
!isEmpty(start) &&
!isEmpty(end),
[indexPatternsLoading, filterQuery, start, end]
);
const fields = useMemo(
@ -221,7 +228,7 @@ const TGridStandaloneComponent: React.FC<TGridStandaloneProps> = ({
entityType,
excludeEcsData: true,
fields,
filterQuery: combinedQueries?.filterQuery,
filterQuery,
id: STANDALONE_ID,
indexNames,
limit: itemsPerPageStore,
@ -266,23 +273,6 @@ const TGridStandaloneComponent: React.FC<TGridStandaloneProps> = ({
[deletedEventIds, events]
);
const filterQuery = useMemo(
() =>
getCombinedFilterQuery({
config: getEsQueryConfig(uiSettings),
dataProviders: EMPTY_DATA_PROVIDERS,
indexPattern: indexPatterns,
browserFields,
filters,
kqlQuery: query,
kqlMode: 'search',
isEventViewer: true,
from: start,
to: end,
}),
[uiSettings, indexPatterns, browserFields, filters, query, start, end]
);
useEffect(() => {
setIsQueryLoading(loading);
}, [loading]);

View file

@ -25285,9 +25285,6 @@
"xpack.securitySolution.detectionEngine.alerts.histogram.showingAlertsTitle": "Affichage de : {modifier}{totalAlertsFormatted} {totalAlerts, plural, =1 {alerte} other {alertes}}",
"xpack.securitySolution.detectionEngine.alerts.histogram.topNLabel": "Premiers {fieldName}",
"xpack.securitySolution.detectionEngine.alerts.openedAlertSuccessToastMessage": "Ouverture réussie de {totalAlerts} {totalAlerts, plural, =1 {alerte} other {alertes}}.",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.selectAllAlertsTitle": "Sélectionner un total de {totalAlertsFormatted} {totalAlerts, plural, =1 {alerte} other {alertes}}",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.selectedAlertsTitle": "{selectedAlertsFormatted} {selectedAlerts, plural, =1 {alerte} other {alertes}} sélectionnée(s)",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.showingAlertsTitle": "Affichage de {totalAlertsFormatted} {totalAlerts, plural, =1 {alerte} other {alertes}}",
"xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditConfirmation.confirmButtonLabel": "Modifier {customRulesCount, plural, =1 {# règle personnalisée} other {# règles personnalisées}}",
"xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.setIndexPatternsWarningCallout": "Vous êtes sur le point d'écraser les modèles d'indexation pour {rulesCount, plural, one {# règle sélectionnée} other {# règles sélectionnées}}. Sélectionnez Enregistrer pour appliquer les modifications.",
"xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.setTagsWarningCallout": "Vous êtes sur le point d'écraser les balises pour {rulesCount, plural, one {# règle sélectionnée} other {# règles sélectionnées}}. Sélectionnez Enregistrer pour appliquer les modifications.",
@ -26167,10 +26164,6 @@
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showBuildingBlockTitle": "Inclure les alertes fondamentales",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showOnlyThreatIndicatorAlerts": "Afficher uniquement les alertes d'indicateur de menaces",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersTitle": "Filtres supplémentaires",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.acknowledgedSelectedTitle": "Marquer comme reconnue",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.closeSelectedTitle": "Fermer la sélection",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.openSelectedTitle": "Ouvrir la sélection",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.clearSelectionTitle": "Effacer la sélection",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.takeActionTitle": "Entreprendre une action",
"xpack.securitySolution.detectionEngine.alertTitle": "Alertes",
"xpack.securitySolution.detectionEngine.buttonManageRules": "Gérer les règles",

View file

@ -25264,9 +25264,6 @@
"xpack.securitySolution.detectionEngine.alerts.histogram.showingAlertsTitle": "{modifier}{totalAlertsFormatted} {totalAlerts, plural, other {件のアラート}}を表示しています",
"xpack.securitySolution.detectionEngine.alerts.histogram.topNLabel": "トップ{fieldName}",
"xpack.securitySolution.detectionEngine.alerts.openedAlertSuccessToastMessage": "{totalAlerts} {totalAlerts, plural, other {件のアラート}}を正常に開きました。",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.selectAllAlertsTitle": "すべての{totalAlertsFormatted} {totalAlerts, plural, other {件のアラート}}を選択",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.selectedAlertsTitle": "Selected {selectedAlertsFormatted} {selectedAlerts, plural, other {件のアラート}}",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.showingAlertsTitle": "すべての{totalAlertsFormatted} {totalAlerts, plural, other {件のアラート}}を表示しています",
"xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditConfirmation.confirmButtonLabel": "{customRulesCount, plural, other {# 個のカスタムルール}}を編集",
"xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.setIndexPatternsWarningCallout": "{rulesCount, plural, other {# 個の選択したルール}} のインデックスパターンを上書きしようとしています。[保存]をクリックすると、変更が適用されます。",
"xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.setTagsWarningCallout": "{rulesCount, plural, other {# 個の選択したルール}}のタグを上書きしようとしています。[保存]をクリックすると、変更が適用されます。\n",
@ -26144,10 +26141,6 @@
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showBuildingBlockTitle": "基本アラートを含める",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showOnlyThreatIndicatorAlerts": "脅威インジケーターアラートのみを表示",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersTitle": "追加のフィルター",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.acknowledgedSelectedTitle": "確認済みに設定",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.closeSelectedTitle": "選択した項目を閉じる",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.openSelectedTitle": "選択した項目を開く",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.clearSelectionTitle": "選択した項目をクリア",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.takeActionTitle": "アクションを実行",
"xpack.securitySolution.detectionEngine.alertTitle": "アラート",
"xpack.securitySolution.detectionEngine.buttonManageRules": "ルールの管理",

View file

@ -25293,9 +25293,6 @@
"xpack.securitySolution.detectionEngine.alerts.histogram.showingAlertsTitle": "正在显示:{modifier}{totalAlertsFormatted} 个{totalAlerts, plural, other {告警}}",
"xpack.securitySolution.detectionEngine.alerts.histogram.topNLabel": "排名靠前的{fieldName}",
"xpack.securitySolution.detectionEngine.alerts.openedAlertSuccessToastMessage": "已成功打开 {totalAlerts} 个{totalAlerts, plural, other {告警}}。",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.selectAllAlertsTitle": "选择全部 {totalAlertsFormatted} 个{totalAlerts, plural, other {告警}}",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.selectedAlertsTitle": "已选择 {selectedAlertsFormatted} 个{selectedAlerts, plural, other {告警}}",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.showingAlertsTitle": "正在显示 {totalAlertsFormatted} 个{totalAlerts, plural, other {告警}}",
"xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditConfirmation.confirmButtonLabel": "编辑 {customRulesCount, plural, other {# 个定制规则}}",
"xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.setIndexPatternsWarningCallout": "您即将覆盖 {rulesCount, plural, other {# 个选定规则}}的索引模式,按“保存”可应用更改。",
"xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.setTagsWarningCallout": "您即将覆盖 {rulesCount, plural, other {# 个选定规则}}的标签,按“保存”可应用更改。",
@ -26175,10 +26172,6 @@
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showBuildingBlockTitle": "包括构建块告警",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showOnlyThreatIndicatorAlerts": "仅显示威胁指标告警",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersTitle": "其他筛选",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.acknowledgedSelectedTitle": "标记为已确认",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.closeSelectedTitle": "关闭所选",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.openSelectedTitle": "打开所选",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.clearSelectionTitle": "清除所选内容",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.takeActionTitle": "采取操作",
"xpack.securitySolution.detectionEngine.alertTitle": "告警",
"xpack.securitySolution.detectionEngine.buttonManageRules": "管理规则",