Source: app/util/raven/middleware.js

import RedirectError from '../RedirectError';

const identity = _ => _;

/** @module */

/**
 * Only copy the keys from the config over from the state
 * @param state
 */
const transformStateKeysToCapture = state => {
  const captureState = {};

  // get comma-separated list of keys from the config in the state
  const stateKeysToCapture =
    state.config &&
    state.config.environmentConfig &&
    state.config.environmentConfig.raven &&
    state.config.environmentConfig.raven.stateKeysToCapture;

  // copy each key over from the state to be captured
  (stateKeysToCapture || '').split(',').forEach(key => {
    if (key in state) {
      captureState[key] = state[key];
    }
  });
  return captureState;
};

/**
 * Creates a middleware to save redux actions as raven breadcrumbs
 * @function createMiddleware
 * @param {function} getRavenClient Retrieve the raven client (that is saved at a later time)
 * @param {object} [options] Customize extra data sent to sentry
 * @param {function} [options.actionTransformer] Transform the action object to send; default to
 * identity function
 * @param {function} [options.stateTransformer] Transform the action state to send; default to
 * identity function
 * @param {function} [options.logger] The logger to use for logging; default to console.error
 * @return {function(*=): function(*): function(*=)}
 * @category tracking
 */
export default function createMiddleware(getRavenClient, options = {}) {
  return store => next => action => {
    const {
      actionTransformer = identity,
      stateTransformer = transformStateKeysToCapture,
      // eslint-disable-next-line no-console
      logger = console.error.bind(console, '[redux-raven-middleware] Reporting error to Sentry:'),
    } = options;

    const ravenClient = getRavenClient();

    try {
      // save breadcrumb for this action
      if (ravenClient && action.type) {
        getRavenClient().captureBreadcrumb({
          category: 'redux',
          message: action.type,
        });
      }

      return next(action);
    } catch (err) {
      // Send the report
      if (ravenClient && !(err instanceof RedirectError)) {
        logger(err);
        const stateToCapture = stateTransformer(store.getState());
        // error will be re-thrown and captured again, so only capture
        // the error here if we have additional state information
        if (stateToCapture) {
          ravenClient.captureException(err, {
            extra: {
              action: actionTransformer(action),
              state: stateToCapture,
            },
          });
        }
      }

      // rethrow error so the app can handle it properly
      throw err;
    }
  };
}