mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Migrates reducers and actions to Redux Toolkit (#21287)
* Refactor: Adds Redux Toolkit package * Refactor: Uses configureStore from Redux Toolkit * Refactor: Migrates applicationReducer * Refactor: Migrates appNotificationsReducer * Refactor: Migrates locationReducer * Refactor: Migrates navModelReducer * Refactor: Migrates teamsReducer and teamReducer * Refactor: Migrates cleanUpAction * Refactor: Migrates alertRulesReducer * Refactor: Cleans up recursiveCleanState * Refactor: Switched to Angular compatible reducers * Refactor: Migrates folderReducer * Refactor: Migrates dashboardReducer * Migrates panelEditorReducer * Refactor: Migrates dataSourcesReducer * Refactor: Migrates usersReducer * Refactor: Migrates organizationReducer * Refactor: Migrates pluginsReducer * Refactor: Migrates ldapReducer and ldapUserReducer * Refactor: Migrates apiKeysReducer * Refactor: Migrates exploreReducer and itemReducer * Refactor: Removes actionCreatorFactory and reducerFactory * Refactor: Moves mocks to test section * Docs: Removes sections about home grown framework * Update contribute/style-guides/redux.md Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> * Refactor: Cleans up some code * Refactor: Adds state typings * Refactor: Cleans up typings * Refactor: Adds comment about ImmerJs autoFreeze Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { DataSourceSettings, NavModel } from '@grafana/data';
|
||||
|
||||
import { DataSourcesListPage, Props } from './DataSourcesListPage';
|
||||
import { DataSourceSettings } from '@grafana/data';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector';
|
||||
import { getMockDataSources } from './__mocks__/dataSourcesMocks';
|
||||
import { setDataSourcesSearchQuery, setDataSourcesLayoutMode } from './state/actions';
|
||||
import { setDataSourcesLayoutMode, setDataSourcesSearchQuery } from './state/reducers';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
|
||||
@@ -2,21 +2,17 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { hot } from 'react-hot-loader';
|
||||
|
||||
// Components
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import OrgActionBar from 'app/core/components/OrgActionBar/OrgActionBar';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import DataSourcesList from './DataSourcesList';
|
||||
|
||||
// Types
|
||||
import { DataSourceSettings } from '@grafana/data';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { DataSourceSettings, NavModel } from '@grafana/data';
|
||||
import { StoreState } from 'app/types';
|
||||
import { LayoutMode } from 'app/core/components/LayoutSelector/LayoutSelector';
|
||||
|
||||
// Actions
|
||||
import { loadDataSources, setDataSourcesLayoutMode, setDataSourcesSearchQuery } from './state/actions';
|
||||
import { loadDataSources } from './state/actions';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
|
||||
import {
|
||||
@@ -25,6 +21,7 @@ import {
|
||||
getDataSourcesLayoutMode,
|
||||
getDataSourcesSearchQuery,
|
||||
} from './state/selectors';
|
||||
import { setDataSourcesLayoutMode, setDataSourcesSearchQuery } from './state/reducers';
|
||||
|
||||
export interface Props {
|
||||
navModel: NavModel;
|
||||
|
||||
@@ -7,10 +7,11 @@ import { List } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { StoreState, DataSourcePluginCategory } from 'app/types';
|
||||
import { addDataSource, loadDataSourcePlugins, setDataSourceTypeSearchQuery } from './state/actions';
|
||||
import { DataSourcePluginCategory, StoreState } from 'app/types';
|
||||
import { addDataSource, loadDataSourcePlugins } from './state/actions';
|
||||
import { getDataSourcePlugins } from './state/selectors';
|
||||
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
|
||||
import { setDataSourceTypeSearchQuery } from './state/reducers';
|
||||
|
||||
export interface Props {
|
||||
navModel: NavModel;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { DataSourceSettingsPage, Props } from './DataSourceSettingsPage';
|
||||
import { DataSourceSettings, DataSourcePlugin, DataSourceConstructor, NavModel } from '@grafana/data';
|
||||
import { DataSourceConstructor, DataSourcePlugin, DataSourceSettings, NavModel } from '@grafana/data';
|
||||
import { getMockDataSource } from '../__mocks__/dataSourcesMocks';
|
||||
import { getMockPlugin } from '../../plugins/__mocks__/pluginMocks';
|
||||
import { setDataSourceName, setIsDefault, dataSourceLoaded } from '../state/actions';
|
||||
import { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers';
|
||||
|
||||
const pluginMock = new DataSourcePlugin({} as DataSourceConstructor<any>);
|
||||
|
||||
|
||||
@@ -15,14 +15,7 @@ import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
// Actions & selectors
|
||||
import { getDataSource, getDataSourceMeta } from '../state/selectors';
|
||||
import {
|
||||
dataSourceLoaded,
|
||||
deleteDataSource,
|
||||
loadDataSource,
|
||||
setDataSourceName,
|
||||
setIsDefault,
|
||||
updateDataSource,
|
||||
} from '../state/actions';
|
||||
import { deleteDataSource, loadDataSource, updateDataSource } from '../state/actions';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { getRouteParamsId } from 'app/core/selectors/location';
|
||||
// Types
|
||||
@@ -32,6 +25,7 @@ import { DataSourcePluginMeta, DataSourceSettings, NavModel } from '@grafana/dat
|
||||
import { getDataSourceLoadingNav } from '../state/navModel';
|
||||
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
|
||||
import { importDataSourcePlugin } from 'app/features/plugins/plugin_loader';
|
||||
import { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers';
|
||||
|
||||
export interface Props {
|
||||
navModel: NavModel;
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
import config from '../../../core/config';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { LayoutMode } from 'app/core/components/LayoutSelector/LayoutSelector';
|
||||
import { updateLocation, updateNavIndex } from 'app/core/actions';
|
||||
import { buildNavModel } from './navModel';
|
||||
import { DataSourceSettings, DataSourcePluginMeta } from '@grafana/data';
|
||||
import { ThunkResult, DataSourcePluginCategory } from 'app/types';
|
||||
import { actionCreatorFactory } from 'app/core/redux';
|
||||
import { DataSourcePluginMeta, DataSourceSettings } from '@grafana/data';
|
||||
import { DataSourcePluginCategory, ThunkResult } from 'app/types';
|
||||
import { getPluginSettings } from 'app/features/plugins/PluginSettingsCache';
|
||||
import { importDataSourcePlugin } from 'app/features/plugins/plugin_loader';
|
||||
import {
|
||||
dataSourceLoaded,
|
||||
dataSourceMetaLoaded,
|
||||
dataSourcePluginsLoad,
|
||||
dataSourcePluginsLoaded,
|
||||
dataSourcesLoaded,
|
||||
} from './reducers';
|
||||
import { buildCategories } from './buildCategories';
|
||||
|
||||
export const dataSourceLoaded = actionCreatorFactory<DataSourceSettings>('LOAD_DATA_SOURCE').create();
|
||||
export const dataSourcesLoaded = actionCreatorFactory<DataSourceSettings[]>('LOAD_DATA_SOURCES').create();
|
||||
export const dataSourceMetaLoaded = actionCreatorFactory<DataSourcePluginMeta>('LOAD_DATA_SOURCE_META').create();
|
||||
export const dataSourcePluginsLoad = actionCreatorFactory('LOAD_DATA_SOURCE_PLUGINS').create();
|
||||
export const dataSourcePluginsLoaded = actionCreatorFactory<DataSourceTypesLoadedPayload>(
|
||||
'LOADED_DATA_SOURCE_PLUGINS'
|
||||
).create();
|
||||
export const setDataSourcesSearchQuery = actionCreatorFactory<string>('SET_DATA_SOURCES_SEARCH_QUERY').create();
|
||||
export const setDataSourcesLayoutMode = actionCreatorFactory<LayoutMode>('SET_DATA_SOURCES_LAYOUT_MODE').create();
|
||||
export const setDataSourceTypeSearchQuery = actionCreatorFactory<string>('SET_DATA_SOURCE_TYPE_SEARCH_QUERY').create();
|
||||
export const setDataSourceName = actionCreatorFactory<string>('SET_DATA_SOURCE_NAME').create();
|
||||
export const setIsDefault = actionCreatorFactory<boolean>('SET_IS_DEFAULT').create();
|
||||
|
||||
export interface DataSourceTypesLoadedPayload {
|
||||
plugins: DataSourcePluginMeta[];
|
||||
categories: DataSourcePluginCategory[];
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { reducerTester } from 'test/core/redux/reducerTester';
|
||||
import { dataSourcesReducer, initialState } from './reducers';
|
||||
import {
|
||||
dataSourcesLoaded,
|
||||
dataSourceLoaded,
|
||||
setDataSourcesSearchQuery,
|
||||
setDataSourcesLayoutMode,
|
||||
dataSourceMetaLoaded,
|
||||
dataSourcePluginsLoad,
|
||||
dataSourcePluginsLoaded,
|
||||
setDataSourceTypeSearchQuery,
|
||||
dataSourceMetaLoaded,
|
||||
dataSourcesLoaded,
|
||||
dataSourcesReducer,
|
||||
initialState,
|
||||
setDataSourceName,
|
||||
setDataSourcesLayoutMode,
|
||||
setDataSourcesSearchQuery,
|
||||
setDataSourceTypeSearchQuery,
|
||||
setIsDefault,
|
||||
} from './actions';
|
||||
import { getMockDataSources, getMockDataSource } from '../__mocks__/dataSourcesMocks';
|
||||
} from './reducers';
|
||||
import { getMockDataSource, getMockDataSources } from '../__mocks__/dataSourcesMocks';
|
||||
import { LayoutModes } from 'app/core/components/LayoutSelector/LayoutSelector';
|
||||
import { DataSourcesState } from 'app/types';
|
||||
import { PluginMetaInfo, PluginType, PluginMeta } from '@grafana/data';
|
||||
import { PluginMeta, PluginMetaInfo, PluginType } from '@grafana/data';
|
||||
|
||||
const mockPlugin = () =>
|
||||
({
|
||||
@@ -74,7 +75,7 @@ describe('dataSourcesReducer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when dataSourceTypesLoad is dispatched', () => {
|
||||
describe('when dataSourcePluginsLoad is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const state: DataSourcesState = { ...initialState, plugins: [mockPlugin()] };
|
||||
|
||||
@@ -85,7 +86,7 @@ describe('dataSourcesReducer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when dataSourceTypesLoaded is dispatched', () => {
|
||||
describe('when dataSourcePluginsLoaded is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const dataSourceTypes = [mockPlugin()];
|
||||
const state: DataSourcesState = { ...initialState, isLoadingDataSources: true };
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
import { AnyAction, createAction } from '@reduxjs/toolkit';
|
||||
import { DataSourcePluginMeta, DataSourceSettings } from '@grafana/data';
|
||||
|
||||
import { DataSourcesState } from 'app/types';
|
||||
import { DataSourceSettings, DataSourcePluginMeta } from '@grafana/data';
|
||||
import {
|
||||
dataSourceLoaded,
|
||||
dataSourcesLoaded,
|
||||
setDataSourcesSearchQuery,
|
||||
setDataSourcesLayoutMode,
|
||||
dataSourcePluginsLoad,
|
||||
dataSourcePluginsLoaded,
|
||||
setDataSourceTypeSearchQuery,
|
||||
dataSourceMetaLoaded,
|
||||
setDataSourceName,
|
||||
setIsDefault,
|
||||
} from './actions';
|
||||
import { LayoutModes } from 'app/core/components/LayoutSelector/LayoutSelector';
|
||||
import { reducerFactory } from 'app/core/redux';
|
||||
import { LayoutMode, LayoutModes } from 'app/core/components/LayoutSelector/LayoutSelector';
|
||||
import { DataSourceTypesLoadedPayload } from './actions';
|
||||
|
||||
export const initialState: DataSourcesState = {
|
||||
dataSources: [],
|
||||
@@ -29,61 +19,80 @@ export const initialState: DataSourcesState = {
|
||||
dataSourceMeta: {} as DataSourcePluginMeta,
|
||||
};
|
||||
|
||||
export const dataSourcesReducer = reducerFactory(initialState)
|
||||
.addMapper({
|
||||
filter: dataSourcesLoaded,
|
||||
mapper: (state, action) => ({
|
||||
export const dataSourceLoaded = createAction<DataSourceSettings>('dataSources/dataSourceLoaded');
|
||||
export const dataSourcesLoaded = createAction<DataSourceSettings[]>('dataSources/dataSourcesLoaded');
|
||||
export const dataSourceMetaLoaded = createAction<DataSourcePluginMeta>('dataSources/dataSourceMetaLoaded');
|
||||
export const dataSourcePluginsLoad = createAction('dataSources/dataSourcePluginsLoad');
|
||||
export const dataSourcePluginsLoaded = createAction<DataSourceTypesLoadedPayload>(
|
||||
'dataSources/dataSourcePluginsLoaded'
|
||||
);
|
||||
export const setDataSourcesSearchQuery = createAction<string>('dataSources/setDataSourcesSearchQuery');
|
||||
export const setDataSourcesLayoutMode = createAction<LayoutMode>('dataSources/setDataSourcesLayoutMode');
|
||||
export const setDataSourceTypeSearchQuery = createAction<string>('dataSources/setDataSourceTypeSearchQuery');
|
||||
export const setDataSourceName = createAction<string>('dataSources/setDataSourceName');
|
||||
export const setIsDefault = createAction<boolean>('dataSources/setIsDefault');
|
||||
|
||||
// Redux Toolkit uses ImmerJs as part of their solution to ensure that state objects are not mutated.
|
||||
// ImmerJs has an autoFreeze option that freezes objects from change which means this reducer can't be migrated to createSlice
|
||||
// because the state would become frozen and during run time we would get errors because Angular would try to mutate
|
||||
// the frozen state.
|
||||
// https://github.com/reduxjs/redux-toolkit/issues/242
|
||||
export const dataSourcesReducer = (state: DataSourcesState = initialState, action: AnyAction): DataSourcesState => {
|
||||
if (dataSourcesLoaded.match(action)) {
|
||||
return {
|
||||
...state,
|
||||
hasFetched: true,
|
||||
dataSources: action.payload,
|
||||
dataSourcesCount: action.payload.length,
|
||||
}),
|
||||
})
|
||||
.addMapper({
|
||||
filter: dataSourceLoaded,
|
||||
mapper: (state, action) => ({ ...state, dataSource: action.payload }),
|
||||
})
|
||||
.addMapper({
|
||||
filter: setDataSourcesSearchQuery,
|
||||
mapper: (state, action) => ({ ...state, searchQuery: action.payload }),
|
||||
})
|
||||
.addMapper({
|
||||
filter: setDataSourcesLayoutMode,
|
||||
mapper: (state, action) => ({ ...state, layoutMode: action.payload }),
|
||||
})
|
||||
.addMapper({
|
||||
filter: dataSourcePluginsLoad,
|
||||
mapper: state => ({ ...state, plugins: [], isLoadingDataSources: true }),
|
||||
})
|
||||
.addMapper({
|
||||
filter: dataSourcePluginsLoaded,
|
||||
mapper: (state, action) => ({
|
||||
};
|
||||
}
|
||||
|
||||
if (dataSourceLoaded.match(action)) {
|
||||
return { ...state, dataSource: action.payload };
|
||||
}
|
||||
|
||||
if (setDataSourcesSearchQuery.match(action)) {
|
||||
return { ...state, searchQuery: action.payload };
|
||||
}
|
||||
|
||||
if (setDataSourcesLayoutMode.match(action)) {
|
||||
return { ...state, layoutMode: action.payload };
|
||||
}
|
||||
|
||||
if (dataSourcePluginsLoad.match(action)) {
|
||||
return { ...state, plugins: [], isLoadingDataSources: true };
|
||||
}
|
||||
|
||||
if (dataSourcePluginsLoaded.match(action)) {
|
||||
return {
|
||||
...state,
|
||||
plugins: action.payload.plugins,
|
||||
categories: action.payload.categories,
|
||||
isLoadingDataSources: false,
|
||||
}),
|
||||
})
|
||||
.addMapper({
|
||||
filter: setDataSourceTypeSearchQuery,
|
||||
mapper: (state, action) => ({ ...state, dataSourceTypeSearchQuery: action.payload }),
|
||||
})
|
||||
.addMapper({
|
||||
filter: dataSourceMetaLoaded,
|
||||
mapper: (state, action) => ({ ...state, dataSourceMeta: action.payload }),
|
||||
})
|
||||
.addMapper({
|
||||
filter: setDataSourceName,
|
||||
mapper: (state, action) => ({ ...state, dataSource: { ...state.dataSource, name: action.payload } }),
|
||||
})
|
||||
.addMapper({
|
||||
filter: setIsDefault,
|
||||
mapper: (state, action) => ({
|
||||
};
|
||||
}
|
||||
|
||||
if (setDataSourceTypeSearchQuery.match(action)) {
|
||||
return { ...state, dataSourceTypeSearchQuery: action.payload };
|
||||
}
|
||||
|
||||
if (dataSourceMetaLoaded.match(action)) {
|
||||
return { ...state, dataSourceMeta: action.payload };
|
||||
}
|
||||
|
||||
if (setDataSourceName.match(action)) {
|
||||
return { ...state, dataSource: { ...state.dataSource, name: action.payload } };
|
||||
}
|
||||
|
||||
if (setIsDefault.match(action)) {
|
||||
return {
|
||||
...state,
|
||||
dataSource: { ...state.dataSource, isDefault: action.payload },
|
||||
}),
|
||||
})
|
||||
.create();
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export default {
|
||||
dataSources: dataSourcesReducer,
|
||||
|
||||
Reference in New Issue
Block a user