From e3bd95348ef3796ebadf9ae79689179627eb85e4 Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Fri, 9 Dec 2022 00:46:57 +0000 Subject: [PATCH 1/5] add redux store persistence implement persistence without using state container or state sync utils, and it works with both the URL and session storage. Signed-off-by: abbyhu2000 --- .../state_management/redux_persistence.ts | 37 +++++++++++++++++++ .../utils/state_management/store.ts | 13 ++++++- 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts diff --git a/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts new file mode 100644 index 000000000000..5a74f1110919 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts @@ -0,0 +1,37 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { VisBuilderServices } from '../../../types'; +import { getPreloadedState } from './preload'; +import { RootState } from './store'; + +export const loadReduxState = async (services: VisBuilderServices) => { + try { + const serializedState = services.osdUrlStateStorage.get('_a'); + if (serializedState === null) { + return await getPreloadedState(services); + } + return serializedState; + } catch (err) { + return await getPreloadedState(services); + } +}; + +export const saveReduxState = ( + { style, visualization, metadata }, + services: VisBuilderServices +) => { + try { + services.osdUrlStateStorage.set( + '_a', + { style, visualization, metadata }, + { + replace: true, + } + ); + } catch (err) { + return; + } +}; diff --git a/src/plugins/vis_builder/public/application/utils/state_management/store.ts b/src/plugins/vis_builder/public/application/utils/state_management/store.ts index f02fc5e946dd..19f60858b19d 100644 --- a/src/plugins/vis_builder/public/application/utils/state_management/store.ts +++ b/src/plugins/vis_builder/public/application/utils/state_management/store.ts @@ -8,8 +8,8 @@ import { reducer as styleReducer } from './style_slice'; import { reducer as visualizationReducer } from './visualization_slice'; import { reducer as metadataReducer } from './metadata_slice'; import { VisBuilderServices } from '../../..'; -import { getPreloadedState } from './preload'; import { setEditorState } from './metadata_slice'; +import { loadReduxState, saveReduxState } from './redux_persistence'; const rootReducer = combineReducers({ style: styleReducer, @@ -25,7 +25,7 @@ export const configurePreloadedStore = (preloadedState: PreloadedState { - const preloadedState = await getPreloadedState(services); + const preloadedState = await loadReduxState(services); const store = configurePreloadedStore(preloadedState); const { metadata: metadataState, style: styleState, visualization: vizState } = store.getState(); @@ -62,6 +62,15 @@ export const getPreloadedStore = async (services: VisBuilderServices) => { previousStore = currentStore; previousMetadata = currentMetadata; + + saveReduxState( + { + style: store.getState().style, + visualization: store.getState().visualization, + metadata: store.getState().metadata, + }, + services + ); }; // the store subscriber will automatically detect changes and call handleChange function From 2a4806123501ad1bbe4fd1989e7eac7379f90f22 Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Thu, 15 Dec 2022 20:07:08 +0000 Subject: [PATCH 2/5] changelog and rebase Signed-off-by: abbyhu2000 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1626f17e776e..9457327f59f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Doc] Add current plugin persistence implementation readme ([#3081](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3081)) - [Table Visualization] Refactor table visualization using React and DataGrid component ([#2863](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2863)) +- [Vis Builder] Add redux store persistence ([#3088](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3088)) + ### 🐛 Bug Fixes - [Vis Builder] Fixes auto bounds for timeseries bar chart visualization ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401)) From 3eb6bb370f6345a343fe66662c58d3768e40ce27 Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Fri, 16 Dec 2022 19:42:11 +0000 Subject: [PATCH 3/5] Console log the error Signed-off-by: abbyhu2000 --- .../utils/state_management/redux_persistence.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts index 5a74f1110919..295d3e2b4478 100644 --- a/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts +++ b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.ts @@ -10,13 +10,14 @@ import { RootState } from './store'; export const loadReduxState = async (services: VisBuilderServices) => { try { const serializedState = services.osdUrlStateStorage.get('_a'); - if (serializedState === null) { - return await getPreloadedState(services); - } - return serializedState; + if (serializedState !== null) return serializedState; } catch (err) { - return await getPreloadedState(services); + /* eslint-disable no-console */ + console.error(err); + /* eslint-enable no-console */ } + + return await getPreloadedState(services); }; export const saveReduxState = ( From cee291e5b7ab8c681898738ec7254f1e9369c333 Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Tue, 27 Dec 2022 20:36:04 +0000 Subject: [PATCH 4/5] rebase and changelog Signed-off-by: abbyhu2000 --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9457327f59f6..cb4fd806d9e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,7 +50,6 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multi DataSource] Test the connection to an external data source when creating or updating ([#2973](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2973)) - [Doc] Add current plugin persistence implementation readme ([#3081](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3081)) - [Table Visualization] Refactor table visualization using React and DataGrid component ([#2863](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2863)) - - [Vis Builder] Add redux store persistence ([#3088](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3088)) ### 🐛 Bug Fixes From 47d1a665ead07c3043c9f2eb9984395ce2c3ea4b Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Thu, 29 Dec 2022 18:24:02 +0000 Subject: [PATCH 5/5] add unit tests Signed-off-by: abbyhu2000 --- src/plugins/data/public/mocks.ts | 6 +++ .../public/application/utils/mocks.ts | 19 ++++++- .../redux_persistence.test.tsx | 53 +++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.test.tsx diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 27ea95100000..8beb241f8768 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -85,6 +85,12 @@ const createStartContract = (): Start => { }, }), get: jest.fn().mockReturnValue(Promise.resolve({})), + getDefault: jest.fn().mockReturnValue( + Promise.resolve({ + name: 'Default name', + id: 'id', + }) + ), clearCache: jest.fn(), } as unknown) as IndexPatternsContract, }; diff --git a/src/plugins/vis_builder/public/application/utils/mocks.ts b/src/plugins/vis_builder/public/application/utils/mocks.ts index df0687efaa6b..6feb98aaa930 100644 --- a/src/plugins/vis_builder/public/application/utils/mocks.ts +++ b/src/plugins/vis_builder/public/application/utils/mocks.ts @@ -9,6 +9,7 @@ import { dataPluginMock } from '../../../../data/public/mocks'; import { embeddablePluginMock } from '../../../../embeddable/public/mocks'; import { expressionsPluginMock } from '../../../../expressions/public/mocks'; import { navigationPluginMock } from '../../../../navigation/public/mocks'; +import { createOsdUrlStateStorage } from '../../../../opensearch_dashboards_utils/public'; import { VisBuilderServices } from '../../types'; export const createVisBuilderServicesMock = () => { @@ -16,10 +17,11 @@ export const createVisBuilderServicesMock = () => { const toastNotifications = coreStartMock.notifications.toasts; const applicationMock = coreStartMock.application; const i18nContextMock = coreStartMock.i18n.Context; - const indexPatternMock = dataPluginMock.createStartContract().indexPatterns; + const indexPatternMock = dataPluginMock.createStartContract(); const embeddableMock = embeddablePluginMock.createStartContract(); const navigationMock = navigationPluginMock.createStartContract(); const expressionMock = expressionsPluginMock.createStartContract(); + const osdUrlStateStorageMock = createOsdUrlStateStorage({ useHash: false }); const visBuilderServicesMock = { ...coreStartMock, @@ -39,6 +41,21 @@ export const createVisBuilderServicesMock = () => { data: indexPatternMock, embeddable: embeddableMock, scopedHistory: (scopedHistoryMock.create() as unknown) as ScopedHistory, + osdUrlStateStorage: osdUrlStateStorageMock, + types: { + all: () => [ + { + name: 'viz', + ui: { + containerConfig: { + style: { + defaults: 'style default states', + }, + }, + }, + }, + ], + }, }; return (visBuilderServicesMock as unknown) as jest.Mocked; diff --git a/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.test.tsx b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.test.tsx new file mode 100644 index 000000000000..ea3db25fbea0 --- /dev/null +++ b/src/plugins/vis_builder/public/application/utils/state_management/redux_persistence.test.tsx @@ -0,0 +1,53 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { VisBuilderServices } from '../../../types'; +import { createVisBuilderServicesMock } from '../mocks'; +import { getPreloadedState } from './preload'; +import { loadReduxState, saveReduxState } from './redux_persistence'; + +describe('test redux state persistence', () => { + let mockServices: jest.Mocked; + let reduxStateParams: any; + + beforeEach(() => { + mockServices = createVisBuilderServicesMock(); + reduxStateParams = { + style: 'style', + visualization: 'visualization', + metadata: 'metadata', + }; + }); + + test('test load redux state when url is empty', async () => { + const defaultStates = { + style: 'style default states', + visualization: { + searchField: '', + activeVisualization: { name: 'viz', aggConfigParams: [] }, + indexPattern: 'id', + }, + metadata: { + editor: { validity: {}, state: 'loading' }, + originatingApp: undefined, + }, + }; + + const returnStates = await loadReduxState(mockServices); + expect(returnStates).toStrictEqual(defaultStates); + }); + + test('test load redux state', async () => { + mockServices.osdUrlStateStorage.set('_a', reduxStateParams, { replace: true }); + const returnStates = await loadReduxState(mockServices); + expect(returnStates).toStrictEqual(reduxStateParams); + }); + + test('test save redux state', () => { + saveReduxState(reduxStateParams, mockServices); + const urlStates = mockServices.osdUrlStateStorage.get('_a'); + expect(urlStates).toStrictEqual(reduxStateParams); + }); +});