Commit d3815beb by Hugo Häggmark

Refactored Datasources as POC

parent 2236a1a3
import { actionCreatorFactory } from './actionCreatorFactory';
import { reducerFactory } from './reducerFactory';
export { actionCreatorFactory, reducerFactory };
...@@ -28,7 +28,7 @@ const dummyActionCreator = actionCreatorFactory<DummyReducerState>('dummy').crea ...@@ -28,7 +28,7 @@ const dummyActionCreator = actionCreatorFactory<DummyReducerState>('dummy').crea
const dummyReducer = reducerFactory(dummyReducerIntialState) const dummyReducer = reducerFactory(dummyReducerIntialState)
.addHandler({ .addHandler({
filter: dummyActionCreator, filter: dummyActionCreator,
handler: (state, action) => ({ ...state, ...action.payload }), mapper: (state, action) => ({ ...state, ...action.payload }),
}) })
.create(); .create();
...@@ -78,7 +78,7 @@ describe('reducerFactory', () => { ...@@ -78,7 +78,7 @@ describe('reducerFactory', () => {
it('then is should throw', () => { it('then is should throw', () => {
const faultyReducer = reducerFactory(dummyReducerIntialState).addHandler({ const faultyReducer = reducerFactory(dummyReducerIntialState).addHandler({
filter: dummyActionCreator, filter: dummyActionCreator,
handler: (state, action) => { mapper: (state, action) => {
return { ...state, ...action.payload }; return { ...state, ...action.payload };
}, },
}); });
...@@ -86,7 +86,7 @@ describe('reducerFactory', () => { ...@@ -86,7 +86,7 @@ describe('reducerFactory', () => {
expect(() => { expect(() => {
faultyReducer.addHandler({ faultyReducer.addHandler({
filter: dummyActionCreator, filter: dummyActionCreator,
handler: 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 interface HandlerConfig<State, Payload> { export interface HandlerConfig<State, Payload> {
filter: ActionCreator<Payload>; filter: ActionCreator<Payload>;
handler: (state: State, action: ActionOf<Payload>) => State; mapper: Mapper<State, Payload>;
} }
export interface AddHandler<State> { export interface AddHandler<State> {
...@@ -11,7 +12,7 @@ export interface AddHandler<State> { ...@@ -11,7 +12,7 @@ export interface AddHandler<State> {
} }
export interface CreateReducer<State> extends AddHandler<State> { export interface CreateReducer<State> extends AddHandler<State> {
create: () => Reducer<State, ActionOf<any>>; create: () => Mapper<State, any>;
} }
export const reducerFactory = <State>(initialState: State): AddHandler<State> => { export const reducerFactory = <State>(initialState: State): AddHandler<State> => {
...@@ -27,18 +28,14 @@ export const reducerFactory = <State>(initialState: State): AddHandler<State> => ...@@ -27,18 +28,14 @@ export const reducerFactory = <State>(initialState: State): AddHandler<State> =>
return instance; return instance;
}; };
const create = (): Reducer<State, ActionOf<any>> => { const create = () => (state: State = initialState, action: ActionOf<any>): State => {
const reducer: Reducer<State, ActionOf<any>> = (state: State = initialState, action: ActionOf<any>) => { const handlerConfig = allHandlerConfigs.filter(config => config.filter.type === action.type)[0];
const validHandlers = allHandlerConfigs
.filter(config => config.filter.type === action.type)
.map(config => config.handler);
return validHandlers.reduce((currentState, handler) => { if (handlerConfig) {
return handler(currentState, action); return handlerConfig.mapper(state, action);
}, state || initialState); }
};
return reducer; return state;
}; };
const instance: CreateReducer<State> = { const instance: CreateReducer<State> = {
......
...@@ -5,6 +5,7 @@ import { NavModel } from 'app/types'; ...@@ -5,6 +5,7 @@ import { NavModel } from 'app/types';
import { DataSourceSettings } from '@grafana/ui/src/types'; import { DataSourceSettings } from '@grafana/ui/src/types';
import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector'; import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector';
import { getMockDataSources } from './__mocks__/dataSourcesMocks'; import { getMockDataSources } from './__mocks__/dataSourcesMocks';
import { setDataSourcesSearchQuery, setDataSourcesLayoutMode } from './state/actions';
const setup = (propOverrides?: object) => { const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
...@@ -13,16 +14,16 @@ const setup = (propOverrides?: object) => { ...@@ -13,16 +14,16 @@ const setup = (propOverrides?: object) => {
loadDataSources: jest.fn(), loadDataSources: jest.fn(),
navModel: { navModel: {
main: { main: {
text: 'Configuration' text: 'Configuration',
}, },
node: { node: {
text: 'Data Sources' text: 'Data Sources',
} },
} as NavModel, } as NavModel,
dataSourcesCount: 0, dataSourcesCount: 0,
searchQuery: '', searchQuery: '',
setDataSourcesSearchQuery: jest.fn(), setDataSourcesSearchQuery,
setDataSourcesLayoutMode: jest.fn(), setDataSourcesLayoutMode,
hasFetched: false, hasFetched: false,
}; };
......
...@@ -5,6 +5,7 @@ import { NavModel } from 'app/types'; ...@@ -5,6 +5,7 @@ import { NavModel } from 'app/types';
import { DataSourceSettings } from '@grafana/ui'; import { DataSourceSettings } from '@grafana/ui';
import { getMockDataSource } from '../__mocks__/dataSourcesMocks'; import { getMockDataSource } from '../__mocks__/dataSourcesMocks';
import { getMockPlugin } from '../../plugins/__mocks__/pluginMocks'; import { getMockPlugin } from '../../plugins/__mocks__/pluginMocks';
import { setDataSourceName, setIsDefault } from '../state/actions';
const setup = (propOverrides?: object) => { const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
...@@ -14,9 +15,9 @@ const setup = (propOverrides?: object) => { ...@@ -14,9 +15,9 @@ const setup = (propOverrides?: object) => {
pageId: 1, pageId: 1,
deleteDataSource: jest.fn(), deleteDataSource: jest.fn(),
loadDataSource: jest.fn(), loadDataSource: jest.fn(),
setDataSourceName: jest.fn(), setDataSourceName,
updateDataSource: jest.fn(), updateDataSource: jest.fn(),
setIsDefault: jest.fn(), setIsDefault,
}; };
Object.assign(props, propOverrides); Object.assign(props, propOverrides);
......
...@@ -8,6 +8,8 @@ import { UpdateLocationAction } from 'app/core/actions/location'; ...@@ -8,6 +8,8 @@ import { UpdateLocationAction } from 'app/core/actions/location';
import { buildNavModel } from './navModel'; 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 { ActionOf } from 'app/core/redux/actionCreatorFactory';
export enum ActionTypes { export enum ActionTypes {
LoadDataSources = 'LOAD_DATA_SOURCES', LoadDataSources = 'LOAD_DATA_SOURCES',
...@@ -22,124 +24,36 @@ export enum ActionTypes { ...@@ -22,124 +24,36 @@ export enum ActionTypes {
SetIsDefault = 'SET_IS_DEFAULT', SetIsDefault = 'SET_IS_DEFAULT',
} }
interface LoadDataSourcesAction { export const dataSourceLoaded = actionCreatorFactory<DataSourceSettings>(ActionTypes.LoadDataSource).create();
type: ActionTypes.LoadDataSources;
payload: DataSourceSettings[];
}
interface SetDataSourcesSearchQueryAction { export const dataSourcesLoaded = actionCreatorFactory<DataSourceSettings[]>(ActionTypes.LoadDataSources).create();
type: ActionTypes.SetDataSourcesSearchQuery;
payload: string;
}
interface SetDataSourcesLayoutModeAction { export const dataSourceMetaLoaded = actionCreatorFactory<Plugin>(ActionTypes.LoadDataSourceMeta).create();
type: ActionTypes.SetDataSourcesLayoutMode;
payload: LayoutMode;
}
interface LoadDataSourceTypesAction { export const dataSourceTypesLoad = actionCreatorFactory(ActionTypes.LoadDataSourceTypes).create();
type: ActionTypes.LoadDataSourceTypes;
}
interface LoadedDataSourceTypesAction { export const dataSourceTypesLoaded = actionCreatorFactory<Plugin[]>(ActionTypes.LoadedDataSourceTypes).create();
type: ActionTypes.LoadedDataSourceTypes;
payload: Plugin[];
}
interface SetDataSourceTypeSearchQueryAction { export const setDataSourcesSearchQuery = actionCreatorFactory<string>(ActionTypes.SetDataSourcesSearchQuery).create();
type: ActionTypes.SetDataSourceTypeSearchQuery;
payload: string;
}
interface LoadDataSourceAction { export const setDataSourcesLayoutMode = actionCreatorFactory<LayoutMode>(ActionTypes.SetDataSourcesLayoutMode).create();
type: ActionTypes.LoadDataSource;
payload: DataSourceSettings;
}
interface LoadDataSourceMetaAction { export const setDataSourceTypeSearchQuery = actionCreatorFactory<string>(
type: ActionTypes.LoadDataSourceMeta; ActionTypes.SetDataSourceTypeSearchQuery
payload: Plugin; ).create();
}
interface SetDataSourceNameAction { export const setDataSourceName = actionCreatorFactory<string>(ActionTypes.SetDataSourceName).create();
type: ActionTypes.SetDataSourceName;
payload: string;
}
interface SetIsDefaultAction { export const setIsDefault = actionCreatorFactory<boolean>(ActionTypes.SetIsDefault).create();
type: ActionTypes.SetIsDefault;
payload: boolean;
}
const dataSourcesLoaded = (dataSources: DataSourceSettings[]): LoadDataSourcesAction => ({ export type Action = UpdateLocationAction | UpdateNavIndexAction | ActionOf<any>;
type: ActionTypes.LoadDataSources,
payload: dataSources,
});
const dataSourceLoaded = (dataSource: DataSourceSettings): LoadDataSourceAction => ({
type: ActionTypes.LoadDataSource,
payload: dataSource,
});
const dataSourceMetaLoaded = (dataSourceMeta: Plugin): LoadDataSourceMetaAction => ({
type: ActionTypes.LoadDataSourceMeta,
payload: dataSourceMeta,
});
const dataSourceTypesLoad = (): LoadDataSourceTypesAction => ({
type: ActionTypes.LoadDataSourceTypes,
});
const dataSourceTypesLoaded = (dataSourceTypes: Plugin[]): LoadedDataSourceTypesAction => ({
type: ActionTypes.LoadedDataSourceTypes,
payload: dataSourceTypes,
});
export const setDataSourcesSearchQuery = (searchQuery: string): SetDataSourcesSearchQueryAction => ({
type: ActionTypes.SetDataSourcesSearchQuery,
payload: searchQuery,
});
export const setDataSourcesLayoutMode = (layoutMode: LayoutMode): SetDataSourcesLayoutModeAction => ({
type: ActionTypes.SetDataSourcesLayoutMode,
payload: layoutMode,
});
export const setDataSourceTypeSearchQuery = (query: string): SetDataSourceTypeSearchQueryAction => ({
type: ActionTypes.SetDataSourceTypeSearchQuery,
payload: query,
});
export const setDataSourceName = (name: string) => ({
type: ActionTypes.SetDataSourceName,
payload: name,
});
export const setIsDefault = (state: boolean) => ({
type: ActionTypes.SetIsDefault,
payload: state,
});
export type Action =
| LoadDataSourcesAction
| SetDataSourcesSearchQueryAction
| SetDataSourcesLayoutModeAction
| UpdateLocationAction
| LoadDataSourceTypesAction
| LoadedDataSourceTypesAction
| SetDataSourceTypeSearchQueryAction
| LoadDataSourceAction
| UpdateNavIndexAction
| LoadDataSourceMetaAction
| SetDataSourceNameAction
| SetIsDefaultAction;
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');
dispatch(dataSourcesLoaded(response)); dataSourcesLoaded(response);
}; };
} }
...@@ -177,7 +91,7 @@ export function addDataSource(plugin: Plugin): ThunkResult<void> { ...@@ -177,7 +91,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));
}; };
......
import { DataSourcesState, Plugin } from 'app/types'; import { DataSourcesState, Plugin } from 'app/types';
import { DataSourceSettings } from '@grafana/ui/src/types'; import { DataSourceSettings } from '@grafana/ui/src/types';
import { Action, ActionTypes } from './actions'; import {
dataSourceLoaded,
dataSourcesLoaded,
setDataSourcesSearchQuery,
setDataSourcesLayoutMode,
dataSourceTypesLoad,
dataSourceTypesLoaded,
setDataSourceTypeSearchQuery,
dataSourceMetaLoaded,
setDataSourceName,
setIsDefault,
} from './actions';
import { LayoutModes } from 'app/core/components/LayoutSelector/LayoutSelector'; import { LayoutModes } from 'app/core/components/LayoutSelector/LayoutSelector';
import { reducerFactory } from 'app/core/redux';
const initialState: DataSourcesState = { const initialState: DataSourcesState = {
dataSources: [] as DataSourceSettings[], dataSources: [],
dataSource: {} as DataSourceSettings, dataSource: {} as DataSourceSettings,
layoutMode: LayoutModes.List, layoutMode: LayoutModes.List,
searchQuery: '', searchQuery: '',
dataSourcesCount: 0, dataSourcesCount: 0,
dataSourceTypes: [] as Plugin[], dataSourceTypes: [],
dataSourceTypeSearchQuery: '', dataSourceTypeSearchQuery: '',
hasFetched: false, hasFetched: false,
isLoadingDataSources: false, isLoadingDataSources: false,
dataSourceMeta: {} as Plugin, dataSourceMeta: {} as Plugin,
}; };
export const dataSourcesReducer = (state = initialState, action: Action): DataSourcesState => { export const dataSourcesReducer = reducerFactory(initialState)
switch (action.type) { .addHandler({
case ActionTypes.LoadDataSources: filter: dataSourcesLoaded,
return { ...state, hasFetched: true, dataSources: action.payload, dataSourcesCount: action.payload.length }; mapper: (state, action) => ({
...state,
case ActionTypes.LoadDataSource: hasFetched: true,
return { ...state, dataSource: action.payload }; dataSources: action.payload,
dataSourcesCount: action.payload.length,
case ActionTypes.SetDataSourcesSearchQuery: }),
return { ...state, searchQuery: action.payload }; })
.addHandler({
case ActionTypes.SetDataSourcesLayoutMode: filter: dataSourceLoaded,
return { ...state, layoutMode: action.payload }; mapper: (state, action) => ({ ...state, dataSource: action.payload }),
})
case ActionTypes.LoadDataSourceTypes: .addHandler({
return { ...state, dataSourceTypes: [], isLoadingDataSources: true }; filter: setDataSourcesSearchQuery,
mapper: (state, action) => ({ ...state, searchQuery: action.payload }),
case ActionTypes.LoadedDataSourceTypes: })
return { ...state, dataSourceTypes: action.payload, isLoadingDataSources: false }; .addHandler({
filter: setDataSourcesLayoutMode,
case ActionTypes.SetDataSourceTypeSearchQuery: mapper: (state, action) => ({ ...state, layoutMode: action.payload }),
return { ...state, dataSourceTypeSearchQuery: action.payload }; })
.addHandler({
case ActionTypes.LoadDataSourceMeta: filter: dataSourceTypesLoad,
return { ...state, dataSourceMeta: action.payload }; mapper: state => ({ ...state, dataSourceTypes: [], isLoadingDataSources: true }),
})
case ActionTypes.SetDataSourceName: .addHandler({
return { ...state, dataSource: { ...state.dataSource, name: action.payload } }; filter: dataSourceTypesLoaded,
mapper: (state, action) => ({
case ActionTypes.SetIsDefault: ...state,
return { ...state, dataSource: { ...state.dataSource, isDefault: action.payload } }; dataSourceTypes: action.payload,
} isLoadingDataSources: false,
}),
return state; })
}; .addHandler({
filter: setDataSourceTypeSearchQuery,
mapper: (state, action) => ({ ...state, dataSourceTypeSearchQuery: action.payload }),
})
.addHandler({
filter: dataSourceMetaLoaded,
mapper: (state, action) => ({ ...state, dataSourceMeta: action.payload }),
})
.addHandler({
filter: setDataSourceName,
mapper: (state, action) => ({ ...state, dataSource: { ...state.dataSource, name: action.payload } }),
})
.addHandler({
filter: setIsDefault,
mapper: (state, action) => ({
...state,
dataSource: { ...state.dataSource, isDefault: action.payload },
}),
})
.create();
export default { export default {
dataSources: dataSourcesReducer, dataSources: dataSourcesReducer,
......
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