Skip to content

Commit

Permalink
[Security Solution] [Timeline] Timeline manager tweaks (#69988) (#71016)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephmilovic authored Jul 8, 2020
1 parent d15242b commit adbe8ff
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
SetEventsLoadingProps,
UpdateTimelineLoading,
} from './types';
import { Ecs } from '../../../graphql/types';

export const buildAlertStatusFilter = (status: Status): Filter[] => [
{
Expand Down Expand Up @@ -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,
Expand All @@ -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: <EuiText size="m">{i18n.ACTION_OPEN_ALERT}</EuiText>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -290,20 +291,21 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({

// 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,
Expand All @@ -328,17 +330,19 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -69,15 +70,17 @@ const AlertsTableComponent: React.FC<Props> = ({
}) => {
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,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,23 +93,14 @@ const EventsViewerComponent: React.FC<Props> = ({
}) => {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const EventsQueryTabBody = ({
initializeTimeline({
id: TimelineId.hostsPageEvents,
defaultModel: eventsDefaultModel,
timelineRowActions: [
timelineRowActions: () => [
getInvestigateInResolverAction({ dispatch, timelineId: TimelineId.hostsPageEvents }),
],
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -39,7 +41,7 @@ interface ManageTimeline {
loadingText: string;
queryFields: string[];
selectAll: boolean;
timelineRowActions: TimelineRowAction[];
timelineRowActions: (ecsData: Ecs) => TimelineRowAction[];
title: string;
unit: (totalCount: number) => string;
}
Expand Down Expand Up @@ -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) => ({
Expand All @@ -85,7 +85,7 @@ export const getTimelineDefaults = (id: string) => ({
id,
isLoading: false,
queryFields: [],
timelineRowActions: [],
timelineRowActions: () => [],
title: i18n.EVENTS,
unit: (n: number) => i18n.UNIT(n),
});
Expand All @@ -112,7 +112,6 @@ const reducerManageTimeline = (
},
} as ManageTimelineById;
case 'SET_TIMELINE_ACTIONS':
case 'SET_TIMELINE_FILTER_MANAGER':
return {
...state,
[action.id]: {
Expand Down Expand Up @@ -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 => {
Expand All @@ -169,7 +167,7 @@ const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseT
}: {
id: string;
queryFields?: string[];
timelineRowActions: TimelineRowAction[];
timelineRowActions: (ecsData: Ecs) => TimelineRowAction[];
}) => {
dispatch({
type: 'SET_TIMELINE_ACTIONS',
Expand All @@ -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({
Expand Down Expand Up @@ -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]
Expand All @@ -234,7 +221,6 @@ const useTimelineManager = (manageTimelineForTesting?: ManageTimelineById): UseT
setIndexToAdd,
setIsTimelineLoading,
setTimelineRowActions,
setTimelineFilterManager,
};
};

Expand All @@ -246,7 +232,6 @@ const init = {
initializeTimeline: () => noop,
setIsTimelineLoading: () => noop,
setTimelineRowActions: () => noop,
setTimelineFilterManager: () => noop,
};

const ManageTimelineContext = createContext<UseTimelineManager>(init);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ export const EventColumnView = React.memo<Props>(
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(() => {
Expand All @@ -105,6 +105,7 @@ export const EventColumnView = React.memo<Props>(

const button = (
<EuiButtonIcon
aria-label="context menu"
data-test-subj="timeline-context-menu-button"
size="s"
iconType="boxesHorizontal"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ export const Body = React.memo<BodyProps>(
}) => {
const containerElementRef = useRef<HTMLDivElement>(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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,26 +172,19 @@ export const TimelineComponent: React.FC<Props> = ({
[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 });
Expand Down

0 comments on commit adbe8ff

Please sign in to comment.