All files / src createActionStream.js

96.55% Statements 28/29
100% Branches 6/6
93.33% Functions 14/15
100% Lines 21/21
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69                  2x                         2x 42x     10x           2x 2x 2x 10x         10x 11x 8x 3x         12x 42x     12x   12x 12x     12x       12x   12x   12x    
import { merge } from 'rxjs/observable/merge';
import { fromPromise } from 'rxjs/observable/fromPromise';
import { map } from 'rxjs/operator/map';
import { switchMap } from 'rxjs/operator/switchMap';
import { filter } from 'rxjs/operator/filter';
import { partition } from 'rxjs/operator/partition';
import { groupBy } from 'rxjs/operator/groupBy';
import { mergeMap } from 'rxjs/operator/mergeMap';
 
const identity = (val) => val;
 
import { CALL_API } from './constants';
 
import {
  makeStartAction,
  makeStartErrorAction,
  makeSuccessAction,
  makeFailureAction,
} from './actions'
 
import applyFunctions from './utils/applyFunctions';
 
const isValid = (api) =>
  typeof api.name === 'string' &&
  typeof api.endpoint === 'string';
 
const makeHOObservableFromActionCreator = (actionCreator) => ({ api, resp, error }) => fromPromise(
  error ?
    Promise.resolve(actionCreator(api)(error.data)) :
    Promise.resolve(actionCreator(api)(resp.data))
);
 
const fromRespToSuccessActionStream = makeHOObservableFromActionCreator(makeSuccessAction);
const fromRespToFailureActionStream = makeHOObservableFromActionCreator(makeFailureAction);
const fromRespToActionStream = (data) =>
  data.resp ?
    fromRespToSuccessActionStream(data) :
    fromRespToFailureActionStream(data);
 
// create an observable of promises
const callApiInGroup = (group$, adapter) => group$::switchMap(
  api => fromPromise(adapter(api).then(
    resp => ({ resp, api }),
    error => ({ error, api })
  ))
);
 
export default (actions$, { getState }, adapter) => {
  const api$ = actions$
    ::map(action => action[CALL_API])
    ::map(applyFunctions(getState));
 
  const [valid$, invalid$] = api$::partition(isValid)
 
  const startError$ = invalid$::map(api => makeStartErrorAction(api)())
  const start$ = valid$::map(api => makeStartAction(api)())
 
  // create a higher-order observable of each api (defined by their name)
  const apiGroup$ = valid$::groupBy(api => api.name)
 
  // create a higher-order observable of outgoing requests streams
  // each item is an observable
  const resp$ = apiGroup$::mergeMap(apiInGroup$ => callApiInGroup(apiInGroup$, adapter));
 
  const fetchDoneActions$ = resp$::mergeMap(fromRespToActionStream);
 
  return merge(start$, startError$, fetchDoneActions$);
}