Skip to content

Instantly share code, notes, and snippets.

@stevenleeg
Created June 28, 2018 17:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevenleeg/f95208060e35c4f624d51d90cf717673 to your computer and use it in GitHub Desktop.
Save stevenleeg/f95208060e35c4f624d51d90cf717673 to your computer and use it in GitHub Desktop.
import {generateRestfulTransitions} from 'utils/restful-transitions';
import translateTransitions from 'utils/translate-transitions';
const {
transitions: restfulTransitions,
initialState: restfulInitialState,
} = generateRestfulTransitions({
singularNoun: 'client',
pluralNoun: 'clients',
});
export const initialState = restfulInitialState;
export const {
Actions: ClientActions,
ActionTypes: ClientActionTypes,
reducers,
saga,
prefix,
} = translateTransitions({
prefix: 'services/clients',
transitions: restfulTransitions,
});
import Immutable from 'immutable';
import {call, put, select, take} from 'redux-saga/effects';
import WSApi from 'utils/ws-api';
import {isLoading} from './restful-selectors';
export const generateRestfulTransitions = ({
// singularNoun,
pluralNoun,
// apiNoun,
urlGenerator,
}) => {
const generateRoute = ({path, urlParams}) => {
path = path === '/' ? '' : path;
const base = urlGenerator ?
urlGenerator({params: urlParams}) : `/${pluralNoun}`;
return `${base}${path}`;
};
const initialState = Immutable.fromJS({
loading: {
fetchItems: false,
fetchItem: false,
},
items: [],
});
const transitions = [
/**
* fetchItems - Calls the index endpoint for the resource
*/
{
name: 'fetchItems',
generateAsync: true,
* saga({Actions}, {queryParams = {}, urlParams = {}, persist = true}) {
let resp;
try {
resp = yield call(WSApi.get, {
params: {
...queryParams,
},
route: generateRoute({urlParams, path: '/'}),
});
} catch (error) {
yield put(Actions.fetchItemsFailure({error}));
return;
}
const {data, included, pagination} = resp;
const items = data.map((item) => {
return WSApi.parseApiItem({item, included});
});
yield put(Actions.fetchItemsSuccess({
items,
persist,
pagination,
queryParams,
urlParams,
}));
},
beginReducer(state) {
return state
.setIn(['loading', 'fetchItems'], true)
.merge({items: new Immutable.List()});
},
successReducer(state, {items}) {
return state
.setIn(['loading', 'fetchItems'], false)
.merge({items});
},
failureReducer(state) {
return state.setIn(['loading', 'fetchItems'], false);
},
},
/**
* fetchItem - Calls the index endpoint for the resource
*/
{
name: 'fetchItem',
generateAsync: true,
* saga({Actions, ActionTypes}, {id, queryParams = {}, urlParams = {}}) {
let resp;
try {
resp = yield call(WSApi.get, {
route: generateRoute({urlParams, path: `/${id}`}),
params: queryParams,
});
} catch (error) {
yield put(Actions.fetchItemFailure({error}));
return;
}
// If we're loading an index let's wait for it to finish, otherwise we
// might get into a race condition and override data.
const loadingItems = yield select(isLoading, {
service: pluralNoun,
op: 'fetchItems',
});
if (loadingItems) {
yield take([
ActionTypes.FETCH_ITEMS_SUCCESS,
ActionTypes.FETCH_ITEMS_FAILURE,
]);
}
const {data, included} = resp;
const item = WSApi.parseApiItem({item: data, included});
yield put(Actions.fetchItemSuccess({item}));
},
beginReducer(state, {silent = false}) {
return state
.merge({errors: new Immutable.List()})
.setIn(['loading', 'fetchItem'], !silent);
},
successReducer(state, {item}) {
let items = state.get('items');
const existingIndex = items.findIndex(i => i.get('id') === item.get('id'));
if (existingIndex !== -1) {
items = items.set(existingIndex, item);
} else {
items = items.push(item);
}
return state
.merge({items})
.setIn(['loading', 'fetchItem'], false);
},
failureReducer(state) {
return state.setIn(['loading', 'fetchItem'], false);
},
},
];
return {transitions, initialState};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment