dataflux 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022-present Massimo Candela
3
+ Copyright (c) 2022-present Massimo Candela <https://massimocandela.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -276,6 +276,7 @@ All the possible options for a model creation are (they are all optional):
276
276
  | parseMoment | Automatically creates Moment.js objects out of ISO8601 strings. E.g., if an object has a property `createdAt: "2022-01-07T21:38:50.295Z"`, this will be transformed to a moment object. | |
277
277
  | hiddenFields | An array of attribute names that will never be sent back to the API. E.g., if you set `hiddenFields: ["pages"]`, a book object can contain an attribute `pages` locally, but this will be stripped out in PUT/POST requests. |
278
278
  | deep | A boolean defining if nested objects should be enriched with the object methods. | true |
279
+ | lazyLoad | A boolean defining if the model should be lazy loaded on the first use. This takes precedence over the lazyLoad declared during store initialization. | false |
279
280
 
280
281
  ### Operations
281
282
  As described in the table above, there are four possible operations: **retrieve, insert, update,** and **delete**. An operation can be defined as an operation object or a function.
@@ -341,28 +342,80 @@ const book = new Model("book", options);
341
342
 
342
343
  #### Operation function
343
344
 
344
- To be even more flexible, you can pass functions and handle yourself the operations. An operation function must return a promise, the promise must return an array of JSON objects when these are ready.
345
+ To be even more flexible, you can pass functions to generate the API urls or retrieve the data. An operation function can return a url or a promise. If the function returns a promise, the promise must resolve in an array of JSON objects when these are ready.
345
346
 
346
347
 
348
+ Example of operation function returning from the API and submitting to the API an array of JSON objects.
349
+
347
350
  ```js
348
351
  const options = {
349
352
  retrieve: () => {
350
353
  // 1) get the data from the API
351
354
  // 2) tranforms the data
352
355
  // 3) return the data to the store
353
- return Promise.resolve(data);
356
+ return axios({ // Example with axios, but you can use whatever you prefer
357
+ url: "https://api.example.net/example",
358
+ method: "get"
359
+ });
354
360
  },
355
361
  insert: (data) => {
356
362
  // 1) recieve the data from the store
357
363
  // 2) transform the data however you like
358
- // 3) send data to server and resolve empty
359
- return Promise.resolve();
364
+ // 3) send data to server
365
+ return axios({
366
+ url: "https://api.example.net/example",
367
+ data,
368
+ method: "post"
369
+ });
360
370
  }
361
371
  };
362
372
 
363
373
  const book = new Model("book", options);
364
374
  ```
365
375
 
376
+ #### Object factory
377
+
378
+ In the examples we saw above, objects are retrieved from an API returning one or more objects. However, sometimes object creation requires a more complex logic. This can be summarized as: the object must be created based on some input parameter
379
+
380
+ Typical examples are:
381
+ * There is no API returning all the objects of a given type, you can only access specific objects based on a parameter (e.g., based on the ID).
382
+ * It doesn't make sense to retrieve all the objects of a given type, since the client needs to access only to a subset of them.
383
+ * The APIs to get/post/put/delete objects are parametric (e.g., you need to specify the ID in the url).
384
+ * The model is polymorphic, and the final object's format is based on some parameter;
385
+ * The model is polymorphic, a different API is used to retrieve the objects based on some input parameter (e.g., they are all books, but there are different APIs by genre).
386
+
387
+ This is a well-know problem, described by the [factory design pattern](https://refactoring.guru/design-patterns/factory-method).
388
+ In DataFlux, the store provides for this use case the `.factory()` method that allows you to implement Factory.
389
+
390
+ To create a factory, you must declare a model as follows:
391
+
392
+ ```js
393
+ const author = new Model("author", {
394
+ lazyLoad: true, // It MUST be lazyLoaded
395
+ retrieve: ({params}) => { // The retrieve function now takes some parameters
396
+
397
+ // You can return a URL or directly one or more JSON objects
398
+ return `https://api.example.net/authors/${params.id}`;
399
+ }
400
+ });
401
+
402
+ store.addModel(author);
403
+ ```
404
+
405
+ It is important to notice in the example above, how `lazyLoad` must be set to `true` and how the retrieve function returns a URL based on an input parameter. As always, the operation function can return a URL (DataFlux will download the objects) or directly a collection of objects.
406
+
407
+ > If you don't specify the insert/update/delete operation functions, the same URL of the retrieve function will be used.
408
+
409
+ Once the parametric retrieve function is declared, you can instantiate the objects with the `store.factory()` method:
410
+
411
+ ```js
412
+ store.factory("author", {id: 4});
413
+ ```
414
+ Invoking `store.factory()` will create a new object in the "author" collection.
415
+
416
+ > store.factory() will not return the object. It just inserts the object in the collection. You will need to use any of the usual .find/.findOne/.findAll/.subscribe to retrieve it.
417
+
418
+
366
419
  #### Object enrichment
367
420
 
368
421
  DataFlux objects can have a `load()` method which enables you to load extra attributes of an object.
@@ -526,13 +579,13 @@ You can operate on the reviews similarly to how you operate on the main model's
526
579
 
527
580
  ```js
