Commit 2f47b225 by Hugo Häggmark

Removed ActionTypes and fixed a noPayloadActionCreatorFactory

parent d3815beb
import { actionCreatorFactory, resetAllActionCreatorTypes } from './actionCreatorFactory'; import {
actionCreatorFactory,
resetAllActionCreatorTypes,
noPayloadActionCreatorFactory,
} from './actionCreatorFactory';
interface Dummy { interface Dummy {
n: number; n: number;
...@@ -11,15 +15,14 @@ interface Dummy { ...@@ -11,15 +15,14 @@ interface Dummy {
b: boolean; b: boolean;
} }
const setup = (payload: Dummy) => { const setup = (payload?: Dummy) => {
resetAllActionCreatorTypes(); resetAllActionCreatorTypes();
const actionCreator = actionCreatorFactory<Dummy>('dummy').create(); const actionCreator = actionCreatorFactory<Dummy>('dummy').create();
const noPayloadactionCreator = noPayloadActionCreatorFactory('NoPayload').create();
const result = actionCreator(payload); const result = actionCreator(payload);
const noPayloadResult = noPayloadactionCreator();
return { return { actionCreator, noPayloadactionCreator, result, noPayloadResult };
actionCreator,
result,
};
}; };
describe('actionCreatorFactory', () => { describe('actionCreatorFactory', () => {
...@@ -46,7 +49,34 @@ describe('actionCreatorFactory', () => { ...@@ -46,7 +49,34 @@ describe('actionCreatorFactory', () => {
setup(payload); setup(payload);
expect(() => { expect(() => {
actionCreatorFactory<Dummy>('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<Dummy>('nOpAyLoAd').create();
}).toThrow(); }).toThrow();
}); });
}); });
......
...@@ -12,10 +12,19 @@ export interface ActionCreator<Payload> { ...@@ -12,10 +12,19 @@ export interface ActionCreator<Payload> {
(payload: Payload): ActionOf<Payload>; (payload: Payload): ActionOf<Payload>;
} }
export interface NoPayloadActionCreator {
readonly type: string;
(): ActionOf<undefined>;
}
export interface ActionCreatorFactory<Payload> { export interface ActionCreatorFactory<Payload> {
create: () => ActionCreator<Payload>; create: () => ActionCreator<Payload>;
} }
export interface NoPayloadActionCreatorFactory {
create: () => NoPayloadActionCreator;
}
export const actionCreatorFactory = <Payload>(type: string): ActionCreatorFactory<Payload> => { export const actionCreatorFactory = <Payload>(type: string): ActionCreatorFactory<Payload> => {
const create = (): ActionCreator<Payload> => { const create = (): ActionCreator<Payload> => {
return Object.assign((payload: Payload): ActionOf<Payload> => ({ type, payload }), { type }); return Object.assign((payload: Payload): ActionOf<Payload> => ({ type, payload }), { type });
...@@ -30,5 +39,19 @@ export const actionCreatorFactory = <Payload>(type: string): ActionCreatorFactor ...@@ -30,5 +39,19 @@ export const actionCreatorFactory = <Payload>(type: string): ActionCreatorFactor
return { create }; return { create };
}; };
export const noPayloadActionCreatorFactory = (type: string): NoPayloadActionCreatorFactory => {
const create = (): NoPayloadActionCreator => {
return Object.assign((): ActionOf<undefined> => ({ 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 // Should only be used by tests
export const resetAllActionCreatorTypes = () => (allActionCreators.length = 0); export const resetAllActionCreatorTypes = () => (allActionCreators.length = 0);
...@@ -26,7 +26,7 @@ const dummyReducerIntialState: DummyReducerState = { ...@@ -26,7 +26,7 @@ const dummyReducerIntialState: DummyReducerState = {
const dummyActionCreator = actionCreatorFactory<DummyReducerState>('dummy').create(); const dummyActionCreator = actionCreatorFactory<DummyReducerState>('dummy').create();
const dummyReducer = reducerFactory(dummyReducerIntialState) const dummyReducer = reducerFactory(dummyReducerIntialState)
.addHandler({ .addMapper({
filter: dummyActionCreator, filter: dummyActionCreator,
mapper: (state, action) => ({ ...state, ...action.payload }), mapper: (state, action) => ({ ...state, ...action.payload }),
}) })
...@@ -76,7 +76,7 @@ describe('reducerFactory', () => { ...@@ -76,7 +76,7 @@ describe('reducerFactory', () => {
describe('given a handler is added', () => { describe('given a handler is added', () => {
describe('when a handler with the same creator is added', () => { describe('when a handler with the same creator is added', () => {
it('then is should throw', () => { it('then is should throw', () => {
const faultyReducer = reducerFactory(dummyReducerIntialState).addHandler({ const faultyReducer = reducerFactory(dummyReducerIntialState).addMapper({
filter: dummyActionCreator, filter: dummyActionCreator,
mapper: (state, action) => { mapper: (state, action) => {
return { ...state, ...action.payload }; return { ...state, ...action.payload };
...@@ -84,7 +84,7 @@ describe('reducerFactory', () => { ...@@ -84,7 +84,7 @@ describe('reducerFactory', () => {
}); });
expect(() => { expect(() => {
faultyReducer.addHandler({ faultyReducer.addMapper({
filter: dummyActionCreator, filter: dummyActionCreator,
mapper: state => { mapper: state => {
return state; return state;
......
import { ActionOf, ActionCreator } from './actionCreatorFactory'; import { ActionOf, ActionCreator } from './actionCreatorFactory';
import { Reducer } from 'redux';
export type Mapper<State, Payload> = (state: State, action: ActionOf<Payload>) => State; export type Mapper<State, Payload> = (state: State, action: ActionOf<Payload>) => State;
export interface HandlerConfig<State, Payload> { export interface MapperConfig<State, Payload> {
filter: ActionCreator<Payload>; filter: ActionCreator<Payload>;
mapper: Mapper<State, Payload>; mapper: Mapper<State, Payload>;
} }
export interface AddHandler<State> { export interface AddMapper<State> {
addHandler: <Payload>(config: HandlerConfig<State, Payload>) => CreateReducer<State>; addMapper: <Payload>(config: MapperConfig<State, Payload>) => CreateReducer<State>;
} }
export interface CreateReducer<State> extends AddHandler<State> { export interface CreateReducer<State> extends AddMapper<State> {
create: () => Mapper<State, any>; create: () => Reducer<State, ActionOf<any>>;
} }
export const reducerFactory = <State>(initialState: State): AddHandler<State> => { export const reducerFactory = <State>(initialState: State): AddMapper<State> => {
const allHandlerConfigs: Array<HandlerConfig<State, any>> = []; const allMapperConfigs: Array<MapperConfig<State, any>> = [];
const addHandler = <Payload>(config: HandlerConfig<State, Payload>): CreateReducer<State> => { const addMapper = <Payload>(config: MapperConfig<State, Payload>): CreateReducer<State> => {
if (allHandlerConfigs.some(c => c.filter.type === config.filter.type)) { if (allMapperConfigs.some(c => c.filter.type === config.filter.type)) {
throw new Error(`There is already a handlers defined with the 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; return instance;
}; };
const create = () => (state: State = initialState, action: ActionOf<any>): State => { const create = (): Reducer<State, ActionOf<any>> => (state: State = initialState, action: ActionOf<any>): State => {
const handlerConfig = allHandlerConfigs.filter(config => config.filter.type === action.type)[0]; const mapperConfig = allMapperConfigs.filter(config => config.filter.type === action.type)[0];
if (handlerConfig) { if (mapperConfig) {
return handlerConfig.mapper(state, action); return mapperConfig.mapper(state, action);
} }
return state; return state;
}; };
const instance: CreateReducer<State> = { const instance: CreateReducer<State> = { addMapper, create };
addHandler,
create,
};
return instance; return instance;
}; };
...@@ -9,51 +9,42 @@ import { buildNavModel } from './navModel'; ...@@ -9,51 +9,42 @@ import { buildNavModel } from './navModel';
import { DataSourceSettings } from '@grafana/ui/src/types'; import { DataSourceSettings } from '@grafana/ui/src/types';
import { Plugin, StoreState } from 'app/types'; import { Plugin, StoreState } from 'app/types';
import { actionCreatorFactory } from 'app/core/redux'; 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<DataSourceSettings>(ActionTypes.LoadDataSource).create(); export const dataSourceLoaded = actionCreatorFactory<DataSourceSettings>('LOAD_DATA_SOURCE').create();
export const dataSourcesLoaded = actionCreatorFactory<DataSourceSettings[]>(ActionTypes.LoadDataSources).create(); export const dataSourcesLoaded = actionCreatorFactory<DataSourceSettings[]>('LOAD_DATA_SOURCES').create();
export const dataSourceMetaLoaded = actionCreatorFactory<Plugin>(ActionTypes.LoadDataSourceMeta).create(); export const dataSourceMetaLoaded = actionCreatorFactory<Plugin>('LOAD_DATA_SOURCE_META').create();
export const dataSourceTypesLoad = actionCreatorFactory(ActionTypes.LoadDataSourceTypes).create(); export const dataSourceTypesLoad = noPayloadActionCreatorFactory('LOAD_DATA_SOURCE_TYPES').create();
export const dataSourceTypesLoaded = actionCreatorFactory<Plugin[]>(ActionTypes.LoadedDataSourceTypes).create(); export const dataSourceTypesLoaded = actionCreatorFactory<Plugin[]>('LOADED_DATA_SOURCE_TYPES').create();
export const setDataSourcesSearchQuery = actionCreatorFactory<string>(ActionTypes.SetDataSourcesSearchQuery).create(); export const setDataSourcesSearchQuery = actionCreatorFactory<string>('SET_DATA_SOURCES_SEARCH_QUERY').create();
export const setDataSourcesLayoutMode = actionCreatorFactory<LayoutMode>(ActionTypes.SetDataSourcesLayoutMode).create(); export const setDataSourcesLayoutMode = actionCreatorFactory<LayoutMode>('SET_DATA_SOURCES_LAYOUT_MODE').create();
export const setDataSourceTypeSearchQuery = actionCreatorFactory<string>( export const setDataSourceTypeSearchQuery = actionCreatorFactory<string>('SET_DATA_SOURCE_TYPE_SEARCH_QUERY').create();
ActionTypes.SetDataSourceTypeSearchQuery
).create();
export const setDataSourceName = actionCreatorFactory<string>(ActionTypes.SetDataSourceName).create(); export const setDataSourceName = actionCreatorFactory<string>('SET_DATA_SOURCE_NAME').create();
export const setIsDefault = actionCreatorFactory<boolean>(ActionTypes.SetIsDefault).create(); export const setIsDefault = actionCreatorFactory<boolean>('SET_IS_DEFAULT').create();
export type Action = UpdateLocationAction | UpdateNavIndexAction | ActionOf<any>; export type Action =
| UpdateLocationAction
| UpdateNavIndexAction
| ActionOf<DataSourceSettings>
| ActionOf<DataSourceSettings[]>
| ActionOf<Plugin>
| ActionOf<Plugin[]>;
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>; type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
export function loadDataSources(): ThunkResult<void> { export function loadDataSources(): ThunkResult<void> {
return async dispatch => { return async dispatch => {
const response = await getBackendSrv().get('/api/datasources'); const response = await getBackendSrv().get('/api/datasources');
dataSourcesLoaded(response); dispatch(dataSourcesLoaded(response));
}; };
} }
...@@ -91,7 +82,7 @@ export function addDataSource(plugin: Plugin): ThunkResult<void> { ...@@ -91,7 +82,7 @@ export function addDataSource(plugin: Plugin): ThunkResult<void> {
export function loadDataSourceTypes(): ThunkResult<void> { export function loadDataSourceTypes(): ThunkResult<void> {
return async dispatch => { return async dispatch => {
dispatch(dataSourceTypesLoad({})); dispatch(dataSourceTypesLoad());
const result = await getBackendSrv().get('/api/plugins', { enabled: 1, type: 'datasource' }); const result = await getBackendSrv().get('/api/plugins', { enabled: 1, type: 'datasource' });
dispatch(dataSourceTypesLoaded(result)); dispatch(dataSourceTypesLoaded(result));
}; };
......
...@@ -29,7 +29,7 @@ const initialState: DataSourcesState = { ...@@ -29,7 +29,7 @@ const initialState: DataSourcesState = {
}; };
export const dataSourcesReducer = reducerFactory(initialState) export const dataSourcesReducer = reducerFactory(initialState)
.addHandler({ .addMapper({
filter: dataSourcesLoaded, filter: dataSourcesLoaded,
mapper: (state, action) => ({ mapper: (state, action) => ({
...state, ...state,
...@@ -38,23 +38,23 @@ export const dataSourcesReducer = reducerFactory(initialState) ...@@ -38,23 +38,23 @@ export const dataSourcesReducer = reducerFactory(initialState)
dataSourcesCount: action.payload.length, dataSourcesCount: action.payload.length,
}), }),
}) })
.addHandler({ .addMapper({
filter: dataSourceLoaded, filter: dataSourceLoaded,
mapper: (state, action) => ({ ...state, dataSource: action.payload }), mapper: (state, action) => ({ ...state, dataSource: action.payload }),
}) })
.addHandler({ .addMapper({
filter: setDataSourcesSearchQuery, filter: setDataSourcesSearchQuery,
mapper: (state, action) => ({ ...state, searchQuery: action.payload }), mapper: (state, action) => ({ ...state, searchQuery: action.payload }),
}) })
.addHandler({ .addMapper({
filter: setDataSourcesLayoutMode, filter: setDataSourcesLayoutMode,
mapper: (state, action) => ({ ...state, layoutMode: action.payload }), mapper: (state, action) => ({ ...state, layoutMode: action.payload }),
}) })
.addHandler({ .addMapper({
filter: dataSourceTypesLoad, filter: dataSourceTypesLoad,
mapper: state => ({ ...state, dataSourceTypes: [], isLoadingDataSources: true }), mapper: state => ({ ...state, dataSourceTypes: [], isLoadingDataSources: true }),
}) })
.addHandler({ .addMapper({
filter: dataSourceTypesLoaded, filter: dataSourceTypesLoaded,
mapper: (state, action) => ({ mapper: (state, action) => ({
...state, ...state,
...@@ -62,19 +62,19 @@ export const dataSourcesReducer = reducerFactory(initialState) ...@@ -62,19 +62,19 @@ export const dataSourcesReducer = reducerFactory(initialState)
isLoadingDataSources: false, isLoadingDataSources: false,
}), }),
}) })
.addHandler({ .addMapper({
filter: setDataSourceTypeSearchQuery, filter: setDataSourceTypeSearchQuery,
mapper: (state, action) => ({ ...state, dataSourceTypeSearchQuery: action.payload }), mapper: (state, action) => ({ ...state, dataSourceTypeSearchQuery: action.payload }),
}) })
.addHandler({ .addMapper({
filter: dataSourceMetaLoaded, filter: dataSourceMetaLoaded,
mapper: (state, action) => ({ ...state, dataSourceMeta: action.payload }), mapper: (state, action) => ({ ...state, dataSourceMeta: action.payload }),
}) })
.addHandler({ .addMapper({
filter: setDataSourceName, filter: setDataSourceName,
mapper: (state, action) => ({ ...state, dataSource: { ...state.dataSource, name: action.payload } }), mapper: (state, action) => ({ ...state, dataSource: { ...state.dataSource, name: action.payload } }),
}) })
.addHandler({ .addMapper({
filter: setIsDefault, filter: setIsDefault,
mapper: (state, action) => ({ mapper: (state, action) => ({
...state, ...state,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment