From 2f47b225a0d6259f4aa2a46daf962539c466dcd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 31 Jan 2019 06:38:40 +0100 Subject: [PATCH] Removed ActionTypes and fixed a noPayloadActionCreatorFactory --- .../core/redux/actionCreatorFactory.test.ts | 44 ++++++++++++++--- public/app/core/redux/actionCreatorFactory.ts | 23 +++++++++ public/app/core/redux/reducerFactory.test.ts | 6 +-- public/app/core/redux/reducerFactory.ts | 36 +++++++------- .../app/features/datasources/state/actions.ts | 49 ++++++++----------- .../features/datasources/state/reducers.ts | 20 ++++---- 6 files changed, 110 insertions(+), 68 deletions(-) diff --git a/public/app/core/redux/actionCreatorFactory.test.ts b/public/app/core/redux/actionCreatorFactory.test.ts index 96464354411..274079311b3 100644 --- a/public/app/core/redux/actionCreatorFactory.test.ts +++ b/public/app/core/redux/actionCreatorFactory.test.ts @@ -1,4 +1,8 @@ -import { actionCreatorFactory, resetAllActionCreatorTypes } from './actionCreatorFactory'; +import { + actionCreatorFactory, + resetAllActionCreatorTypes, + noPayloadActionCreatorFactory, +} from './actionCreatorFactory'; interface Dummy { n: number; @@ -11,15 +15,14 @@ interface Dummy { b: boolean; } -const setup = (payload: Dummy) => { +const setup = (payload?: Dummy) => { resetAllActionCreatorTypes(); const actionCreator = actionCreatorFactory('dummy').create(); + const noPayloadactionCreator = noPayloadActionCreatorFactory('NoPayload').create(); const result = actionCreator(payload); + const noPayloadResult = noPayloadactionCreator(); - return { - actionCreator, - result, - }; + return { actionCreator, noPayloadactionCreator, result, noPayloadResult }; }; describe('actionCreatorFactory', () => { @@ -46,7 +49,34 @@ describe('actionCreatorFactory', () => { setup(payload); expect(() => { - actionCreatorFactory('DuMmY').create(); + noPayloadActionCreatorFactory('DuMmY').create(); + }).toThrow(); + }); + }); +}); + +describe('noPayloadActionCreatorFactory', () => { + describe('when calling create', () => { + it('then it should create correct type string', () => { + const { noPayloadResult, noPayloadactionCreator } = setup(); + + expect(noPayloadactionCreator.type).toEqual('NoPayload'); + expect(noPayloadResult.type).toEqual('NoPayload'); + }); + + it('then it should create correct payload', () => { + const { noPayloadResult } = setup(); + + expect(noPayloadResult.payload).toBeUndefined(); + }); + }); + + describe('when calling create with existing type', () => { + it('then it should throw error', () => { + setup(); + + expect(() => { + actionCreatorFactory('nOpAyLoAd').create(); }).toThrow(); }); }); diff --git a/public/app/core/redux/actionCreatorFactory.ts b/public/app/core/redux/actionCreatorFactory.ts index fd12ac6ca36..d6477144df4 100644 --- a/public/app/core/redux/actionCreatorFactory.ts +++ b/public/app/core/redux/actionCreatorFactory.ts @@ -12,10 +12,19 @@ export interface ActionCreator { (payload: Payload): ActionOf; } +export interface NoPayloadActionCreator { + readonly type: string; + (): ActionOf; +} + export interface ActionCreatorFactory { create: () => ActionCreator; } +export interface NoPayloadActionCreatorFactory { + create: () => NoPayloadActionCreator; +} + export const actionCreatorFactory = (type: string): ActionCreatorFactory => { const create = (): ActionCreator => { return Object.assign((payload: Payload): ActionOf => ({ type, payload }), { type }); @@ -30,5 +39,19 @@ export const actionCreatorFactory = (type: string): ActionCreatorFactor return { create }; }; +export const noPayloadActionCreatorFactory = (type: string): NoPayloadActionCreatorFactory => { + const create = (): NoPayloadActionCreator => { + return Object.assign((): ActionOf => ({ type, payload: undefined }), { type }); + }; + + if (allActionCreators.some(t => (t && type ? t.toLocaleUpperCase() === type.toLocaleUpperCase() : false))) { + throw new Error(`There is already an actionCreator defined with the type ${type}`); + } + + allActionCreators.push(type); + + return { create }; +}; + // Should only be used by tests export const resetAllActionCreatorTypes = () => (allActionCreators.length = 0); diff --git a/public/app/core/redux/reducerFactory.test.ts b/public/app/core/redux/reducerFactory.test.ts index 5057b6d84b5..48cffc1ca7a 100644 --- a/public/app/core/redux/reducerFactory.test.ts +++ b/public/app/core/redux/reducerFactory.test.ts @@ -26,7 +26,7 @@ const dummyReducerIntialState: DummyReducerState = { const dummyActionCreator = actionCreatorFactory('dummy').create(); const dummyReducer = reducerFactory(dummyReducerIntialState) - .addHandler({ + .addMapper({ filter: dummyActionCreator, mapper: (state, action) => ({ ...state, ...action.payload }), }) @@ -76,7 +76,7 @@ describe('reducerFactory', () => { describe('given a handler is added', () => { describe('when a handler with the same creator is added', () => { it('then is should throw', () => { - const faultyReducer = reducerFactory(dummyReducerIntialState).addHandler({ + const faultyReducer = reducerFactory(dummyReducerIntialState).addMapper({ filter: dummyActionCreator, mapper: (state, action) => { return { ...state, ...action.payload }; @@ -84,7 +84,7 @@ describe('reducerFactory', () => { }); expect(() => { - faultyReducer.addHandler({ + faultyReducer.addMapper({ filter: dummyActionCreator, mapper: state => { return state; diff --git a/public/app/core/redux/reducerFactory.ts b/public/app/core/redux/reducerFactory.ts index 6858dd41879..c70d91e5e5f 100644 --- a/public/app/core/redux/reducerFactory.ts +++ b/public/app/core/redux/reducerFactory.ts @@ -1,47 +1,45 @@ import { ActionOf, ActionCreator } from './actionCreatorFactory'; +import { Reducer } from 'redux'; export type Mapper = (state: State, action: ActionOf) => State; -export interface HandlerConfig { +export interface MapperConfig { filter: ActionCreator; mapper: Mapper; } -export interface AddHandler { - addHandler: (config: HandlerConfig) => CreateReducer; +export interface AddMapper { + addMapper: (config: MapperConfig) => CreateReducer; } -export interface CreateReducer extends AddHandler { - create: () => Mapper; +export interface CreateReducer extends AddMapper { + create: () => Reducer>; } -export const reducerFactory = (initialState: State): AddHandler => { - const allHandlerConfigs: Array> = []; +export const reducerFactory = (initialState: State): AddMapper => { + const allMapperConfigs: Array> = []; - const addHandler = (config: HandlerConfig): CreateReducer => { - if (allHandlerConfigs.some(c => c.filter.type === config.filter.type)) { - throw new Error(`There is already a handlers defined with the type ${config.filter.type}`); + const addMapper = (config: MapperConfig): CreateReducer => { + if (allMapperConfigs.some(c => c.filter.type === config.filter.type)) { + throw new Error(`There is already a Mappers defined with the type ${config.filter.type}`); } - allHandlerConfigs.push(config); + allMapperConfigs.push(config); return instance; }; - const create = () => (state: State = initialState, action: ActionOf): State => { - const handlerConfig = allHandlerConfigs.filter(config => config.filter.type === action.type)[0]; + const create = (): Reducer> => (state: State = initialState, action: ActionOf): State => { + const mapperConfig = allMapperConfigs.filter(config => config.filter.type === action.type)[0]; - if (handlerConfig) { - return handlerConfig.mapper(state, action); + if (mapperConfig) { + return mapperConfig.mapper(state, action); } return state; }; - const instance: CreateReducer = { - addHandler, - create, - }; + const instance: CreateReducer = { addMapper, create }; return instance; }; diff --git a/public/app/features/datasources/state/actions.ts b/public/app/features/datasources/state/actions.ts index 4cd61537cea..2e21b3066d1 100644 --- a/public/app/features/datasources/state/actions.ts +++ b/public/app/features/datasources/state/actions.ts @@ -9,51 +9,42 @@ import { buildNavModel } from './navModel'; import { DataSourceSettings } from '@grafana/ui/src/types'; import { Plugin, StoreState } from 'app/types'; import { actionCreatorFactory } from 'app/core/redux'; -import { ActionOf } from 'app/core/redux/actionCreatorFactory'; +import { ActionOf, noPayloadActionCreatorFactory } from 'app/core/redux/actionCreatorFactory'; -export enum ActionTypes { - LoadDataSources = 'LOAD_DATA_SOURCES', - LoadDataSourceTypes = 'LOAD_DATA_SOURCE_TYPES', - LoadedDataSourceTypes = 'LOADED_DATA_SOURCE_TYPES', - LoadDataSource = 'LOAD_DATA_SOURCE', - LoadDataSourceMeta = 'LOAD_DATA_SOURCE_META', - SetDataSourcesSearchQuery = 'SET_DATA_SOURCES_SEARCH_QUERY', - SetDataSourcesLayoutMode = 'SET_DATA_SOURCES_LAYOUT_MODE', - SetDataSourceTypeSearchQuery = 'SET_DATA_SOURCE_TYPE_SEARCH_QUERY', - SetDataSourceName = 'SET_DATA_SOURCE_NAME', - SetIsDefault = 'SET_IS_DEFAULT', -} +export const dataSourceLoaded = actionCreatorFactory('LOAD_DATA_SOURCE').create(); -export const dataSourceLoaded = actionCreatorFactory(ActionTypes.LoadDataSource).create(); +export const dataSourcesLoaded = actionCreatorFactory('LOAD_DATA_SOURCES').create(); -export const dataSourcesLoaded = actionCreatorFactory(ActionTypes.LoadDataSources).create(); +export const dataSourceMetaLoaded = actionCreatorFactory('LOAD_DATA_SOURCE_META').create(); -export const dataSourceMetaLoaded = actionCreatorFactory(ActionTypes.LoadDataSourceMeta).create(); +export const dataSourceTypesLoad = noPayloadActionCreatorFactory('LOAD_DATA_SOURCE_TYPES').create(); -export const dataSourceTypesLoad = actionCreatorFactory(ActionTypes.LoadDataSourceTypes).create(); +export const dataSourceTypesLoaded = actionCreatorFactory('LOADED_DATA_SOURCE_TYPES').create(); -export const dataSourceTypesLoaded = actionCreatorFactory(ActionTypes.LoadedDataSourceTypes).create(); +export const setDataSourcesSearchQuery = actionCreatorFactory('SET_DATA_SOURCES_SEARCH_QUERY').create(); -export const setDataSourcesSearchQuery = actionCreatorFactory(ActionTypes.SetDataSourcesSearchQuery).create(); +export const setDataSourcesLayoutMode = actionCreatorFactory('SET_DATA_SOURCES_LAYOUT_MODE').create(); -export const setDataSourcesLayoutMode = actionCreatorFactory(ActionTypes.SetDataSourcesLayoutMode).create(); +export const setDataSourceTypeSearchQuery = actionCreatorFactory('SET_DATA_SOURCE_TYPE_SEARCH_QUERY').create(); -export const setDataSourceTypeSearchQuery = actionCreatorFactory( - ActionTypes.SetDataSourceTypeSearchQuery -).create(); +export const setDataSourceName = actionCreatorFactory('SET_DATA_SOURCE_NAME').create(); -export const setDataSourceName = actionCreatorFactory(ActionTypes.SetDataSourceName).create(); +export const setIsDefault = actionCreatorFactory('SET_IS_DEFAULT').create(); -export const setIsDefault = actionCreatorFactory(ActionTypes.SetIsDefault).create(); - -export type Action = UpdateLocationAction | UpdateNavIndexAction | ActionOf; +export type Action = + | UpdateLocationAction + | UpdateNavIndexAction + | ActionOf + | ActionOf + | ActionOf + | ActionOf; type ThunkResult = ThunkAction; export function loadDataSources(): ThunkResult { return async dispatch => { const response = await getBackendSrv().get('/api/datasources'); - dataSourcesLoaded(response); + dispatch(dataSourcesLoaded(response)); }; } @@ -91,7 +82,7 @@ export function addDataSource(plugin: Plugin): ThunkResult { export function loadDataSourceTypes(): ThunkResult { return async dispatch => { - dispatch(dataSourceTypesLoad({})); + dispatch(dataSourceTypesLoad()); const result = await getBackendSrv().get('/api/plugins', { enabled: 1, type: 'datasource' }); dispatch(dataSourceTypesLoaded(result)); }; diff --git a/public/app/features/datasources/state/reducers.ts b/public/app/features/datasources/state/reducers.ts index 68bc30201d6..20aa58c8594 100644 --- a/public/app/features/datasources/state/reducers.ts +++ b/public/app/features/datasources/state/reducers.ts @@ -29,7 +29,7 @@ const initialState: DataSourcesState = { }; export const dataSourcesReducer = reducerFactory(initialState) - .addHandler({ + .addMapper({ filter: dataSourcesLoaded, mapper: (state, action) => ({ ...state, @@ -38,23 +38,23 @@ export const dataSourcesReducer = reducerFactory(initialState) dataSourcesCount: action.payload.length, }), }) - .addHandler({ + .addMapper({ filter: dataSourceLoaded, mapper: (state, action) => ({ ...state, dataSource: action.payload }), }) - .addHandler({ + .addMapper({ filter: setDataSourcesSearchQuery, mapper: (state, action) => ({ ...state, searchQuery: action.payload }), }) - .addHandler({ + .addMapper({ filter: setDataSourcesLayoutMode, mapper: (state, action) => ({ ...state, layoutMode: action.payload }), }) - .addHandler({ + .addMapper({ filter: dataSourceTypesLoad, mapper: state => ({ ...state, dataSourceTypes: [], isLoadingDataSources: true }), }) - .addHandler({ + .addMapper({ filter: dataSourceTypesLoaded, mapper: (state, action) => ({ ...state, @@ -62,19 +62,19 @@ export const dataSourcesReducer = reducerFactory(initialState) isLoadingDataSources: false, }), }) - .addHandler({ + .addMapper({ filter: setDataSourceTypeSearchQuery, mapper: (state, action) => ({ ...state, dataSourceTypeSearchQuery: action.payload }), }) - .addHandler({ + .addMapper({ filter: dataSourceMetaLoaded, mapper: (state, action) => ({ ...state, dataSourceMeta: action.payload }), }) - .addHandler({ + .addMapper({ filter: setDataSourceName, mapper: (state, action) => ({ ...state, dataSource: { ...state.dataSource, name: action.payload } }), }) - .addHandler({ + .addMapper({ filter: setIsDefault, mapper: (state, action) => ({ ...state,