diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/default_config.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/default_config.tsx
index 6d82897aaf010..d21551287fb3e 100644
--- a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/default_config.tsx
+++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/default_config.tsx
@@ -36,6 +36,7 @@ import {
SetEventsLoadingProps,
UpdateTimelineLoading,
} from './types';
+import { Ecs } from '../../../graphql/types';
export const buildAlertStatusFilter = (status: Status): Filter[] => [
{
@@ -173,11 +174,28 @@ export const requiredFieldsForActions = [
'signal.rule.id',
];
+interface AlertActionArgs {
+ apolloClient?: ApolloClient<{}>;
+ canUserCRUD: boolean;
+ createTimeline: CreateTimeline;
+ dispatch: Dispatch;
+ ecsRowData: Ecs;
+ hasIndexWrite: boolean;
+ onAlertStatusUpdateFailure: (status: Status, error: Error) => void;
+ onAlertStatusUpdateSuccess: (count: number, status: Status) => void;
+ setEventsDeleted: ({ eventIds, isDeleted }: SetEventsDeletedProps) => void;
+ setEventsLoading: ({ eventIds, isLoading }: SetEventsLoadingProps) => void;
+ status: Status;
+ timelineId: string;
+ updateTimelineIsLoading: UpdateTimelineLoading;
+}
+
export const getAlertActions = ({
apolloClient,
canUserCRUD,
createTimeline,
dispatch,
+ ecsRowData,
hasIndexWrite,
onAlertStatusUpdateFailure,
onAlertStatusUpdateSuccess,
@@ -186,20 +204,7 @@ export const getAlertActions = ({
status,
timelineId,
updateTimelineIsLoading,
-}: {
- apolloClient?: ApolloClient<{}>;
- canUserCRUD: boolean;
- createTimeline: CreateTimeline;
- dispatch: Dispatch;
- hasIndexWrite: boolean;
- onAlertStatusUpdateFailure: (status: Status, error: Error) => void;
- onAlertStatusUpdateSuccess: (count: number, status: Status) => void;
- setEventsDeleted: ({ eventIds, isDeleted }: SetEventsDeletedProps) => void;
- setEventsLoading: ({ eventIds, isLoading }: SetEventsLoadingProps) => void;
- status: Status;
- timelineId: string;
- updateTimelineIsLoading: UpdateTimelineLoading;
-}): TimelineRowAction[] => {
+}: AlertActionArgs): TimelineRowAction[] => {
const openAlertActionComponent: TimelineRowAction = {
ariaLabel: 'Open alert',
content: {i18n.ACTION_OPEN_ALERT},
diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx
index 9ff368aff2bf6..f99a0256c0b3f 100644
--- a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx
@@ -7,8 +7,8 @@
import React from 'react';
import { shallow } from 'enzyme';
-import { TestProviders } from '../../../common/mock/test_providers';
import { TimelineId } from '../../../../common/types/timeline';
+import { TestProviders } from '../../../common/mock';
import { AlertsTableComponent } from './index';
describe('AlertsTableComponent', () => {
diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx
index 65f225c054598..b655c100a1c6f 100644
--- a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx
+++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx
@@ -48,6 +48,7 @@ import {
displaySuccessToast,
displayErrorToast,
} from '../../../common/components/toasters';
+import { Ecs } from '../../../graphql/types';
import { getInvestigateInResolverAction } from '../../../timelines/components/timeline/body/helpers';
interface OwnProps {
@@ -290,20 +291,21 @@ export const AlertsTableComponent: React.FC = ({
// Send to Timeline / Update Alert Status Actions for each table row
const additionalActions = useMemo(
- () =>
+ () => (ecsRowData: Ecs) =>
getAlertActions({
apolloClient,
canUserCRUD,
+ createTimeline: createTimelineCallback,
+ ecsRowData,
dispatch,
hasIndexWrite,
- createTimeline: createTimelineCallback,
- setEventsLoading: setEventsLoadingCallback,
+ onAlertStatusUpdateFailure,
+ onAlertStatusUpdateSuccess,
setEventsDeleted: setEventsDeletedCallback,
+ setEventsLoading: setEventsLoadingCallback,
status: filterGroup,
timelineId,
updateTimelineIsLoading,
- onAlertStatusUpdateSuccess,
- onAlertStatusUpdateFailure,
}),
[
apolloClient,
@@ -328,17 +330,19 @@ export const AlertsTableComponent: React.FC = ({
return [...defaultFilters, ...buildAlertStatusFilter(filterGroup)];
}
}, [defaultFilters, filterGroup]);
+ const { filterManager } = useKibana().services.data.query;
const { initializeTimeline, setTimelineRowActions } = useManageTimeline();
useEffect(() => {
initializeTimeline({
defaultModel: alertsDefaultModel,
documentType: i18n.ALERTS_DOCUMENT_TYPE,
+ filterManager,
footerText: i18n.TOTAL_COUNT_OF_ALERTS,
id: timelineId,
loadingText: i18n.LOADING_ALERTS,
selectAll: canUserCRUD ? selectAll : false,
- timelineRowActions: [getInvestigateInResolverAction({ dispatch, timelineId })],
+ timelineRowActions: () => [getInvestigateInResolverAction({ dispatch, timelineId })],
title: i18n.ALERTS_TABLE_TITLE,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx
index 12b2853f8f7e1..bf2d8948b7292 100644
--- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx
@@ -14,6 +14,7 @@ import { alertsDefaultModel } from './default_headers';
import { useManageTimeline } from '../../../timelines/components/manage_timeline';
import { getInvestigateInResolverAction } from '../../../timelines/components/timeline/body/helpers';
import * as i18n from './translations';
+import { useKibana } from '../../lib/kibana';
export interface OwnProps {
end: number;
@@ -69,15 +70,17 @@ const AlertsTableComponent: React.FC = ({
}) => {
const dispatch = useDispatch();
const alertsFilter = useMemo(() => [...defaultAlertsFilters, ...pageFilters], [pageFilters]);
+ const { filterManager } = useKibana().services.data.query;
const { initializeTimeline } = useManageTimeline();
useEffect(() => {
initializeTimeline({
id: timelineId,
documentType: i18n.ALERTS_DOCUMENT_TYPE,
+ filterManager,
defaultModel: alertsDefaultModel,
footerText: i18n.TOTAL_COUNT_OF_ALERTS,
- timelineRowActions: [getInvestigateInResolverAction({ dispatch, timelineId })],
+ timelineRowActions: () => [getInvestigateInResolverAction({ dispatch, timelineId })],
title: i18n.ALERTS_TABLE_TITLE,
unit: i18n.UNIT,
});
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
index 910030d41a23e..0a1f95d51e300 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
@@ -93,23 +93,14 @@ const EventsViewerComponent: React.FC = ({
}) => {
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
const kibana = useKibana();
- const { filterManager } = useKibana().services.data.query;
const [isQueryLoading, setIsQueryLoading] = useState(false);
- const {
- getManageTimelineById,
- setIsTimelineLoading,
- setTimelineFilterManager,
- } = useManageTimeline();
+ const { getManageTimelineById, setIsTimelineLoading } = useManageTimeline();
useEffect(() => {
setIsTimelineLoading({ id, isLoading: isQueryLoading });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isQueryLoading]);
- useEffect(() => {
- setTimelineFilterManager({ id, filterManager });
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [filterManager]);
const { queryFields, title, unit } = useMemo(() => getManageTimelineById(id), [
getManageTimelineById,
diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx
index 58026a28a04ce..e2ec1b0e95b58 100644
--- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx
@@ -65,7 +65,7 @@ export const EventsQueryTabBody = ({
initializeTimeline({
id: TimelineId.hostsPageEvents,
defaultModel: eventsDefaultModel,
- timelineRowActions: [
+ timelineRowActions: () => [
getInvestigateInResolverAction({ dispatch, timelineId: TimelineId.hostsPageEvents }),
],
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx
index c71087de4a07e..99d79a2441ccc 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx
@@ -14,16 +14,18 @@ import { SubsetTimelineModel } from '../../store/timeline/model';
import * as i18n from '../../../common/components/events_viewer/translations';
import * as i18nF from '../timeline/footer/translations';
import { timelineDefaults as timelineDefaultModel } from '../../store/timeline/defaults';
+import { Ecs } from '../../../graphql/types';
interface ManageTimelineInit {
documentType?: string;
defaultModel?: SubsetTimelineModel;
+ filterManager?: FilterManager;
footerText?: string;
id: string;
indexToAdd?: string[] | null;
loadingText?: string;
selectAll?: boolean;
- timelineRowActions: TimelineRowAction[];
+ timelineRowActions: (ecsData: Ecs) => TimelineRowAction[];
title?: string;
unit?: (totalCount: number) => string;
}
@@ -39,7 +41,7 @@ interface ManageTimeline {
loadingText: string;
queryFields: string[];
selectAll: boolean;
- timelineRowActions: TimelineRowAction[];
+ timelineRowActions: (ecsData: Ecs) => TimelineRowAction[];
title: string;
unit: (totalCount: number) => string;
}
@@ -67,12 +69,10 @@ type ActionManageTimeline =
| {
type: 'SET_TIMELINE_ACTIONS';
id: string;
- payload: { queryFields?: string[]; timelineRowActions: TimelineRowAction[] };
- }
- | {
- type: 'SET_TIMELINE_FILTER_MANAGER';
- id: string;
- payload: { filterManager: FilterManager };
+ payload: {
+ queryFields?: string[];
+ timelineRowActions: (ecsData: Ecs) => TimelineRowAction[];
+ };
};
export const getTimelineDefaults = (id: string) => ({
@@ -85,7 +85,7 @@ export const getTimelineDefaults = (id: string) => ({
id,
isLoading: false,
queryFields: [],
- timelineRowActions: [],
+ timelineRowActions: () => [],
title: i18n.EVENTS,
unit: (n: number) => i18n.UNIT(n),
});
@@ -112,7 +112,6 @@ const reducerManageTimeline = (
},
} as ManageTimelineById;
case 'SET_TIMELINE_ACTIONS':
- case 'SET_TIMELINE_FILTER_MANAGER':
return {
...state,
[action.id]: {
@@ -143,9 +142,8 @@ interface UseTimelineManager {
setTimelineRowActions: (actionsArgs: {
id: string;
queryFields?: string[];
- timelineRowActions: TimelineRowAction[];
+ timelineRowActions: (ecsData: Ecs) => TimelineRowAction[];
}) => void;
- setTimelineFilterManager: (filterArgs: { id: string; filterManager: FilterManager }) => void;
}
const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseTimelineManager => {
@@ -169,7 +167,7 @@ const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseT
}: {
id: string;
queryFields?: string[];
- timelineRowActions: TimelineRowAction[];
+ timelineRowActions: (ecsData: Ecs) => TimelineRowAction[];
}) => {
dispatch({
type: 'SET_TIMELINE_ACTIONS',
@@ -180,17 +178,6 @@ const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseT
[]
);
- const setTimelineFilterManager = useCallback(
- ({ id, filterManager }: { id: string; filterManager: FilterManager }) => {
- dispatch({
- type: 'SET_TIMELINE_FILTER_MANAGER',
- id,
- payload: { filterManager },
- });
- },
- []
- );
-
const setIsTimelineLoading = useCallback(
({ id, isLoading }: { id: string; isLoading: boolean }) => {
dispatch({
@@ -219,7 +206,7 @@ const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseT
if (state[id] != null) {
return state[id];
}
- initializeTimeline({ id, timelineRowActions: [] });
+ initializeTimeline({ id, timelineRowActions: () => [] });
return getTimelineDefaults(id);
},
[initializeTimeline, state]
@@ -234,7 +221,6 @@ const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseT
setIndexToAdd,
setIsTimelineLoading,
setTimelineRowActions,
- setTimelineFilterManager,
};
};
@@ -246,7 +232,6 @@ const init = {
initializeTimeline: () => noop,
setIsTimelineLoading: () => noop,
setTimelineRowActions: () => noop,
- setTimelineFilterManager: () => noop,
};
const ManageTimelineContext = createContext(init);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
index ae00edf5d1429..88ee8346c8ab2 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
@@ -89,10 +89,10 @@ export const EventColumnView = React.memo(
updateNote,
}) => {
const { getManageTimelineById } = useManageTimeline();
- const timelineActions = useMemo(() => getManageTimelineById(timelineId).timelineRowActions, [
- getManageTimelineById,
- timelineId,
- ]);
+ const timelineActions = useMemo(
+ () => getManageTimelineById(timelineId).timelineRowActions(ecsData),
+ [ecsData, getManageTimelineById, timelineId]
+ );
const [isPopoverOpen, setPopover] = useState(false);
const onButtonClick = useCallback(() => {
@@ -105,6 +105,7 @@ export const EventColumnView = React.memo(
const button = (
(
}) => {
const containerElementRef = useRef(null);
const { getManageTimelineById } = useManageTimeline();
- const timelineActions = useMemo(() => getManageTimelineById(id).timelineRowActions, [
- getManageTimelineById,
- id,
- ]);
+ const timelineActions = useMemo(
+ () => (data.length > 0 ? getManageTimelineById(id).timelineRowActions(data[0].ecs) : []),
+ [data, getManageTimelineById, id]
+ );
const additionalActionWidth = useMemo(() => {
let hasContextMenu = false;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx
index ed7f8a447c51d..b930325c3d35d 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx
@@ -172,26 +172,19 @@ export const TimelineComponent: React.FC = ({
[sort.columnId, sort.sortDirection]
);
const [isQueryLoading, setIsQueryLoading] = useState(false);
- const {
- initializeTimeline,
- setIndexToAdd,
- setIsTimelineLoading,
- setTimelineFilterManager,
- } = useManageTimeline();
+ const { initializeTimeline, setIndexToAdd, setIsTimelineLoading } = useManageTimeline();
useEffect(() => {
initializeTimeline({
+ filterManager,
id,
indexToAdd,
- timelineRowActions: [getInvestigateInResolverAction({ dispatch, timelineId: id })],
+ timelineRowActions: () => [getInvestigateInResolverAction({ dispatch, timelineId: id })],
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
setIsTimelineLoading({ id, isLoading: isQueryLoading || loadingIndexName });
}, [loadingIndexName, id, isQueryLoading, setIsTimelineLoading]);
- useEffect(() => {
- setTimelineFilterManager({ id, filterManager });
- }, [filterManager, id, setTimelineFilterManager]);
useEffect(() => {
setIndexToAdd({ id, indexToAdd });