@onehat/data 1.5.2 → 1.6.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.
@@ -44,7 +44,7 @@ describe('Entity', function() {
44
44
  expect(this.entity.id).to.be.eq(1);
45
45
  });
46
46
 
47
- it('createId, isTempId', function() {
47
+ it('createTempId, isTempId', function() {
48
48
  const schema = new Schema({
49
49
  name: 'baz',
50
50
  model: {
@@ -62,33 +62,14 @@ describe('Entity', function() {
62
62
  entity = new Entity(schema, data);
63
63
  entity.initialize();
64
64
  expect(entity.id).to.be.null;
65
+ expect(entity.isTempId).to.be.false;
65
66
 
66
- entity.createId();
67
+ entity.createTempId();
67
68
  expect(entity.id).to.be.not.null;
68
- expect(Entity.isTempId(entity.id)).to.be.true;
69
+ expect(entity.isTempId).to.be.true;
69
70
 
70
-
71
-
72
- const schema2 = new Schema({
73
- name: 'baz',
74
- model: {
75
- idProperty: 'foo',
76
- displayProperty: 'bar',
77
- properties: [
78
- { name: 'foo', type: 'uuid' }, // MOD
79
- { name: 'bar' },
80
- ],
81
- },
82
- }),
83
- entity2 = new Entity(schema2, data);
84
-
85
- entity2.initialize();
86
- expect(entity2.id).to.be.null;
87
-
88
- entity2.createId();
89
- expect(entity2.id).to.be.not.null;
90
- const idProperty = entity2.getIdProperty();
91
- expect(Entity.isTempId(entity2.id)).to.be.false;
71
+ entity.markSaved();
72
+ expect(entity.isTempId).to.be.false;
92
73
  });
93
74
 
94
75
 
@@ -407,6 +388,12 @@ describe('Entity', function() {
407
388
  const entity = new Entity(this.schema, {});
408
389
  entity.initialize();
409
390
  expect(entity.isPhantom).to.be.true;
391
+
392
+ entity.createTempId();
393
+ expect(entity.isPhantom).to.be.true;
394
+
395
+ entity.markSaved();
396
+ expect(entity.isPhantom).to.be.false;
410
397
  });
411
398
 
412
399
  it('isDirty', function() {
@@ -469,6 +456,15 @@ describe('Entity', function() {
469
456
  expect(this.entity.isDirty).to.be.true;
470
457
  });
471
458
 
459
+ it('setId', function() {
460
+ expect(this.entity.foo).to.be.eq(1);
461
+ expect(this.entity.isTempId).to.be.false;
462
+
463
+ this.entity.setId(2);
464
+ expect(this.entity.foo).to.be.eq(2);
465
+ expect(this.entity.isTempId).to.be.false;
466
+ });
467
+
472
468
  it('_recalculateDependentProperties', function() {
473
469
  const schema = new Schema({
474
470
  name: 'baz',
@@ -508,6 +504,7 @@ describe('Entity', function() {
508
504
 
509
505
  entity.bar = 'Test';
510
506
  entity.markSaved();
507
+ expect(entity.isTempId).to.be.false;
511
508
 
512
509
  expect(entity.isPersisted).to.be.true;
513
510
  const expected = {
@@ -19,6 +19,14 @@ describe('IntegerProperty', function() {
19
19
  expect(className).to.be.eq('Integer');
20
20
  });
21
21
 
22
+ it('newId', function() {
23
+ const id1 = this.property.newId();
24
+ expect(id1.valueOf()).to.be.eq(this.property.idStartsAt);
25
+
26
+ const id2 = this.property.newId();
27
+ expect(id2.valueOf()).to.be.eq(this.property.idStartsAt +1);
28
+ });
29
+
22
30
  // it('default value', function() {
23
31
  // const property = this.property,
24
32
  // rawValue = property.getDefaultValue();
@@ -112,6 +112,20 @@ describe('Property', function() {
112
112
  it('getMapping', function() {
113
113
  expect(this.property.getMapping()).to.be.eq('id');
114
114
  });
115
+
116
+ it('getMapping', function() {
117
+ expect(this.property.getMapping()).to.be.eq('id');
118
+ });
119
+
120
+ it('isTempId', function() {
121
+ const definition = {
122
+ type: 'int',
123
+ isTempId: true,
124
+ },
125
+ Property = PropertyTypes[definition.type];
126
+ const property = new Property(definition);
127
+ expect(property.isTempId).to.be.true;
128
+ });
115
129
 
116
130
  });
117
131
 
@@ -10,9 +10,21 @@ describe('StringProperty', function() {
10
10
  this.property = new Property(definition);
11
11
  });
12
12
 
13
- it('className', function() {
14
- const className = this.property.getClassName();
15
- expect(className).to.be.eq('String');
13
+ describe('general', function() {
14
+
15
+ it('className', function() {
16
+ const className = this.property.getClassName();
17
+ expect(className).to.be.eq('String');
18
+ });
19
+
20
+ it('newId', function() {
21
+ const id1 = this.property.newId();
22
+ expect(id1.valueOf()).to.be.eq('TEMP-1');
23
+
24
+ const id2 = this.property.newId();
25
+ expect(id2.valueOf()).to.be.eq('TEMP-2');
26
+ });
27
+
16
28
  });
17
29
 
18
30
  describe('parse', function() {
@@ -622,11 +622,18 @@ describe('Repository Base', function() {
622
622
  expect(_.isEqual(entity, deleted[0])).to.be.true;
623
623
  });
624
624
 
625
- it('isInRepository', function() {
625
+ it('isInRepository, hasId', function() {
626
626
  this.repository.setAutoSave(false);
627
- const entity = this.repository.getByIx(0),
628
- result = this.repository.isInRepository(entity);
629
-
627
+ const id = 1,
628
+ entity = this.repository.getById(id);
629
+
630
+ let result = this.repository.isInRepository(entity);
631
+ expect(result).to.be.true;
632
+
633
+ result = this.repository.isInRepository(id);
634
+ expect(result).to.be.true;
635
+
636
+ result = this.repository.hasId(id);
630
637
  expect(result).to.be.true;
631
638
  });
632
639
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.5.2",
3
+ "version": "1.6.0",
4
4
  "description": "JS data modeling package with adapters for many storage mediums.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/Entity.js CHANGED
@@ -4,8 +4,6 @@ import EventEmitter from '@onehat/events';
4
4
  import PropertyTypes from './Property';
5
5
  import _ from 'lodash';
6
6
 
7
- const TEMP_PREFIX = 'TEMP-';
8
-
9
7
  /**
10
8
  * Class represents a single Entity (i.e. a record)
11
9
  * which is a collection of Properties with current values.
@@ -158,31 +156,24 @@ class Entity extends EventEmitter {
158
156
 
159
157
  /**
160
158
  * Generates a new unique id and assigns it to this entity.
161
- * If the idProperty is of type 'uuid', then it generates a new UUID.
162
- * If not, then it generates a temp id.
163
159
  */
164
- createId = () => {
160
+ createTempId = () => {
165
161
  if (this.isDestroyed) {
166
- throw Error('this.generateTempId is no longer valid. Entity has been destroyed.');
162
+ throw Error('this.createTempId is no longer valid. Entity has been destroyed.');
167
163
  }
168
164
  if (!_.isNil(this.id)) {
169
165
  throw new Error('Entity id already exists.');
170
166
  }
171
- const idProperty = this.getIdProperty(),
172
- id = (idProperty.type === 'uuid') ? idProperty.newId() : _.uniqueId(TEMP_PREFIX);
173
- this.setId(id);
174
- }
175
167
 
176
- /**
177
- * Determines whether submitted id is a "temp" id.
178
- * @param {any} id
179
- * @return {boolean} isTempId
180
- */
181
- static isTempId(id) {
182
- if (_.isString(id) && id.match(new RegExp('^' + TEMP_PREFIX))) {
183
- return true;
168
+ const idProperty = this.getIdProperty();
169
+
170
+ if (!idProperty.newId) {
171
+ throw new Error('idProperty.newId() does not exist');
184
172
  }
185
- return false;
173
+
174
+ this.setId(idProperty.newId());
175
+
176
+ idProperty.isTempId = true;
186
177
  }
187
178
 
188
179
  /**
@@ -739,6 +730,14 @@ class Entity extends EventEmitter {
739
730
  return this.getId();
740
731
  }
741
732
 
733
+ /**
734
+ * Is this Entity's idProperty using a temporary ID?
735
+ * @return {boolean} isTempId
736
+ */
737
+ get isTempId() {
738
+ return this.getIdProperty().isTempId;
739
+ }
740
+
742
741
  /**
743
742
  * Gets the "Display" Property object for this Entity.
744
743
  * This is the Property whose value can easily identify the whole Entity itself.
@@ -793,13 +792,13 @@ class Entity extends EventEmitter {
793
792
  const idProperty = this.getIdProperty(),
794
793
  id = idProperty.getSubmitValue();
795
794
 
796
- // No id
795
+ // No ID
797
796
  if (_.isNil(id)) {
798
797
  return true;
799
798
  }
800
799
 
801
- // Temp id
802
- if (Entity.isTempId(id)) {
800
+ // ID is temporary
801
+ if (idProperty.isTempId) {
803
802
  return true;
804
803
  }
805
804
 
@@ -851,23 +850,25 @@ class Entity extends EventEmitter {
851
850
  */
852
851
  setId = (id, force = false) => {
853
852
  let isChanged = false;
854
- const property = this.getIdProperty();
853
+ const idProperty = this.getIdProperty();
855
854
 
856
- property.pauseEvents(); // We don't need property_change to fire
857
- if (property.setValue(id)) {
855
+ idProperty.pauseEvents(); // We don't need property_change to fire
856
+ if (idProperty.setValue(id)) {
858
857
  isChanged = true;
859
858
  }
860
- property.resumeEvents();
859
+ idProperty.resumeEvents();
861
860
 
862
861
  if (isChanged || force) {
863
862
  // Set this id on the _originalData* objects
864
- if (property.hasMapping) {
865
- _.merge(this._originalData, Entity.getReverseMappedRawValue(property));
863
+ if (idProperty.hasMapping) {
864
+ _.merge(this._originalData, Entity.getReverseMappedRawValue(idProperty));
866
865
  } else {
867
- this._originalData[property.name] = property.getRawValue();
866
+ this._originalData[idProperty.name] = idProperty.getRawValue();
868
867
  }
869
- this._originalDataParsed[property.name] = property.getParsedValue();
868
+ this._originalDataParsed[idProperty.name] = idProperty.getParsedValue();
870
869
  }
870
+
871
+ idProperty.isTempId = false;
871
872
 
872
873
  return isChanged;
873
874
  }
@@ -973,6 +974,7 @@ class Entity extends EventEmitter {
973
974
  throw Error('this.markSaved is no longer valid. Entity has been destroyed.');
974
975
  }
975
976
  this.isPersisted = true;
977
+ this.getIdProperty().isTempId = false;
976
978
  this._originalData = this._getReconstructedOriginalData();
977
979
  this._originalDataParsed = this.getParsedValues();
978
980
  }
@@ -4,6 +4,8 @@ import Property from './Property';
4
4
  import Parsers from '../Util/Parsers';
5
5
  import _ from 'lodash';
6
6
 
7
+ let lastId = 0;
8
+
7
9
  /**
8
10
  * Class represents a Property that stores an integer value.
9
11
  * @extends Property
@@ -15,6 +17,7 @@ export default class IntegerProperty extends Property {
15
17
 
16
18
  const defaults = {
17
19
  // defaultValue: 0,
20
+ idStartsAt: 100 * 1000 * 1000 * 1000, // 100 billion
18
21
  };
19
22
 
20
23
  _.merge(this, defaults, config);
@@ -29,6 +32,32 @@ export default class IntegerProperty extends Property {
29
32
  }
30
33
  return Parsers.ParseInt(value);
31
34
  }
35
+
36
+ /**
37
+ * Generates a new id
38
+ * Mainly for temporary, in-memory usage
39
+ */
40
+ newId = () => {
41
+ let id,
42
+ hasId = false;
43
+
44
+ const entity = this.getEntity(),
45
+ repository = entity && entity.repository;
46
+
47
+ if (lastId < this.idStartsAt) {
48
+ lastId = this.idStartsAt;
49
+ }
50
+
51
+ id = lastId++;
52
+ hasId = repository ? repository.hasId(id) : false;
53
+
54
+ while(hasId) {
55
+ id = lastId++;
56
+ hasId = repository.hasId(id);
57
+ }
58
+ return id;
59
+ }
60
+
32
61
 
33
62
  };
34
63
 
@@ -83,6 +83,12 @@ export default class Property extends EventEmitter {
83
83
  * @member {boolean} isSortable - Whether this property type is sortable
84
84
  */
85
85
  isSortable: true,
86
+
87
+ /**
88
+ * @member {boolean} isTempId - Whether this property's ID is temporary
89
+ */
90
+ isTempId: false,
91
+
86
92
 
87
93
 
88
94
  // OneBuild META attributes, just bring these over wholesale for now
@@ -4,6 +4,8 @@ import Property from './Property';
4
4
  import Parsers from '../Util/Parsers';
5
5
  import _ from 'lodash';
6
6
 
7
+ const TEMP_PREFIX = 'TEMP-';
8
+
7
9
  /**
8
10
  * Class represents a Property that stores string data.
9
11
  * @extends Property
@@ -29,6 +31,23 @@ export default class StringProperty extends Property {
29
31
  }
30
32
  return Parsers.ParseString(value);
31
33
  }
34
+
35
+ newId = () => {
36
+ let id,
37
+ hasId = false;
38
+
39
+ const entity = this.getEntity(),
40
+ repository = entity && entity.repository;
41
+
42
+ id = _.uniqueId(TEMP_PREFIX);
43
+ hasId = repository ? repository.hasId(id) : false;
44
+
45
+ while(hasId) {
46
+ id = _.uniqueId(TEMP_PREFIX);
47
+ hasId = repository.hasId(id);
48
+ }
49
+ return id;
50
+ }
32
51
  };
33
52
 
34
53
  StringProperty.className = 'String';
@@ -395,7 +395,7 @@ class AjaxRepository extends Repository {
395
395
  });
396
396
 
397
397
  // Set the total records that pass filter
398
- this.total = total;
398
+ this.total = total;
399
399
  this._setPaginationVars();
400
400
 
401
401
  this.isLoading = false;
@@ -134,7 +134,7 @@ class MemoryRepository extends Repository {
134
134
  // Add to internal store
135
135
  _.each(entities, (entity) => {
136
136
  if (entity.isPhantom) {
137
- entity.createId();
137
+ entity.createTempId();
138
138
  }
139
139
  this._keyedEntities[entity.id] = entity;
140
140
  });
@@ -865,8 +865,8 @@ export default class Repository extends EventEmitter {
865
865
  this.entities.push(entity);
866
866
 
867
867
  // Create id if needed
868
- if (entity.isPhantom) {
869
- entity.createId(); // either a UUID or a temp id
868
+ if (entity.isPhantom) { // i.e. idProperty has no value
869
+ entity.createTempId();
870
870
  }
871
871
 
872
872
  this.emit('add', entity);
@@ -893,7 +893,7 @@ export default class Repository extends EventEmitter {
893
893
  const entity = Repository._createEntity(this.schema, data, this, isPersisted, originalIsMapped);
894
894
 
895
895
  if (entity.isPhantom) {
896
- entity.createId(); // either a UUID or a temp id
896
+ entity.createTempId();
897
897
  }
898
898
 
899
899
  return entity;
@@ -1168,6 +1168,15 @@ export default class Repository extends EventEmitter {
1168
1168
  return !_.isNil(this.getById(idOrEntity));
1169
1169
  }
1170
1170
 
1171
+ /**
1172
+ * Convenience function
1173
+ * Alias for isInRepository
1174
+ * NOTE: It only searches in memory. Doesn't query server
1175
+ */
1176
+ hasId = (id) => {
1177
+ return this.isInRepository(id);
1178
+ }
1179
+
1171
1180
  /**
1172
1181
  * Queues up batch operations for saving
1173
1182
  * new, edited, and deleted entities to storage medium.