mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[SecuritySolution] add global filter to topN (#112401)
* add global filter to topN * sort lines * add unit test * fix duplicate queries Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
0cbdf3f259
commit
210fb50d0b
9 changed files with 103 additions and 34 deletions
|
@ -177,8 +177,7 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
<InspectButtonContainer>
|
||||
{tGridEnabled ? (
|
||||
timelinesUi.getTGrid<'embedded'>({
|
||||
id,
|
||||
type: 'embedded',
|
||||
additionalFilters,
|
||||
browserFields,
|
||||
bulkActions,
|
||||
columns,
|
||||
|
@ -189,9 +188,12 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
end,
|
||||
entityType,
|
||||
filters: globalFilters,
|
||||
filterStatus: currentFilter,
|
||||
globalFullScreen,
|
||||
graphEventId,
|
||||
graphOverlay,
|
||||
hasAlertsCrud,
|
||||
id,
|
||||
indexNames: selectedPatterns,
|
||||
indexPattern,
|
||||
isLive,
|
||||
|
@ -199,19 +201,17 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
itemsPerPage,
|
||||
itemsPerPageOptions: itemsPerPageOptions!,
|
||||
kqlMode,
|
||||
query,
|
||||
leadingControlColumns,
|
||||
onRuleChange,
|
||||
query,
|
||||
renderCellValue,
|
||||
rowRenderers,
|
||||
setQuery,
|
||||
start,
|
||||
sort,
|
||||
additionalFilters,
|
||||
graphEventId,
|
||||
filterStatus: currentFilter,
|
||||
leadingControlColumns,
|
||||
trailingControlColumns,
|
||||
start,
|
||||
tGridEventRenderedViewEnabled,
|
||||
trailingControlColumns,
|
||||
type: 'embedded',
|
||||
unit,
|
||||
})
|
||||
) : (
|
||||
|
|
|
@ -21,6 +21,7 @@ import { useSourcererScope } from '../../../containers/sourcerer';
|
|||
import { TooltipWithKeyboardShortcut } from '../../accessibility';
|
||||
import { getAdditionalScreenReaderOnlyContext } from '../utils';
|
||||
import { SHOW_TOP_N_KEYBOARD_SHORTCUT } from '../keyboard_shortcut_constants';
|
||||
import { Filter } from '../../../../../../../../src/plugins/data/public';
|
||||
|
||||
const SHOW_TOP = (fieldName: string) =>
|
||||
i18n.translate('xpack.securitySolution.hoverActions.showTopTooltip', {
|
||||
|
@ -35,11 +36,12 @@ interface Props {
|
|||
Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon | typeof EuiContextMenuItem;
|
||||
enablePopOver?: boolean;
|
||||
field: string;
|
||||
globalFilters?: Filter[];
|
||||
onClick: () => void;
|
||||
onFilterAdded?: () => void;
|
||||
ownFocus: boolean;
|
||||
showTopN: boolean;
|
||||
showTooltip?: boolean;
|
||||
showTopN: boolean;
|
||||
timelineId?: string | null;
|
||||
value?: string[] | string | null;
|
||||
}
|
||||
|
@ -56,6 +58,7 @@ export const ShowTopNButton: React.FC<Props> = React.memo(
|
|||
showTopN,
|
||||
timelineId,
|
||||
value,
|
||||
globalFilters,
|
||||
}) => {
|
||||
const activeScope: SourcererScopeName =
|
||||
timelineId === TimelineId.active
|
||||
|
@ -128,9 +131,10 @@ export const ShowTopNButton: React.FC<Props> = React.memo(
|
|||
timelineId={timelineId ?? undefined}
|
||||
toggleTopN={onClick}
|
||||
value={value}
|
||||
globalFilters={globalFilters}
|
||||
/>
|
||||
),
|
||||
[browserFields, field, indexPattern, onClick, onFilterAdded, timelineId, value]
|
||||
[browserFields, field, indexPattern, onClick, onFilterAdded, timelineId, value, globalFilters]
|
||||
);
|
||||
|
||||
return showTopN ? (
|
||||
|
|
|
@ -160,6 +160,60 @@ let testProps = {
|
|||
};
|
||||
|
||||
describe('StatefulTopN', () => {
|
||||
describe('rendering globalFilter', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
const globalFilters = [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'signal.rule.id',
|
||||
params: {
|
||||
query: 'd62249f0-1632-11ec-b035-19607969bc20',
|
||||
},
|
||||
},
|
||||
query: {
|
||||
match_phrase: {
|
||||
'signal.rule.id': 'd62249f0-1632-11ec-b035-19607969bc20',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<StatefulTopN {...testProps} globalFilters={globalFilters} />
|
||||
</TestProviders>
|
||||
);
|
||||
});
|
||||
|
||||
test(`provides filters from non Redux state when rendering in alerts table`, () => {
|
||||
const props = wrapper.find('[data-test-subj="top-n"]').first().props() as Props;
|
||||
|
||||
expect(props.filters).toEqual([
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'signal.rule.id',
|
||||
params: {
|
||||
query: 'd62249f0-1632-11ec-b035-19607969bc20',
|
||||
},
|
||||
},
|
||||
query: {
|
||||
match_phrase: {
|
||||
'signal.rule.id': 'd62249f0-1632-11ec-b035-19607969bc20',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rendering in a global NON-timeline context', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
|
||||
|
|
|
@ -41,11 +41,11 @@ const makeMapStateToProps = () => {
|
|||
// The mapped Redux state provided to this component includes the global
|
||||
// filters that appear at the top of most views in the app, and all the
|
||||
// filters in the active timeline:
|
||||
const mapStateToProps = (state: State) => {
|
||||
const mapStateToProps = (state: State, ownProps: { globalFilters?: Filter[] }) => {
|
||||
const activeTimeline: TimelineModel = getTimeline(state, TimelineId.active) ?? timelineDefaults;
|
||||
const activeTimelineFilters = activeTimeline.filters ?? EMPTY_FILTERS;
|
||||
const activeTimelineInput: inputsModel.InputsRange = getInputsTimeline(state);
|
||||
|
||||
const { globalFilters } = ownProps;
|
||||
return {
|
||||
activeTimelineEventType: activeTimeline.eventType,
|
||||
activeTimelineFilters:
|
||||
|
@ -59,7 +59,7 @@ const makeMapStateToProps = () => {
|
|||
dataProviders:
|
||||
activeTimeline.activeTab === TimelineTabs.query ? activeTimeline.dataProviders : [],
|
||||
globalQuery: getGlobalQuerySelector(state),
|
||||
globalFilters: getGlobalFiltersQuerySelector(state),
|
||||
globalFilters: globalFilters ?? getGlobalFiltersQuerySelector(state),
|
||||
kqlMode: activeTimeline.kqlMode,
|
||||
};
|
||||
};
|
||||
|
@ -82,6 +82,7 @@ export interface OwnProps {
|
|||
toggleTopN: () => void;
|
||||
onFilterAdded?: () => void;
|
||||
value?: string[] | string | null;
|
||||
globalFilters?: Filter[];
|
||||
}
|
||||
type PropsFromRedux = ConnectedProps<typeof connector>;
|
||||
type Props = OwnProps & PropsFromRedux;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import React, { useCallback, useState, useMemo } from 'react';
|
||||
import { Filter } from '../../../../../../../src/plugins/data/public';
|
||||
|
||||
import type {
|
||||
BrowserFields,
|
||||
|
@ -167,11 +168,13 @@ export const defaultCellActions: TGridCellAction[] = [
|
|||
({
|
||||
browserFields,
|
||||
data,
|
||||
globalFilters,
|
||||
timelineId,
|
||||
pageSize,
|
||||
}: {
|
||||
browserFields: BrowserFields;
|
||||
data: TimelineNonEcsData[][];
|
||||
globalFilters?: Filter[];
|
||||
timelineId: string;
|
||||
pageSize: number;
|
||||
}) =>
|
||||
|
@ -205,6 +208,7 @@ export const defaultCellActions: TGridCellAction[] = [
|
|||
enablePopOver
|
||||
data-test-subj="hover-actions-show-top-n"
|
||||
field={columnId}
|
||||
globalFilters={globalFilters}
|
||||
onClick={onClick}
|
||||
onFilterAdded={onFilterAdded}
|
||||
ownFocus={false}
|
||||
|
|
|
@ -391,7 +391,6 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
const alertsTableDefaultFilters = useMemo(
|
||||
() => [
|
||||
...buildAlertsRuleIdFilter(ruleId),
|
||||
...filters,
|
||||
...(ruleRegistryEnabled
|
||||
? [
|
||||
// TODO: Once we are past experimental phase this code should be removed
|
||||
|
@ -400,7 +399,7 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
: [...buildShowBuildingBlockFilter(showBuildingBlockAlerts)]),
|
||||
...buildThreatMatchFilter(showOnlyThreatIndicatorAlerts),
|
||||
],
|
||||
[ruleId, filters, ruleRegistryEnabled, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts]
|
||||
[ruleId, ruleRegistryEnabled, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts]
|
||||
);
|
||||
|
||||
const alertMergedFilters = useMemo(
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
import { EuiDataGridColumn, EuiDataGridColumnCellActionProps } from '@elastic/eui';
|
||||
import { IFieldSubType } from '../../../../../../../src/plugins/data/common';
|
||||
import { Filter, IFieldSubType } from '../../../../../../../src/plugins/data/common';
|
||||
import { BrowserFields } from '../../../search_strategy/index_fields';
|
||||
import { TimelineNonEcsData } from '../../../search_strategy/timeline';
|
||||
|
||||
|
@ -45,14 +45,16 @@ export type ColumnId = string;
|
|||
export type TGridCellAction = ({
|
||||
browserFields,
|
||||
data,
|
||||
timelineId,
|
||||
globalFilters,
|
||||
pageSize,
|
||||
timelineId,
|
||||
}: {
|
||||
browserFields: BrowserFields;
|
||||
/** each row of data is represented as one TimelineNonEcsData[] */
|
||||
data: TimelineNonEcsData[][];
|
||||
timelineId: string;
|
||||
globalFilters?: Filter[];
|
||||
pageSize: number;
|
||||
timelineId: string;
|
||||
}) => (props: EuiDataGridColumnCellActionProps) => ReactNode;
|
||||
|
||||
/** The specification of a column header */
|
||||
|
|
|
@ -74,6 +74,7 @@ import type { EuiTheme } from '../../../../../../../src/plugins/kibana_react/com
|
|||
import { ViewSelection } from '../event_rendered_view/selector';
|
||||
import { EventRenderedView } from '../event_rendered_view';
|
||||
import { useDataGridHeightHack } from './height_hack';
|
||||
import { Filter } from '../../../../../../../src/plugins/data/public';
|
||||
|
||||
const StatefulAlertStatusBulkActions = lazy(
|
||||
() => import('../toolbar/bulk_actions/alert_status_bulk_actions')
|
||||
|
@ -86,6 +87,7 @@ interface OwnProps {
|
|||
bulkActions?: BulkActionsProp;
|
||||
data: TimelineItem[];
|
||||
defaultCellActions?: TGridCellAction[];
|
||||
filters?: Filter[];
|
||||
filterQuery: string;
|
||||
filterStatus?: AlertStatus;
|
||||
id: string;
|
||||
|
@ -300,15 +302,18 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
|
|||
data,
|
||||
defaultCellActions,
|
||||
filterQuery,
|
||||
filters,
|
||||
filterStatus,
|
||||
hasAlertsCrud,
|
||||
hasAlertsCrudPermissions,
|
||||
id,
|
||||
indexNames,
|
||||
isEventViewer = false,
|
||||
isLoading,
|
||||
isSelectAllChecked,
|
||||
itemsPerPageOptions,
|
||||
leadingControlColumns = EMPTY_CONTROL_COLUMNS,
|
||||
loadingEventIds,
|
||||
isLoading,
|
||||
loadPage,
|
||||
onRuleChange,
|
||||
pageSize,
|
||||
|
@ -322,11 +327,9 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
|
|||
tableView = 'gridView',
|
||||
tabType,
|
||||
totalItems,
|
||||
totalSelectAllAlerts,
|
||||
trailingControlColumns = EMPTY_CONTROL_COLUMNS,
|
||||
unit = defaultUnit,
|
||||
hasAlertsCrud,
|
||||
hasAlertsCrudPermissions,
|
||||
totalSelectAllAlerts,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const getManageTimeline = useMemo(() => tGridSelectors.getManageTimelineById(), []);
|
||||
|
@ -641,10 +644,11 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
|
|||
columnHeaders.map((header) => {
|
||||
const buildAction = (tGridCellAction: TGridCellAction) =>
|
||||
tGridCellAction({
|
||||
data: data.map((row) => row.data),
|
||||
browserFields,
|
||||
timelineId: id,
|
||||
data: data.map((row) => row.data),
|
||||
globalFilters: filters,
|
||||
pageSize,
|
||||
timelineId: id,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -653,7 +657,7 @@ export const BodyComponent = React.memo<StatefulBodyProps>(
|
|||
header.tGridCellActions?.map(buildAction) ?? defaultCellActions?.map(buildAction),
|
||||
};
|
||||
}),
|
||||
[browserFields, columnHeaders, data, defaultCellActions, id, pageSize]
|
||||
[browserFields, columnHeaders, data, defaultCellActions, id, pageSize, filters]
|
||||
);
|
||||
|
||||
const renderTGridCellValue = useMemo(() => {
|
||||
|
|
|
@ -347,30 +347,31 @@ const TGridIntegratedComponent: React.FC<TGridIntegratedProps> = ({
|
|||
>
|
||||
<ScrollableFlexItem grow={1}>
|
||||
<StatefulBody
|
||||
hasAlertsCrud={hasAlertsCrud}
|
||||
activePage={pageInfo.activePage}
|
||||
browserFields={browserFields}
|
||||
bulkActions={bulkActions}
|
||||
filterQuery={filterQuery}
|
||||
data={nonDeletedEvents}
|
||||
defaultCellActions={defaultCellActions}
|
||||
filterQuery={filterQuery}
|
||||
filters={filters}
|
||||
filterStatus={filterStatus}
|
||||
hasAlertsCrud={hasAlertsCrud}
|
||||
id={id}
|
||||
indexNames={indexNames}
|
||||
isEventViewer={true}
|
||||
itemsPerPageOptions={itemsPerPageOptions}
|
||||
leadingControlColumns={leadingControlColumns}
|
||||
loadPage={loadPage}
|
||||
onRuleChange={onRuleChange}
|
||||
pageSize={itemsPerPage}
|
||||
refetch={refetch}
|
||||
renderCellValue={renderCellValue}
|
||||
rowRenderers={rowRenderers}
|
||||
tabType={TimelineTabs.query}
|
||||
tableView={tableView}
|
||||
tabType={TimelineTabs.query}
|
||||
totalItems={totalCountMinusDeleted}
|
||||
unit={unit}
|
||||
filterStatus={filterStatus}
|
||||
leadingControlColumns={leadingControlColumns}
|
||||
trailingControlColumns={trailingControlColumns}
|
||||
refetch={refetch}
|
||||
indexNames={indexNames}
|
||||
unit={unit}
|
||||
/>
|
||||
</ScrollableFlexItem>
|
||||
</FullWidthFlexGroup>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue