mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[SECURITY SOLUTIONS][Timeline] correlation bug (#96099)
* fix pagination bug with correlation * fix close resolver to go back to prev tab
This commit is contained in:
parent
d2a484c5bd
commit
f042ec8945
12 changed files with 114 additions and 6 deletions
|
@ -204,6 +204,7 @@ export const mockGlobalState: State = {
|
|||
timelineById: {
|
||||
test: {
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.notes,
|
||||
deletedEventIds: [],
|
||||
id: 'test',
|
||||
savedObjectId: null,
|
||||
|
|
|
@ -2062,6 +2062,7 @@ export const mockTimelineResults: OpenTimelineResult[] = [
|
|||
|
||||
export const mockTimelineModel: TimelineModel = {
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.notes,
|
||||
columns: [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
|
@ -2209,6 +2210,7 @@ export const defaultTimelineProps: CreateTimelineProps = {
|
|||
from: '2018-11-05T18:58:25.937Z',
|
||||
timeline: {
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
columns: [
|
||||
{ columnHeaderType: 'not-filtered', id: '@timestamp', type: 'number', width: 190 },
|
||||
{ columnHeaderType: 'not-filtered', id: 'message', width: 180 },
|
||||
|
|
|
@ -108,6 +108,7 @@ describe('alert actions', () => {
|
|||
notes: null,
|
||||
timeline: {
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
columns: [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
|
|
|
@ -240,6 +240,7 @@ describe('helpers', () => {
|
|||
const newTimeline = defaultTimelineToTimelineModel(timeline, false);
|
||||
expect(newTimeline).toEqual({
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
columns: [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
|
@ -350,6 +351,7 @@ describe('helpers', () => {
|
|||
const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineType.template);
|
||||
expect(newTimeline).toEqual({
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
columns: [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
|
@ -460,6 +462,7 @@ describe('helpers', () => {
|
|||
const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineType.default);
|
||||
expect(newTimeline).toEqual({
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
columns: [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
|
@ -568,6 +571,7 @@ describe('helpers', () => {
|
|||
const newTimeline = defaultTimelineToTimelineModel(timeline, false);
|
||||
expect(newTimeline).toEqual({
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
columns: [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
|
@ -676,6 +680,7 @@ describe('helpers', () => {
|
|||
const newTimeline = defaultTimelineToTimelineModel(timeline, false);
|
||||
expect(newTimeline).toEqual({
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
savedObjectId: 'savedObject-1',
|
||||
columns: [
|
||||
{
|
||||
|
@ -852,6 +857,7 @@ describe('helpers', () => {
|
|||
const newTimeline = defaultTimelineToTimelineModel(timeline, false);
|
||||
expect(newTimeline).toEqual({
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
savedObjectId: 'savedObject-1',
|
||||
columns: [
|
||||
{
|
||||
|
@ -1000,6 +1006,7 @@ describe('helpers', () => {
|
|||
const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineType.template);
|
||||
expect(newTimeline).toEqual({
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
columns: [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
|
@ -1110,6 +1117,7 @@ describe('helpers', () => {
|
|||
const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineType.default);
|
||||
expect(newTimeline).toEqual({
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
columns: [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
|
|
|
@ -208,4 +208,35 @@ describe('useTimelineEvents', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('Correlation pagination is calling search strategy when switching page', async () => {
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate, rerender } = renderHook<
|
||||
UseTimelineEventsProps,
|
||||
[boolean, TimelineArgs]
|
||||
>((args) => useTimelineEvents(args), {
|
||||
initialProps: {
|
||||
...props,
|
||||
language: 'eql',
|
||||
eqlOptions: {
|
||||
eventCategoryField: 'category',
|
||||
tiebreakerField: '',
|
||||
timestampField: '@timestamp',
|
||||
query: 'find it EQL',
|
||||
size: 100,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// useEffect on params request
|
||||
await waitForNextUpdate();
|
||||
rerender({ ...props, startDate, endDate });
|
||||
// useEffect on params request
|
||||
await waitForNextUpdate();
|
||||
expect(mockSearch).toHaveBeenCalledTimes(2);
|
||||
result.current[1].loadPage(4);
|
||||
await waitForNextUpdate();
|
||||
expect(mockSearch).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -143,7 +143,6 @@ export const useTimelineEvents = ({
|
|||
activeTimeline.setExpandedDetail({});
|
||||
activeTimeline.setActivePage(newActivePage);
|
||||
}
|
||||
|
||||
setActivePage(newActivePage);
|
||||
},
|
||||
[clearSignalsState, id]
|
||||
|
@ -294,22 +293,22 @@ export const useTimelineEvents = ({
|
|||
querySize: prevRequest?.pagination.querySize ?? 0,
|
||||
sort: prevRequest?.sort ?? initSortDefault,
|
||||
timerange: prevRequest?.timerange ?? {},
|
||||
...(prevEqlRequest?.eventCategoryField
|
||||
...(!isEmpty(prevEqlRequest?.eventCategoryField)
|
||||
? {
|
||||
eventCategoryField: prevEqlRequest?.eventCategoryField,
|
||||
}
|
||||
: {}),
|
||||
...(prevEqlRequest?.size
|
||||
...(!isEmpty(prevEqlRequest?.size)
|
||||
? {
|
||||
size: prevEqlRequest?.size,
|
||||
}
|
||||
: {}),
|
||||
...(prevEqlRequest?.tiebreakerField
|
||||
...(!isEmpty(prevEqlRequest?.tiebreakerField)
|
||||
? {
|
||||
tiebreakerField: prevEqlRequest?.tiebreakerField,
|
||||
}
|
||||
: {}),
|
||||
...(prevEqlRequest?.timestampField
|
||||
...(!isEmpty(prevEqlRequest?.timestampField)
|
||||
? {
|
||||
timestampField: prevEqlRequest?.timestampField,
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ const { from: start, to: end } = normalizeTimeRange({ from: '', to: '' }, false)
|
|||
export const timelineDefaults: SubsetTimelineModel &
|
||||
Pick<TimelineModel, 'filters' | 'eqlOptions'> = {
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.query,
|
||||
columns: defaultHeaders,
|
||||
dataProviders: [],
|
||||
dateRange: { start, end },
|
||||
|
|
|
@ -16,6 +16,7 @@ describe('Epic Timeline', () => {
|
|||
test('should return a TimelineInput instead of TimelineModel ', () => {
|
||||
const timelineModel: TimelineModel = {
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.notes,
|
||||
columns: [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
|
|
|
@ -305,6 +305,9 @@ export const updateGraphEventId = ({
|
|||
[id]: {
|
||||
...timeline,
|
||||
graphEventId,
|
||||
...(graphEventId === '' && id === TimelineId.active
|
||||
? { activeTab: timeline.prevActiveTab, prevActiveTab: timeline.activeTab }
|
||||
: {}),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@ export interface ColumnHeaderOptions {
|
|||
export interface TimelineModel {
|
||||
/** The selected tab to displayed in the timeline */
|
||||
activeTab: TimelineTabs;
|
||||
prevActiveTab: TimelineTabs;
|
||||
/** The columns displayed in the timeline */
|
||||
columns: ColumnHeaderOptions[];
|
||||
/** Timeline saved object owner */
|
||||
|
@ -142,6 +143,7 @@ export type SubsetTimelineModel = Readonly<
|
|||
Pick<
|
||||
TimelineModel,
|
||||
| 'activeTab'
|
||||
| 'prevActiveTab'
|
||||
| 'columns'
|
||||
| 'dataProviders'
|
||||
| 'deletedEventIds'
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
*/
|
||||
|
||||
import { cloneDeep } from 'lodash/fp';
|
||||
import { TimelineType, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline';
|
||||
import {
|
||||
TimelineType,
|
||||
TimelineStatus,
|
||||
TimelineTabs,
|
||||
TimelineId,
|
||||
} from '../../../../common/types/timeline';
|
||||
|
||||
import {
|
||||
IS_OPERATOR,
|
||||
|
@ -39,6 +44,7 @@ import {
|
|||
updateTimelineSort,
|
||||
updateTimelineTitleAndDescription,
|
||||
upsertTimelineColumn,
|
||||
updateGraphEventId,
|
||||
} from './helpers';
|
||||
import { ColumnHeaderOptions, TimelineModel } from './model';
|
||||
import { timelineDefaults } from './defaults';
|
||||
|
@ -69,6 +75,7 @@ const basicDataProvider: DataProvider = {
|
|||
};
|
||||
const basicTimeline: TimelineModel = {
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.graph,
|
||||
columns: [],
|
||||
dataProviders: [{ ...basicDataProvider }],
|
||||
dateRange: {
|
||||
|
@ -1757,4 +1764,55 @@ describe('Timeline', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#updateGraphEventId', () => {
|
||||
test('should return a new reference and not the same reference', () => {
|
||||
const update = updateGraphEventId({
|
||||
id: 'foo',
|
||||
graphEventId: '123',
|
||||
timelineById: timelineByIdMock,
|
||||
});
|
||||
expect(update).not.toBe(timelineByIdMock);
|
||||
});
|
||||
|
||||
test('should empty graphEventId', () => {
|
||||
const update = updateGraphEventId({
|
||||
id: 'foo',
|
||||
graphEventId: '',
|
||||
timelineById: timelineByIdMock,
|
||||
});
|
||||
expect(update.foo.graphEventId).toEqual('');
|
||||
});
|
||||
|
||||
test('should empty graphEventId and not change activeTab and prevActiveTab because TimelineId !== TimelineId.active', () => {
|
||||
const update = updateGraphEventId({
|
||||
id: 'foo',
|
||||
graphEventId: '',
|
||||
timelineById: timelineByIdMock,
|
||||
});
|
||||
expect(update.foo.graphEventId).toEqual('');
|
||||
expect(update.foo.activeTab).toEqual(timelineByIdMock.foo.activeTab);
|
||||
expect(update.foo.prevActiveTab).toEqual(timelineByIdMock.foo.prevActiveTab);
|
||||
});
|
||||
|
||||
test('should empty graphEventId and return to the previous tab if TimelineId === TimelineId.active', () => {
|
||||
const mock = cloneDeep(timelineByIdMock);
|
||||
mock[TimelineId.active] = {
|
||||
...timelineByIdMock.foo,
|
||||
activeTab: TimelineTabs.graph,
|
||||
prevActiveTab: TimelineTabs.eql,
|
||||
};
|
||||
delete mock.foo;
|
||||
|
||||
const update = updateGraphEventId({
|
||||
id: TimelineId.active,
|
||||
graphEventId: '',
|
||||
timelineById: mock,
|
||||
});
|
||||
|
||||
expect(update[TimelineId.active].graphEventId).toEqual('');
|
||||
expect(update[TimelineId.active].activeTab).toEqual(TimelineTabs.eql);
|
||||
expect(update[TimelineId.active].prevActiveTab).toEqual(TimelineTabs.graph);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -526,6 +526,7 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState)
|
|||
[id]: {
|
||||
...state.timelineById[id],
|
||||
activeTab,
|
||||
prevActiveTab: state.timelineById[id].activeTab,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue