Skip to content

Instantly share code, notes, and snippets.

@michaelgilley
Created June 28, 2017 20:27
Show Gist options
  • Save michaelgilley/c8f79091a7e1ede774474060ee17bd07 to your computer and use it in GitHub Desktop.
Save michaelgilley/c8f79091a7e1ede774474060ee17bd07 to your computer and use it in GitHub Desktop.
import { put, call, all } from 'redux-saga/effects';
import xhr from 'services/xhr';
const trailingSlashes = /\/*$/;
const filterAllActions = actions => all([].concat(actions).map(a => !!a && put(a)).filter(a => a));
// Run a single api call inline
// example:
// yield fork(simpleApiSaga, myApi.get, {
// id: '123',
// onSuccess: myActions.getApiSuccess,
// onError: myActions.getApiFailed,
// });
export function* simpleApiSaga(method, params) {
const { onError, onSuccess, ...args } = params;
const { error, payload } = yield call(method, args);
let actions;
if (error && onError) actions = onError(error);
if (!error && onSuccess) actions = onSuccess(payload);
if (!actions) return;
actions = [].concat(actions);
yield all(actions.map(a => put(a)));
}
// Run multiple api calls concurrently.
// example:
// yield fork(batchApiSaga, [
// [rolesApi.get],
// [userGroupsApi.get, { flatten: true }],
// ], {
// onSuccess: ([roles, users]) => actions.success({ roles, users }),
// onError: ([rolesErr, usersErr]) => actions.error({ rolesErr, usersErr }),
// })
export function* batchApiSaga(calls, { onSuccess, onError } = {}) {
const raw = yield all(calls.map(args => call(...args)));
let hasSuccess;
let hasError;
const [successes, errors] = raw.reduce((results, { payload, error }) => {
if (payload) {
results[0].push(payload);
results[1].push(undefined);
hasSuccess = true;
} else {
results[0].push(undefined);
results[1].push(error);
hasError = true;
}
return results;
}, [[], []]);
if (onSuccess && hasSuccess) yield filterAllActions(onSuccess(successes));
if (onError && hasError) yield filterAllActions(onError(errors));
}
export const successHandler = ({ res, body: payload }) => ({ res, payload });
export const errorHandler = ({ res, body, message }) => ({ res, error: body || message });
export function apiHelper(uri, options) {
return xhr({
...options,
uri,
})
.then(successHandler)
.catch(errorHandler);
}
export const makeMethod = (...args) => ({ id = '', page = 0, ...rest } = {}) => {
let [type, namespace] = args;
if (args.length === 1 || !args[0]) {
type = 'get';
namespace = args[0] || args[1];
}
// Use `payload` first incase the body needs to be an array,
// which can't be destructured like an object.
const obj = rest.payload || rest;
if (obj.count !== undefined) {
obj.start = obj.count * page;
}
const uri = `${namespace}/${id}`.replace(trailingSlashes, '');
return apiHelper(uri, { type, obj });
};
export const scaffoldResource = (namespace, verbs) => {
const resource = {};
(verbs || ['get', 'post', 'patch', 'del']).forEach((type) => {
resource[type] = makeMethod(type, namespace);
});
return resource;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment