mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[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:
parent
908a01b5a6
commit
fe646b297d
19 changed files with 259 additions and 1134 deletions
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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',
|
||||
}
|
||||
);
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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',
|
||||
}
|
||||
);
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -41,9 +41,6 @@ jest.mock('../helpers', () => {
|
|||
filter: [],
|
||||
},
|
||||
}),
|
||||
buildCombinedQuery: () => ({
|
||||
filterQuery: '{"bool":{"must":[],"filter":[]}}',
|
||||
}),
|
||||
};
|
||||
});
|
||||
const defaultProps: TGridIntegratedProps = tGridIntegratedProps;
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "ルールの管理",
|
||||
|
|
|
@ -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": "管理规则",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue