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:
Hugo Häggmark
2020-01-13 08:03:22 +01:00
committed by GitHub
parent b0515f46cc
commit 4f0fa776be
97 changed files with 2392 additions and 2305 deletions

View File

@@ -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 = {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>);

View File

@@ -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;

View File

@@ -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[];

View File

@@ -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 };

View File

@@ -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,