-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[TIP] Investigate in timeline #140496
[TIP] Investigate in timeline #140496
Changes from 12 commits
6da1cb1
00c7528
ea4868b
f0f6548
891c409
894e240
5861e18
938597d
d621e20
e78ee30
032866c
b12e48b
ec85eba
5af030b
eb5538a
7d81350
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* 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 ACTION_INVESTIGATE_IN_TIMELINE = i18n.translate( | ||
'xpack.securitySolution.threatIntelligence.investigateInTimelineTitle', | ||
{ | ||
defaultMessage: 'Investigate in timeline', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember talking with UX writer they mentioned that they always use Timeline capitalized and it's the only feature that behaves this way, let's change to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Though I don't see where this defaultMeassage appears, I'd expect it to show up as a tooltip on the action icon, but it's not there. Btw about tooltips, I see that our expand icon has a tooltip "View Details", but Investigate in Timeline icon doesn't have a tooltip. I think we should add it, but it can be in a separate PR if it's not a one-lines somewhere There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point. I've added the tooltip here as well as in every other places in the plugin I could find |
||
} | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* | ||
* 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 { useCallback, useMemo } from 'react'; | ||
import { useDispatch } from 'react-redux'; | ||
import { timelineDefaults } from '../timelines/store/timeline/defaults'; | ||
import { APP_UI_ID } from '../../common/constants'; | ||
import type { DataProvider } from '../../common/types'; | ||
import { TimelineId, TimelineType } from '../../common/types'; | ||
import { useDeepEqualSelector } from '../common/hooks/use_selector'; | ||
import { useKibana } from '../common/lib/kibana'; | ||
import { useStartTransaction } from '../common/lib/apm/use_start_transaction'; | ||
import { timelineActions, timelineSelectors } from '../timelines/store/timeline'; | ||
import { useCreateTimeline } from '../timelines/components/timeline/properties/use_create_timeline'; | ||
import type { CreateTimelineProps } from '../detections/components/alerts_table/types'; | ||
import { dispatchUpdateTimeline } from '../timelines/components/open_timeline/helpers'; | ||
|
||
interface UseInvestigateInTimelineActionProps { | ||
/** | ||
* Created when the user clicks on the Investigate in Timeline button. | ||
* DataProvider contain the field(s) and value(s) displayed in the timeline. | ||
*/ | ||
dataProviders: DataProvider[]; | ||
/** | ||
* Start date used in the createTimeline method. | ||
*/ | ||
from: string; | ||
/** | ||
* End date used in the createTimeline method. | ||
*/ | ||
to: string; | ||
} | ||
|
||
/** | ||
* Hook passed down to the Threat Intelligence plugin, via context. | ||
* This code is closely duplicated from here: https://github.com/elastic/kibana/blob/main/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx, | ||
* the main changes being: | ||
* - no exceptions are handled at the moment | ||
* - we use dataProviders, from and to directly instead of consuming ecsData | ||
*/ | ||
export const useInvestigateInTimeline = ({ | ||
dataProviders, | ||
from, | ||
to, | ||
}: UseInvestigateInTimelineActionProps) => { | ||
const { | ||
data: { query }, | ||
} = useKibana().services; | ||
const dispatch = useDispatch(); | ||
const { startTransaction } = useStartTransaction(); | ||
|
||
const filterManagerBackup = useMemo(() => query.filterManager, [query.filterManager]); | ||
const getManageTimeline = useMemo(() => timelineSelectors.getManageTimelineById(), []); | ||
const { filterManager: activeFilterManager } = useDeepEqualSelector((state) => | ||
getManageTimeline(state, TimelineId.active ?? '') | ||
); | ||
const filterManager = useMemo( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. out of curiosity what this |
||
() => activeFilterManager ?? filterManagerBackup, | ||
[activeFilterManager, filterManagerBackup] | ||
); | ||
|
||
const updateTimelineIsLoading = useCallback( | ||
(payload) => dispatch(timelineActions.updateIsLoading(payload)), | ||
[dispatch] | ||
); | ||
|
||
const clearActiveTimeline = useCreateTimeline({ | ||
timelineId: TimelineId.active, | ||
timelineType: TimelineType.default, | ||
}); | ||
|
||
const createTimeline = useCallback( | ||
({ from: fromTimeline, timeline, to: toTimeline, ruleNote }: CreateTimelineProps) => { | ||
clearActiveTimeline(); | ||
updateTimelineIsLoading({ id: TimelineId.active, isLoading: false }); | ||
dispatchUpdateTimeline(dispatch)({ | ||
duplicate: true, | ||
from: fromTimeline, | ||
id: TimelineId.active, | ||
notes: [], | ||
timeline: { | ||
...timeline, | ||
filterManager, | ||
indexNames: timeline.indexNames ?? [], | ||
show: true, | ||
}, | ||
to: toTimeline, | ||
ruleNote, | ||
})(); | ||
}, | ||
[dispatch, filterManager, updateTimelineIsLoading, clearActiveTimeline] | ||
); | ||
|
||
const investigateInTimelineClick = useCallback(async () => { | ||
startTransaction({ name: `${APP_UI_ID} threat indicator investigateInTimeline` }); | ||
await createTimeline({ | ||
from, | ||
notes: null, | ||
timeline: { | ||
...timelineDefaults, | ||
dataProviders, | ||
id: TimelineId.active, | ||
indexNames: [], | ||
dateRange: { | ||
start: from, | ||
end: to, | ||
}, | ||
eventType: 'all', | ||
filters: [], | ||
kqlQuery: { | ||
filterQuery: { | ||
kuery: { | ||
kind: 'kuery', | ||
expression: '', | ||
}, | ||
serializedQuery: '', | ||
}, | ||
}, | ||
}, | ||
to, | ||
ruleNote: '', | ||
}); | ||
}, [startTransaction, createTimeline, dataProviders, from, to]); | ||
|
||
return investigateInTimelineClick; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,11 +6,17 @@ | |
*/ | ||
|
||
import React, { useContext, VFC } from 'react'; | ||
import { InvestigateInTimelineButtonIcon } from '../../../timeline/components/investigate_in_timeline_button_icon'; | ||
import { Indicator } from '../../../../../common/types/indicator'; | ||
import { OpenIndicatorFlyoutButton } from '../open_indicator_flyout_button/open_indicator_flyout_button'; | ||
import { IndicatorsTableContext } from './context'; | ||
import { useStyles } from './styles'; | ||
|
||
const INVESTIGATE_TEST_ID = 'tiIndicatorTableInvestigateInTimelineButtonIcon'; | ||
|
||
export const ActionsRowCell: VFC<{ indicator: Indicator }> = ({ indicator }) => { | ||
const styles = useStyles(); | ||
|
||
const indicatorTableContext = useContext(IndicatorsTableContext); | ||
|
||
if (!indicatorTableContext) { | ||
|
@@ -20,10 +26,13 @@ export const ActionsRowCell: VFC<{ indicator: Indicator }> = ({ indicator }) => | |
const { setExpanded, expanded } = indicatorTableContext; | ||
|
||
return ( | ||
<OpenIndicatorFlyoutButton | ||
indicator={indicator} | ||
onOpen={setExpanded} | ||
isOpen={Boolean(expanded && expanded._id === indicator._id)} | ||
/> | ||
<div css={styles.rowActionsDiv}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can EUI flex components be used here, eg There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup done, good cleanup I could delete the |
||
<OpenIndicatorFlyoutButton | ||
indicator={indicator} | ||
onOpen={setExpanded} | ||
isOpen={Boolean(expanded && expanded._id === indicator._id)} | ||
/> | ||
<InvestigateInTimelineButtonIcon data={indicator} data-test-subj={INVESTIGATE_TEST_ID} /> | ||
</div> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* 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 { CSSObject } from '@emotion/react'; | ||
|
||
export const useStyles = () => { | ||
const rowActionsDiv: CSSObject = { | ||
display: 'flex', | ||
}; | ||
|
||
return { | ||
rowActionsDiv, | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