Source: app/util/route-requirements/processRequirements.js

/* global WP_DEFINE_DEVELOPMENT */
import debugLib from 'debug';
import RouteRequirement from './RouteRequirement';

const debug = debugLib('SlimmingWorld:processRequirements');

/** @module */

/**
 * Checks each of the given requirements to see if they are met, and calls their `onFail`
 * handler otherwise. If any of the requirements fail, it will not process further requirements.
 *
 * @param requirements An array of route requirements, as returned by the
 * {@link parseRouteRequirements} util.
 * @param dispatch {function} The redux dispatch function
 * @param getState {function} The redux getState function
 * @param accountState
 * @param renderProps The current render props returned by react-router
 * @param callbacks An object containing callbacks that a {@link RouteRequirement} instance can
 * execute in its `onFail` handler. Should include the following callbacks:
 *  - `redirect(path)` A function that will redirect to the given path
 *  - `redirectToLogin()` A function that will redirect to the login endpoint
 * @returns {Promise} A promise that resolve with a boolean indicating if all requirements are
 * met. If this is false, we should abort rendering of the current page.
 * @category routing
 */
function processRequirements(
  requirements,
  { dispatch, getState, accountState, renderProps },
  callbacks,
) {
  if (WP_DEFINE_DEVELOPMENT) {
    if (!Array.isArray(requirements)) {
      throw new TypeError('Expected requirements to be an Array');
    }
    requirements.forEach(requirement => {
      if (!(requirement instanceof RouteRequirement)) {
        throw new TypeError('Elements in requirements should be instanceof RouteRequirement');
      }
    });
  }

  return requirements.reduce(
    (prevRequirement, requirement) =>
      prevRequirement.then(meetsRequirements => {
        if (!meetsRequirements) return false;

        debug(`Processing route requirement "${requirement.name}"`);
        return Promise.resolve(
          requirement.test({ getState, accountState, renderProps, dispatch }),
        ).then(requirementOk => {
          if (!requirementOk) {
            debug(`Route requirement "${requirement.name}" failed. Calling onFail callback.`);
            requirement.onFail({ getState, accountState, renderProps }, callbacks);
          } else {
            debug(`Route requirement "${requirement.name}" passed`);
          }
          return requirementOk;
        });
      }),
    Promise.resolve(true),
  );
}

export default processRequirements;