diff --git a/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/hooks/use_init_data_view_manager.ts b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/hooks/use_init_data_view_manager.ts index 81f3104cb7192..f3d5167bd8ac4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/hooks/use_init_data_view_manager.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/hooks/use_init_data_view_manager.ts @@ -122,6 +122,7 @@ export const useInitDataViewManager = () => { ].map((scope) => createDataViewSelectedListener({ scope, + spaces: services.spaces, dataViews: services.dataViews, notifications: services.notifications, storage: services.storage, diff --git a/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/data_view_selected.test.ts b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/data_view_selected.test.ts index b618bdd06985d..b5cb78508f947 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/data_view_selected.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/data_view_selected.test.ts @@ -14,6 +14,7 @@ import type { RootState } from '../reducer'; import { DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID, PageScope } from '../../constants'; import { DEFAULT_ALERT_DATA_VIEW_ID } from '../../../../common/constants'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; +import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { CoreStart } from '@kbn/core/public'; const mockDataViewsService = { @@ -87,6 +88,9 @@ const mockLogger = loggingSystemMock.createLogger(); const mockDispatch = jest.fn(); const mockGetState = jest.fn(() => mockedState); +const mockSpaces = { + getActiveSpace: jest.fn().mockResolvedValue({ id: 'default' }), +} as unknown as SpacesPluginStart; const mockStorage = { set: jest.fn(), get: jest.fn(), @@ -116,6 +120,7 @@ describe('createDataViewSelectedListener', () => { } as unknown as CoreStart['notifications'], logger: mockLogger, scope: PageScope.default, + spaces: mockSpaces, storage: mockStorage, }); }); @@ -198,6 +203,7 @@ describe('createDataViewSelectedListener', () => { }, } as unknown as CoreStart['notifications'], scope: PageScope.analyzer, + spaces: mockSpaces, logger: mockLogger, storage: mockStorage, }); @@ -208,7 +214,7 @@ describe('createDataViewSelectedListener', () => { ); expect(mockStorage.set).toHaveBeenCalledWith( - 'securitySolution.dataViewManager.selectedDataView.analyzer', + 'securitySolution.dataViewManager.selectedDataView.default.analyzer', 'adhoc_test-*' ); }); @@ -231,6 +237,7 @@ describe('createDataViewSelectedListener', () => { }, } as unknown as CoreStart['notifications'], scope: PageScope.analyzer, + spaces: mockSpaces, logger: mockLogger, storage: mockStorage, }); @@ -241,7 +248,7 @@ describe('createDataViewSelectedListener', () => { ); expect(mockStorage.set).toHaveBeenCalledWith( - 'securitySolution.dataViewManager.selectedDataView.analyzer', + 'securitySolution.dataViewManager.selectedDataView.default.analyzer', 'persisted_test-*' ); }); @@ -277,6 +284,7 @@ describe('createDataViewSelectedListener', () => { }, } as unknown as CoreStart['notifications'], scope: PageScope.analyzer, + spaces: mockSpaces, logger: mockLogger, storage: mockStorage, }); @@ -288,7 +296,7 @@ describe('createDataViewSelectedListener', () => { ); expect(mockStorage.remove).toHaveBeenCalledWith( - 'securitySolution.dataViewManager.selectedDataView.analyzer' + 'securitySolution.dataViewManager.selectedDataView.default.analyzer' ); }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/data_view_selected.ts b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/data_view_selected.ts index e6930e0045258..059b0c945fb0e 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/data_view_selected.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/data_view_selected.ts @@ -10,6 +10,7 @@ import type { DataView, DataViewLazy, DataViewsServicePublic } from '@kbn/data-views-plugin/public'; import type { AnyAction, Dispatch, ListenerEffectAPI } from '@reduxjs/toolkit'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; +import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { isEmpty } from 'lodash'; import type { Logger } from '@kbn/logging'; import type { CoreStart } from '@kbn/core/public'; @@ -18,6 +19,7 @@ import { scopes } from '../reducer'; import { selectDataViewAsync } from '../actions'; import { sharedDataViewManagerSlice } from '../slices'; import { PageScope } from '../../constants'; +import { getSelectedDataViewStorageKey } from './storage_keys'; /** * Creates a Redux listener for handling data view selection logic in the data view manager. @@ -37,6 +39,7 @@ import { PageScope } from '../../constants'; */ export const createDataViewSelectedListener = (dependencies: { scope: PageScope; + spaces: SpacesPluginStart; dataViews: DataViewsServicePublic; notifications: CoreStart['notifications']; storage: Storage; @@ -49,6 +52,7 @@ export const createDataViewSelectedListener = (dependencies: { listenerApi: ListenerEffectAPI> ) => { const logger = dependencies.logger; + const spaceId = (await dependencies.spaces.getActiveSpace()).id; logger.debug( `Data view selection requested for scope: ${ dependencies.scope @@ -124,7 +128,7 @@ export const createDataViewSelectedListener = (dependencies: { // This cleans local storage for the analyzer data view to prevent the error from happening again on page refresh. if (action.payload.scope === PageScope.analyzer && action.payload.id) { dependencies.storage.remove( - `securitySolution.dataViewManager.selectedDataView.${action.payload.scope}` + getSelectedDataViewStorageKey(spaceId, action.payload.scope) ); } dataViewByIdError = error; @@ -185,7 +189,7 @@ export const createDataViewSelectedListener = (dependencies: { listenerApi.dispatch(currentScopeActions.setSelectedDataView(resolvedIdToUse)); if (action.payload.scope === PageScope.analyzer) { dependencies.storage.set( - `securitySolution.dataViewManager.selectedDataView.${action.payload.scope}`, + getSelectedDataViewStorageKey(spaceId, action.payload.scope), resolvedIdToUse ); } diff --git a/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/init_listener.ts b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/init_listener.ts index 473f6903dbe84..337946fbc59e9 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/init_listener.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/init_listener.ts @@ -17,6 +17,7 @@ import { PageScope } from '../../constants'; import { selectDataViewAsync } from '../actions'; import { createDefaultDataView } from '../../utils/create_default_data_view'; import { createExploreDataView } from '../../utils/create_explore_data_view'; +import { getSelectedDataViewStorageKey } from './storage_keys'; import type { DataViewSpec } from '../types'; /** @@ -58,6 +59,8 @@ export const createInitListener = ( ) => { try { const logger = dependencies.logger; + const spaceId = (await dependencies.spaces.getActiveSpace()).id; + // Initialize default data views first const { defaultDataView, alertDataView, attackDataView } = await createDefaultDataView({ dataViewService: dependencies.dataViews, @@ -165,7 +168,7 @@ export const createInitListener = ( ); } const storedDataViewId = dependencies.storage.get( - `securitySolution.dataViewManager.selectedDataView.${scope}` + getSelectedDataViewStorageKey(spaceId, scope) ) as string | null | undefined; const state = listenerApi.getState(); if ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/storage_keys.ts b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/storage_keys.ts new file mode 100644 index 0000000000000..0ae0fe24746c0 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/data_view_manager/redux/listeners/storage_keys.ts @@ -0,0 +1,14 @@ +/* + * 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. + */ + +/** + * Returns the localStorage key for a scope's selected data view. + * The key includes the space ID so that each space maintains its own + * selection independently. + */ +export const getSelectedDataViewStorageKey = (spaceId: string, scope: string): string => + `securitySolution.dataViewManager.selectedDataView.${spaceId}.${scope}`;