2 * ============LICENSE_START========================================================================
3 * ONAP : ccsdk feature sdnr wt odlux
4 * =================================================================================================
5 * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
6 * =================================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software distributed under the License
13 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14 * or implied. See the License for the specific language governing permissions and limitations under
16 * ============LICENSE_END==========================================================================
18 import { Action, IActionHandler } from './action';
19 import { Store, Dispatch, Enhancer } from './store';
21 export interface MiddlewareArg<T> {
26 export interface Middleware<T> {
27 (obj: MiddlewareArg<T>): Function;
30 class InitialisationAction extends Action { };
31 const initialisationAction = new InitialisationAction();
33 export type ActionHandlerMapObject<S extends { [key: string]: any }, A extends Action = Action> = {
34 [K in keyof S]: IActionHandler<S[K], A>
37 export const combineActionHandler = <TState extends { [key: string]: any }, TAction extends Action = Action>(actionHandlers: ActionHandlerMapObject<TState, TAction>) : IActionHandler<TState, TAction> => {
38 const finalActionHandlers = {} as { [key: string]: any }; // https://github.com/microsoft/TypeScript/issues/31808
39 Object.keys(actionHandlers).forEach(actionHandlerKey => {
40 const handler = actionHandlers[actionHandlerKey];
41 if (typeof handler === 'function') {
42 finalActionHandlers[actionHandlerKey] = handler;
46 // ensure initialisation
47 Object.keys(finalActionHandlers).forEach(key => {
48 const actionHandler = finalActionHandlers[key];
49 const initialState = actionHandler(undefined, initialisationAction);
50 if (typeof initialState === 'undefined') {
51 const errorMessage = `Action handler ${ key } returned undefiend during initialization.`;
52 throw new Error(errorMessage);
56 return function combination<TAction extends Action>(state: TState = ({} as TState), action: TAction) {
57 let hasChanged = false;
58 const nextState = {} as { [key: string]: any }; // https://github.com/microsoft/TypeScript/issues/31808
59 Object.keys(finalActionHandlers).forEach(key => {
60 const actionHandler = finalActionHandlers[key];
61 const previousState = state[key];
62 const nextStateKey = actionHandler(previousState, action);
63 if (typeof nextStateKey === 'undefined') {
64 const errorMessage = `Given ${ action.constructor } and action handler ${ key } returned undefiend.`;
65 throw new Error(errorMessage);
67 nextState[key] = nextStateKey;
68 hasChanged = hasChanged || nextStateKey !== previousState;
70 return (hasChanged ? nextState : state) as TState;
74 export const chainMiddleware = <TStoreState>(...middlewares: Middleware<TStoreState>[]): Enhancer<TStoreState> => {
75 return (store: Store<TStoreState>) => {
76 const middlewareAPI = {
77 getState() { return store.state },
78 dispatch: <TAction extends Action>(action: TAction) => store.dispatch(action) // we want to use the combinded dispatch
79 // we should NOT use the flux dispatcher here, since the action would affect ALL stores
81 const chain = middlewares.map(middleware => middleware(middlewareAPI));
82 return compose(...chain)(store.dispatch) as Dispatch;
87 * Composes single-argument functions from right to left. The rightmost
88 * function can take multiple arguments as it provides the signature for
89 * the resulting composite function.
91 * @param {...Function} funcs The functions to compose.
92 * @returns {Function} A function obtained by composing the argument functions
93 * from right to left. For example, compose(f, g, h) is identical to doing
94 * (...args) => f(g(h(...args))).
96 const compose = (...funcs: Function[]) => {
97 if (funcs.length === 0) {
98 return (arg: any) => arg
101 if (funcs.length === 1) {
105 return funcs.reduce((a, b) => (...args: any[]) => a(b(...args)));