├── .babelrc
├── .gitignore
├── README.md
├── dist
└── index.js
├── index.js
├── package-lock.json
├── package.json
├── src
├── detailEntity
│ ├── detailedEntity.js
│ ├── detailedEntityActions.js
│ └── detailedEntityReducer.js
├── helpers.js
├── index.js
└── queriedEntity
│ ├── queriedEntity.js
│ ├── queriedEntityActions.js
│ └── queriedEntityReducer.js
├── test
├── sampleTestApp
│ ├── App.js
│ ├── mock-adapter.js
│ ├── reducer.js
│ └── store.js
└── test.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ],
6 | "plugins": [
7 | ["@babel/plugin-proposal-decorators", { "legacy": true }],
8 | ["@babel/plugin-proposal-class-properties", { "loose" : true }]
9 | ]
10 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 |
4 | # production
5 | /build
6 |
7 | # misc
8 | .DS_Store
9 | .env.local
10 | .env.development.local
11 | .env.test.local
12 | .env.production.local
13 |
14 | npm-debug.log*
15 | yarn-debug.log*
16 | yarn-error.log*
17 |
18 | #IDE
19 | /.idea
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rest React Redux
2 |
3 | [](https://www.twotalltotems.com)
4 |
5 | A higher order component for RestAPI abstraction. Reduces boilerplate and encourages clean code.
6 |
7 | By decorating your component with queriedEntity/detailedEntity and initializing the first network call, you can
8 | have access to a managed data entity named according to the "entity name" you passed as argument.
9 |
10 | ## Installation
11 |
12 | npm:
13 | ```sh
14 | npm install rest-react-redux --save
15 | ```
16 |
17 |
18 | What you need to do:
19 | - Create the proper reducer structure
20 | - Annotate your component/container with the proper HOC
21 | - Enjoy the props introduced to your component and not ever worry about writing actions and reducers
22 |
23 | This library is supposed to be used only on a restful endpoint. here is an example:
24 | - *GET*: `http://www.url.com/contacts?page=1&page-size=10` returns a list of contacts and metadata about the query
25 | - *POST*: `http://www.url.com/contacts` creates a contact
26 | - *GET*, *PUT*, *PATCH* and *DELETE*: `http://www.url.com/contacts/contactId` gets, updates, patches or deletes the specific contact respectively
27 |
28 | ## Restrictions in the current version
29 | - JSON endpoints: network request and response body must be of `application/json` content type
30 | - Redux-thunk: your application redux store must contain redux-thunk as middleware
31 | ```js
32 | import {createStore, compose, applyMiddleware} from 'redux';
33 | import thunk from 'redux-thunk';
34 | export const store = createStore(rootReducer, compose(applyMiddleware(thunk)));
35 | ```
36 | - Axios: your application must use axios as the network call library. BaseUrl and headers must be set in your application scope
37 | ```js
38 | import axios from 'axios';
39 | axios.defaults.baseURL = 'http://www.url.com';
40 | ```
41 | - reducer: using `queriedEntityReducer` or `detailedEntityReducer` you must create a field in the root reducer matching the name of the entity you create. This is where the library manages the data
42 | ```js
43 | import { queriedEntityReducer, detailedEntityReducer } from 'rest-react-redux';
44 | const reducers = {
45 | contacts: queriedEntityReducer('contact'),
46 | contact: detailedEntityReducer('contact'),
47 | }
48 | ```
49 |
50 | ## Higher Order Components
51 | ### queriedEntity
52 |
53 | if you intend to work with an endpoint that returns a list use `queriedEntity` to decorate your components:
54 | ```js
55 | import { queriedEntity } from 'rest-react-redux';
56 |
57 | @queriedEntity('contact')
58 | class ContactsPage extends React.Component {
59 | ...
60 | }
61 | ```
62 | NOTE: For those of you who do not enjoy decorating as much as I do, use the standard way!
63 |
64 | ### queriedEntity(entityName, config[optional])
65 |
66 | | Config field | Explanation | Default Value |
67 | | ------ | ------ | ------ |
68 | | resultField | the result field name in response body that matches the list of items | `content` |
69 | | retain_number | maximum number of queries to cache | `10` |
70 | | hideLoadIfDataFound | if set to `false`, will trigger loading UI even if data had been cached | `true` |
71 | | reducerName | The reducer name used for the entity. Default is the plural form of the entityName | `[entityName]s` |
72 | | preloadValidTime | The time (milliseconds) that a preloaded query is valid and should not be re-fetched | `10000` |
73 | | smartPreload | If set to `true` the library times the network calls specific to the defined entity. If the average is greater than 0.3 seconds, preloading will be cancelled. Overwrite this time with the next field | `false` |
74 | | smartThresholdTime | The acceptable average time (milliseconds) for network calls to continue preloading in `smartPreload` mode | `300` |
75 |
76 | #### Properties injected to the wrapped component
77 | | property (props) | Explanation | Example | Sample value |
78 | | ------ | ------ | ------ | ------ |
79 | | [entityName]s | The query result | `contacts` | `[{id: 1, name: 'John Doe'}]` |
80 | | [entityName]sQueryParams | The last successful parameters with which query was performed | `contactsQueryParams` | `{page: 1}` |
81 | | [entityName]sMetadata | The metadata that is received from the endpoint | `contactsMetadata` | `{totalPages: 10}` |
82 | | initialQuery[EntityName]s | A *must-be-called* function that initializes the query. Receives url and parameters (object) | `initialQueryContacts('/contacts/', {page: 1})` |
83 | | query[EntityName]s | Any query after initial query call. It will append the new partial params on top of the previous ones | `queryContacts({pageSize: 1})` |
84 | | create[EntityName] | Creates an object and performs the latest query again | `createContact({name: 'Foo Bar'})` |
85 | | update[EntityName] | Updates/replaces an entity. After success, will update the store and queries again | `updateContact({id: 1, name: 'Foo Bar'})` |
86 | | patch[EntityName] | Patches an entity. After success, will update the store and queries again | `patchContact({id: 1, name: 'Foo Bar'})` |
87 | | delete[EntityName] | Removes an entity. After success, will update the store and queries again | `deleteContact({id: 1, name: 'Foo Bar'})` |
88 | | set[EntityName]sPreloader | Sets a function for pre-loading data for a smooth UX | `setContactsPreloader(customPreloader)`* |
89 | | loading[EntityName]s | Network loading status | `loadingContacts` | `true` |
90 |
91 | *Note: the preloader function will receive (partialParams, params, queryMetadata) as arguments. The function should return an array of partialParams. See the preloading
92 | section for more information
93 |
94 | ### detailedEntity
95 |
96 | if you intend to work with an endpoint that returns a detailed entity use `detailedEntity` to decorate your components:
97 | ```js
98 | import { detailedEntity } from 'rest-react-redux';
99 |
100 | @detailedEntity('contact')
101 | class ContactsPage extends React.Component {
102 | ...
103 | }
104 | ```
105 | ### detailedEntity(entityName, config[optional])
106 |
107 | | Config field | Explanation | Default Value |
108 | | ------ | ------ | ------ |
109 | | retain_number | maximum number of queries to cache | `10` |
110 | | hideLoadIfDataFound | if set to `false`, will trigger loading UI even if data had been cached | `true` |
111 | | reducerName | The reducer name used for the entity. Default is the entityName | `[entityName]` |
112 |
113 | #### Properties injected to the wrapped component
114 | | property (props) | Explanation | Example | Sample value |
115 | | ------ | ------ | ------ | ------ |
116 | | [entityName] | The result | `contact` | `{id: 1, name: 'John Doe'}` |
117 | | initialGet[EntityName] | A *must-be-called* function that initializes the entity. Receives url and object id | `initialGetContact('/contacts/12', 12)` |
118 | | get[EntityName] | Any get call after the initial get call. This is usually used for receiving updates if any | `getContact()` |
119 | | update[EntityName] | Updates/replaces the entity. After success, will update the store and gets again | `updateContact({id: 1, name: 'Foo Bar'})` |
120 | | patch[EntityName] | Patches the entity. After success, will update the store and gets again | `patchContact({id: 1, name: 'Foo Bar'})` |
121 | | delete[EntityName] | Removes the entity. After success, will update the store | `deleteContact()` |
122 | | loading[EntityName] | Network loading status | `loadingContact` | `true` |
123 |
124 | ### Preloading
125 | For a better user experience, you may pre-load the data that have a good chance of being loaded later. There are
126 | two main methods of preloading data:
127 |
128 | #### Entity specific preloading
129 | If you are dealing with a queried entity like calendar events, table data, chat messages etc.
130 | it might be a good idea to dynamically preload data based on what the user queries. For instance, loading the previous and
131 | the next pages of a table sounds like a good investment! To do that you simply need to set
132 | a function via property `set[EntityName]sPreloader`:
133 |
134 | ```js
135 | import { queriedEntity } from 'rest-react-redux';
136 |
137 | @queriedEntity('contact')
138 | class ContactsPage extends React.Component {
139 |
140 | componentDidMount() {
141 | this.props.initialQueryContacts('/contacts/', {page: 1, size: 20});
142 | this.props.setContactsPreloader((partialParams) => {
143 |
144 | const page = partialParams.page;
145 |
146 | // If the query does not contain a new page do not preload
147 | if (!partialParams.page) return [];
148 |
149 | // Preload previous and next pages
150 | return [{page: page - 1}, {page: page + 1}];
151 | })
152 | }
153 | }
154 | ```
155 |
156 | #### Generic preloading
157 | You may need to preload entities that are not related to the component you are focusing at.
158 | For instance, if a user gets to the main dashboard, you want to preload all the contacts before
159 | the user goes to the contacts page. To achieve that you can use the exposed `queryEntities` or `getEntity`
160 | action generators:
161 |
162 | `queryEntities(entityName, url, params)`
163 | `getEntity(entityName, url)`
164 |
165 | Usage example:
166 |
167 | ```js
168 | import { queryEntities, getEntity } from 'rest-react-redux';
169 | import store from '../store';
170 |
171 | class Dashboard extends React.Component {
172 |
173 | componentDidMount() {
174 | // Preload a contact list query
175 | store.dispatch(queryEntities('contact', '/contacts/', {page: 1}));
176 |
177 | // Preload a detailed contact
178 | store.dispatch(getEntity('contact', '/contacts/1'));
179 | }
180 | }
181 | ```
182 |
183 |
184 | ### Todos
185 |
186 | - Remove the redux-thunk dependency
187 | - Remove the JSON request/response requirement
188 | - Remove the need to update the reducer for each entity
189 | - Tests
190 |
191 | License
192 | ----
193 |
194 | MIT
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory(require("axios"), require("react"), require("react-redux"));
4 | else if(typeof define === 'function' && define.amd)
5 | define(["axios", "react", "react-redux"], factory);
6 | else if(typeof exports === 'object')
7 | exports["rest-react-redux"] = factory(require("axios"), require("react"), require("react-redux"));
8 | else
9 | root["rest-react-redux"] = factory(root["axios"], root["react"], root["react-redux"]);
10 | })(window, function(__WEBPACK_EXTERNAL_MODULE_axios__, __WEBPACK_EXTERNAL_MODULE_react__, __WEBPACK_EXTERNAL_MODULE_react_redux__) {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 | /******/
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 | /******/
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId]) {
20 | /******/ return installedModules[moduleId].exports;
21 | /******/ }
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ i: moduleId,
25 | /******/ l: false,
26 | /******/ exports: {}
27 | /******/ };
28 | /******/
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 | /******/
32 | /******/ // Flag the module as loaded
33 | /******/ module.l = true;
34 | /******/
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 | /******/
39 | /******/
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 | /******/
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 | /******/
46 | /******/ // define getter function for harmony exports
47 | /******/ __webpack_require__.d = function(exports, name, getter) {
48 | /******/ if(!__webpack_require__.o(exports, name)) {
49 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
50 | /******/ }
51 | /******/ };
52 | /******/
53 | /******/ // define __esModule on exports
54 | /******/ __webpack_require__.r = function(exports) {
55 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
56 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
57 | /******/ }
58 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
59 | /******/ };
60 | /******/
61 | /******/ // create a fake namespace object
62 | /******/ // mode & 1: value is a module id, require it
63 | /******/ // mode & 2: merge all properties of value into the ns
64 | /******/ // mode & 4: return value when already ns object
65 | /******/ // mode & 8|1: behave like require
66 | /******/ __webpack_require__.t = function(value, mode) {
67 | /******/ if(mode & 1) value = __webpack_require__(value);
68 | /******/ if(mode & 8) return value;
69 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
70 | /******/ var ns = Object.create(null);
71 | /******/ __webpack_require__.r(ns);
72 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
73 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
74 | /******/ return ns;
75 | /******/ };
76 | /******/
77 | /******/ // getDefaultExport function for compatibility with non-harmony modules
78 | /******/ __webpack_require__.n = function(module) {
79 | /******/ var getter = module && module.__esModule ?
80 | /******/ function getDefault() { return module['default']; } :
81 | /******/ function getModuleExports() { return module; };
82 | /******/ __webpack_require__.d(getter, 'a', getter);
83 | /******/ return getter;
84 | /******/ };
85 | /******/
86 | /******/ // Object.prototype.hasOwnProperty.call
87 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
88 | /******/
89 | /******/ // __webpack_public_path__
90 | /******/ __webpack_require__.p = "";
91 | /******/
92 | /******/
93 | /******/ // Load entry module and return exports
94 | /******/ return __webpack_require__(__webpack_require__.s = "./src/index.js");
95 | /******/ })
96 | /************************************************************************/
97 | /******/ ({
98 |
99 | /***/ "./src/detailEntity/detailedEntity.js":
100 | /*!********************************************!*\
101 | !*** ./src/detailEntity/detailedEntity.js ***!
102 | \********************************************/
103 | /*! no static exports found */
104 | /***/ (function(module, exports, __webpack_require__) {
105 |
106 | "use strict";
107 | eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _react = _interopRequireDefault(__webpack_require__(/*! react */ \"react\"));\n\nvar _reactRedux = __webpack_require__(/*! react-redux */ \"react-redux\");\n\nvar _detailedEntityActions = __webpack_require__(/*! ./detailedEntityActions */ \"./src/detailEntity/detailedEntityActions.js\");\n\nvar _helpers = __webpack_require__(/*! ../helpers */ \"./src/helpers.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _extends() { _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; }; return _extends.apply(this, arguments); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n/**\n * Detailed entity abstraction (Higher Order Component)\n * Retrieves entityName, end point url. Dispatches queries and keeps track of the queries.\n * API contract: should accept page and pageSize as params, should return a json with the property item as list\n * This component will recycles cached queries after. It retains at most RETAIN_NUMBER of queries\n * Per entityName used, there must be a reducer with the same name located at reducers/index.js\n *\n * staticURL is a url provided at the annotation level and requires no logic to generate it i.e. no\n * parameter exists inside it. If your url contains custom parameters (usually ids that component knows about it)\n * use dynamicURL on the initialQuery. This will override the staticURL if any provided.\n */\n// These props should be filtered before inject\nvar filteredProps = {};\n['getEntity', 'pushToQueue', 'createItem', 'updateItem', 'patchItem', 'deleteItem'].forEach(function (prop) {\n return filteredProps[prop] = undefined;\n}); // Number of queries to cache in store\n\nvar RETAIN_NUMBER = 10;\n\nvar _default = function _default(entityName) {\n var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},\n reducerName = _ref.reducerName,\n _ref$retain_number = _ref.retain_number,\n retain_number = _ref$retain_number === void 0 ? RETAIN_NUMBER : _ref$retain_number;\n\n return function (WrappedComponent) {\n var _class, _temp;\n\n return (0, _reactRedux.connect)(function (state) {\n return _defineProperty({}, entityName, state[reducerName || entityName]);\n }, {\n getEntity: _detailedEntityActions.getEntity,\n pushToQueue: _detailedEntityActions.pushToQueue,\n createItem: _detailedEntityActions.createItem,\n updateItem: _detailedEntityActions.updateItem,\n patchItem: _detailedEntityActions.patchItem,\n deleteItem: _detailedEntityActions.deleteItem\n })((_temp = _class =\n /*#__PURE__*/\n function (_React$Component) {\n _inherits(_class, _React$Component);\n\n function _class() {\n var _getPrototypeOf2;\n\n var _this;\n\n _classCallCheck(this, _class);\n\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(_class)).call.apply(_getPrototypeOf2, [this].concat(args)));\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"state\", {\n entityId: null,\n loadingData: false\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"initialGet\", function (url, entityId) {\n _this.setState({\n loadingData: true,\n url: url,\n entityId: entityId\n });\n\n return _this.get(url, entityId);\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"get\", function () {\n var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.state.url;\n var entityId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.state.entityId;\n var entity = _this.props[entityName][entityId];\n if (!entity) _this.props.freeze();\n\n _this.setState({\n loadingData: true\n });\n\n return _this.props.getEntity(entityName, url).then(function () {\n _this.setState({\n loadingData: false\n });\n\n _this.props.unfreeze();\n\n _this.collectGarbage();\n }).catch(function () {\n _this.setState({\n loadingData: false\n });\n\n _this.props.unfreeze();\n });\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"checkSetup\", function () {\n var _this$state = _this.state,\n url = _this$state.url,\n entityId = _this$state.entityId;\n if (!url) throw new Error(\"No url specified for \".concat(entityName));\n if (!entityId) throw new Error(\"No entityId specified for \".concat(entityName));\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"collectGarbage\", function () {\n return _this.props.pushToQueue(entityName, _this.state.entityId, retain_number);\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"update\", function (entity) {\n _this.checkSetup();\n\n _this.props.freeze();\n\n return _this.props.updateItem(entityName, entity, _this.state.entityId, _this.state.url).then(function () {\n _this.props.unfreeze();\n\n _this.get();\n });\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"patch\", function (fields) {\n _this.checkSetup();\n\n _this.props.freeze();\n\n return _this.props.patchItem(entityName, fields, _this.state.entityId, _this.state.url).then(function () {\n _this.props.unfreeze();\n\n _this.get();\n });\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"delete\", function () {\n _this.checkSetup();\n\n _this.props.freeze();\n\n return _this.props.deleteItem(entityName, _this.state.entityId, _this.state.url).then(function () {\n _this.props.unfreeze();\n\n _this.get();\n });\n });\n\n return _this;\n }\n\n _createClass(_class, [{\n key: \"render\",\n\n /** @ignore */\n value: function render() {\n var _injectedProps;\n\n var entityId = this.state.entityId;\n var entity = this.props[entityName][entityId];\n\n var item = _defineProperty({}, entityName, entity || {});\n\n var injectedProps = (_injectedProps = {}, _defineProperty(_injectedProps, entityName, entity || {}), _defineProperty(_injectedProps, 'initialGet' + (0, _helpers.CFL)(entityName), this.initialGet), _defineProperty(_injectedProps, 'get' + (0, _helpers.CFL)(entityName), this.get), _defineProperty(_injectedProps, 'update' + (0, _helpers.CFL)(entityName), this.update), _defineProperty(_injectedProps, 'patch' + (0, _helpers.CFL)(entityName), this.patch), _defineProperty(_injectedProps, 'delete' + (0, _helpers.CFL)(entityName), this.delete), _defineProperty(_injectedProps, 'loading' + (0, _helpers.CFL)(entityName), this.state.loadingData), _injectedProps);\n return _react.default.createElement(WrappedComponent, _extends({}, this.props, filteredProps, item, injectedProps));\n }\n }]);\n\n return _class;\n }(_react.default.Component), _defineProperty(_class, \"defaultProps\", {\n freeze: function freeze() {},\n unfreeze: function unfreeze() {}\n }), _temp));\n };\n};\n\nexports.default = _default;\n\n//# sourceURL=webpack://rest-react-redux/./src/detailEntity/detailedEntity.js?");
108 |
109 | /***/ }),
110 |
111 | /***/ "./src/detailEntity/detailedEntityActions.js":
112 | /*!***************************************************!*\
113 | !*** ./src/detailEntity/detailedEntityActions.js ***!
114 | \***************************************************/
115 | /*! no static exports found */
116 | /***/ (function(module, exports, __webpack_require__) {
117 |
118 | "use strict";
119 | eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.deleteItem = exports.patchItem = exports.updateItem = exports.createItem = exports.pushToQueue = exports.getEntity = exports.patchItemDispatch = void 0;\n\nvar _axios = _interopRequireDefault(__webpack_require__(/*! axios */ \"axios\"));\n\nvar types = _interopRequireWildcard(__webpack_require__(/*! ../helpers */ \"./src/helpers.js\"));\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n/**\n * Actions\n */\nvar updateItemDispatch = function updateItemDispatch(payload, entityName) {\n return {\n type: types.UPDATE_ITEM(entityName),\n payload: payload\n };\n};\n\nvar patchItemDispatch = function patchItemDispatch(payload, entityName) {\n return {\n type: types.PATCH_ITEM(entityName),\n payload: payload\n };\n};\n\nexports.patchItemDispatch = patchItemDispatch;\n\nvar deleteItemDispatch = function deleteItemDispatch(payload, entityName) {\n var type = types.REMOVE_ITEM(entityName);\n return {\n type: type,\n payload: payload\n };\n};\n\nvar insertItem = function insertItem(payload, entityName) {\n return {\n type: types.INSERT_ITEM(entityName),\n payload: payload\n };\n};\n\nvar getEntity = function getEntity(entityName, url) {\n return function (dispatch) {\n return _axios.default.get(url).then(function (_ref) {\n var data = _ref.data;\n dispatch(insertItem(data, entityName));\n });\n };\n};\n\nexports.getEntity = getEntity;\n\nvar pushToQueue = function pushToQueue(entityName, id, retain_number) {\n return {\n type: types.PUSH_TO_TRACKING_QUEUE_DETAILED(entityName),\n payload: {\n id: id,\n retain_number: retain_number\n }\n };\n};\n\nexports.pushToQueue = pushToQueue;\n\nvar createItem = function createItem(entityName, entity, url) {\n // The returned data will not be of any special use since the position it is placed in db is unknown\n // therefore no dispatch is made to store\n return function () {\n return _axios.default.post(url, entity);\n };\n};\n\nexports.createItem = createItem;\n\nvar updateItem = function updateItem(entityName, entity, entityId, url) {\n return function (dispatch) {\n return _axios.default.put(url, entity).then(function () {\n dispatch(updateItemDispatch({\n entityId: entityId,\n entity: entity\n }, entityName));\n });\n };\n};\n\nexports.updateItem = updateItem;\n\nvar patchItem = function patchItem(entityName, entity, entityId, url) {\n return function (dispatch) {\n return _axios.default.patch(url, entity).then(function () {\n dispatch(patchItemDispatch({\n entity: entity,\n entityId: entityId\n }, entityName));\n });\n };\n};\n\nexports.patchItem = patchItem;\n\nvar deleteItem = function deleteItem(entityName, entityId, url) {\n return function (dispatch) {\n return _axios.default.delete(url).then(function () {\n dispatch(deleteItemDispatch(entityId, entityName));\n });\n };\n};\n\nexports.deleteItem = deleteItem;\n\n//# sourceURL=webpack://rest-react-redux/./src/detailEntity/detailedEntityActions.js?");
120 |
121 | /***/ }),
122 |
123 | /***/ "./src/detailEntity/detailedEntityReducer.js":
124 | /*!***************************************************!*\
125 | !*** ./src/detailEntity/detailedEntityReducer.js ***!
126 | \***************************************************/
127 | /*! no static exports found */
128 | /***/ (function(module, exports, __webpack_require__) {
129 |
130 | "use strict";
131 | eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar types = _interopRequireWildcard(__webpack_require__(/*! ../helpers */ \"./src/helpers.js\"));\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }\n\nfunction _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }\n\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance\"); }\n\nfunction _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter); }\n\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nvar defaultState = {\n tracker: []\n};\n/**\n * Reducer generator for storing and to caching detailed entities\n */\n\nvar _default = function _default(entityName) {\n return function itemDetailRepo() {\n var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultState;\n var action = arguments.length > 1 ? arguments[1] : undefined;\n\n switch (action.type) {\n case types.INSERT_ITEM(entityName):\n {\n var entity = action.payload;\n return _objectSpread({}, state, _defineProperty({}, entity.id, entity));\n }\n\n case types.UPDATE_ITEM(entityName):\n {\n var _action$payload = action.payload,\n _entity = _action$payload.entity,\n entityId = _action$payload.entityId;\n return _objectSpread({}, state, _defineProperty({}, entityId, _objectSpread({\n id: entityId\n }, _entity)));\n }\n\n case types.PATCH_ITEM(entityName):\n {\n var _action$payload2 = action.payload,\n _entity2 = _action$payload2.entity,\n _entityId = _action$payload2.entityId;\n return _objectSpread({}, state, _defineProperty({}, _entityId, _objectSpread({}, state[_entityId], _entity2)));\n }\n\n case types.REMOVE_ITEM(entityName):\n {\n var _entityId2 = action.payload;\n\n var tracker = _toConsumableArray(state.tracker);\n\n var index = tracker.indexOf(_entityId2);\n\n if (index > -1) {\n delete state[tracker[index]];\n tracker.splice(index, 1);\n }\n\n return _objectSpread({}, state, {\n tracker: tracker\n });\n }\n\n case types.PUSH_TO_TRACKING_QUEUE_DETAILED(entityName):\n {\n var _tracker = _toConsumableArray(state.tracker);\n\n var _action$payload3 = action.payload,\n id = _action$payload3.id,\n retain_number = _action$payload3.retain_number;\n\n var _index = _tracker.indexOf(id);\n\n if (_index > -1) _tracker.splice(_index, 1);else if (_tracker.length >= retain_number) {\n delete state[_tracker[0]];\n\n _tracker.shift();\n }\n\n _tracker.push(id);\n\n return _objectSpread({}, state, {\n tracker: _tracker\n });\n }\n\n default:\n return state;\n }\n };\n};\n\nexports.default = _default;\n\n//# sourceURL=webpack://rest-react-redux/./src/detailEntity/detailedEntityReducer.js?");
132 |
133 | /***/ }),
134 |
135 | /***/ "./src/helpers.js":
136 | /*!************************!*\
137 | !*** ./src/helpers.js ***!
138 | \************************/
139 | /*! no static exports found */
140 | /***/ (function(module, exports, __webpack_require__) {
141 |
142 | "use strict";
143 | eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.detailedUrl = exports.PL = exports.CFL = exports.LOADING = exports.encodeAPICall = exports.DELETE_ENTITY = exports.PATCH_ENTITY = exports.UPDATE_ENTITY = exports.UPDATE_NETWORK_TIMER = exports.PUSH_TO_TRACKING_QUEUE = exports.INSERT_QUERY = exports.PUSH_TO_TRACKING_QUEUE_DETAILED = exports.PATCH_ITEM = exports.REMOVE_ITEM = exports.UPDATE_ITEM = exports.INSERT_ITEM = void 0;\n\n/**\n * Detailed entity action type generators\n */\nvar INSERT_ITEM = function INSERT_ITEM(entityName) {\n return 'INSERT_' + entityName.toUpperCase();\n};\n\nexports.INSERT_ITEM = INSERT_ITEM;\n\nvar UPDATE_ITEM = function UPDATE_ITEM(entityName) {\n return 'UPDATE_' + entityName.toUpperCase();\n};\n\nexports.UPDATE_ITEM = UPDATE_ITEM;\n\nvar REMOVE_ITEM = function REMOVE_ITEM(entityName) {\n return 'REMOVE_' + entityName.toUpperCase();\n};\n\nexports.REMOVE_ITEM = REMOVE_ITEM;\n\nvar PATCH_ITEM = function PATCH_ITEM(entityName) {\n return 'PATCH_' + entityName.toUpperCase();\n};\n\nexports.PATCH_ITEM = PATCH_ITEM;\n\nvar PUSH_TO_TRACKING_QUEUE_DETAILED = function PUSH_TO_TRACKING_QUEUE_DETAILED(entityName) {\n return 'PUSH_TO_TRACKING_QUEUE_DETAILED_' + entityName.toUpperCase();\n};\n/**\n * Queried entity action type generators\n */\n\n\nexports.PUSH_TO_TRACKING_QUEUE_DETAILED = PUSH_TO_TRACKING_QUEUE_DETAILED;\n\nvar INSERT_QUERY = function INSERT_QUERY(entityName) {\n return 'INSERT_QUERY_' + entityName.toUpperCase();\n};\n\nexports.INSERT_QUERY = INSERT_QUERY;\n\nvar PUSH_TO_TRACKING_QUEUE = function PUSH_TO_TRACKING_QUEUE(entityName) {\n return 'PUSH_TO_TRACKING_QUEUE_QUERY_' + entityName.toUpperCase();\n};\n\nexports.PUSH_TO_TRACKING_QUEUE = PUSH_TO_TRACKING_QUEUE;\n\nvar UPDATE_NETWORK_TIMER = function UPDATE_NETWORK_TIMER(entityName) {\n return 'UPDATE_NETWORK_TIMER_' + entityName.toUpperCase();\n};\n\nexports.UPDATE_NETWORK_TIMER = UPDATE_NETWORK_TIMER;\n\nvar UPDATE_ENTITY = function UPDATE_ENTITY(entityName) {\n return 'UPDATE_ENTITY_' + entityName.toUpperCase();\n};\n\nexports.UPDATE_ENTITY = UPDATE_ENTITY;\n\nvar PATCH_ENTITY = function PATCH_ENTITY(entityName) {\n return 'PATCH_ENTITY_' + entityName.toUpperCase();\n};\n\nexports.PATCH_ENTITY = PATCH_ENTITY;\n\nvar DELETE_ENTITY = function DELETE_ENTITY(entityName) {\n return 'DELETE_ENTITY_' + entityName.toUpperCase();\n};\n/**\n * Generates a unique key from url and query params\n * @returns {number}\n */\n\n\nexports.DELETE_ENTITY = DELETE_ENTITY;\n\nvar encodeAPICall = function encodeAPICall(url, params) {\n var sortedParams = {};\n Object.keys(params).sort().forEach(function (key) {\n return sortedParams[key] = params[key];\n });\n var string = JSON.stringify(sortedParams);\n return hashCode(url + string);\n};\n/**\n * A placeholder constant for signalling the loading status in redux to prevent re-call\n */\n\n\nexports.encodeAPICall = encodeAPICall;\nvar LOADING = 'LOADING';\n/**\n * Generates a numerical hash from a string\n * @returns {number}\n */\n\nexports.LOADING = LOADING;\n\nvar hashCode = function hashCode(string) {\n var hash = 0;\n if (string.length === 0) return hash;\n\n for (var i = 0; i < string.length; i++) {\n var char = string.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n\n return hash;\n};\n/**\n * Capitalizes first letter of a word\n * @return {string}\n */\n\n\nvar CFL = function CFL(word) {\n if (!word) return word;\n return word.slice(0, 1).toUpperCase() + word.slice(1, word.length);\n};\n/**\n * Pluralizes a word\n * @return {string}\n */\n\n\nexports.CFL = CFL;\n\nvar PL = function PL(word) {\n if (!word || word.length === 0) return word;\n if (/(x|s|ch|sh)$/i.test(word)) return word + 'es';\n if (/y$/i.test(word)) return word.slice(0, word.length - 1) + 'ies';\n return word + 's';\n};\n/**\n * Constructs a detailed entity url by appending id after the base url\n * @returns {string}\n */\n\n\nexports.PL = PL;\n\nvar detailedUrl = function detailedUrl(baseUrl, id) {\n var hasTrailingSlash = baseUrl.endsWith('/');\n return baseUrl + (!hasTrailingSlash ? '/' : '') + id + (hasTrailingSlash ? '/' : '');\n};\n\nexports.detailedUrl = detailedUrl;\n\n//# sourceURL=webpack://rest-react-redux/./src/helpers.js?");
144 |
145 | /***/ }),
146 |
147 | /***/ "./src/index.js":
148 | /*!**********************!*\
149 | !*** ./src/index.js ***!
150 | \**********************/
151 | /*! no static exports found */
152 | /***/ (function(module, exports, __webpack_require__) {
153 |
154 | "use strict";
155 | eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nObject.defineProperty(exports, \"queriedEntityReducer\", {\n enumerable: true,\n get: function get() {\n return _queriedEntityReducer.default;\n }\n});\nObject.defineProperty(exports, \"queriedEntity\", {\n enumerable: true,\n get: function get() {\n return _queriedEntity.default;\n }\n});\nObject.defineProperty(exports, \"detailedEntityReducer\", {\n enumerable: true,\n get: function get() {\n return _detailedEntityReducer.default;\n }\n});\nObject.defineProperty(exports, \"detailedEntity\", {\n enumerable: true,\n get: function get() {\n return _detailedEntity.default;\n }\n});\nObject.defineProperty(exports, \"queryEntities\", {\n enumerable: true,\n get: function get() {\n return _queriedEntityActions.queryEntities;\n }\n});\nObject.defineProperty(exports, \"getEntity\", {\n enumerable: true,\n get: function get() {\n return _detailedEntityActions.getEntity;\n }\n});\n\nvar _queriedEntityReducer = _interopRequireDefault(__webpack_require__(/*! ./queriedEntity/queriedEntityReducer */ \"./src/queriedEntity/queriedEntityReducer.js\"));\n\nvar _queriedEntity = _interopRequireDefault(__webpack_require__(/*! ./queriedEntity/queriedEntity */ \"./src/queriedEntity/queriedEntity.js\"));\n\nvar _detailedEntityReducer = _interopRequireDefault(__webpack_require__(/*! ./detailEntity/detailedEntityReducer */ \"./src/detailEntity/detailedEntityReducer.js\"));\n\nvar _detailedEntity = _interopRequireDefault(__webpack_require__(/*! ./detailEntity/detailedEntity */ \"./src/detailEntity/detailedEntity.js\"));\n\nvar _queriedEntityActions = __webpack_require__(/*! ./queriedEntity/queriedEntityActions */ \"./src/queriedEntity/queriedEntityActions.js\");\n\nvar _detailedEntityActions = __webpack_require__(/*! ./detailEntity/detailedEntityActions */ \"./src/detailEntity/detailedEntityActions.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n//# sourceURL=webpack://rest-react-redux/./src/index.js?");
156 |
157 | /***/ }),
158 |
159 | /***/ "./src/queriedEntity/queriedEntity.js":
160 | /*!********************************************!*\
161 | !*** ./src/queriedEntity/queriedEntity.js ***!
162 | \********************************************/
163 | /*! no static exports found */
164 | /***/ (function(module, exports, __webpack_require__) {
165 |
166 | "use strict";
167 | eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _react = _interopRequireDefault(__webpack_require__(/*! react */ \"react\"));\n\nvar _reactRedux = __webpack_require__(/*! react-redux */ \"react-redux\");\n\nvar _helpers = __webpack_require__(/*! ../helpers */ \"./src/helpers.js\");\n\nvar _queriedEntityActions = __webpack_require__(/*! ./queriedEntityActions */ \"./src/queriedEntity/queriedEntityActions.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _extends() { _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; }; return _extends.apply(this, arguments); }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n/**\n * Queried entity abstraction (Higher Order Component)\n * Retrieves entityName, end point url and params. Dispatches queries and keeps track of the queries.\n * This component will recycles cached queries after. It retains at most RETAIN_NUMBER of queries\n * Per entityName used, there must be a reducer with the same name located at reducers/index.js\n */\n// Default number of queries to cache in store\nvar RETAIN_NUMBER = 10; // Default time for a valid preload (milliseconds)\n\nvar PRELOAD_VALID_TIME = 10000; // Default time that is compared with average network time to decide whether to perform preload (milliseconds)\n\nvar SMART_THRESHOLD_TIME = 300; // Default field that maps the results in the response body, if set to null, the whole response will be returned;\n\nvar RESULT_FIELD = 'content'; // These props should be filtered before inject\n\nvar filteredProps = {};\n['queryEntities', 'pushToQueue', 'createEntity', 'updateEntity', 'patchEntity', 'deleteEntity'].forEach(function (prop) {\n return filteredProps[prop] = undefined;\n});\n\nvar _default = function _default(entityName) {\n var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},\n _ref$resultField = _ref.resultField,\n resultField = _ref$resultField === void 0 ? RESULT_FIELD : _ref$resultField,\n _ref$hideLoadIfDataFo = _ref.hideLoadIfDataFound,\n hideLoadIfDataFound = _ref$hideLoadIfDataFo === void 0 ? true : _ref$hideLoadIfDataFo,\n _ref$retain_number = _ref.retain_number,\n retain_number = _ref$retain_number === void 0 ? RETAIN_NUMBER : _ref$retain_number,\n reducer_name = _ref.reducer_name,\n _ref$preloadValidTime = _ref.preloadValidTime,\n preloadValidTime = _ref$preloadValidTime === void 0 ? PRELOAD_VALID_TIME : _ref$preloadValidTime,\n _ref$smartPreload = _ref.smartPreload,\n smartPreload = _ref$smartPreload === void 0 ? false : _ref$smartPreload,\n _ref$smartThresholdTi = _ref.smartThresholdTime,\n smartThresholdTime = _ref$smartThresholdTi === void 0 ? SMART_THRESHOLD_TIME : _ref$smartThresholdTi;\n\n return function (WrappedComponent) {\n var _class, _temp;\n\n return (0, _reactRedux.connect)(function (state) {\n return _defineProperty({}, (0, _helpers.PL)(entityName), state[reducer_name || (0, _helpers.PL)(entityName)]);\n }, {\n queryEntities: _queriedEntityActions.queryEntities,\n pushToQueue: _queriedEntityActions.pushToQueue,\n createEntity: _queriedEntityActions.createEntity,\n updateEntity: _queriedEntityActions.updateEntity,\n patchEntity: _queriedEntityActions.patchEntity,\n deleteEntity: _queriedEntityActions.deleteEntity\n })((_temp = _class =\n /*#__PURE__*/\n function (_React$Component) {\n _inherits(_class, _React$Component);\n\n function _class() {\n var _getPrototypeOf2;\n\n var _this;\n\n _classCallCheck(this, _class);\n\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(_class)).call.apply(_getPrototypeOf2, [this].concat(args)));\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"state\", {\n params: {},\n loadingData: false\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"preLoaderFunc\", undefined);\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"initialQuery\", function (url) {\n var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n _this.setState({\n url: url\n });\n\n return _this.query(params, url, true);\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"query\", function () {\n var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.state.params;\n var url = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.state.url;\n var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n var oldParams = initial ? _objectSpread({}, params) : _objectSpread({}, _this.state.params);\n\n var newParams = _objectSpread({}, oldParams, params);\n\n _this.setState({\n params: newParams,\n loadingData: true\n });\n\n var data = _this.props[(0, _helpers.PL)(entityName)][(0, _helpers.encodeAPICall)(url, newParams)];\n\n if (!data || !hideLoadIfDataFound) _this.props.freeze();\n\n _this.preload(params, url); // If it should not load the data\n\n\n if (!_this.shouldLoad(data)) return Promise.resolve();\n return _this.props.queryEntities(entityName, url, newParams, !data, false, smartPreload).then(function () {\n _this.setState({\n loadingData: false\n });\n\n _this.props.unfreeze();\n\n _this.collectGarbage(url, newParams);\n }).catch(function () {\n _this.setState({\n loadingData: false,\n params: oldParams\n });\n\n _this.props.unfreeze();\n });\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"checkSetup\", function () {\n var url = _this.state.url;\n if (!url) throw new Error(\"No url specified for \".concat(entityName));\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"shouldLoad\", function (data) {\n if (!data) return true;\n if (data === _helpers.LOADING) return false; // Check whether pre-loaded less than 10 seconds ago\n\n if (data.preloadedAt && new Date() - data.preloadedAt < preloadValidTime) return false;\n return true;\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"create\", function (entity) {\n _this.checkSetup();\n\n _this.props.freeze();\n\n return _this.props.createEntity(entityName, entity, _this.state.url).then(function () {\n _this.props.unfreeze();\n\n _this.query();\n });\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"update\", function (entity) {\n _this.checkSetup();\n\n _this.props.freeze();\n\n return _this.props.updateEntity(entityName, entity, _this.state.url, resultField).then(function () {\n _this.props.unfreeze();\n\n _this.query();\n });\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"patch\", function (fields) {\n _this.checkSetup();\n\n _this.props.freeze();\n\n return _this.props.patchEntity(entityName, fields, _this.state.url, resultField).then(function () {\n _this.props.unfreeze();\n\n _this.query();\n });\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"delete\", function (entity) {\n _this.checkSetup();\n\n if (typeof entity === 'string') entity = {\n id: entity\n };\n\n _this.props.freeze();\n\n return _this.props.deleteEntity(entityName, entity, _this.state.url, resultField).then(function () {\n _this.props.unfreeze();\n\n _this.query();\n });\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"setPreLoader\", function (preLoaderFunc) {\n _this.preLoaderFunc = preLoaderFunc;\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"preload\", function (params, url) {\n if (!_this.preLoaderFunc) return; // If in smartPreload mode and average of network calls are above 0.3 seconds do not preload\n\n if (smartPreload) {\n var _this$props$PL$networ = _this.props[(0, _helpers.PL)(entityName)].networkTimer,\n average = _this$props$PL$networ.average,\n numberOfCalls = _this$props$PL$networ.numberOfCalls;\n\n if (numberOfCalls > 3 && average > smartThresholdTime) return;\n } // The next 3 lines are repetitive and should be optimized\n\n\n var queryData = _this.props[(0, _helpers.PL)(entityName)][(0, _helpers.encodeAPICall)(url, params)] || {};\n var queryMetadata = resultField ? _objectSpread({}, queryData) : undefined;\n if (resultField) delete queryMetadata[resultField];\n\n var paramsList = _this.preLoaderFunc(params, _objectSpread({}, _this.state.params, params), _objectSpread({}, queryMetadata));\n\n paramsList.forEach(function (params) {\n var fullParams = _objectSpread({}, _this.state.params, params);\n\n var data = _this.props[(0, _helpers.PL)(entityName)][(0, _helpers.encodeAPICall)(url, fullParams)];\n\n if (data) return;\n\n _this.props.queryEntities(entityName, url, fullParams, !data, true, smartPreload).then(function () {\n _this.collectGarbage(url, fullParams);\n }).catch(function () {});\n });\n });\n\n _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"collectGarbage\", function (url, params) {\n return _this.props.pushToQueue(entityName, (0, _helpers.encodeAPICall)(url, params), retain_number);\n });\n\n return _this;\n }\n\n _createClass(_class, [{\n key: \"render\",\n value: function render() {\n var _injectedProps;\n\n var _this$state = this.state,\n url = _this$state.url,\n params = _this$state.params;\n var queryData = this.props[(0, _helpers.PL)(entityName)][(0, _helpers.encodeAPICall)(url, params)] || {};\n var queryMetadata = resultField ? _objectSpread({}, queryData) : undefined;\n if (resultField) delete queryMetadata[resultField];\n var injectedProps = (_injectedProps = {}, _defineProperty(_injectedProps, (0, _helpers.PL)(entityName) + 'QueryParams', this.state.params), _defineProperty(_injectedProps, (0, _helpers.PL)(entityName), (resultField ? queryData && queryData[resultField] : queryData) || []), _defineProperty(_injectedProps, (0, _helpers.PL)(entityName) + 'Metadata', queryMetadata), _defineProperty(_injectedProps, 'initialQuery' + (0, _helpers.CFL)((0, _helpers.PL)(entityName)), this.initialQuery), _defineProperty(_injectedProps, 'query' + (0, _helpers.CFL)((0, _helpers.PL)(entityName)), this.query), _defineProperty(_injectedProps, 'create' + (0, _helpers.CFL)(entityName), this.create), _defineProperty(_injectedProps, 'update' + (0, _helpers.CFL)(entityName), this.update), _defineProperty(_injectedProps, 'patch' + (0, _helpers.CFL)(entityName), this.patch), _defineProperty(_injectedProps, 'delete' + (0, _helpers.CFL)(entityName), this.delete), _defineProperty(_injectedProps, 'set' + (0, _helpers.CFL)((0, _helpers.PL)(entityName)) + 'Preloader', this.setPreLoader), _defineProperty(_injectedProps, 'loading' + (0, _helpers.CFL)((0, _helpers.PL)(entityName)), this.state.loadingData), _injectedProps);\n return _react.default.createElement(WrappedComponent, _extends({}, this.props, filteredProps, injectedProps));\n }\n }]);\n\n return _class;\n }(_react.default.Component), _defineProperty(_class, \"defaultProps\", {\n freeze: function freeze() {},\n unfreeze: function unfreeze() {}\n }), _temp));\n };\n};\n\nexports.default = _default;\n\n//# sourceURL=webpack://rest-react-redux/./src/queriedEntity/queriedEntity.js?");
168 |
169 | /***/ }),
170 |
171 | /***/ "./src/queriedEntity/queriedEntityActions.js":
172 | /*!***************************************************!*\
173 | !*** ./src/queriedEntity/queriedEntityActions.js ***!
174 | \***************************************************/
175 | /*! no static exports found */
176 | /***/ (function(module, exports, __webpack_require__) {
177 |
178 | "use strict";
179 | eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.deleteEntity = exports.patchEntity = exports.updateEntity = exports.createEntity = exports.pushToQueue = exports.queryEntities = exports.patchEntityDispatch = void 0;\n\nvar types = _interopRequireWildcard(__webpack_require__(/*! ../helpers */ \"./src/helpers.js\"));\n\nvar _axios = _interopRequireDefault(__webpack_require__(/*! axios */ \"axios\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }\n\nfunction _objectDestructuringEmpty(obj) { if (obj == null) throw new TypeError(\"Cannot destructure undefined\"); }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n/**\n * Actions\n */\nvar insertQuery = function insertQuery(payload, entityName) {\n return {\n type: types.INSERT_QUERY(entityName),\n payload: payload\n };\n};\n\nvar updateEntityDispatch = function updateEntityDispatch(payload, entityName) {\n return {\n type: types.UPDATE_ENTITY(entityName),\n payload: payload\n };\n};\n\nvar patchEntityDispatch = function patchEntityDispatch(payload, entityName) {\n return {\n type: types.PATCH_ENTITY(entityName),\n payload: payload\n };\n};\n\nexports.patchEntityDispatch = patchEntityDispatch;\n\nvar deleteEntityDispatch = function deleteEntityDispatch(payload, entityName) {\n return {\n type: types.DELETE_ENTITY(entityName),\n payload: payload\n };\n};\n\nvar updateNetworkTimer = function updateNetworkTimer(payload, entityName) {\n return {\n type: types.UPDATE_NETWORK_TIMER(entityName),\n payload: payload\n };\n};\n\nvar queryEntities = function queryEntities(entityName, url, params) {\n var hasData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n var setPreloadFlag = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;\n var smartPreload = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;\n return function (dispatch) {\n var query = (0, types.encodeAPICall)(url, params);\n hasData && dispatch(insertQuery({\n LOADING: types.LOADING,\n query: query\n }, entityName));\n var time;\n if (smartPreload) time = new Date();\n return _axios.default.get(url, {\n params: params\n }).then(function (_ref) {\n var data = _ref.data;\n\n if (smartPreload) {\n var timeDiff = new Date() - time;\n dispatch(updateNetworkTimer(timeDiff, entityName));\n }\n\n var payload = setPreloadFlag ? _objectSpread({}, data, {\n query: query,\n preloadedAt: new Date()\n }) : _objectSpread({}, data, {\n query: query\n });\n dispatch(insertQuery(payload, entityName));\n });\n };\n};\n\nexports.queryEntities = queryEntities;\n\nvar pushToQueue = function pushToQueue(entityName, key, retain_number) {\n return {\n type: types.PUSH_TO_TRACKING_QUEUE(entityName),\n payload: {\n key: key,\n retain_number: retain_number\n }\n };\n};\n\nexports.pushToQueue = pushToQueue;\n\nvar createEntity = function createEntity(entityName, entity, url, onSuccess, onFailure) {\n // The returned data will not be of any special use since the position it is placed in db is unknown\n // therefore no dispatch is made to store\n return function () {\n return _axios.default.post(url, entity);\n };\n};\n\nexports.createEntity = createEntity;\n\nvar updateEntity = function updateEntity(entityName, entity, url, resultField) {\n if (!entity.id) throw new Error(\"Entity \".concat(entityName, \" does not have id to update\"));\n return function (dispatch) {\n return _axios.default.put((0, types.detailedUrl)(url, entity.id), entity).then(function (_ref2) {\n _objectDestructuringEmpty(_ref2);\n\n dispatch(updateEntityDispatch({\n entity: entity,\n resultField: resultField\n }, entityName));\n });\n };\n};\n\nexports.updateEntity = updateEntity;\n\nvar patchEntity = function patchEntity(entityName, entity, url, resultField) {\n if (!entity.id) throw new Error(\"Entity \".concat(entityName, \" does not have id to patch\"));\n return function (dispatch) {\n return _axios.default.patch((0, types.detailedUrl)(url, entity.id), entity).then(function () {\n dispatch(patchEntityDispatch({\n entity: entity,\n resultField: resultField\n }, entityName));\n });\n };\n};\n\nexports.patchEntity = patchEntity;\n\nvar deleteEntity = function deleteEntity(entityName, entity, url, resultField) {\n if (!entity.id) throw new Error(\"Entity \".concat(entityName, \" does not have id to delete\"));\n return function (dispatch) {\n return _axios.default.delete((0, types.detailedUrl)(url, entity.id)).then(function () {\n dispatch(deleteEntityDispatch({\n entity: entity,\n resultField: resultField\n }, entityName));\n });\n };\n};\n\nexports.deleteEntity = deleteEntity;\n\n//# sourceURL=webpack://rest-react-redux/./src/queriedEntity/queriedEntityActions.js?");
180 |
181 | /***/ }),
182 |
183 | /***/ "./src/queriedEntity/queriedEntityReducer.js":
184 | /*!***************************************************!*\
185 | !*** ./src/queriedEntity/queriedEntityReducer.js ***!
186 | \***************************************************/
187 | /*! no static exports found */
188 | /***/ (function(module, exports, __webpack_require__) {
189 |
190 | "use strict";
191 | eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar types = _interopRequireWildcard(__webpack_require__(/*! ../helpers */ \"./src/helpers.js\"));\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }\n\nfunction _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }\n\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance\"); }\n\nfunction _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter); }\n\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nvar defaultState = {\n tracker: [],\n networkTimer: {\n average: 0,\n numberOfCalls: 0\n }\n};\n/**\n * Reducer generator for storing and to caching queried entities\n */\n\nvar _default = function _default(entityName) {\n return function () {\n var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultState;\n var action = arguments.length > 1 ? arguments[1] : undefined;\n\n switch (action.type) {\n case types.INSERT_QUERY(entityName):\n {\n return _objectSpread({}, state, _defineProperty({}, action.payload.query, action.payload));\n }\n\n case types.UPDATE_NETWORK_TIMER(entityName):\n {\n var _state$networkTimer = state.networkTimer,\n average = _state$networkTimer.average,\n numberOfCalls = _state$networkTimer.numberOfCalls; // Cumulative averaging\n\n var newAverage = (average * numberOfCalls + action.payload) / (numberOfCalls + 1);\n var newNumberOfCalls = numberOfCalls + 1;\n var networkTimer = {\n average: newAverage,\n numberOfCalls: newNumberOfCalls\n };\n return _objectSpread({}, state, {\n networkTimer: networkTimer\n });\n }\n\n case types.PUSH_TO_TRACKING_QUEUE(entityName):\n {\n var tracker = _toConsumableArray(state.tracker);\n\n var _action$payload = action.payload,\n key = _action$payload.key,\n retain_number = _action$payload.retain_number;\n var index = tracker.indexOf(key);\n if (index > -1) tracker.splice(index, 1);else if (tracker.length >= retain_number) {\n delete state[tracker[0]];\n tracker.shift();\n }\n tracker.push(key);\n return _objectSpread({}, state, {\n tracker: tracker\n });\n }\n\n case types.UPDATE_ENTITY(entityName):\n {\n var _tracker = state.tracker;\n if (_tracker.length === 0) return state;\n var lastQuery = _tracker[_tracker.length - 1];\n var lastQueryData = state[lastQuery];\n if (!lastQueryData) return state;\n var _action$payload2 = action.payload,\n entity = _action$payload2.entity,\n resultField = _action$payload2.resultField;\n var newQueryData = lastQueryData[resultField].map(function (data) {\n if (entity.id === data.id) return entity;\n return data;\n });\n return _objectSpread({}, state, _defineProperty({}, lastQuery, newQueryData));\n }\n\n case types.PATCH_ENTITY(entityName):\n {\n var _tracker2 = state.tracker;\n if (_tracker2.length === 0) return state;\n var _lastQuery = _tracker2[_tracker2.length - 1];\n var _lastQueryData = state[_lastQuery];\n if (!_lastQueryData) return state;\n var patchedFields = action.payload;\n\n var _newQueryData = _lastQueryData.map(function (data) {\n if (patchedFields.id === _newQueryData.id) return _objectSpread({}, data, patchedFields);\n return data;\n });\n\n return _objectSpread({}, state, _defineProperty({}, _lastQuery, _newQueryData));\n }\n\n case types.DELETE_ENTITY(entityName):\n {\n var _tracker3 = state.tracker;\n if (_tracker3.length === 0) return state;\n var _action$payload3 = action.payload,\n _resultField = _action$payload3.resultField,\n _entity = _action$payload3.entity;\n var _lastQuery2 = _tracker3[_tracker3.length - 1];\n var _lastQueryData2 = state[_lastQuery2];\n if (!_lastQueryData2) return state;\n\n var _newQueryData2 = _lastQueryData2[_resultField].filter(function (data) {\n return _entity.id !== data.id;\n });\n\n return _objectSpread({}, state, _defineProperty({}, _lastQuery2, _newQueryData2));\n }\n\n default:\n return state;\n }\n };\n};\n\nexports.default = _default;\n\n//# sourceURL=webpack://rest-react-redux/./src/queriedEntity/queriedEntityReducer.js?");
192 |
193 | /***/ }),
194 |
195 | /***/ "axios":
196 | /*!************************!*\
197 | !*** external "axios" ***!
198 | \************************/
199 | /*! no static exports found */
200 | /***/ (function(module, exports) {
201 |
202 | eval("module.exports = __WEBPACK_EXTERNAL_MODULE_axios__;\n\n//# sourceURL=webpack://rest-react-redux/external_%22axios%22?");
203 |
204 | /***/ }),
205 |
206 | /***/ "react":
207 | /*!************************!*\
208 | !*** external "react" ***!
209 | \************************/
210 | /*! no static exports found */
211 | /***/ (function(module, exports) {
212 |
213 | eval("module.exports = __WEBPACK_EXTERNAL_MODULE_react__;\n\n//# sourceURL=webpack://rest-react-redux/external_%22react%22?");
214 |
215 | /***/ }),
216 |
217 | /***/ "react-redux":
218 | /*!******************************!*\
219 | !*** external "react-redux" ***!
220 | \******************************/
221 | /*! no static exports found */
222 | /***/ (function(module, exports) {
223 |
224 | eval("module.exports = __WEBPACK_EXTERNAL_MODULE_react_redux__;\n\n//# sourceURL=webpack://rest-react-redux/external_%22react-redux%22?");
225 |
226 | /***/ })
227 |
228 | /******/ });
229 | });
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var lib = require('./dist/index');
4 |
5 | // Reducer
6 | Object.defineProperty(exports , 'queriedEntityReducer', {
7 | value: lib.queriedEntityReducer
8 | });
9 |
10 | // HOC
11 | Object.defineProperty(exports , 'queriedEntity', {
12 | value: lib.queriedEntity
13 | });
14 |
15 | // Reducer
16 | Object.defineProperty(exports , 'detailedEntityReducer', {
17 | value: lib.detailedEntityReducer
18 | });
19 |
20 | // HOC
21 | Object.defineProperty(exports , 'detailedEntity', {
22 | value: lib.detailedEntity
23 | });
24 |
25 | // Action
26 | Object.defineProperty(exports , 'queryEntities', {
27 | value: lib.queryEntities
28 | });
29 |
30 | // Action
31 | Object.defineProperty(exports , 'getEntity', {
32 | value: lib.getEntity
33 | });
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rest-react-redux",
3 | "version": "0.0.8",
4 | "description": "A library that provides higher order components for communicating with a backend server via standard rest API protocol",
5 | "homepage": "https://github.com/barimani/Rest-React-Redux",
6 | "main": "index.js",
7 | "scripts": {
8 | "test": "mocha --compilers js:@babel/register",
9 | "build": "webpack"
10 | },
11 | "keywords": [
12 | "react",
13 | "redux",
14 | "API",
15 | "crud",
16 | "rest"
17 | ],
18 | "author": "Bari Barimani",
19 | "license": "MIT",
20 | "dependencies": {
21 | "axios": "^0.18.0",
22 | "react": "^16.0.0",
23 | "react-redux": "^5.0.0",
24 | "redux": "^4.0.0",
25 | "redux-thunk": "^2.2.0"
26 | },
27 | "devDependencies": {
28 | "@babel/core": "^7.0.0-beta.55",
29 | "@babel/plugin-proposal-class-properties": "^7.0.0-beta.55",
30 | "@babel/plugin-proposal-decorators": "^7.0.0-rc.1",
31 | "@babel/preset-env": "^7.0.0-beta.55",
32 | "@babel/preset-react": "^7.0.0-beta.55",
33 | "@babel/register": "^7.0.0-rc.1",
34 | "axios-mock-adapter": "^1.15.0",
35 | "babel-loader": "^8.0.0-beta.4",
36 | "babel-plugin-transform-class-properties": "^6.24.1",
37 | "babel-preset-es2015-no-commonjs": "0.0.2",
38 | "babel-preset-stage-2": "^6.24.1",
39 | "chai": "^4.1.2",
40 | "enzyme": "^3.4.1",
41 | "enzyme-adapter-react-16": "^1.2.0",
42 | "jsdom": "^11.12.0",
43 | "mocha": "^5.2.0",
44 | "react-dom": "^16.4.2",
45 | "webpack": "^4.16.4",
46 | "webpack-cli": "^3.1.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/detailEntity/detailedEntity.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import {getEntity, createItem, deleteItem, patchItem, pushToQueue, updateItem} from "./detailedEntityActions";
4 | import {CFL} from "../helpers";
5 |
6 | /**
7 | * Detailed entity abstraction (Higher Order Component)
8 | * Retrieves entityName, end point url. Dispatches queries and keeps track of the queries.
9 | * API contract: should accept page and pageSize as params, should return a json with the property item as list
10 | * This component will recycles cached queries after. It retains at most RETAIN_NUMBER of queries
11 | * Per entityName used, there must be a reducer with the same name located at reducers/index.js
12 | *
13 | * staticURL is a url provided at the annotation level and requires no logic to generate it i.e. no
14 | * parameter exists inside it. If your url contains custom parameters (usually ids that component knows about it)
15 | * use dynamicURL on the initialQuery. This will override the staticURL if any provided.
16 | */
17 |
18 | // These props should be filtered before inject
19 | const filteredProps = {};
20 | ['getEntity', 'pushToQueue', 'createItem', 'updateItem', 'patchItem', 'deleteItem']
21 | .forEach(prop => filteredProps[prop] = undefined);
22 |
23 | // Number of queries to cache in store
24 | const RETAIN_NUMBER = 10;
25 |
26 |
27 | export default (entityName, {reducerName, retain_number = RETAIN_NUMBER} = {}) => WrappedComponent =>
28 | connect(state => ({[entityName]: state[reducerName || entityName]}),
29 | {getEntity, pushToQueue, createItem, updateItem, patchItem, deleteItem})(
30 | class extends React.Component {
31 |
32 | static defaultProps = {freeze: () => {}, unfreeze: () => {}};
33 |
34 | state = {entityId: null, loadingData: false};
35 |
36 | initialGet = (url, entityId) => {
37 | this.setState({loadingData: true, url, entityId});
38 | return this.get(url, entityId);
39 | };
40 |
41 | get = (url = this.state.url, entityId = this.state.entityId) => {
42 | const entity = this.props[entityName][entityId];
43 | if (!entity) this.props.freeze();
44 | this.setState({loadingData: true});
45 | return this.props.getEntity(entityName, url)
46 | .then(() => {this.setState({loadingData: false});this.props.unfreeze();this.collectGarbage();})
47 | .catch(() => {this.setState({loadingData: false});this.props.unfreeze();});
48 | };
49 |
50 | // Checks whether initialGet is called and url is known
51 | checkSetup = () => {
52 | const { url, entityId } = this.state;
53 | if (!url) throw new Error(`No url specified for ${entityName}`);
54 | if (!entityId) throw new Error(`No entityId specified for ${entityName}`);
55 | };
56 |
57 | // Garbage collector so the redux storage will not blow up!
58 | collectGarbage = () => this.props.pushToQueue(entityName, this.state.entityId, retain_number);
59 |
60 | // Entity must contain id and the whole properties of the model
61 | update = entity => {
62 | this.checkSetup();
63 | this.props.freeze();
64 | return this.props.updateItem(entityName, entity, this.state.entityId, this.state.url)
65 | .then(() => {
66 | this.props.unfreeze();
67 | this.get();
68 | });
69 | };
70 |
71 | // The fields to be patched, field should contain id
72 | patch = fields => {
73 | this.checkSetup();
74 | this.props.freeze();
75 | return this.props.patchItem(entityName, fields, this.state.entityId, this.state.url)
76 | .then(() => {
77 | this.props.unfreeze();
78 | this.get();
79 | });
80 | };
81 |
82 | // Accepts the entity object that contains id or the id itself as a string
83 | delete = () => {
84 | this.checkSetup();
85 | this.props.freeze();
86 | return this.props.deleteItem(entityName, this.state.entityId, this.state.url)
87 | .then(() => {
88 | this.props.unfreeze();
89 | this.get();
90 | });
91 | };
92 |
93 | /** @ignore */
94 | render() {
95 | const {entityId} = this.state;
96 | const entity = this.props[entityName][entityId];
97 | const item = {[entityName]: entity || {}};
98 | const injectedProps = {
99 | [entityName]: entity || {},
100 | ['initialGet' + CFL(entityName)]: this.initialGet,
101 | ['get' + CFL(entityName)]: this.get,
102 | ['update' + CFL(entityName)]: this.update,
103 | ['patch' + CFL(entityName)]: this.patch,
104 | ['delete' + CFL(entityName)]: this.delete,
105 | ['loading' + CFL(entityName)]: this.state.loadingData
106 | };
107 | return (
108 |