dataflux 1.7.5 → 1.9.1

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/README.md CHANGED
@@ -258,11 +258,12 @@ function MyComponent() {
258
258
  The store can be configured with the following options:
259
259
 
260
260
 
261
- | Option | Description | Default |
262
- |-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
263
- | autoSave | It can be `true`, `false`, or an amount of milliseconds (integer). If `false`, you will have to perform `store.save()` manually. If `true`, the store will automatically perform `save()` when objects change. If an amount of milliseconds is provided, the objects are saved periodically AND when a change is detected. See [Editing objects](#editing-objects) for more information. | true |
264
- | saveDelay | An amount of milliseconds used to defer synching operations with the server. It triggers `store.save()` milliseconds after the last change on the store's objects is detedect. This allows to bundle together multiple changes operated by an interacting user. See [Editing objects](#editing-objects) for more information. | 1000 |
265
- | lazyLoad | A boolean. If set to `false`, the store is pre-populated with all the models' objects. If set to `true`, models' objects are loaded only on first usage (e.g., 'find', 'subscribe', 'getRelation'). LazyLoad operates per model, only the objects of the used models are loaded. | false |
261
+ | Option | Description | Default |
262
+ |-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
263
+ | autoSave | It can be `true`, `false`, or an amount of milliseconds (integer). If `false`, you will have to perform `store.save()` manually. If `true`, the store will automatically perform `store.save()` when objects change. If an amount of milliseconds is provided, the objects are saved periodically AND when a change is detected. See [Editing objects](#editing-objects) for more information. | true |
264
+ | saveDelay | An amount of milliseconds used to defer synching operations with the server. It triggers `store.save()` milliseconds after the last change on the store's objects is detedect. This allows to bundle together multiple changes operated by an interacting user. See [Editing objects](#editing-objects) for more information. | 1000 |
265
+ | lazyLoad | A boolean. If set to `false`, the store is pre-populated with all the models' objects. If set to `true`, models' objects are loaded only on first usage (e.g., 'find', 'subscribe', 'getRelation'). LazyLoad operates per model, only the objects of the used models are loaded. | false |
266
+ | autoRefresh | It can be `true`, `false`, or an amount of milliseconds (integer). If `false`, you will have to perform `store.refresh()` manually. If `true`, the store will automatically perform `store.refresh()` every 2 minutes. If an amount of milliseconds is provided, the `store.refresh()` is performed periodically. See [store methods](#store-methods) for more information. | false |
266
267
 
267
268
 
268
269
 
@@ -296,6 +297,7 @@ All the possible options for a model creation are (they are all optional):
296
297
  | 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. |
297
298
  | deep | A boolean defining if nested objects should be enriched with the object methods. | true |
298
299
  | 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 |
300
+ | validate | A dictionary containing functions to validate the objects of this model. See [objects validation](#objects-validation) | no validation |
299
301
 
300
302
  ### Operations
301
303
  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.
@@ -308,10 +310,23 @@ An operation object is an object like follows:
308
310
  {
309
311
  "method": "get",
310
312
  "url": "https://api.example.com",
311
- "headers": {"Authorization": "bearer XXXX"}
313
+ "headers": {
314
+ "Authorization": "bearer XXXX"
315
+ },
316
+ "batch": false
312
317
  }
313
318
  ```
314
319
 
320
+ Possible parameters are:
321
+
322
+ | Parameter | Description | Default |
323
+ |-----------|--------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
324
+ | method | HTTP method, accepted are get,post,put, and delete | "get" for retrieve, "post" for insert, "put" for update, and "delete" for delete |
325
+ | url | The url of the api | |
326
+ | headers | Headers for the HTTP request ([list](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)). |
327
+ | batch | A boolean declaring if the API is able to receive an array of objects or an object at a time. This is not applicable for `retrieve`. | false |
328
+
329
+
315
330
  Usage example:
316
331
 
317
332
  ```js
@@ -540,41 +555,73 @@ The store has the following method.
540
555
  | find(type, filterFunction) | The promise-oriented method to access objects given a type and a filter function. If the filter function is missing, all the objects are returned. See [example 1](#example-1). |
541
556
  | delete(objects) | It deletes an array of objects. See [example 1](#example-3). |
542
557
  | delete(type, filterFunction) | It deleted objects given an array and a filter function. See [example 1](#example-3). |
543
- | insert(type, object) | It creates a new object of a given type and inserts it in the store. |
558
+ | insert(type, object) | It creates a new object of a given type and inserts it in the store. The object inserted MUST be ready to be persisted, read `mock()` below. |
559
+ | mock(type, object) | It creates a mock object of a given type and inserts it in the store. The mock object behaves exactly like a real object, except that it is not persisted (sent to the API) as long as you don't call `object.insert()`. This is useful when you want to create an object but you need to be able to change some properties before to send it to the API. Read [insert vs. mock](#insert-vs-mock). |
544
560
  | subscribe(type, callback, filterFunction) | The callback-oriented method to access objects given a type and a filter function. It returns the key of the subscription, needed to unsubscribe. If the filter function is missing, all the objects are returned. **DataFlux remembers your query and calls the callback every time any change is affecting the result of your query.** See [example 5](#example-5---observability). |
545
561
  | multipleSubscribe(subscriptions, callback) | A method to subscribe to multiple models. The first parameter is an array of models' names and filterFunctions, the second parameter is the callback to be called when the cumulative dataset is ready. E.g., `multipleSubscribe([["book", filterFunction1], ["author", filterFunction2]], callback)`. It returns the key of the subscription. See [example 5](#example-5---observability). |
546
562
  | unsubscribe(key) | Method to terminate a subscription given a subscription key. See [example 5](#example-5---observability). |
547
563
  | findOne(type, stateAttribute, context, filterFunction) | This method automatically injects and updates the React state with the requested data. If multiple objects satisfy the query, only the first is selected. The `stateAttribute` is the name of the attribute that will be added/updated in the state, the `context` is the React.Component. It automatically unsubscribe when the React.Component will unmount. See [example 6](#example-6---observability--react). |
548
564
  | findAll(type, stateAttribute, context, filterFunction) | This method automatically injects and updates the React state with the requested data. The `stateAttribute` is the name of the attribute that will be added/updated in the state, the `context` is the React.Component. It automatically unsubscribe when the React.Component will unmount. If the filter function is missing, all the objects are returned. See [example 6](#example-6---observability--react). |
549
565
  | preload(type) | This method allows to preLoad all objects of a given model. If you initialize the store with `lazyLoad:true`, the objects of a model are retrieved from the API at the first query performed on that model (e.g., at the first `.find()`). However, sometimes you may want to speed up the first query by pre loading the objects of a specific model while keeping `lazyLoad:true` on the store; in such a case you can use `store.preload(type)`. |
566
+ | save() | Persist the changes. Edited local objects will be sent to the REST APIs (insert/update/delete). See also [editing objects](#editing-objects). |
567
+ | save() | Persist the changes (`local -> remote`). Edited local objects will be sent to the REST APIs (insert/update/delete). See also [editing objects](#editing-objects). |
568
+ | refresh() | This method syncs all the objects in the store with the remote version offered by the REST APIs (`remote -> local`). Remote changes are applied locally, including adding/removing objects. Objects edited locally but not yet persisted are preserved locally (tip: you can also create a store with the `autoRefresh` option). |
569
+ ### Insert vs. Mock
570
+
571
+ If you do:
572
+ ```js
573
+ store.insert("book", {title: "The little prince"});
574
+ ```
575
+ when the store will try to save, the object `{title: "The little prince"}` will be sent to the API in a post request. However, the API may require an attribute "price" for each book object, hence the post request will fail.
576
+ Of course, you can add the attribute directly in the `.insert()` call; however, the value of this attribute may not be known at that time. For example, the user may need to input the price in a text field.
577
+
578
+ You may think to delay the `.insert()` up to when all the required attributes are available; however, **this is a bad idea**, because as long as the object is not in the store, you will not benefit from the observability provided by DataFlux. _E.g., your React state will not update automatically and you will need to handle the changes triggered by the input fields yourself_ ([reinventing the wheel](https://en.wikipedia.org/wiki/Reinventing_the_wheel)).
579
+
580
+ Solution, use `.mock()` to create a mock object:
581
+
582
+ ```js
583
+ store.mock("book", {title: "The little prince"});
584
+ ```
585
+
586
+ The mock object behaves exactly like a normal object: you can retrieve it with `.find`/`.findAll`/`.findOne`/`.subscribe`.
587
+
588
+ However, the mock object is not sent to the API as long as you don't call `.insert()` on the object itself. When you call `.insert()`, the mock object is promoted to real object.
589
+
590
+ > Warning: to promote a mock object, you need to call `object.insert()` on the object itself (you must first retrieve it) and NOT `store.insert()`.
591
+
550
592
 
551
593
  ## Store events
552
594
  The store emits the following events:
553
595
 
554
- | Name | Description |
555
- |---------|---------------------------------------------------------------------------------------------------------------------------------------|
556
- | error | To listen the errors emitted by the store. |
557
- | save | Possible emitted values are `start` and `end`. They are emitted when the store starts/finishes to persist the data (API interaction). |
558
- | loading | The event is emitted while a new model is loaded. The value contains something like `{status: "start", model: "book"}` |
596
+ | Name | Description |
597
+ |------------|---------------------------------------------------------------------------------------------------------------------------------------|
598
+ | error | To listen the errors emitted by the store. |
599
+ | save | Possible emitted values are `start` and `end`. They are emitted when the store starts/finishes to persist the data (API interaction). |
600
+ | loading | The event is emitted while a new model is loaded. The value contains something like `{status: "start", model: "book"}` |
601
+ | refreshing | The event is emitted while a model is refreshed. The value contains something like `{status: "start", model: "book"}` |
559
602
 
560
603
  ## Objects methods
561
604
  Each object created is enriched with the following methods.
562
605
 
563
606
 
564
- | Method | Description |
565
- |------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
566
- | getId() | It returns a unique ID used by the store to identify the object. The ID is unique inside a single model. Be aware, `object.id` and `objet.getId()` may return different values, since store's IDs can be different from the one of the REST API. |
567
- | set(attribute, value, hidden) | A method to set an attribute to the object. It provides some advantages compared to doing `object.attribute = value`, these are discussed in [below](#editing-objects). The third parameter is optional, and when set to true will set the attribute as hidden (see [hiddenFields](#models-creation)). |
568
- | setConstant(attribute, value) | A method to set an unmodifiable hidden attribute on the object. Setting the attribute as a constant will not propagate an update. |
607
+ | Method | Description |
608
+ |------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
609
+ | getId() | It returns a unique ID used by the store to identify the object. The ID is unique inside a single model. Be aware, `object.id` and `objet.getId()` may return different values, since store's IDs can be different from the one of the REST API. |
610
+ | set(attribute, value, hidden) | A method to set an attribute to the object. It provides some advantages compared to doing `object.attribute = value`, these are discussed in [below](#editing-objects). The third parameter is optional, and when set to true will set the attribute as hidden (see [hiddenFields](#models-creation)). |
611
+ | setConstant(attribute, value) | A method to set an unmodifiable hidden attribute on the object. Setting the attribute as a constant will not propagate an update. |
569
612
  | get(attribute, defaultValue) | Method to retrieve the value of an attribute. It does not provide any advantage compared to accessing directly the attribute (e.g., `author.name`); except for hidden fields and constants, which can be retrieved only with the `.get` method. Additionally, you can provide a default value as a second parameter in case the object doesn't have that attribute. |
570
- | getRelation(model, filterFunction) | To get all the objects respecting a specific relation with this object (see [model relations](#model-relations)). |
571
- | save() | Method to save the object. You can do `store.save()` instead. |
572
- | destroy() | Method to delete the object. You can do `store.delete()` instead. |
573
- | toJSON() | It returns a pure JSON representation of the object. |
574
- | toString() | It returns a string representation of the object. |
575
- | getFingerprint() | It returns a hash of the object. The hash changes at every change of the object or of any nested object. Useful to detect object changes. |
576
- | getModel() | It returns the model of this object. Mostly useful to do `object.getModel().getType()` and obtain a string defining the type of the object. |
577
- ### Deeper Objects
613
+ | getRelation(model, filterFunction) | To get all the objects respecting a specific relation with this object (see [model relations](#model-relations)). |
614
+ | save() | Method to save the object. You can do `store.save()` instead. |
615
+ | destroy() | Method to delete the object. You can do `store.delete()` instead. |
616
+ | toJSON() | It returns a pure JSON representation of the object. |
617
+ | toString() | It returns a string representation of the object. |
618
+ | getFingerprint() | It returns a hash of the object. The hash changes at every change of the object or of any nested object. Useful to detect object changes. |
619
+ | getModel() | It returns the model of this object. Mostly useful to do `object.getModel().getType()` and obtain a string defining the type of the object. |
620
+ | getError() | If an operation on an object triggers an error, this error can be retrieved with `getError()`. This allows to observe specific objects' errors, instead of the generic `store.on("error", ...)`. |
621
+ | getError(attributeName) | This method allows you to check if the specificed attribute generated any error according to the validation property specified in the model. See [objects validation](#objects-validation). |
622
+ | setError(error) | Additionally to DataFlux's errors, you can trigger your own errors with this method. Other components observing this objet's error will be notified. |
623
+
624
+ ### Deep Objects
578
625
  When a model is declared with the option `deep: true` (default, see [model creation](#models-creation)), all the sub objects will also offer many of the methods above.
579
626
 
580
627
  Imagine the API returns:
@@ -611,8 +658,75 @@ store.find("book")
611
658
  });
612
659
  ```
613
660
 
661
+ ### Objects validation
662
+
663
+ DataFlux supports automatic validation of the objects. This is important for two reasons:
664
+ * Objects that contain invalid attributes' values are not sent back to the API;
665
+ * Validation errors can be used to automatically suggest errors in the UI.
666
+
667
+ To specify the validation of the objects for a specific model, you need to add a `validate` dictionary during model creation.
668
+
669
+ ```js
670
+ const book = new Model("book", {
671
+ retrieve: {
672
+ url: "https://rest.example.net/api/v1/books/"
673
+ },
674
+ validate: {
675
+ isbn: ({isbn}) => {
676
+ if (typeof(isbn) !== "number") {
677
+ throw new Error("The isbn must be a number");
678
+ }
679
+ },
680
+ title: ({title}) => {
681
+ if (!title) {
682
+ throw new Error("The title is mandatory");
683
+ }
684
+ }
685
+ }
686
+ });
687
+ ```
688
+
689
+ Each key of the `validate` dictionary is an attribute of the object (a field name), each value is a function receiving in input the object and throwing an error in case the field is not valid.
690
+
691
+ > Be aware: A validation function cannot return a boolean, you have to throw an error.
692
+
693
+ To validate a specific attribute of an object, you can do `object.getError(attributeName)` (e.g., `book.getError(isbn)`). In case of error, a string describing the error is returned, `false` otherwise.
694
+
695
+ Example of usage in a React component.
696
+
697
+ ```js
698
+ class MyComponent extends React.Component {
699
+ constructor(props) {
700
+ super(props);
701
+ }
702
+
703
+ render(){
704
+ const {book} = this.state;
705
+
706
+ // A textfield to edit the title of a book
707
+ return <TextField
708
+ value={book.title}
709
+ onChange={store.handleChange(book, "title")}
710
+ error={object.getError("title")}
711
+ // E.g., in material UI the test field will be red in case of errors
712
+ />;
713
+ }
714
+ }
715
+ ```
716
+
717
+ > Be aware: if you do `object.getError()` without specifying any attribute, you will receive object errors not associated with any field, such as API errors. This is similar to `store.on("error")`
718
+
614
719
 
615
720
  ## Editing objects
721
+
722
+ The preferred method to edit objects is using the `.set()` method that each object has, instead of editing directly the attributes. However, there are a few notions to keep in mind.
723
+
724
+ #### Client-side object validaion will not work when editing attributes directly.
725
+
726
+ You will be able to validate objects only if you use `.set()`. If you edit directly the attribute (e.g., `book.title = "test"`) errors will be discovered only by the API.
727
+
728
+ #### Auto save may not work when editing attributes directly.
729
+
616
730
  The option `autoSave` can be `true`, `false`, or a number (milliseconds).
617
731
 
618
732
  * When `autoSave` is set to `false`, the following operations are equivalent:
package/dist/BasicObj.js CHANGED
@@ -24,20 +24,21 @@ function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedec
24
24
 
25
25
  function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
26
26
 
27
- function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
27
+ function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
28
28
 
29
- 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; } }
29
+ function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
30
30
 
31
- function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
31
+ function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
32
32
 
33
33
  function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
34
34
 
35
- function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
35
+ 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; } }
36
36
 
37
37
  function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
38
38
 
39
39
  var dateRegex = new RegExp("^[0-9][0-9][0-9][0-9]-[0-9].*T[0-9].*Z$");
40
40
  exports.dateRegex = dateRegex;
41
+ var globalError = "_object";
41
42
 
42
43
  function setValues(values, model, SubObj, parent, context) {
43
44
  Object.keys(values).forEach(function (key) {
@@ -64,6 +65,10 @@ var _setHidden = /*#__PURE__*/new WeakMap();
64
65
 
65
66
  var _id = /*#__PURE__*/new WeakMap();
66
67
 
68
+ var _error = /*#__PURE__*/new WeakMap();
69
+
70
+ var _model = /*#__PURE__*/new WeakMap();
71
+
67
72
  var BasicObj = /*#__PURE__*/_createClass(function BasicObj(values, model) {
68
73
  var _this = this;
69
74
 
@@ -79,10 +84,26 @@ var BasicObj = /*#__PURE__*/_createClass(function BasicObj(values, model) {
79
84
  value: null
80
85
  });
81
86
 
87
+ _classPrivateFieldInitSpec(this, _error, {
88
+ writable: true,
89
+ value: {}
90
+ });
91
+
92
+ _classPrivateFieldInitSpec(this, _model, {
93
+ writable: true,
94
+ value: void 0
95
+ });
96
+
97
+ _defineProperty(this, "setId", function (id) {
98
+ _classPrivateFieldSet(_this, _id, id);
99
+ });
100
+
82
101
  _defineProperty(this, "getId", function () {
83
102
  if (!_classPrivateFieldGet(_this, _id)) {
84
103
  if (_this.id && (typeof _this.id === "string" || typeof _this.id === "number")) {
85
104
  _classPrivateFieldSet(_this, _id, _this.id.toString());
105
+
106
+ delete _this.setId;
86
107
  } else {
87
108
  _classPrivateFieldSet(_this, _id, (0, _uuid.v4)());
88
109
  }
@@ -106,15 +127,39 @@ var BasicObj = /*#__PURE__*/_createClass(function BasicObj(values, model) {
106
127
  }
107
128
 
108
129
  _this[attribute] = value;
130
+
131
+ _classPrivateFieldGet(_this, _model).validateObjectAttribute(_this, attribute);
109
132
  }
110
133
 
111
134
  return _this.update();
112
135
  });
113
136
 
114
- _defineProperty(this, "setConstant", function (attribute, value) {
137
+ _defineProperty(this, "getError", function () {
115
138
  var _classPrivateFieldGet3;
116
139
 
117
- _classPrivateFieldGet(_this, _setHidden)[attribute] = (_classPrivateFieldGet3 = _classPrivateFieldGet(_this, _setHidden)[attribute]) !== null && _classPrivateFieldGet3 !== void 0 ? _classPrivateFieldGet3 : value;
140
+ var attribute = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
141
+ var key = attribute ? attribute : globalError;
142
+ return (_classPrivateFieldGet3 = _classPrivateFieldGet(_this, _error)[key]) !== null && _classPrivateFieldGet3 !== void 0 ? _classPrivateFieldGet3 : false;
143
+ });
144
+
145
+ _defineProperty(this, "setError", function (error) {
146
+ var attribute = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
147
+
148
+ if (error && attribute) {
149
+ _classPrivateFieldGet(_this, _error)[attribute] = error;
150
+ } else if (error && !attribute) {
151
+ _classPrivateFieldGet(_this, _error)[globalError] = error;
152
+ } else if (!error && attribute) {
153
+ delete _classPrivateFieldGet(_this, _error)[attribute];
154
+ } else {
155
+ _classPrivateFieldSet(_this, _error, {});
156
+ }
157
+ });
158
+
159
+ _defineProperty(this, "setConstant", function (attribute, value) {
160
+ var _classPrivateFieldGet4;
161
+
162
+ _classPrivateFieldGet(_this, _setHidden)[attribute] = (_classPrivateFieldGet4 = _classPrivateFieldGet(_this, _setHidden)[attribute]) !== null && _classPrivateFieldGet4 !== void 0 ? _classPrivateFieldGet4 : value;
118
163
  });
119
164
 
120
165
  _defineProperty(this, "toJSON", function () {
@@ -153,6 +198,8 @@ var BasicObj = /*#__PURE__*/_createClass(function BasicObj(values, model) {
153
198
  _defineProperty(this, "update", function () {
154
199
  return Promise.resolve();
155
200
  });
201
+
202
+ _classPrivateFieldSet(this, _model, model);
156
203
  });
157
204
 
158
205
  exports.BasicObj = BasicObj;
package/dist/Model.js CHANGED
@@ -7,8 +7,6 @@ exports["default"] = void 0;
7
7
 
8
8
  var _modelHooksUtils = require("./modelHooksUtils");
9
9
 
10
- var _batchPromises = _interopRequireDefault(require("batch-promises"));
11
-
12
10
  var _axios2 = _interopRequireDefault(require("axios"));
13
11
 
14
12
  var _BasicObj = require("./BasicObj");
@@ -95,22 +93,29 @@ var _addRelationByFilter = /*#__PURE__*/new WeakMap();
95
93
 
96
94
  var _removeHiddenFields = /*#__PURE__*/new WeakMap();
97
95
 
98
- var _bulkOperation = /*#__PURE__*/new WeakMap();
99
-
100
96
  var _toArray = /*#__PURE__*/new WeakMap();
101
97
 
102
98
  var _unWrap = /*#__PURE__*/new WeakMap();
103
99
 
104
100
  var _insertObjects = /*#__PURE__*/new WeakMap();
105
101
 
102
+ var _assignId = /*#__PURE__*/new WeakMap();
103
+
106
104
  var _updateObjects = /*#__PURE__*/new WeakMap();
107
105
 
108
106
  var _deleteObjects = /*#__PURE__*/new WeakMap();
109
107
 
108
+ var _hanldeApiError = /*#__PURE__*/new WeakMap();
109
+
110
+ var _cleanApiError = /*#__PURE__*/new WeakMap();
111
+
112
+ var _removeFromStoreSilentlyAfterFailure = /*#__PURE__*/new WeakMap();
113
+
110
114
  var Model = /*#__PURE__*/_createClass(function Model(name) {
111
115
  var _this = this,
112
116
  _options$deep,
113
- _options$parseMoment;
117
+ _options$parseMoment,
118
+ _options$validate;
114
119
 
115
120
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
116
121
 
@@ -178,6 +183,31 @@ var Model = /*#__PURE__*/_createClass(function Model(name) {
178
183
  value: void 0
179
184
  });
180
185
 
186
+ _defineProperty(this, "validateObjectAttribute", function (object, key) {
187
+ var validate = _this.options.validate;
188
+
189
+ if (validate && validate[key]) {
190
+ try {
191
+ validate[key](object);
192
+ object.setError(false, key);
193
+ } catch (error) {
194
+ object.setError(error.message, key);
195
+ }
196
+ }
197
+ });
198
+
199
+ _defineProperty(this, "isObjectValid", function (object) {
200
+ for (var key in object) {
201
+ if (typeof object[key] !== "function") {
202
+ if (object.getError(key)) {
203
+ return false;
204
+ }
205
+ }
206
+ }
207
+
208
+ return true;
209
+ });
210
+
181
211
  _defineProperty(this, "getStore", function () {
182
212
  return _classPrivateFieldGet(_this, _store);
183
213
  });
@@ -272,15 +302,15 @@ var Model = /*#__PURE__*/_createClass(function Model(name) {
272
302
  });
273
303
 
274
304
  _defineProperty(this, "insertObjects", function (objects) {
275
- return objects.length ? _classPrivateFieldGet(_this, _bulkOperation).call(_this, objects, _classPrivateFieldGet(_this, _insertObjects)) : Promise.resolve();
305
+ return objects.length ? _classPrivateFieldGet(_this, _insertObjects).call(_this, objects) : Promise.resolve();
276
306
  });
277
307
 
278
308
  _defineProperty(this, "updateObjects", function (objects) {
279
- return objects.length ? _classPrivateFieldGet(_this, _bulkOperation).call(_this, objects, _classPrivateFieldGet(_this, _updateObjects)) : Promise.resolve();
309
+ return objects.length ? _classPrivateFieldGet(_this, _updateObjects).call(_this, objects) : Promise.resolve();
280
310
  });
281
311
 
282
312
  _defineProperty(this, "deleteObjects", function (objects) {
283
- return objects.length ? _classPrivateFieldGet(_this, _bulkOperation).call(_this, objects, _classPrivateFieldGet(_this, _deleteObjects)) : Promise.resolve();
313
+ return objects.length ? _classPrivateFieldGet(_this, _deleteObjects).call(_this, objects) : Promise.resolve();
284
314
  });
285
315
 
286
316
  _defineProperty(this, "factory", function (params) {
@@ -339,26 +369,11 @@ var Model = /*#__PURE__*/_createClass(function Model(name) {
339
369
  }
340
370
  });
341
371
 
342
- _classPrivateFieldInitSpec(this, _bulkOperation, {
343
- writable: true,
344
- value: function value(objects, action) {
345
- if (_classPrivateFieldGet(_this, _singleItemQuery)) {
346
- return (0, _batchPromises["default"])(_classPrivateFieldGet(_this, _batchSize), objects.map(function (i) {
347
- return _classPrivateFieldGet(_this, _removeHiddenFields).call(_this, i.toJSON());
348
- }), action);
349
- } else {
350
- return action(objects.map(function (i) {
351
- return _classPrivateFieldGet(_this, _removeHiddenFields).call(_this, i.toJSON());
352
- }));
353
- }
354
- }
355
- });
356
-
357
372
  _classPrivateFieldInitSpec(this, _toArray, {
358
373
  writable: true,
359
374
  value: function value(data) {
360
375
  if (Array.isArray(data)) {
361
- if (data.every(function (str) {
376
+ if (data.length && data.every(function (str) {
362
377
  return ["string", "number"].includes(_typeof(str));
363
378
  })) {
364
379
  return [{
@@ -381,7 +396,11 @@ var Model = /*#__PURE__*/_createClass(function Model(name) {
381
396
 
382
397
  _classPrivateFieldInitSpec(this, _unWrap, {
383
398
  writable: true,
384
- value: function value(data) {
399
+ value: function value(objects) {
400
+ var data = Object.values(objects).map(function (object) {
401
+ return _classPrivateFieldGet(_this, _removeHiddenFields).call(_this, object.toJSON());
402
+ });
403
+
385
404
  if (data.value != null && Object.keys(data).length === 1) {
386
405
  return data.value;
387
406
  } else if (Array.isArray(data) && data.length === 1 && data[0].value != null && Object.keys(data[0]).length === 1) {
@@ -394,22 +413,109 @@ var Model = /*#__PURE__*/_createClass(function Model(name) {
394
413
 
395
414
  _classPrivateFieldInitSpec(this, _insertObjects, {
396
415
  writable: true,
397
- value: function value(data) {
398
- return (0, _modelHooksUtils.executeHook)("insert", _classPrivateFieldGet(_this, _insertHook), _classPrivateFieldGet(_this, _unWrap).call(_this, data), _classPrivateFieldGet(_this, _axios)).then(_classPrivateFieldGet(_this, _toArray));
416
+ value: function value(objects) {
417
+ var operation = "insert";
418
+ return (0, _modelHooksUtils.executeHook)(operation, _classPrivateFieldGet(_this, _insertHook), _classPrivateFieldGet(_this, _unWrap).call(_this, objects), _classPrivateFieldGet(_this, _axios)).then(function (data) {
419
+ if (data) _classPrivateFieldGet(_this, _assignId).call(_this, data, objects);
420
+
421
+ _classPrivateFieldGet(_this, _cleanApiError).call(_this, objects);
422
+
423
+ return data;
424
+ }).then(_classPrivateFieldGet(_this, _toArray))["catch"](function (error) {
425
+ _classPrivateFieldGet(_this, _removeFromStoreSilentlyAfterFailure).call(_this, objects);
426
+
427
+ return _classPrivateFieldGet(_this, _hanldeApiError).call(_this, error, objects, operation);
428
+ });
429
+ }
430
+ });
431
+
432
+ _classPrivateFieldInitSpec(this, _assignId, {
433
+ writable: true,
434
+ value: function value(data, objects) {
435
+ if (data.length === 1) {
436
+ var newId = data[0].id;
437
+ objects[0].setId(newId);
438
+ delete objects[0].setId;
439
+ }
399
440
  }
400
441
  });
401
442
 
402
443
  _classPrivateFieldInitSpec(this, _updateObjects, {
403
444
  writable: true,
404
- value: function value(data) {
405
- return (0, _modelHooksUtils.executeHook)("update", _classPrivateFieldGet(_this, _updateHook), _classPrivateFieldGet(_this, _unWrap).call(_this, data), _classPrivateFieldGet(_this, _axios)).then(_classPrivateFieldGet(_this, _toArray));
445
+ value: function value(objects) {
446
+ var operation = "update";
447
+ return (0, _modelHooksUtils.executeHook)(operation, _classPrivateFieldGet(_this, _updateHook), _classPrivateFieldGet(_this, _unWrap).call(_this, objects), _classPrivateFieldGet(_this, _axios)).then(function (data) {
448
+ _classPrivateFieldGet(_this, _cleanApiError).call(_this, objects);
449
+
450
+ return data;
451
+ }).then(_classPrivateFieldGet(_this, _toArray))["catch"](function (error) {
452
+ return _classPrivateFieldGet(_this, _hanldeApiError).call(_this, error, objects, operation);
453
+ });
406
454
  }
407
455
  });
408
456
 
409
457
  _classPrivateFieldInitSpec(this, _deleteObjects, {
410
458
  writable: true,
411
- value: function value(data) {
412
- return (0, _modelHooksUtils.executeHook)("delete", _classPrivateFieldGet(_this, _deleteHook), _classPrivateFieldGet(_this, _unWrap).call(_this, data), _classPrivateFieldGet(_this, _axios)).then(_classPrivateFieldGet(_this, _toArray));
459
+ value: function value(objects) {
460
+ var operation = "delete";
461
+ return (0, _modelHooksUtils.executeHook)(operation, _classPrivateFieldGet(_this, _deleteHook), _classPrivateFieldGet(_this, _unWrap).call(_this, objects), _classPrivateFieldGet(_this, _axios)).then(function (data) {
462
+ _classPrivateFieldGet(_this, _cleanApiError).call(_this, objects);
463
+
464
+ return data;
465
+ }).then(_classPrivateFieldGet(_this, _toArray))["catch"](function (error) {
466
+ return _classPrivateFieldGet(_this, _hanldeApiError).call(_this, error, objects, operation);
467
+ });
468
+ }
469
+ });
470
+
471
+ _classPrivateFieldInitSpec(this, _hanldeApiError, {
472
+ writable: true,
473
+ value: function value(error, objects, operation) {
474
+ var _error$response$data, _error3, _error3$response, _ref, _error$message, _error4, _error5;
475
+
476
+ error = (_error$response$data = (_error3 = error) === null || _error3 === void 0 ? void 0 : (_error3$response = _error3.response) === null || _error3$response === void 0 ? void 0 : _error3$response.data) !== null && _error$response$data !== void 0 ? _error$response$data : error;
477
+ var targets = objects.map(function (object) {
478
+ return object.getId();
479
+ }); // Set errors
480
+
481
+ var strError = (_ref = (_error$message = (_error4 = error) === null || _error4 === void 0 ? void 0 : _error4.message) !== null && _error$message !== void 0 ? _error$message : (_error5 = error) === null || _error5 === void 0 ? void 0 : _error5.error) !== null && _ref !== void 0 ? _ref : error;
482
+ Object.values(objects).map(function (object) {
483
+ return object.setError(strError);
484
+ });
485
+ return Promise.reject(_objectSpread(_objectSpread({}, error), {}, {
486
+ targets: targets,
487
+ operation: operation
488
+ }));
489
+ }
490
+ });
491
+
492
+ _classPrivateFieldInitSpec(this, _cleanApiError, {
493
+ writable: true,
494
+ value: function value(objects) {
495
+ Object.values(objects).map(function (object) {
496
+ return object.setError(false);
497
+ });
498
+ }
499
+ });
500
+
501
+ _classPrivateFieldInitSpec(this, _removeFromStoreSilentlyAfterFailure, {
502
+ writable: true,
503
+ value: function value(objects) {
504
+ var _iterator2 = _createForOfIteratorHelper(objects.map(function (object) {
505
+ return object.getId();
506
+ })),
507
+ _step2;
508
+
509
+ try {
510
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
511
+ var target = _step2.value;
512
+ delete _this.getStore().models[_this.getType()].storedObjects[target];
513
+ }
514
+ } catch (err) {
515
+ _iterator2.e(err);
516
+ } finally {
517
+ _iterator2.f();
518
+ }
413
519
  }
414
520
  });
415
521
 
@@ -418,7 +524,8 @@ var Model = /*#__PURE__*/_createClass(function Model(name) {
418
524
  this.options = _objectSpread(_objectSpread({}, options), {}, {
419
525
  deep: (_options$deep = options.deep) !== null && _options$deep !== void 0 ? _options$deep : true,
420
526
  parseMoment: (_options$parseMoment = options.parseMoment) !== null && _options$parseMoment !== void 0 ? _options$parseMoment : false,
421
- lazyLoad: options.lazyLoad
527
+ lazyLoad: options.lazyLoad,
528
+ validate: (_options$validate = options.validate) !== null && _options$validate !== void 0 ? _options$validate : {}
422
529
  });
423
530
 
424
531
  _classPrivateFieldSet(this, _store, null);
@@ -439,12 +546,12 @@ var Model = /*#__PURE__*/_createClass(function Model(name) {
439
546
  throw new Error("The load option must be a function");
440
547
  }
441
548
 
442
- var _ref = _typeof(options) === "object" ? (0, _modelHooksUtils.getHooksFromOptions)(options) : (0, _modelHooksUtils.getHooksFromUrl)(options),
443
- _ref2 = _slicedToArray(_ref, 4),
444
- retrieveHook = _ref2[0],
445
- insertHook = _ref2[1],
446
- updateHook = _ref2[2],
447
- deleteHook = _ref2[3];
549
+ var _ref2 = _typeof(options) === "object" ? (0, _modelHooksUtils.getHooksFromOptions)(options) : (0, _modelHooksUtils.getHooksFromUrl)(options),
550
+ _ref3 = _slicedToArray(_ref2, 4),
551
+ retrieveHook = _ref3[0],
552
+ insertHook = _ref3[1],
553
+ updateHook = _ref3[2],
554
+ deleteHook = _ref3[3];
448
555
 
449
556
  _classPrivateFieldSet(this, _retrieveHook, retrieveHook);
450
557