UNPKG

redux-observable-sans

Version:
140 lines (114 loc) 5.13 kB
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } import 'whatwg-fetch'; import { ofType } from 'redux-observable'; import { merge, from, race } from 'rxjs'; import { catchError, mapTo, mergeMap, take, takeUntil } from 'rxjs/operators'; // fetch does not throw on anything but network errors // thus status needs to be passed down along with body var responseToJson = function responseToJson(response) { return response.json().then(function (cleanJson) { return { code: response.status, body: cleanJson }; }); }; // check kind of response by code // then return stream of success actions or stream of failure actions // in case of 401, then failure stream includes action.type === `UNAUTHORIZED` var mapResponseToSuccessOrFailureStream = function mapResponseToSuccessOrFailureStream(type, url, httpType) { return function (response) { console.error('response =', response); if (response.code >= 200 && response.code < 300) { return from([rpcSuccess(type)(response.body)]); } return from(rpcFailure(type, url, httpType)(response)); }; }; // action creators export var rpcRequest = function rpcRequest(type) { return function (payload) { return { type: type + '_REQUEST', payload: payload }; }; }; export var rpcSuccess = function rpcSuccess(type) { return function (payload) { return { type: type + '_SUCCESS', payload: payload }; }; }; export var rpcFailure = function rpcFailure(type, url, httpType) { return function (response) { return [{ type: type + '_FAILURE', payload: _extends({ error: true, message: response.message }, response && response.xhr ? response.xhr.response : response.body) }, { type: response && response.code === 401 ? httpType + '_UNAUTHORIZED' : httpType + '_ERROR', payload: _extends({ url: url, error: true, message: response.message || response.body && response.body.message }, response) }]; }; }; export var createType = function createType(id) { return 'HTTP' + (id ? '_' + id : ''); }; // Race between the AJAX call and an EPIC_END. // If the EPIC_END, emit a cancel action to // put the store in the correct state // This is needed for hot reloading var httpEpic = function httpEpic() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var preUrl = !options.url ? '/' : options.url[options.url.length] === '/' ? options.url : options.url + '/'; var setHeaders = typeof options.setHeaders === 'function' ? options.setHeaders : function () { return {}; }; var httpType = createType(options.id); return function (action$, state$, deps) { return action$.pipe(ofType(httpType), mergeMap(function (_ref) { var payload = _ref.payload; var rawBody = payload.body, postUrl = payload.url, type = payload.type, settings = _objectWithoutProperties(payload, ['body', 'url', 'type']); var headers = _extends({ Accept: 'application/json', 'Content-Type': 'application/json' }, setHeaders(state$, deps)); var options = _extends({ headers: headers }, settings); var body = rawBody && typeof rawBody.toJS === 'function' ? rawBody.toJS() // handle immutable : rawBody; if (body && ['POST', 'PATCH', 'PUT'].some(function (method) { return method === settings.method; })) options.body = JSON.stringify(body); var url = preUrl + (typeof postUrl === 'function' ? postUrl(payload.body) : postUrl || ''); var reqAction$ = from([rpcRequest(type)(_extends({ url: url }, settings, { body: body }))]); console.error('options =', options); var sideEffect$ = from(fetch(url, options).then(responseToJson)).pipe(mergeMap(mapResponseToSuccessOrFailureStream(type, url, httpType)), takeUntil(action$.ofType(type + '_CANCELLED')), catchError(function (err) { return from(rpcFailure(type, url, httpType)(err)); })); var hotReloading$ = action$.pipe(ofType('EPIC_END'), mapTo({ type: type + '_CANCELLED' }), take(1)); return merge(reqAction$, race(sideEffect$, hotReloading$)); })); }; }; export { httpEpic }; export var httpAction = function httpAction(dispatch, id) { return function (type, url) { var method = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'GET'; var settings = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; return function (body) { return dispatch({ type: createType(id), payload: _extends({ type: type, url: url, method: method }, settings, { body: body }) }); }; }; };