[8.16] [Bug][Security Solution] - Reliably persist dataview selections for timeline (#211343) (#213492)

# Backport

This will backport the following commits from `main` to `8.16`:
- [[Bug][Security Solution] - Reliably persist dataview selections for
timeline (#211343)](https://github.com/elastic/kibana/pull/211343)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Michael
Olorunnisola","email":"michael.olorunnisola@elastic.co"},"sourceCommit":{"committedDate":"2025-03-06T21:09:22Z","message":"[Bug][Security
Solution] - Reliably persist dataview selections for timeline
(#211343)\n\nresolves
https://github.com/elastic/kibana/issues/198944\n\n##
Summary\n\nCurrently, the redux store can become out of sync with the
state in the\nUI, leading to the selected dataview not being preserved
in the store,\nand thereby not being saved when the timeline is saved.
This PR sets the\nselected dataview and patterns at the point of saving
to ensure that\nthey are set and not overriden.\n\nFor additional
background, see referenced
issues.","sha":"4abf1a151e9b10a02a633a5f9e88607a55e3f4ba","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:fix","v9.0.0","Team:Threat
Hunting:Investigations","backport:version","v8.18.0","v9.1.0","v8.19.0","v8.16.6","v8.17.4"],"title":"[Bug][Security
Solution] - Reliably persist dataview selections for
timeline","number":211343,"url":"https://github.com/elastic/kibana/pull/211343","mergeCommit":{"message":"[Bug][Security
Solution] - Reliably persist dataview selections for timeline
(#211343)\n\nresolves
https://github.com/elastic/kibana/issues/198944\n\n##
Summary\n\nCurrently, the redux store can become out of sync with the
state in the\nUI, leading to the selected dataview not being preserved
in the store,\nand thereby not being saved when the timeline is saved.
This PR sets the\nselected dataview and patterns at the point of saving
to ensure that\nthey are set and not overriden.\n\nFor additional
background, see referenced
issues.","sha":"4abf1a151e9b10a02a633a5f9e88607a55e3f4ba"}},"sourceBranch":"main","suggestedTargetBranches":["8.18","8.x","8.16","8.17"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/213488","number":213488,"state":"OPEN"},{"branch":"8.18","label":"v8.18.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/211343","number":211343,"mergeCommit":{"message":"[Bug][Security
Solution] - Reliably persist dataview selections for timeline
(#211343)\n\nresolves
https://github.com/elastic/kibana/issues/198944\n\n##
Summary\n\nCurrently, the redux store can become out of sync with the
state in the\nUI, leading to the selected dataview not being preserved
in the store,\nand thereby not being saved when the timeline is saved.
This PR sets the\nselected dataview and patterns at the point of saving
to ensure that\nthey are set and not overriden.\n\nFor additional
background, see referenced
issues.","sha":"4abf1a151e9b10a02a633a5f9e88607a55e3f4ba"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.16","label":"v8.16.6","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.17","label":"v8.17.4","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
This commit is contained in:
Michael Olorunnisola 2025-03-07 02:22:07 -05:00 committed by GitHub
parent dc8ba9985b
commit 0ae7625cf8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 28 additions and 6 deletions

View file

@ -12,7 +12,7 @@ import { TimelineId, TimelineTabs } from '../../../../common/types/timeline';
import { TimelineTypeEnum, TimelineStatusEnum } from '../../../../common/api/timeline';
import { convertTimelineAsInput } from './timeline_save';
import type { TimelineModel } from '../model';
import { createMockStore, kibanaMock } from '../../../common/mock';
import { createMockStore, kibanaMock, mockGlobalState } from '../../../common/mock';
import { selectTimelineById } from '../selectors';
import { copyTimeline, persistTimeline } from '../../containers/api';
import { refreshTimelines } from './helpers';
@ -79,7 +79,14 @@ describe('Timeline save middleware', () => {
await store.dispatch(saveTimeline({ id: TimelineId.test, saveAsNew: false }));
expect(startTimelineSavingMock).toHaveBeenCalled();
expect(persistTimeline as unknown as jest.Mock).toHaveBeenCalled();
expect(persistTimeline as unknown as jest.Mock).toHaveBeenCalledWith(
expect.objectContaining({
timeline: expect.objectContaining({
dataViewId: mockGlobalState.sourcerer.sourcererScopes.timeline.selectedDataViewId,
indexNames: mockGlobalState.sourcerer.sourcererScopes.timeline.selectedPatterns,
}),
})
);
expect(refreshTimelines as unknown as jest.Mock).toHaveBeenCalled();
expect(endTimelineSavingMock).toHaveBeenCalled();
expect(selectTimelineById(store.getState(), TimelineId.test)).toEqual(

View file

@ -19,6 +19,7 @@ import {
isPhrasesFilter,
} from '@kbn/es-query';
import { sourcererSelectors } from '../../../sourcerer/store';
import {
updateTimeline,
startTimelineSaving,
@ -42,6 +43,7 @@ import type {
import type { TimelineModel } from '../model';
import type { ColumnHeaderOptions } from '../../../../common/types/timeline';
import { refreshTimelines } from './helpers';
import { SourcererScopeName } from '../../../sourcerer/store/model';
function isSaveTimelineAction(action: Action): action is ReturnType<typeof saveTimeline> {
return action.type === saveTimeline.type;
@ -54,10 +56,19 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State
if (isSaveTimelineAction(action)) {
const { id: localTimelineId } = action.payload;
const timeline = selectTimelineById(store.getState(), localTimelineId);
const storeState = store.getState();
const timeline = selectTimelineById(storeState, localTimelineId);
const { timelineId, timelineVersion, templateTimelineId, templateTimelineVersion } =
extractTimelineIdsAndVersions(timeline);
const timelineTimeRange = inputsSelectors.timelineTimeRangeSelector(store.getState());
const timelineTimeRange = inputsSelectors.timelineTimeRangeSelector(storeState);
const selectedDataViewIdSourcerer = sourcererSelectors.sourcererScopeSelectedDataViewId(
storeState,
SourcererScopeName.timeline
);
const selectedPatterns = sourcererSelectors.sourcererScopeSelectedPatterns(
storeState,
SourcererScopeName.timeline
);
store.dispatch(startTimelineSaving({ id: localTimelineId }));
@ -67,6 +78,8 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State
timelineId,
timeline: {
...convertTimelineAsInput(timeline, timelineTimeRange),
dataViewId: selectedDataViewIdSourcerer,
indexNames: selectedPatterns,
templateTimelineId,
templateTimelineVersion,
},
@ -77,6 +90,8 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State
version: timelineVersion,
timeline: {
...convertTimelineAsInput(timeline, timelineTimeRange),
dataViewId: selectedDataViewIdSourcerer,
indexNames: selectedPatterns,
templateTimelineId,
templateTimelineVersion,
},

View file

@ -40,7 +40,7 @@ import { closeTimeline, openTimelineById } from '../../../tasks/timeline';
const siemDataViewTitle = 'Security Default Data View';
const dataViews = ['logs-*', 'metrics-*', '.kibana-event-log-*'];
describe('Timeline scope', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
describe('Timeline scope', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
waitForRulesBootstrap();
});
@ -134,7 +134,7 @@ describe('Timeline scope', { tags: ['@ess', '@serverless', '@skipInServerless']
});
const defaultPatterns = [`auditbeat-*`, `${DEFAULT_ALERTS_INDEX}-default`];
it('alerts checkbox behaves as expected', () => {
it('alerts checkbox behaves as expected', { tags: ['@skipInServerless'] }, () => {
isDataViewSelection(siemDataViewTitle);
defaultPatterns.forEach((pattern) => isSourcererSelection(pattern));
openDataViewSelection();