Skip to content

Commit

Permalink
[Vis Builder] Add redux store persistence (#3088)
Browse files Browse the repository at this point in the history
* 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 <abigailhu2000@gmail.com>

* changelog and rebase

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>

* Console log the error

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>

* rebase and changelog

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>

* add unit tests

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>

Signed-off-by: abbyhu2000 <abigailhu2000@gmail.com>
  • Loading branch information
abbyhu2000 authored Dec 29, 2022
1 parent bd52024 commit bfc59d4
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ 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

Expand Down
6 changes: 6 additions & 0 deletions src/plugins/data/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
19 changes: 18 additions & 1 deletion src/plugins/vis_builder/public/application/utils/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ 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 = () => {
const coreStartMock = coreMock.createStart();
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,
Expand All @@ -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<VisBuilderServices>;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<VisBuilderServices>;
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);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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<RootState>('_a');
if (serializedState !== null) return serializedState;
} catch (err) {
/* eslint-disable no-console */
console.error(err);
/* eslint-enable no-console */
}

return await getPreloadedState(services);
};

export const saveReduxState = (
{ style, visualization, metadata },
services: VisBuilderServices
) => {
try {
services.osdUrlStateStorage.set<RootState>(
'_a',
{ style, visualization, metadata },
{
replace: true,
}
);
} catch (err) {
return;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -25,7 +25,7 @@ export const configurePreloadedStore = (preloadedState: PreloadedState<RootState
};

export const getPreloadedStore = async (services: VisBuilderServices) => {
const preloadedState = await getPreloadedState(services);
const preloadedState = await loadReduxState(services);
const store = configurePreloadedStore(preloadedState);

const { metadata: metadataState, style: styleState, visualization: vizState } = store.getState();
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit bfc59d4

Please sign in to comment.