UNPKG

redux-resource-xhr

Version:

Action creators for Redux Resource using the xhr library

337 lines (269 loc) 9.4 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('redux-resource'), require('xhr')) : typeof define === 'function' && define.amd ? define(['exports', 'redux-resource', 'xhr'], factory) : (factory((global.ReduxResourceXHR = {}),global.ReduxResource,global.xhr)); }(this, (function (exports,reduxResource,xhr) { 'use strict'; xhr = xhr && xhr.hasOwnProperty('default') ? xhr['default'] : xhr; var has = Object.prototype.hasOwnProperty; /** * Decode a URI encoded string. * * @param {String} input The URI encoded string. * @returns {String} The decoded string. * @api private */ function decode(input) { return decodeURIComponent(input.replace(/\+/g, ' ')); } /** * Simple query string parser. * * @param {String} query The query string that needs to be parsed. * @returns {Object} * @api public */ function querystring(query) { var parser = /([^=?&]+)=?([^&]*)/g , result = {} , part; // // Little nifty parsing hack, leverage the fact that RegExp.exec increments // the lastIndex property so we can continue executing this loop until we've // parsed all results. // for (; part = parser.exec(query); result[decode(part[1])] = decode(part[2]) ); return result; } /** * Transform a query string to an object. * * @param {Object} obj Object that should be transformed. * @param {String} prefix Optional prefix. * @returns {String} * @api public */ function querystringify(obj, prefix) { prefix = prefix || ''; var pairs = []; // // Optionally prefix with a '?' if needed // if ('string' !== typeof prefix) prefix = '?'; for (var key in obj) { if (has.call(obj, key)) { pairs.push(encodeURIComponent(key) +'='+ encodeURIComponent(obj[key])); } } return pairs.length ? prefix + pairs.join('&') : ''; } // // Expose the module. // var stringify = querystringify; var parse = querystring; var querystringify_1 = { stringify: stringify, parse: parse }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 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; }; /* eslint no-param-reassign: 'off' */ // This is a small wrapper around `xhr`. // It has two improvements: // 1. Omit the `cb`, and a Promise will be returned instead // 2. Pass a `qs` option for query string support function buildUrl(uri, options) { var qsStringify = options.qsStringify || querystringify_1.stringify; if (options.qs) { var stringified = qsStringify(options.qs, options.qsStringifyOptions); if (stringified[0] !== '?') { stringified = '?' + stringified; } uri += stringified; } return uri; } function request(uri, options, cb) { var params = {}; // This handles the `xhr(options, cb)` syntax if ((typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) === 'object') { params = uri; } else if (typeof uri === 'string' && (typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') { // This handles the `xhr(uri, options, cb)` syntax params = options; params.uri = uri; } else { // This handles the `xhr(uri, cb)` syntax params.uri = uri; } // This adds support for the `qs` option var urlString = params.uri ? params.uri : params.url; params.uri = buildUrl(urlString, params); if (params.url) { delete params.url; } var callback = void 0; if (typeof options === 'function') { callback = options; } else if (typeof cb === 'function') { callback = cb; } // Return the `xhr` if a callback was passed. Otherwise, a Promise is returned if (callback) { return xhr(params, callback); } else { return new Promise(function (resolve, reject) { xhr(params, function (err, res) { if (err) { reject(err); } else { resolve(res); } }); }); } } // Also grabbed from xhr's source. This adds the convenience APIs; // `xhr.get()`, for instance. // Supported signatures: // // xhr[method](url, callback) // xhr[method](url, options, callback) // xhr[method](options, callback) // ['get', 'put', 'post', 'patch', 'head', 'delete'].forEach(function (method) { request[method === 'delete' ? 'del' : method] = function (uri, options, callback) { var opts = void 0, cb = void 0; if ((typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) === 'object') { opts = uri; cb = options; } else if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') { opts = Object.assign({ uri: uri }, options); cb = callback; } else if (typeof uri === 'string' && (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') { opts = { uri: uri }; cb = options; } opts.method = method.toUpperCase(); return request(opts, cb); }; }); function crudRequest(crudAction, options) { var dispatch = options.dispatch, _options$xhrOptions = options.xhrOptions, xhrOptions = _options$xhrOptions === undefined ? {} : _options$xhrOptions, transformData = options.transformData, _options$actionDefaul = options.actionDefaults, actionDefaults = _options$actionDefaul === undefined ? {} : _options$actionDefaul, onPending = options.onPending, onFailed = options.onFailed, onSucceeded = options.onSucceeded, onAborted = options.onAborted; var resourceName = actionDefaults.resourceName, resourceType = actionDefaults.resourceType, requestProperties = actionDefaults.requestProperties; var typeToUse = resourceType || resourceName; var crudActionOption = crudAction ? crudAction : ''; // Catch common configuration problems in dev { var lowercaseCrud = crudActionOption.toLowerCase(); var isValidCrudType = lowercaseCrud === 'update' || lowercaseCrud === 'delete' || lowercaseCrud === 'read' || lowercaseCrud === 'create'; var url = xhrOptions.url, uri = xhrOptions.uri; if (!typeToUse) { console.warn('A resourceType was not passed to a Redux Resource Action ' + 'creator. A resourceType must be passed so that Redux Resource ' + 'knows which resource slice to update. Refer to the Request Actions ' + 'guide for more: https://redux-resource.js.org/docs/requests/request-actions.html'); } if (!isValidCrudType) { console.warn('An invalid "crudAction" was passed to a Redux Resource action creator. ' + 'It must be one of: "create", "read", "update", "delete"'); } if (!url && !uri) { console.warn('No URL was passed to a Redux Resource action creator. You must ' + 'pass either "xhrOptions.url" or "xhrOptions.uri". For more, refer to ' + 'the Redux Resource XHR documentation: ' + 'https://redux-resource.js.org/docs/extras/redux-resource-xhr.html'); } } var crudType = crudActionOption.toUpperCase(); var pendingAction = _extends({}, actionDefaults, { type: reduxResource.actionTypes[crudType + '_RESOURCES_PENDING'], requestProperties: requestProperties }); if (onPending) { onPending(pendingAction); } else { dispatch(pendingAction); } var req = request(xhrOptions, function (err, res, body) { var statusCode = res ? res.statusCode : null; if (req.aborted) { var abortedAction = _extends({}, actionDefaults, { type: reduxResource.actionTypes[crudType + '_RESOURCES_IDLE'], requestProperties: requestProperties, res: res }); if (onAborted) { onAborted(abortedAction, res); } else { dispatch(abortedAction); } } else if (err || statusCode >= 400 || statusCode === 0) { var failedAction = _extends({}, actionDefaults, { type: reduxResource.actionTypes[crudType + '_RESOURCES_FAILED'], requestProperties: _extends({}, requestProperties, { statusCode: statusCode }), res: res, err: err }); if (onFailed) { onFailed(failedAction, err, res); } else { dispatch(failedAction); } } else { var resources = void 0; if (body) { if (transformData) { resources = transformData(body, options); } else { resources = body; } } else { resources = actionDefaults.resources; } var succeededAction = _extends({}, actionDefaults, { type: reduxResource.actionTypes[crudType + '_RESOURCES_SUCCEEDED'], requestProperties: _extends({}, requestProperties, { statusCode: statusCode }), resources: resources, res: res }); if (onSucceeded) { onSucceeded(succeededAction, res, body); } else { dispatch(succeededAction); } } }); return req; } exports.crudRequest = crudRequest; exports.xhr = request; Object.defineProperty(exports, '__esModule', { value: true }); })));