528
581
  store.find("book")
529
- .then(([book]) => {
530
- const firstReview = book.reviews[0];
582
+ .then(([book]) => {
583
+ const firstReview = book.reviews[0];
531
584
 
532
- // Examples of what you can do:
533
- firstReview.detroy(); // The first review is removed from the array book.reviews
534
- firstReview.set("stars", 5); // Set the stars of the first review to 5
535
- });
585
+ // Examples of what you can do:
586
+ firstReview.detroy(); // The first review is removed from the array book.reviews
587
+ firstReview.set("stars", 5); // Set the stars of the first review to 5
588
+ });
536
589
  ```
537
590
 
538
591
 
package/dist/Model.js CHANGED
@@ -63,18 +63,6 @@ function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!priva
63
63
 
64
64
  function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
65
65
 
66
- var applyData = function applyData(obj, data) {
67
- for (var att in data) {
68
- if (att !== "id" || obj.id === undefined || att === "id" && obj.id === data.id) {
69
- obj[att] = data[att];
70
- } else {
71
- return Promise.reject("The loading function cannot change the id of the object.");
72
- }
73
- }
74
-
75
- return Promise.resolve(obj);
76
- };
77
-
78
66
  var _type = /*#__PURE__*/new WeakMap();
79
67
 
80
68
  var _store = /*#__PURE__*/new WeakMap();
@@ -294,6 +282,14 @@ var Model = /*#__PURE__*/_createClass(function Model(name) {
294
282
  return objects.length ? _classPrivateFieldGet(_this, _bulkOperation).call(_this, objects, _classPrivateFieldGet(_this, _deleteObjects)) : Promise.resolve();
295
283
  });
296
284
 
285
+ _defineProperty(this, "factory", function (params) {
286
+ if (!_this.options.lazyLoad) {
287
+ return Promise.reject("Factory can be used only on a model declared with lazyLoad: true");
288
+ } else {
289
+ return (0, _modelHooksUtils.executeHook)("retrieve", _classPrivateFieldGet(_this, _retrieveHook), params, _classPrivateFieldGet(_this, _axios));
290
+ }
291
+ });
292
+
297
293
  _classPrivateFieldInitSpec(this, _addRelationByField, {
298
294
  writable: true,
299
295
  value: function value(model, localField) {
@@ -387,7 +383,8 @@ var Model = /*#__PURE__*/_createClass(function Model(name) {
387
383
 
388
384
  this.options = _objectSpread(_objectSpread({}, options), {}, {
389
385
  deep: (_options$deep = options.deep) !== null && _options$deep !== void 0 ? _options$deep : true,
390
- parseMoment: (_options$parseMoment = options.parseMoment) !== null && _options$parseMoment !== void 0 ? _options$parseMoment : false
386
+ parseMoment: (_options$parseMoment = options.parseMoment) !== null && _options$parseMoment !== void 0 ? _options$parseMoment : false,
387
+ lazyLoad: options.lazyLoad
391
388
  });
392
389
 
393
390
  _classPrivateFieldSet(this, _store, null);
package/dist/PubSub.js CHANGED
@@ -19,6 +19,29 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
19
19
 
20
20
  function _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; }
21
21
 
22
+ /*
23
+ * MIT License
24
+ *
25
+ * Copyright (c) 2022 Massimo Candela <https://massimocandela.com>
26
+ *
27
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
28
+ * of this software and associated documentation files (the "Software"), to deal
29
+ * in the Software without restriction, including without limitation the rights
30
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31
+ * copies of the Software, and to permit persons to whom the Software is
32
+ * furnished to do so, subject to the following conditions:
33
+ *
34
+ * The above copyright notice and this permission notice shall be included in all
35
+ * copies or substantial portions of the Software.
36
+ *
37
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
43
+ * SOFTWARE.
44
+ */
22
45
  var PubSub = /*#__PURE__*/_createClass(function PubSub() {
23
46
  var _this = this;
24
47
 
package/dist/Store.js CHANGED
@@ -120,16 +120,19 @@ var Store = /*#__PURE__*/function () {
120
120
  var type = model.getType();
121
121
 
122
122
  if (!_this2.models[type]) {
123
+ var _model$options$lazyLo;
124
+
123
125
  _this2.models[type] = {
124
126
  model: model,
125
127
  storedObjects: {}
126
128
  };
127
129
  model.setStore(_this2);
130
+ var lazyLoad = (_model$options$lazyLo = model.options.lazyLoad) !== null && _model$options$lazyLo !== void 0 ? _model$options$lazyLo : _this2.options.lazyLoad;
128
131
 
129
- if (!_this2.options.lazyLoad) {
130
- resolve(_classPrivateMethodGet(_this2, _loadObjects, _loadObjects2).call(_this2, type));
131
- } else {
132
+ if (lazyLoad) {
132
133
  resolve();
134
+ } else {
135
+ resolve(_classPrivateMethodGet(_this2, _loadObjects, _loadObjects2).call(_this2, type));
133
136
  }
134
137
  } else {
135
138
  var error = "The model already exists";
@@ -296,6 +299,38 @@ var Store = /*#__PURE__*/function () {
296
299
  };
297
300
  });
298
301
  }
302
+ }, {
303
+ key: "factory",
304
+ value: function factory(type, params) {
305
+ var _this8 = this;
306
+
307
+ var item = this.models[type];
308
+ this.pubSub.publish("loading", {
309
+ status: "start",
310
+ model: type
311
+ });
312
+ return item.promise = item.model.factory(params).then(function (items) {
313
+ var _iterator3 = _createForOfIteratorHelper(items),
314
+ _step3;
315
+
316
+ try {
317
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
318
+ var _item = _step3.value;
319
+
320
+ _classPrivateMethodGet(_this8, _insertObject, _insertObject2).call(_this8, type, _item, false);
321
+ }
322
+ } catch (err) {
323
+ _iterator3.e(err);
324
+ } finally {
325
+ _iterator3.f();
326
+ }
327
+
328
+ _this8.pubSub.publish("loading", {
329
+ status: "end",
330
+ model: type
331
+ });
332
+ });
333
+ }
299
334
  }]);
300
335
 
301
336
  return Store;
@@ -310,25 +345,25 @@ function _error2(error) {
310
345
  }
311
346
 
312
347
  function _deleteByFilter2(type, filterFunction) {
313
- var _this8 = this;
348
+ var _this9 = this;
314
349
 
315
350
  return _classPrivateMethodGet(this, _getPromise, _getPromise2).call(this, type).then(function () {
316
- var deleted = Object.values(_this8.models[type].storedObjects).filter(function (i) {
351
+ var deleted = Object.values(_this9.models[type].storedObjects).filter(function (i) {
317
352
  return filterFunction(i.object);
318
353
  });
319
354
 
320
- var _iterator3 = _createForOfIteratorHelper(deleted),
321
- _step3;
355
+ var _iterator4 = _createForOfIteratorHelper(deleted),
356
+ _step4;
322
357
 
323
358
  try {
324
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
325
- var object = _step3.value;
359
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
360
+ var object = _step4.value;
326
361
  object.status = "deleted";
327
362
  }
328
363
  } catch (err) {
329
- _iterator3.e(err);
364
+ _iterator4.e(err);
330
365
  } finally {
331
- _iterator3.f();
366
+ _iterator4.f();
332
367
  }
333
368
 
334
369
  return deleted.map(function (i) {
@@ -338,7 +373,7 @@ function _deleteByFilter2(type, filterFunction) {
338
373
  }
339
374
 
340
375
  function _getPromise2(type) {
341
- var _this9 = this;
376
+ var _this10 = this;
342
377
 
343
378
  if (!this.models[type]) {
344
379
  return Promise.reject("The model doesn't exist");
@@ -346,7 +381,7 @@ function _getPromise2(type) {
346
381
  return Promise.reject("The model is not loaded");
347
382
  } else if (!this.models[type].promise && this.options.lazyLoad) {
348
383
  return _classPrivateMethodGet(this, _loadObjects, _loadObjects2).call(this, type).then(function () {
349
- return _this9.models[type].promise;
384
+ return _this10.models[type].promise;
350
385
  });
351
386
  } else {
352
387
  return this.models[type].promise;
@@ -373,7 +408,7 @@ function _insertObject2(type, item) {
373
408
  }
374
409
 
375
410
  function _loadObjects2(type) {
376
- var _this10 = this;
411
+ var _this11 = this;
377
412
 
378
413
  var item = this.models[type];
379
414
  this.pubSub.publish("loading", {
@@ -381,22 +416,22 @@ function _loadObjects2(type) {
381
416
  model: type
382
417
  });
383
418
  return item.promise = item.model.retrieveAll().then(function (items) {
384
- var _iterator4 = _createForOfIteratorHelper(items),
385
- _step4;
419
+ var _iterator5 = _createForOfIteratorHelper(items),
420
+ _step5;
386
421
 
387
422
  try {
388
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
389
- var _item = _step4.value;
423
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
424
+ var _item2 = _step5.value;
390
425
 
391
- _classPrivateMethodGet(_this10, _insertObject, _insertObject2).call(_this10, type, _item, false);
426
+ _classPrivateMethodGet(_this11, _insertObject, _insertObject2).call(_this11, type, _item2, false);
392
427
  }
393
428
  } catch (err) {
394
- _iterator4.e(err);
429
+ _iterator5.e(err);
395
430
  } finally {
396
- _iterator4.f();
431
+ _iterator5.f();
397
432
  }
398
433
 
399
- _this10.pubSub.publish("loading", {
434
+ _this11.pubSub.publish("loading", {
400
435
  status: "end",
401
436
  model: type
402
437
  });
@@ -114,7 +114,8 @@ var executeHook = function executeHook(type, hook, data, axios) {
114
114
  return getDataStringHook(hook, data, axios);
115
115
 
116
116
  case "function":
117
- return hook(data);
117
+ var res = hook(data);
118
+ return typeof res === "string" ? getDataStringHook(res, data, axios) : res;
118
119
 
119
120
  default:
120
121
  return Promise.reject("The ".concat(type, " hook must be a URL or a function returning a promise"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataflux",
3
- "version": "1.6.2",
3
+ "version": "1.7.0",
4
4
  "description": "DataFlux, automatically interfaces with your REST APIs to create a 2-way-synced local data store. Transparently manages data propagation in the React state.",
5
5
  "main": "dist/index.js",
6
6
  "bin": "dist/index.js",
package/CNAME DELETED
@@ -1 +0,0 @@
1
- dataflux.js.org
package/_config.yml DELETED
@@ -1 +0,0 @@
1
- theme: jekyll-theme-cayman