Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [Security Solution] [Timeline] Timeline manager tweaks (#69988) #71016

Merged
merged 1 commit into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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