@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.
- package/cypress/integration/Entity.spec.js +22 -25
- package/cypress/integration/Property/Integer.spec.js +8 -0
- package/cypress/integration/Property/Property.spec.js +14 -0
- package/cypress/integration/Property/String.spec.js +15 -3
- package/cypress/integration/Repository/Repository.spec.js +11 -4
- package/package.json +1 -1
- package/src/Entity.js +32 -30
- package/src/Property/Integer.js +29 -0
- package/src/Property/Property.js +6 -0
- package/src/Property/String.js +19 -0
- package/src/Repository/Ajax.js +1 -1
- package/src/Repository/Memory.js +1 -1
- package/src/Repository/Repository.js +12 -3
|
@@ -44,7 +44,7 @@ describe('Entity', function() {
|
|
|
44
44
|
expect(this.entity.id).to.be.eq(1);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
it('
|
|
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.
|
|
67
|
+
entity.createTempId();
|
|
67
68
|
expect(entity.id).to.be.not.null;
|
|
68
|
-
expect(
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
628
|
-
|
|
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
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
|
-
|
|
160
|
+
createTempId = () => {
|
|
165
161
|
if (this.isDestroyed) {
|
|
166
|
-
throw Error('this.
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
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
|
|
795
|
+
// No ID
|
|
797
796
|
if (_.isNil(id)) {
|
|
798
797
|
return true;
|
|
799
798
|
}
|
|
800
799
|
|
|
801
|
-
//
|
|
802
|
-
if (
|
|
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
|
|
853
|
+
const idProperty = this.getIdProperty();
|
|
855
854
|
|
|
856
|
-
|
|
857
|
-
if (
|
|
855
|
+
idProperty.pauseEvents(); // We don't need property_change to fire
|
|
856
|
+
if (idProperty.setValue(id)) {
|
|
858
857
|
isChanged = true;
|
|
859
858
|
}
|
|
860
|
-
|
|
859
|
+
idProperty.resumeEvents();
|
|
861
860
|
|
|
862
861
|
if (isChanged || force) {
|
|
863
862
|
// Set this id on the _originalData* objects
|
|
864
|
-
if (
|
|
865
|
-
_.merge(this._originalData, Entity.getReverseMappedRawValue(
|
|
863
|
+
if (idProperty.hasMapping) {
|
|
864
|
+
_.merge(this._originalData, Entity.getReverseMappedRawValue(idProperty));
|
|
866
865
|
} else {
|
|
867
|
-
this._originalData[
|
|
866
|
+
this._originalData[idProperty.name] = idProperty.getRawValue();
|
|
868
867
|
}
|
|
869
|
-
this._originalDataParsed[
|
|
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
|
}
|
package/src/Property/Integer.js
CHANGED
|
@@ -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
|
|
package/src/Property/Property.js
CHANGED
|
@@ -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
|
package/src/Property/String.js
CHANGED
|
@@ -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';
|
package/src/Repository/Ajax.js
CHANGED
package/src/Repository/Memory.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|