sdbondi
9/20/2017 - 7:16 PM

saga-resource

import {put, call, select} from 'redux-saga/effects';
import {takeEvery} from 'redux-saga';
import feathers from '../feathers';
import createResourceActionCreators from '../actions/resource';
import {resource as resourceActions} from '../actions';

import resourceEndpoints from './endpoints';

const initialOptions = {
  pagination: {perPage: 10, page: 1},
};

const getQueryPagination = p => ({
  $skip: (p.page - 1) * p.perPage,
  $limit: p.perPage,
});

const paginationSelector = resourceName => state =>
  state[resourceName].resource.pagination;

export default function createResourceSagas(
  resourceName,
  options = initialOptions
) {
  const actions = {
    ...resourceActions,
    ...createResourceActionCreators(resourceName),
  };

  const selectPagination = paginationSelector(resourceName);

  const sagas = {
    *fetch(service, {query, nextPage, cb}) {
      try {
        const pagination = options.disablePagination
          ? null
          : yield select(selectPagination);
        if (nextPage) {
          // XXX: Mutation
          pagination.page = nextPage;
        }

        const result = yield call(() =>
          service.find({
            query: options.disablePagination
              ? query
              : {
                  ...query,
                  ...getQueryPagination({...options.pagination, ...pagination}),
                },
          })
        );

        if (cb && cb(null, result) === true) {
          return;
        }
        yield put(actions.fetchReceive(result, nextPage));
      } catch (error) {
        if (cb && cb(error) === true) {
          return;
        }
        yield put(actions.fetchError(error));
      }
    },

    *create(service, {item, cb}) {
      try {
        const result = yield call(service.create.bind(service), item);

        if (cb && cb(null, result) === true) {
          return;
        }
        yield put(actions.createReceive(result));
      } catch (error) {
        if (cb && cb(error) === true) {
          return;
        }
        yield put(actions.createError(error));
      }
    },

    *update(service, {id, item, cb}) {
      try {
        const result = yield call(service.update.bind(service), id, item);

        if (cb && cb(null, result) === true) {
          return;
        }
        yield put(actions.updateReceive(result));
      } catch (error) {
        if (cb && cb(error) === true) {
          return;
        }
        yield put(actions.updateError(error));
      }
    },


    *patch(service, {id, item, cb}) {
      try {
        const result = yield call(service.patch.bind(service), id, item);

        if (cb && cb(null, result) === true) {
          return;
        }
        yield put(actions.updateReceive(result));
      } catch (error) {
        if (cb && cb(error) === true) {
          return;
        }
        yield put(actions.updateError(error));
      }
    },

    *remove(service, {id, cb}) {
      try {
        const result = yield call(service.remove.bind(service), id);
        if (cb && cb(null, result) === true) {
          return;
        }
        yield put(actions.removeReceive(result));
      } catch (error) {
        yield put(actions.removeError(error));
      }
    },
  };

  const namespaced = fn =>
    function* nsFunction(service, action) {
      if (action.ns !== resourceName) {
        return;
      }
      yield* fn(service, action);
    };

  return function* root() {
    const endpoint = resourceEndpoints[resourceName];
    if (!endpoint) {
      throw new Error(`Endpoint does not exist for resource ${resourceName}`);
    }

    const service = feathers.service(endpoint);

    yield [
      takeEvery(
        actions.RESOURCE_FETCH_REQUEST,
        namespaced(sagas.fetch),
        service
      ),
      takeEvery(
        actions.RESOURCE_CREATE_REQUEST,
        namespaced(sagas.create),
        service
      ),
      takeEvery(
        actions.RESOURCE_UPDATE_REQUEST,
        namespaced(sagas.update),
        service
      ),
      takeEvery(
        actions.RESOURCE_PATCH_REQUEST,
        namespaced(sagas.patch),
        service
      ),
      takeEvery(
        actions.RESOURCE_REMOVE_REQUEST,
        namespaced(sagas.remove),
        service
      ),
    ];
  };
}