Procházet zdrojové kódy

Refactored Datasources as POC

Hugo Häggmark před 6 roky
rodič
revize
d3815beb1c

+ 4 - 0
public/app/core/redux/index.ts

@@ -0,0 +1,4 @@
+import { actionCreatorFactory } from './actionCreatorFactory';
+import { reducerFactory } from './reducerFactory';
+
+export { actionCreatorFactory, reducerFactory };

+ 3 - 3
public/app/core/redux/reducerFactory.test.ts

@@ -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', () => {
       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', () => {
         expect(() => {
         expect(() => {
           faultyReducer.addHandler({
           faultyReducer.addHandler({
             filter: dummyActionCreator,
             filter: dummyActionCreator,
-            handler: state => {
+            mapper: state => {
               return state;
               return state;
             },
             },
           });
           });

+ 10 - 13
public/app/core/redux/reducerFactory.ts

@@ -1,9 +1,10 @@
 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> {
 }
 }
 
 
 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> =>
     return instance;
     return instance;
   };
   };
 
 
-  const create = (): Reducer<State, ActionOf<any>> => {
-    const reducer: Reducer<State, ActionOf<any>> = (state: State = initialState, action: ActionOf<any>) => {
-      const validHandlers = allHandlerConfigs
-        .filter(config => config.filter.type === action.type)
-        .map(config => config.handler);
+  const create = () => (state: State = initialState, action: ActionOf<any>): State => {
+    const handlerConfig = allHandlerConfigs.filter(config => config.filter.type === action.type)[0];
 
 
-      return validHandlers.reduce((currentState, handler) => {
-        return handler(currentState, action);
-      }, state || initialState);
-    };
+    if (handlerConfig) {
+      return handlerConfig.mapper(state, action);
+    }
 
 
-    return reducer;
+    return state;
   };
   };
 
 
   const instance: CreateReducer<State> = {
   const instance: CreateReducer<State> = {

+ 6 - 5
public/app/features/datasources/DataSourcesListPage.test.tsx

@@ -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) => {
     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(),
-    setDataSourcesLayoutMode: jest.fn(),
+    setDataSourcesSearchQuery,
+    setDataSourcesLayoutMode,
     hasFetched: false,
     hasFetched: false,
   };
   };
 
 

+ 3 - 2
public/app/features/datasources/settings/DataSourceSettingsPage.test.tsx

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

+ 17 - 103
public/app/features/datasources/state/actions.ts

@@ -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 {
   SetIsDefault = 'SET_IS_DEFAULT',
   SetIsDefault = 'SET_IS_DEFAULT',
 }
 }
 
 
-interface LoadDataSourcesAction {
-  type: ActionTypes.LoadDataSources;
-  payload: DataSourceSettings[];
-}
+export const dataSourceLoaded = actionCreatorFactory<DataSourceSettings>(ActionTypes.LoadDataSource).create();
 
 
-interface SetDataSourcesSearchQueryAction {
-  type: ActionTypes.SetDataSourcesSearchQuery;
-  payload: string;
-}
+export const dataSourcesLoaded = actionCreatorFactory<DataSourceSettings[]>(ActionTypes.LoadDataSources).create();
 
 
-interface SetDataSourcesLayoutModeAction {
-  type: ActionTypes.SetDataSourcesLayoutMode;
-  payload: LayoutMode;
-}
+export const dataSourceMetaLoaded = actionCreatorFactory<Plugin>(ActionTypes.LoadDataSourceMeta).create();
 
 
-interface LoadDataSourceTypesAction {
-  type: ActionTypes.LoadDataSourceTypes;
-}
+export const dataSourceTypesLoad = actionCreatorFactory(ActionTypes.LoadDataSourceTypes).create();
 
 
-interface LoadedDataSourceTypesAction {
-  type: ActionTypes.LoadedDataSourceTypes;
-  payload: Plugin[];
-}
+export const dataSourceTypesLoaded = actionCreatorFactory<Plugin[]>(ActionTypes.LoadedDataSourceTypes).create();
 
 
-interface SetDataSourceTypeSearchQueryAction {
-  type: ActionTypes.SetDataSourceTypeSearchQuery;
-  payload: string;
-}
+export const setDataSourcesSearchQuery = actionCreatorFactory<string>(ActionTypes.SetDataSourcesSearchQuery).create();
 
 
-interface LoadDataSourceAction {
-  type: ActionTypes.LoadDataSource;
-  payload: DataSourceSettings;
-}
+export const setDataSourcesLayoutMode = actionCreatorFactory<LayoutMode>(ActionTypes.SetDataSourcesLayoutMode).create();
 
 
-interface LoadDataSourceMetaAction {
-  type: ActionTypes.LoadDataSourceMeta;
-  payload: Plugin;
-}
+export const setDataSourceTypeSearchQuery = actionCreatorFactory<string>(
+  ActionTypes.SetDataSourceTypeSearchQuery
+).create();
 
 
-interface SetDataSourceNameAction {
-  type: ActionTypes.SetDataSourceName;
-  payload: string;
-}
+export const setDataSourceName = actionCreatorFactory<string>(ActionTypes.SetDataSourceName).create();
 
 
-interface SetIsDefaultAction {
-  type: ActionTypes.SetIsDefault;
-  payload: boolean;
-}
+export const setIsDefault = actionCreatorFactory<boolean>(ActionTypes.SetIsDefault).create();
 
 
-const dataSourcesLoaded = (dataSources: DataSourceSettings[]): LoadDataSourcesAction => ({
-  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;
+export type Action = UpdateLocationAction | UpdateNavIndexAction | ActionOf<any>;
 
 
 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> {
 
 
 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));
   };
   };

