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 {
n: number;
......@@ -11,15 +15,14 @@ interface Dummy {
b: boolean;
}
const setup = (payload: Dummy) => {
const setup = (payload?: Dummy) => {
resetAllActionCreatorTypes();
const actionCreator = actionCreatorFactory<Dummy>('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>('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();
});
});
......
......@@ -12,10 +12,19 @@ export interface ActionCreator<Payload> {
(payload: Payload): ActionOf<Payload>;
}
export interface NoPayloadActionCreator {
readonly type: string;
(): ActionOf<undefined>;
}
export interface ActionCreatorFactory<Payload> {
create: () => ActionCreator<Payload>;
}
export interface NoPayloadActionCreatorFactory {
create: () => NoPayloadActionCreator;
}
export const actionCreatorFactory = <Payload>(type: string): ActionCreatorFactory<Payload> => {
const create = (): ActionCreator<Payload> => {
return Object.assign((payload: Payload): ActionOf<Payload> => ({ type, payload }), { type });
......@@ -30,5 +39,19 @@ export const actionCreatorFactory = <Payload>(type: string): ActionCreatorFactor
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
export const resetAllActionCreatorTypes = () => (allActionCreators.length = 0);
......@@ -26,7 +26,7 @@ const dummyReducerIntialState: DummyReducerState = {
const dummyActionCreator = actionCreatorFactory<DummyReducerState>('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;
......
import { ActionOf, ActionCreator } from './actionCreatorFactory';
import { Reducer } from 'redux';
export type Mapper<State, Payload> = (state: State, action: ActionOf<Payload>) => State;
export interface HandlerConfig<State, Payload> {
export interface MapperConfig<State, Payload> {
filter: ActionCreator<Payload>;
mapper: Mapper<State, Payload>;
}
export interface AddHandler<State> {
addHandler: <Payload>(config: HandlerConfig<State, Payload>) => CreateReducer<State>;
export interface AddMapper<State> {
addMapper: <Payload>(config: MapperConfig<State, Payload>) => CreateReducer<State>;
}
export interface CreateReducer<State> extends AddHandler<State> {
create: () => Mapper<State, any>;
export interface CreateReducer<State> extends AddMapper<State> {
create: () => Reducer<State, ActionOf<any>>;
}
export const reducerFactory = <State>(initialState: State): AddHandler<State> => {
const allHandlerConfigs: Array<HandlerConfig<State, any>> = [];
export const reducerFactory = <State>(initialState: State): AddMapper<State> => {
const allMapperConfigs: Array<MapperConfig<State, any>> = [];
const addHandler = <Payload>(config: HandlerConfig<State, Payload>): CreateReducer<State> => {
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 = <Payload>(config: MapperConfig<State, Payload>): CreateReducer<State> => {
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<any>): State => {
const handlerConfig = allHandlerConfigs.filter(config => config.filter.type === action.type)[0];
const create = (): Reducer<State, ActionOf<any>> => (state: State = initialState, action: ActionOf<any>): 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<State> = {
addHandler,
create,
};
const instance: CreateReducer<State> = { addMapper, create };
return instance;
};
......@@ -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';
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',
}
import { ActionOf, noPayloadActionCreatorFactory } from 'app/core/redux/actionCreatorFactory';
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>(
ActionTypes.SetDataSourceTypeSearchQuery
).create();
export const setDataSourceTypeSearchQuery = actionCreatorFactory<string>('SET_DATA_SOURCE_TYPE_SEARCH_QUERY').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>;
export function loadDataSources(): ThunkResult<void> {
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<void> {
export function loadDataSourceTypes(): ThunkResult<void> {
return async dispatch => {
dispatch(dataSourceTypesLoad({}));
dispatch(dataSourceTypesLoad());
const result = await getBackendSrv().get('/api/plugins', { enabled: 1, type: 'datasource' });
dispatch(dataSourceTypesLoaded(result));
};
......
......@@ -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,
......
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