@onehat/data 1.4.14 → 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 +98 -36
- package/cypress/integration/Property/Integer.spec.js +8 -0
- package/cypress/integration/Property/Property.spec.js +18 -0
- package/cypress/integration/Property/String.spec.js +15 -3
- package/cypress/integration/Repository/Repository.spec.js +17 -4
- package/package.json +1 -1
- package/src/Entity.js +98 -30
- package/src/Property/Integer.js +29 -0
- package/src/Property/Property.js +17 -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 +32 -2
|
@@ -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
|
|
|
@@ -198,6 +179,17 @@ describe('Entity', function() {
|
|
|
198
179
|
expect(_.isEqual(this.entity.submitValues, expected)).to.be.true;
|
|
199
180
|
});
|
|
200
181
|
|
|
182
|
+
it('getSubmitValuesMapped & submitValuesMapped', function() {
|
|
183
|
+
const result = this.entity.getSubmitValuesMapped(),
|
|
184
|
+
expected = {
|
|
185
|
+
foo: 1,
|
|
186
|
+
bar: 'one',
|
|
187
|
+
'baz.test.val': true,
|
|
188
|
+
};
|
|
189
|
+
expect(_.isEqual(result, expected)).to.be.true;
|
|
190
|
+
expect(_.isEqual(this.entity.submitValuesMapped, expected)).to.be.true;
|
|
191
|
+
});
|
|
192
|
+
|
|
201
193
|
it('getDisplayValues & displayValues', function() {
|
|
202
194
|
const result = this.entity.getDisplayValues(),
|
|
203
195
|
expected = {
|
|
@@ -258,20 +250,74 @@ describe('Entity', function() {
|
|
|
258
250
|
});
|
|
259
251
|
|
|
260
252
|
it('getReverseMappedRawValue', function() {
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
253
|
+
const definition1 = {
|
|
254
|
+
name: 'foo',
|
|
255
|
+
mapping: 'a.b.c',
|
|
256
|
+
type: 'int',
|
|
257
|
+
},
|
|
258
|
+
Property1 = PropertyTypes[definition1.type];
|
|
259
|
+
const property1 = new Property1(definition1, 'fakeEntity');
|
|
260
|
+
|
|
261
|
+
property1.setValue('47');
|
|
262
|
+
|
|
263
|
+
const reverseMapped1 = Entity.getReverseMappedRawValue(property1),
|
|
264
|
+
expected1 = { a: { b: { c: '47' }, }, };
|
|
265
|
+
|
|
266
|
+
expect(_.isEqual(reverseMapped1, expected1)).to.be.true;
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
// Now, with no mapping
|
|
270
|
+
const definition2 = {
|
|
271
|
+
name: 'foo',
|
|
272
|
+
type: 'int',
|
|
273
|
+
},
|
|
274
|
+
Property2 = PropertyTypes[definition2.type];
|
|
275
|
+
const property2 = new Property2(definition2, 'fakeEntity');
|
|
268
276
|
|
|
269
|
-
|
|
277
|
+
property2.setValue('47');
|
|
270
278
|
|
|
271
|
-
const
|
|
272
|
-
|
|
279
|
+
const reverseMapped2 = Entity.getReverseMappedRawValue(property2),
|
|
280
|
+
expected2 = { foo: '47' };
|
|
273
281
|
|
|
274
|
-
expect(_.isEqual(
|
|
282
|
+
expect(_.isEqual(reverseMapped2, expected2)).to.be.true;
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('getReverseMappedRawValues (check deep matches)', function() {
|
|
286
|
+
const schema = new Schema({
|
|
287
|
+
name: 'baz',
|
|
288
|
+
model: {
|
|
289
|
+
idProperty: 'foo',
|
|
290
|
+
displayProperty: 'bar',
|
|
291
|
+
properties: [
|
|
292
|
+
{ name: 'foo', type: 'int' },
|
|
293
|
+
{ name: 'bar' },
|
|
294
|
+
{ name: 'baz', mapping: 'baz.test.val', type: 'bool', defaultValue: null, },
|
|
295
|
+
{ name: 'baz2', mapping: 'baz.test2', type: 'bool', defaultValue: null, },
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
}),
|
|
299
|
+
data = {
|
|
300
|
+
foo: 1,
|
|
301
|
+
bar: 'one',
|
|
302
|
+
baz: {
|
|
303
|
+
test: {
|
|
304
|
+
val: true,
|
|
305
|
+
},
|
|
306
|
+
test2: false,
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
entity = new Entity(schema, data);
|
|
310
|
+
entity.initialize();
|
|
311
|
+
|
|
312
|
+
const result = entity.getReverseMappedRawValues();
|
|
313
|
+
expect(_.isEqual(result, data)).to.be.true;
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('build a new entity from an existing one using getDataForNewEntity', function() {
|
|
317
|
+
const entity = new Entity(this.schema, this.entity.getDataForNewEntity());
|
|
318
|
+
entity.initialize();
|
|
319
|
+
|
|
320
|
+
expect(_.isEqual(entity.submitValues, this.entity.submitValues)).to.be.true;
|
|
275
321
|
});
|
|
276
322
|
|
|
277
323
|
it('getChanged', function() {
|
|
@@ -342,6 +388,12 @@ describe('Entity', function() {
|
|
|
342
388
|
const entity = new Entity(this.schema, {});
|
|
343
389
|
entity.initialize();
|
|
344
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;
|
|
345
397
|
});
|
|
346
398
|
|
|
347
399
|
it('isDirty', function() {
|
|
@@ -404,6 +456,15 @@ describe('Entity', function() {
|
|
|
404
456
|
expect(this.entity.isDirty).to.be.true;
|
|
405
457
|
});
|
|
406
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
|
+
|
|
407
468
|
it('_recalculateDependentProperties', function() {
|
|
408
469
|
const schema = new Schema({
|
|
409
470
|
name: 'baz',
|
|
@@ -443,6 +504,7 @@ describe('Entity', function() {
|
|
|
443
504
|
|
|
444
505
|
entity.bar = 'Test';
|
|
445
506
|
entity.markSaved();
|
|
507
|
+
expect(entity.isTempId).to.be.false;
|
|
446
508
|
|
|
447
509
|
expect(entity.isPersisted).to.be.true;
|
|
448
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();
|
|
@@ -108,6 +108,24 @@ describe('Property', function() {
|
|
|
108
108
|
const property = entity.getProperty('bar');
|
|
109
109
|
expect(property.isDisplayProperty).to.be.true;
|
|
110
110
|
});
|
|
111
|
+
|
|
112
|
+
it('getMapping', function() {
|
|
113
|
+
expect(this.property.getMapping()).to.be.eq('id');
|
|
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
|
+
});
|
|
111
129
|
|
|
112
130
|
});
|
|
113
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() {
|
|
@@ -511,6 +511,12 @@ describe('Repository Base', function() {
|
|
|
511
511
|
expect(didFireAdd).to.be.true;
|
|
512
512
|
});
|
|
513
513
|
|
|
514
|
+
it('createStandaloneEntity', async function() {
|
|
515
|
+
const entity = await this.repository.createStandaloneEntity({ key: 6, value: 'six' });
|
|
516
|
+
expect(entity.id).to.be.eq(6);
|
|
517
|
+
expect(_.size(this.repository.entities)).to.be.eq(5);
|
|
518
|
+
});
|
|
519
|
+
|
|
514
520
|
it('addMultiple', async function() {
|
|
515
521
|
await this.repository.addMultiple([
|
|
516
522
|
{ key: 6, value: 'six' },
|
|
@@ -616,11 +622,18 @@ describe('Repository Base', function() {
|
|
|
616
622
|
expect(_.isEqual(entity, deleted[0])).to.be.true;
|
|
617
623
|
});
|
|
618
624
|
|
|
619
|
-
it('isInRepository', function() {
|
|
625
|
+
it('isInRepository, hasId', function() {
|
|
620
626
|
this.repository.setAutoSave(false);
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
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);
|
|
624
637
|
expect(result).to.be.true;
|
|
625
638
|
});
|
|
626
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
|
/**
|
|
@@ -457,6 +448,36 @@ class Entity extends EventEmitter {
|
|
|
457
448
|
return this.getSubmitValues();
|
|
458
449
|
}
|
|
459
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Gets an object of properties/values for this Entity,
|
|
453
|
+
* and returns them with the mapped names
|
|
454
|
+
* Values are the "submit" values, not the "raw" or "parsed" or "display" values.
|
|
455
|
+
* @return {object} propertyValues
|
|
456
|
+
*/
|
|
457
|
+
getSubmitValuesMapped = () => {
|
|
458
|
+
if (this.isDestroyed) {
|
|
459
|
+
throw Error('this.getSubmitValuesMapped is no longer valid. Entity has been destroyed.');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
let propertyValues = {};
|
|
463
|
+
_.forOwn(this.properties, (property) => {
|
|
464
|
+
const name = property.hasMapping ? property.getMapping() : property.name;
|
|
465
|
+
propertyValues[name] = property.getSubmitValue();
|
|
466
|
+
});
|
|
467
|
+
return propertyValues;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Gets "submit" values for this Entity, and returns them with the mapped names
|
|
472
|
+
* @return {object} values
|
|
473
|
+
*/
|
|
474
|
+
get submitValuesMapped() {
|
|
475
|
+
if (this.isDestroyed) {
|
|
476
|
+
throw Error('this.submitValuesMapped is no longer valid. Entity has been destroyed.');
|
|
477
|
+
}
|
|
478
|
+
return this.getSubmitValuesMapped();
|
|
479
|
+
}
|
|
480
|
+
|
|
460
481
|
/**
|
|
461
482
|
* Gets an object of values for this Entity,
|
|
462
483
|
* Values are the "display" values, not the "raw" or "parsed" or "submit" values.
|
|
@@ -569,6 +590,11 @@ class Entity extends EventEmitter {
|
|
|
569
590
|
* @static
|
|
570
591
|
*/
|
|
571
592
|
static getReverseMappedRawValue(property) {
|
|
593
|
+
if (!property.hasMapping) {
|
|
594
|
+
const obj = {};
|
|
595
|
+
obj[property.name] = property.rawValue;
|
|
596
|
+
return obj;
|
|
597
|
+
}
|
|
572
598
|
const mapStack = property.mapping.split('.'),
|
|
573
599
|
rawValue = property.getRawValue();
|
|
574
600
|
|
|
@@ -591,6 +617,37 @@ class Entity extends EventEmitter {
|
|
|
591
617
|
return value;
|
|
592
618
|
}
|
|
593
619
|
|
|
620
|
+
/**
|
|
621
|
+
* Builds up an object of original values for this entity, from which another entity could be easily created
|
|
622
|
+
* @return {object} value - An object representing the 'path' to the raw value.
|
|
623
|
+
* e.g. With a mapping of 'a.b.c' and a property rawValue of '47', the
|
|
624
|
+
* following object will be returned:{ a: { b: { c: '47' }, }, }
|
|
625
|
+
*/
|
|
626
|
+
getReverseMappedRawValues = () => {
|
|
627
|
+
if (this.isDestroyed) {
|
|
628
|
+
throw Error('this.getReverseMappedRawValues is no longer valid. Entity has been destroyed.');
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
let propertyValues = {};
|
|
632
|
+
_.forOwn(this.properties, (property) => {
|
|
633
|
+
const reverseMapped = Entity.getReverseMappedRawValue(property);
|
|
634
|
+
_.merge(propertyValues, reverseMapped);
|
|
635
|
+
});
|
|
636
|
+
return propertyValues;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Convenience function
|
|
641
|
+
* Build a new entity with this data
|
|
642
|
+
*/
|
|
643
|
+
getDataForNewEntity = () => {
|
|
644
|
+
if (this.isDestroyed) {
|
|
645
|
+
throw Error('this.getDataForNewEntity is no longer valid. Entity has been destroyed.');
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return this.getReverseMappedRawValues();
|
|
649
|
+
}
|
|
650
|
+
|
|
594
651
|
/**
|
|
595
652
|
* Gets the values that have changed since last time saved
|
|
596
653
|
* @return {array|boolean} diff - Array of property names that have changed, or false
|
|
@@ -673,6 +730,14 @@ class Entity extends EventEmitter {
|
|
|
673
730
|
return this.getId();
|
|
674
731
|
}
|
|
675
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
|
+
|
|
676
741
|
/**
|
|
677
742
|
* Gets the "Display" Property object for this Entity.
|
|
678
743
|
* This is the Property whose value can easily identify the whole Entity itself.
|
|
@@ -727,13 +792,13 @@ class Entity extends EventEmitter {
|
|
|
727
792
|
const idProperty = this.getIdProperty(),
|
|
728
793
|
id = idProperty.getSubmitValue();
|
|
729
794
|
|
|
730
|
-
// No
|
|
795
|
+
// No ID
|
|
731
796
|
if (_.isNil(id)) {
|
|
732
797
|
return true;
|
|
733
798
|
}
|
|
734
799
|
|
|
735
|
-
//
|
|
736
|
-
if (
|
|
800
|
+
// ID is temporary
|
|
801
|
+
if (idProperty.isTempId) {
|
|
737
802
|
return true;
|
|
738
803
|
}
|
|
739
804
|
|
|
@@ -785,23 +850,25 @@ class Entity extends EventEmitter {
|
|
|
785
850
|
*/
|
|
786
851
|
setId = (id, force = false) => {
|
|
787
852
|
let isChanged = false;
|
|
788
|
-
const
|
|
853
|
+
const idProperty = this.getIdProperty();
|
|
789
854
|
|
|
790
|
-
|
|
791
|
-
if (
|
|
855
|
+
idProperty.pauseEvents(); // We don't need property_change to fire
|
|
856
|
+
if (idProperty.setValue(id)) {
|
|
792
857
|
isChanged = true;
|
|
793
858
|
}
|
|
794
|
-
|
|
859
|
+
idProperty.resumeEvents();
|
|
795
860
|
|
|
796
861
|
if (isChanged || force) {
|
|
797
862
|
// Set this id on the _originalData* objects
|
|
798
|
-
if (
|
|
799
|
-
_.merge(this._originalData, Entity.getReverseMappedRawValue(
|
|
863
|
+
if (idProperty.hasMapping) {
|
|
864
|
+
_.merge(this._originalData, Entity.getReverseMappedRawValue(idProperty));
|
|
800
865
|
} else {
|
|
801
|
-
this._originalData[
|
|
866
|
+
this._originalData[idProperty.name] = idProperty.getRawValue();
|
|
802
867
|
}
|
|
803
|
-
this._originalDataParsed[
|
|
868
|
+
this._originalDataParsed[idProperty.name] = idProperty.getParsedValue();
|
|
804
869
|
}
|
|
870
|
+
|
|
871
|
+
idProperty.isTempId = false;
|
|
805
872
|
|
|
806
873
|
return isChanged;
|
|
807
874
|
}
|
|
@@ -907,6 +974,7 @@ class Entity extends EventEmitter {
|
|
|
907
974
|
throw Error('this.markSaved is no longer valid. Entity has been destroyed.');
|
|
908
975
|
}
|
|
909
976
|
this.isPersisted = true;
|
|
977
|
+
this.getIdProperty().isTempId = false;
|
|
910
978
|
this._originalData = this._getReconstructedOriginalData();
|
|
911
979
|
this._originalDataParsed = this.getParsedValues();
|
|
912
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
|
|
@@ -405,6 +411,17 @@ export default class Property extends EventEmitter {
|
|
|
405
411
|
return this.__proto__.constructor.className;
|
|
406
412
|
}
|
|
407
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Gets the mapped name of this Property.
|
|
416
|
+
* @return {string} name
|
|
417
|
+
*/
|
|
418
|
+
getMapping = () => {
|
|
419
|
+
if (this.isDestroyed) {
|
|
420
|
+
throw Error('this.getMapping is no longer valid. Property has been destroyed.');
|
|
421
|
+
}
|
|
422
|
+
return this.mapping;
|
|
423
|
+
}
|
|
424
|
+
|
|
408
425
|
/**
|
|
409
426
|
* Destroy this object.
|
|
410
427
|
* - Removes all circular references to parent objects
|
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);
|
|
@@ -878,6 +878,27 @@ export default class Repository extends EventEmitter {
|
|
|
878
878
|
return entity;
|
|
879
879
|
}
|
|
880
880
|
|
|
881
|
+
/**
|
|
882
|
+
* Creates a new static Entity that does NOT persist in storage medium.
|
|
883
|
+
* @param {object} data - Either raw data object or Entity. If raw data, keys are Property names, Values are Property values.
|
|
884
|
+
* @param {boolean} isPersisted - Whether the new entity should be marked as already being persisted in storage medium.
|
|
885
|
+
* @param {boolean} originalIsMapped - Has data already been mapped according to schema?
|
|
886
|
+
* @return {object} entity - new Entity object
|
|
887
|
+
*/
|
|
888
|
+
createStandaloneEntity = async (data, isPersisted = false, originalIsMapped = false) => {
|
|
889
|
+
if (this.isDestroyed) {
|
|
890
|
+
throw Error('this.createStandaloneEntity is no longer valid. Repository has been destroyed.');
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
const entity = Repository._createEntity(this.schema, data, this, isPersisted, originalIsMapped);
|
|
894
|
+
|
|
895
|
+
if (entity.isPhantom) {
|
|
896
|
+
entity.createTempId();
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return entity;
|
|
900
|
+
}
|
|
901
|
+
|
|
881
902
|
/**
|
|
882
903
|
* Convenience function to add entity with mapped data.
|
|
883
904
|
*/
|
|
@@ -1147,6 +1168,15 @@ export default class Repository extends EventEmitter {
|
|
|
1147
1168
|
return !_.isNil(this.getById(idOrEntity));
|
|
1148
1169
|
}
|
|
1149
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
|
+
|
|
1150
1180
|
/**
|
|
1151
1181
|
* Queues up batch operations for saving
|
|
1152
1182
|
* new, edited, and deleted entities to storage medium.
|