+ 69 - 38
public/app/features/datasources/state/reducers.ts

@@ -1,56 +1,87 @@
 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 => {
-  switch (action.type) {
-    case ActionTypes.LoadDataSources:
-      return { ...state, hasFetched: true, dataSources: action.payload, dataSourcesCount: action.payload.length };
-
-    case ActionTypes.LoadDataSource:
-      return { ...state, dataSource: action.payload };
-
-    case ActionTypes.SetDataSourcesSearchQuery:
-      return { ...state, searchQuery: action.payload };
-
-    case ActionTypes.SetDataSourcesLayoutMode:
-      return { ...state, layoutMode: action.payload };
-
-    case ActionTypes.LoadDataSourceTypes:
-      return { ...state, dataSourceTypes: [], isLoadingDataSources: true };
-
-    case ActionTypes.LoadedDataSourceTypes:
-      return { ...state, dataSourceTypes: action.payload, isLoadingDataSources: false };
-
-    case ActionTypes.SetDataSourceTypeSearchQuery:
-      return { ...state, dataSourceTypeSearchQuery: action.payload };
-
-    case ActionTypes.LoadDataSourceMeta:
-      return { ...state, dataSourceMeta: action.payload };
-
-    case ActionTypes.SetDataSourceName:
-      return { ...state, dataSource: { ...state.dataSource, name: action.payload } };
-
-    case ActionTypes.SetIsDefault:
-      return { ...state, dataSource: { ...state.dataSource, isDefault: action.payload } };
-  }
-
-  return state;
-};
+export const dataSourcesReducer = reducerFactory(initialState)
+  .addHandler({
+    filter: dataSourcesLoaded,
+    mapper: (state, action) => ({
+      ...state,
+      hasFetched: true,
+      dataSources: action.payload,
+      dataSourcesCount: action.payload.length,
+    }),
+  })
+  .addHandler({
+    filter: dataSourceLoaded,
+    mapper: (state, action) => ({ ...state, dataSource: action.payload }),
+  })
+  .addHandler({
+    filter: setDataSourcesSearchQuery,
+    mapper: (state, action) => ({ ...state, searchQuery: action.payload }),
+  })
+  .addHandler({
+    filter: setDataSourcesLayoutMode,
+    mapper: (state, action) => ({ ...state, layoutMode: action.payload }),
+  })
+  .addHandler({
+    filter: dataSourceTypesLoad,
+    mapper: state => ({ ...state, dataSourceTypes: [], isLoadingDataSources: true }),
+  })
+  .addHandler({
+    filter: dataSourceTypesLoaded,
+    mapper: (state, action) => ({
+      ...state,
+      dataSourceTypes: action.payload,
+      isLoadingDataSources: false,
+    }),
+  })
+  .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,