@onehat/data 1.15.7 → 1.16.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.15.7",
3
+ "version": "1.16.1",
4
4
  "description": "JS data modeling package with adapters for many storage mediums.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -63,7 +63,7 @@
63
63
  "cypress": "5.2.0",
64
64
  "ink-docstrap": "^1.3.2",
65
65
  "jsdoc": "^4.0.0",
66
- "webpack": "^5.75.0",
66
+ "webpack": "^5.76.0",
67
67
  "joi": "^17.7.0",
68
68
  "yup": "^0.32.11"
69
69
 
package/src/Entity.js CHANGED
@@ -32,9 +32,10 @@ class Entity extends EventEmitter {
32
32
  * @param {Schema} schema - Schema object
33
33
  * @param {object} rawData - Raw data object. Keys are Property names, Values are Property values.
34
34
  * @param {Repository} repository
35
- * @param {boolean} - Has rawData already been mapped according to schema?
35
+ * @param {boolean} originalIsMapped - Has data already been mapped according to schema?
36
+ * @param {boolean} isDelayedSave - Should the repository skip autosave when immediately adding the record?
36
37
  */
37
- constructor(schema, rawData = {}, repository = null, originalIsMapped = false, isDelayedSave = false) {
38
+ constructor(schema, rawData = {}, repository = null, originalIsMapped = false, isDelayedSave = false, isRemotePhantomMode = false) {
38
39
  super(...arguments);
39
40
 
40
41
  if (!schema) {
@@ -133,11 +134,26 @@ class Entity extends EventEmitter {
133
134
  this.isDestroyed = false;
134
135
 
135
136
  /**
136
- * @member {boolean} isFrozen - Prevent the entity from being saved, so an editor can change it before it gets saved to remote storage.
137
+ * @member {boolean} isFrozen - Prevent the entity from being autoSaved on add, so an editor can change it before it gets saved to remote storage.
137
138
  * @public
138
139
  */
139
140
  this.isDelayedSave = isDelayedSave;
140
141
 
142
+ /**
143
+ * @member {boolean} isRemotePhantomMode - Whether this Entity uses the "alternate" CRUD mode, with tempIds from server (see OneBuild repository)
144
+ * On a Repository, this mode overrides repository.isAutoSave, entity.isPersisted, && entity.isDelayedSave.
145
+ * On an Entity, this mode affects the isPhantom getter.
146
+ * @readonly
147
+ */
148
+ this.isRemotePhantomMode = isRemotePhantomMode;
149
+
150
+ /**
151
+ * @member {boolean} isRemotePhantom - Whether this entity is actually phantom on server; used only when this.isRemotePhantomMode.
152
+ * If this.isRemotePhantomMode, isRemotePhantom defaults to true for all new Entities.
153
+ * @public
154
+ */
155
+ this.isRemotePhantom = this.isRemotePhantomMode;
156
+
141
157
  /**
142
158
  * @member {boolean} lastModified - Last time this entity was modified
143
159
  * @public
@@ -945,9 +961,15 @@ class Entity extends EventEmitter {
945
961
  if (this.isDestroyed) {
946
962
  throw Error('this.isPhantom is no longer valid. Entity has been destroyed.');
947
963
  }
948
- const idProperty = this.getIdProperty(),
949
- id = idProperty.getSubmitValue();
950
964
 
965
+ if (this.isRemotePhantomMode) {
966
+ return this.isRemotePhantom;
967
+ }
968
+
969
+ const
970
+ idProperty = this.getIdProperty(),
971
+ id = idProperty.getSubmitValue();
972
+
951
973
  // No ID
952
974
  if (_.isNil(id)) {
953
975
  return true;
@@ -533,10 +533,15 @@ class AjaxRepository extends Repository {
533
533
 
534
534
  this._operations.add = true;
535
535
 
536
- const method = this.methods.add,
536
+ const
537
+ method = this.methods.add,
537
538
  url = this.api.add,
538
539
  data = entity.getSubmitValues();
539
540
 
541
+ if (entity.isRemotePhantomMode) {
542
+ data.isRemotePhantom = true;
543
+ }
544
+
540
545
  return this._send(method, url, data)
541
546
  .then(result => {
542
547
  if (this.debugMode) {
@@ -555,6 +560,9 @@ class AjaxRepository extends Repository {
555
560
  }
556
561
 
557
562
  entity.loadOriginalData(root[0]);
563
+ if (entity.isRemotePhantomMode) {
564
+ entity.isRemotePhantom = true;
565
+ }
558
566
  });
559
567
  }
560
568
 
@@ -573,10 +581,17 @@ class AjaxRepository extends Repository {
573
581
 
574
582
  this._operations.add = true;
575
583
 
576
- const method = this.methods.add,
584
+ const
585
+ method = this.methods.add,
577
586
  url = this.api.batchAdd,
578
587
  data = {
579
- entities: _.map(entities, entity => entity.submitValues),
588
+ entities: _.map(entities, entity => {
589
+ const values = entity.submitValues;
590
+ if (entity.isRemotePhantomMode) {
591
+ values.isRemotePhantom = true;
592
+ }
593
+ return values;
594
+ }),
580
595
  };
581
596
 
582
597
  return this._send(method, url, data)
@@ -600,6 +615,9 @@ class AjaxRepository extends Repository {
600
615
  // TODO: Check this
601
616
  _.each(entities, (entity, ix) => {
602
617
  entity.loadOriginalData(root[ix]);
618
+ if (entity.isRemotePhantomMode) {
619
+ entity.isRemotePhantom = true;
620
+ }
603
621
  });
604
622
  });
605
623
  }
@@ -617,10 +635,15 @@ class AjaxRepository extends Repository {
617
635
 
618
636
  this._operations.edit = true;
619
637
 
620
- const method = this.methods.edit,
638
+ const
639
+ method = this.methods.edit,
621
640
  url = this.api.edit,
622
641
  data = entity.getSubmitValues();
623
642
 
643
+ if (entity.isRemotePhantomMode) {
644
+ data.isRemotePhantom = false;
645
+ }
646
+
624
647
  return this._send(method, url, data)
625
648
  .then(result => {
626
649
  if (this.debugMode) {
@@ -639,6 +662,9 @@ class AjaxRepository extends Repository {
639
662
  }
640
663
 
641
664
  entity.loadOriginalData(root[0]);
665
+ if (entity.isRemotePhantomMode && entity.isRemotePhantom) {
666
+ entity.isRemotePhantom = false;
667
+ }
642
668
  });
643
669
  }
644
670
 
@@ -657,10 +683,17 @@ class AjaxRepository extends Repository {
657
683
 
658
684
  this._operations.edit = true;
659
685
 
660
- const method = this.methods.edit,
686
+ const
687
+ method = this.methods.edit,
661
688
  url = this.api.batchEdit,
662
689
  data = {
663
- entities: _.map(entities, entity => entity.submitValues),
690
+ entities: _.map(entities, entity => {
691
+ const values = entity.submitValues;
692
+ if (entity.isRemotePhantomMode) {
693
+ values.isRemotePhantom = false;
694
+ }
695
+ return values;
696
+ }),
664
697
  };
665
698
 
666
699
  return this._send(method, url, data)
@@ -684,6 +717,9 @@ class AjaxRepository extends Repository {
684
717
  // TODO: Check this
685
718
  _.each(entities, (entity, ix) => {
686
719
  entity.loadOriginalData(root[ix]);
720
+ if (entity.isRemotePhantomMode && entity.isRemotePhantom) {
721
+ entity.isRemotePhantom = false;
722
+ }
687
723
  });
688
724
  });
689
725
  }
@@ -701,7 +737,8 @@ class AjaxRepository extends Repository {
701
737
 
702
738
  this._operations.delete = true;
703
739
 
704
- const method = this.methods.delete,
740
+ const
741
+ method = this.methods.delete,
705
742
  url = this.api.delete,
706
743
  data = { id: entity.id, };
707
744
 
@@ -744,7 +781,8 @@ class AjaxRepository extends Repository {
744
781
 
745
782
  this._operations.delete = true;
746
783
 
747
- const method = this.methods.delete,
784
+ const
785
+ method = this.methods.delete,
748
786
  url = this.api.batchDelete,
749
787
  ids = _.map(entities, entity => entity.id),
750
788
  data = { ids, };
@@ -86,6 +86,17 @@ export default class Repository extends EventEmitter {
86
86
  */
87
87
  isRemoteSort: false,
88
88
 
89
+ /**
90
+ * @member {boolean} isRemotePhantomMode - Whether this Repository uses the "alternate" CRUD mode.
91
+ * In this CRUD mode, records are *immediately* saved to server when added to Repository,
92
+ * but still marked as "phantom" until the their first "edit" operation takes place.
93
+ *
94
+ * This mode overrides repository.isAutoSave, entity.isPersisted, && entity.isDelayedSave.
95
+ *
96
+ * @readonly
97
+ */
98
+ isRemotePhantomMode: false,
99
+
89
100
  /**
90
101
  * @member {boolean} isPaginated - Whether this Repository is paginated
91
102
  */
@@ -297,7 +308,7 @@ export default class Repository extends EventEmitter {
297
308
  const methodDefinitions = this.schema.repository.methods || this.originalConfig.methods; // The latter is mainly for lfr repositories
298
309
  if (!_.isEmpty(methodDefinitions)) {
299
310
  _.each(methodDefinitions, (method, name) => {
300
- this[name] = method; // NOTE: Methods must be defined in schema as "function() {}", not as "() => {}" so "this" will be assigned correctly
311
+ this[name] = method; // NOTE: Methods must be defined in schema as "function() {}", not as "() => {}" so scope of "this" will be correct
301
312
  });
302
313
  }
303
314
  }
@@ -977,19 +988,19 @@ export default class Repository extends EventEmitter {
977
988
  let entity = data;
978
989
  if (!(data instanceof Entity)) {
979
990
  // Create the new entity
980
- entity = Repository._createEntity(this.schema, data, this, isPersisted, originalIsMapped, isDelayedSave);
991
+ entity = Repository._createEntity(this.schema, data, this, isPersisted, originalIsMapped, isDelayedSave, this.isRemotePhantomMode);
981
992
  }
982
993
  this._relayEntityEvents(entity);
983
994
  this.entities.push(entity);
984
995
 
985
996
  // Create id if needed
986
- if (entity.isPhantom) { // i.e. idProperty has no value
997
+ if (!this.isRemotePhantomMode && entity.isPhantom) {
987
998
  entity.createTempId();
988
999
  }
989
1000
 
990
1001
  this.emit('add', entity);
991
1002
 
992
- if (this.isAutoSave && !entity.isPersisted && !entity.isDelayedSave) {
1003
+ if (this.isRemotePhantomMode || (this.isAutoSave && !entity.isPersisted && !entity.isDelayedSave)) {
993
1004
  await this.save(entity);
994
1005
  }
995
1006
 
@@ -998,9 +1009,11 @@ export default class Repository extends EventEmitter {
998
1009
 
999
1010
  /**
1000
1011
  * Creates a new static Entity that does NOT persist in storage medium.
1012
+ * Used when we want to work with an entity, but don't want that entity to appear in a repository.
1001
1013
  * @param {object} data - Either raw data object or Entity. If raw data, keys are Property names, Values are Property values.
1002
1014
  * @param {boolean} isPersisted - Whether the new entity should be marked as already being persisted in storage medium.
1003
1015
  * @param {boolean} originalIsMapped - Has data already been mapped according to schema?
1016
+ * @param {boolean} isDelayedSave - Should the repository skip autosave when immediately adding the record?
1004
1017
  * @return {object} entity - new Entity object
1005
1018
  */
1006
1019
  createStandaloneEntity = async (data, isPersisted = false, originalIsMapped = false, isDelayedSave = false) => {
@@ -1008,8 +1021,12 @@ export default class Repository extends EventEmitter {
1008
1021
  this.throwError('this.createStandaloneEntity is no longer valid. Repository has been destroyed.');
1009
1022
  return;
1010
1023
  }
1024
+ if (this.isRemotePhantomMode) {
1025
+ this.throwError('This repository uses isRemotePhantomMode, and therefore cannot create standalone entities.');
1026
+ return;
1027
+ }
1011
1028
 
1012
- const entity = Repository._createEntity(this.schema, data, this, isPersisted, originalIsMapped, isDelayedSave);
1029
+ const entity = Repository._createEntity(this.schema, data, this, isPersisted, originalIsMapped, isDelayedSave, this.isRemotePhantomMode);
1013
1030
 
1014
1031
  if (entity.isPhantom) {
1015
1032
  entity.createTempId();
@@ -1029,6 +1046,7 @@ export default class Repository extends EventEmitter {
1029
1046
  * Convenience function to create multiple new Entities in storage medium.
1030
1047
  * @param {array} data - Array of data objects or Entities.
1031
1048
  * @param {boolean} isPersisted - Whether the new entities should be marked as already being persisted in storage medium.
1049
+ * @param {boolean} originalIsMapped - Has data already been mapped according to schema?
1032
1050
  * @return {array} entities - new Entity objects
1033
1051
  */
1034
1052
  addMultiple = async (allData, isPersisted = false, originalIsMapped = false) => {
@@ -1052,11 +1070,13 @@ export default class Repository extends EventEmitter {
1052
1070
  * @param {object} rawData - Raw data object. Keys are Property names, Values are Property values.
1053
1071
  * @param {boolean} repository - Optional repository to connect the entity to.
1054
1072
  * @param {boolean} isPersisted - Whether the new entity should be marked as already being persisted in storage medium.
1073
+ * @param {boolean} originalIsMapped - Has data already been mapped according to schema?
1074
+ * @param {boolean} isDelayedSave - Should the repository skip autosave when immediately adding the record?
1055
1075
  * @return {object} entity - new Entity object
1056
1076
  * @private
1057
1077
  */
1058
- static _createEntity = (schema, rawData, repository = null, isPersisted = false, originalIsMapped = false, isDelayedSave = false) => {
1059
- const entity = new Entity(schema, rawData, repository, originalIsMapped, isDelayedSave);
1078
+ static _createEntity = (schema, rawData, repository = null, isPersisted = false, originalIsMapped = false, isDelayedSave = false, isRemotePhantomMode = false) => {
1079
+ const entity = new Entity(schema, rawData, repository, originalIsMapped, isDelayedSave, isRemotePhantomMode);
1060
1080
  entity.initialize();
1061
1081
  entity.isPersisted = isPersisted;
1062
1082
  return entity;