18F/e-QIP-prototype

View on GitHub
src/sagas/initialize.js

Summary

Maintainability
A
35 mins
Test Coverage
/* eslint import/no-cycle: 0 */
import {
  take, put, call, race, spawn,
} from 'redux-saga/effects'

import * as actionTypes from 'constants/actionTypes'
import { STATUS_SUBMITTED } from 'constants/enums/applicationStatuses'

import { fetchForm, fetchStatus } from 'actions/api'

import { updateApplication } from 'actions/ApplicationActions'

import { env } from 'config'

import { setFormData, formWatcher } from 'sagas/form'
import { handleLogout } from 'sagas/session'

export function* loggedOutWatcher() {
  const { error } = yield race({
    success: take(actionTypes.LOGIN_SUCCESS),
    error: take(actionTypes.LOGIN_ERROR),
  })

  if (error) {
    // Restart the watcher for the next login attempt
    yield call(loggedOutWatcher)
  } else {
    yield call(initializeApp) // eslint-disable-line
  }
}

export function* loggedInWatcher() {
  const { logout } = yield race({
    loggedIn: call(formWatcher),
    logout: take(actionTypes.LOGOUT),
  })

  if (logout) {
    const { timedOut } = logout
    yield call(handleLogout, timedOut)
  }
}

/** This is a somewhat generic handler for API fetch failures */
export function* handleFetchError(action) {
  const { error } = action

  if (error && error.response) {
    switch (error.response.status) {
      case 401:
      case 403:
        yield spawn(loggedOutWatcher)
        yield call(env.History().push, '/login')
        break
      default:
        yield call(env.History().push, '/error')
    }
  } else {
    console.warn('Unknown error', error)
    yield call(env.History().push, '/error')
  }
}

export function* handleFetchFormSuccess(data, path) {
  const { response } = data

  if (response && response.data) {
    yield call(setFormData, response.data)
    yield call(env.History().replace, path)
  } else {
    console.warn('Missing response', response)
    yield call(env.History().push, '/error')
  }
}

/** Initialize app (on page load) */
export function* handleInitError(action) {
  yield call(handleFetchError, action)
}

export function* handleInitSuccess(action, path = '/form/identification/intro') {
  const { response } = action

  if (response && response.data) {
    const { Status, Hash } = response.data
    // TODO - consolidate these into one action
    yield put(updateApplication('Settings', 'status', Status))
    yield put(updateApplication('Settings', 'hash', Hash))

    // Check to see if the account is locked
    const status = response.data
    if (status.Status === STATUS_SUBMITTED) {
      yield call(env.History().push, '/locked')
      return
    }

    // start watching for logged in events
    yield spawn(loggedInWatcher)

    // attempt to load /form
    yield call(env.History().push, '/loading')
    yield put(fetchForm())

    // watch for success or failure
    const { data, error } = yield race({
      data: take(actionTypes.FETCH_FORM_SUCCESS),
      error: take(actionTypes.FETCH_FORM_ERROR),
    })

    if (error) {
      yield call(handleFetchError, error)
    } else {
      yield call(handleFetchFormSuccess, data, path)
    }
  } else {
    console.warn('Missing response', response)
    yield call(env.History().push, '/error')
  }
}

export function* initializeApp(path) {
  // attempt to load /status
  yield put(fetchStatus())

  // watch for success or failure
  const { data, error } = yield race({
    data: take(actionTypes.FETCH_STATUS_SUCCESS),
    error: take(actionTypes.FETCH_STATUS_ERROR),
  })

  if (error) {
    yield call(handleInitError, error)
  } else {
    yield call(handleInitSuccess, data, path)
  }
}

/**
 * Main saga entry point
 * kicks off auto-login if logged in
 * kicks off login screen if not
 */
export function* initializeAppWatcher() {
  const initAction = yield take(actionTypes.INIT_APP)
  const { path } = initAction
  yield call(initializeApp, path)
}