@onehat/data 1.4.13 → 1.5.2
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 +77 -12
- package/cypress/integration/Property/Property.spec.js +4 -0
- package/cypress/integration/Repository/Repository.spec.js +6 -0
- package/package.json +1 -1
- package/src/Entity.js +80 -1
- package/src/Property/Property.js +11 -0
- package/src/Repository/Repository.js +35 -6
|
@@ -14,7 +14,7 @@ describe('Entity', function() {
|
|
|
14
14
|
properties: [
|
|
15
15
|
{ name: 'foo', type: 'int' },
|
|
16
16
|
{ name: 'bar' },
|
|
17
|
-
{ name: 'baz', mapping: 'baz.test.val', type: 'bool' },
|
|
17
|
+
{ name: 'baz', mapping: 'baz.test.val', type: 'bool', defaultValue: null, },
|
|
18
18
|
],
|
|
19
19
|
},
|
|
20
20
|
});
|
|
@@ -198,6 +198,17 @@ describe('Entity', function() {
|
|
|
198
198
|
expect(_.isEqual(this.entity.submitValues, expected)).to.be.true;
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
+
it('getSubmitValuesMapped & submitValuesMapped', function() {
|
|
202
|
+
const result = this.entity.getSubmitValuesMapped(),
|
|
203
|
+
expected = {
|
|
204
|
+
foo: 1,
|
|
205
|
+
bar: 'one',
|
|
206
|
+
'baz.test.val': true,
|
|
207
|
+
};
|
|
208
|
+
expect(_.isEqual(result, expected)).to.be.true;
|
|
209
|
+
expect(_.isEqual(this.entity.submitValuesMapped, expected)).to.be.true;
|
|
210
|
+
});
|
|
211
|
+
|
|
201
212
|
it('getDisplayValues & displayValues', function() {
|
|
202
213
|
const result = this.entity.getDisplayValues(),
|
|
203
214
|
expected = {
|
|
@@ -258,20 +269,74 @@ describe('Entity', function() {
|
|
|
258
269
|
});
|
|
259
270
|
|
|
260
271
|
it('getReverseMappedRawValue', function() {
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
272
|
+
const definition1 = {
|
|
273
|
+
name: 'foo',
|
|
274
|
+
mapping: 'a.b.c',
|
|
275
|
+
type: 'int',
|
|
276
|
+
},
|
|
277
|
+
Property1 = PropertyTypes[definition1.type];
|
|
278
|
+
const property1 = new Property1(definition1, 'fakeEntity');
|
|
279
|
+
|
|
280
|
+
property1.setValue('47');
|
|
281
|
+
|
|
282
|
+
const reverseMapped1 = Entity.getReverseMappedRawValue(property1),
|
|
283
|
+
expected1 = { a: { b: { c: '47' }, }, };
|
|
284
|
+
|
|
285
|
+
expect(_.isEqual(reverseMapped1, expected1)).to.be.true;
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
// Now, with no mapping
|
|
289
|
+
const definition2 = {
|
|
290
|
+
name: 'foo',
|
|
291
|
+
type: 'int',
|
|
292
|
+
},
|
|
293
|
+
Property2 = PropertyTypes[definition2.type];
|
|
294
|
+
const property2 = new Property2(definition2, 'fakeEntity');
|
|
268
295
|
|
|
269
|
-
|
|
296
|
+
property2.setValue('47');
|
|
270
297
|
|
|
271
|
-
const
|
|
272
|
-
|
|
298
|
+
const reverseMapped2 = Entity.getReverseMappedRawValue(property2),
|
|
299
|
+
expected2 = { foo: '47' };
|
|
273
300
|
|
|
274
|
-
expect(_.isEqual(
|
|
301
|
+
expect(_.isEqual(reverseMapped2, expected2)).to.be.true;
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('getReverseMappedRawValues (check deep matches)', function() {
|
|
305
|
+
const schema = new Schema({
|
|
306
|
+
name: 'baz',
|
|
307
|
+
model: {
|
|
308
|
+
idProperty: 'foo',
|
|
309
|
+
displayProperty: 'bar',
|
|
310
|
+
properties: [
|
|
311
|
+
{ name: 'foo', type: 'int' },
|
|
312
|
+
{ name: 'bar' },
|
|
313
|
+
{ name: 'baz', mapping: 'baz.test.val', type: 'bool', defaultValue: null, },
|
|
314
|
+
{ name: 'baz2', mapping: 'baz.test2', type: 'bool', defaultValue: null, },
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
}),
|
|
318
|
+
data = {
|
|
319
|
+
foo: 1,
|
|
320
|
+
bar: 'one',
|
|
321
|
+
baz: {
|
|
322
|
+
test: {
|
|
323
|
+
val: true,
|
|
324
|
+
},
|
|
325
|
+
test2: false,
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
entity = new Entity(schema, data);
|
|
329
|
+
entity.initialize();
|
|
330
|
+
|
|
331
|
+
const result = entity.getReverseMappedRawValues();
|
|
332
|
+
expect(_.isEqual(result, data)).to.be.true;
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('build a new entity from an existing one using getDataForNewEntity', function() {
|
|
336
|
+
const entity = new Entity(this.schema, this.entity.getDataForNewEntity());
|
|
337
|
+
entity.initialize();
|
|
338
|
+
|
|
339
|
+
expect(_.isEqual(entity.submitValues, this.entity.submitValues)).to.be.true;
|
|
275
340
|
});
|
|
276
341
|
|
|
277
342
|
it('getChanged', function() {
|
|
@@ -108,6 +108,10 @@ 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
|
+
});
|
|
111
115
|
|
|
112
116
|
});
|
|
113
117
|
|
|
@@ -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' },
|
package/package.json
CHANGED
package/src/Entity.js
CHANGED
|
@@ -32,8 +32,10 @@ class Entity extends EventEmitter {
|
|
|
32
32
|
* @constructor
|
|
33
33
|
* @param {Schema} schema - Schema object
|
|
34
34
|
* @param {object} rawData - Raw data object. Keys are Property names, Values are Property values.
|
|
35
|
+
* @param {Repository} repository
|
|
36
|
+
* @param {boolean} - Has rawData already been mapped according to schema?
|
|
35
37
|
*/
|
|
36
|
-
constructor(schema, rawData = {}, repository = null) {
|
|
38
|
+
constructor(schema, rawData = {}, repository = null, originalIsMapped = false) {
|
|
37
39
|
super(...arguments);
|
|
38
40
|
|
|
39
41
|
if (!schema) {
|
|
@@ -86,6 +88,12 @@ class Entity extends EventEmitter {
|
|
|
86
88
|
*/
|
|
87
89
|
this._originalDataParsed = null;
|
|
88
90
|
|
|
91
|
+
/**
|
|
92
|
+
* @member {boolean} originalIsMapped - Has the original data already been mapped according to schema?
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
this.originalIsMapped = originalIsMapped;
|
|
96
|
+
|
|
89
97
|
/**
|
|
90
98
|
* @member {Object} properties - Object of all Properties, keyed by id (for quick access)
|
|
91
99
|
* These properties are actually created in the initialize() function.
|
|
@@ -204,6 +212,11 @@ class Entity extends EventEmitter {
|
|
|
204
212
|
throw new Error('PropertyType ' + type + ' does not exist.');
|
|
205
213
|
}
|
|
206
214
|
|
|
215
|
+
if (this.originalIsMapped) {
|
|
216
|
+
// Data has already been mapped according to schema, so alter definition
|
|
217
|
+
definition = _.clone(definition); // Clone it so you don't alter original in schema
|
|
218
|
+
definition.mapping = definition.name;
|
|
219
|
+
}
|
|
207
220
|
const Property = PropertyTypes[type],
|
|
208
221
|
property = new Property(definition, this._proxy);
|
|
209
222
|
|
|
@@ -444,6 +457,36 @@ class Entity extends EventEmitter {
|
|
|
444
457
|
return this.getSubmitValues();
|
|
445
458
|
}
|
|
446
459
|
|
|
460
|
+
/**
|
|
461
|
+
* Gets an object of properties/values for this Entity,
|
|
462
|
+
* and returns them with the mapped names
|
|
463
|
+
* Values are the "submit" values, not the "raw" or "parsed" or "display" values.
|
|
464
|
+
* @return {object} propertyValues
|
|
465
|
+
*/
|
|
466
|
+
getSubmitValuesMapped = () => {
|
|
467
|
+
if (this.isDestroyed) {
|
|
468
|
+
throw Error('this.getSubmitValuesMapped is no longer valid. Entity has been destroyed.');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
let propertyValues = {};
|
|
472
|
+
_.forOwn(this.properties, (property) => {
|
|
473
|
+
const name = property.hasMapping ? property.getMapping() : property.name;
|
|
474
|
+
propertyValues[name] = property.getSubmitValue();
|
|
475
|
+
});
|
|
476
|
+
return propertyValues;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Gets "submit" values for this Entity, and returns them with the mapped names
|
|
481
|
+
* @return {object} values
|
|
482
|
+
*/
|
|
483
|
+
get submitValuesMapped() {
|
|
484
|
+
if (this.isDestroyed) {
|
|
485
|
+
throw Error('this.submitValuesMapped is no longer valid. Entity has been destroyed.');
|
|
486
|
+
}
|
|
487
|
+
return this.getSubmitValuesMapped();
|
|
488
|
+
}
|
|
489
|
+
|
|
447
490
|
/**
|
|
448
491
|
* Gets an object of values for this Entity,
|
|
449
492
|
* Values are the "display" values, not the "raw" or "parsed" or "submit" values.
|
|
@@ -556,6 +599,11 @@ class Entity extends EventEmitter {
|
|
|
556
599
|
* @static
|
|
557
600
|
*/
|
|
558
601
|
static getReverseMappedRawValue(property) {
|
|
602
|
+
if (!property.hasMapping) {
|
|
603
|
+
const obj = {};
|
|
604
|
+
obj[property.name] = property.rawValue;
|
|
605
|
+
return obj;
|
|
606
|
+
}
|
|
559
607
|
const mapStack = property.mapping.split('.'),
|
|
560
608
|
rawValue = property.getRawValue();
|
|
561
609
|
|
|
@@ -578,6 +626,37 @@ class Entity extends EventEmitter {
|
|
|
578
626
|
return value;
|
|
579
627
|
}
|
|
580
628
|
|
|
629
|
+
/**
|
|
630
|
+
* Builds up an object of original values for this entity, from which another entity could be easily created
|
|
631
|
+
* @return {object} value - An object representing the 'path' to the raw value.
|
|
632
|
+
* e.g. With a mapping of 'a.b.c' and a property rawValue of '47', the
|
|
633
|
+
* following object will be returned:{ a: { b: { c: '47' }, }, }
|
|
634
|
+
*/
|
|
635
|
+
getReverseMappedRawValues = () => {
|
|
636
|
+
if (this.isDestroyed) {
|
|
637
|
+
throw Error('this.getReverseMappedRawValues is no longer valid. Entity has been destroyed.');
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
let propertyValues = {};
|
|
641
|
+
_.forOwn(this.properties, (property) => {
|
|
642
|
+
const reverseMapped = Entity.getReverseMappedRawValue(property);
|
|
643
|
+
_.merge(propertyValues, reverseMapped);
|
|
644
|
+
});
|
|
645
|
+
return propertyValues;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Convenience function
|
|
650
|
+
* Build a new entity with this data
|
|
651
|
+
*/
|
|
652
|
+
getDataForNewEntity = () => {
|
|
653
|
+
if (this.isDestroyed) {
|
|
654
|
+
throw Error('this.getDataForNewEntity is no longer valid. Entity has been destroyed.');
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
return this.getReverseMappedRawValues();
|
|
658
|
+
}
|
|
659
|
+
|
|
581
660
|
/**
|
|
582
661
|
* Gets the values that have changed since last time saved
|
|
583
662
|
* @return {array|boolean} diff - Array of property names that have changed, or false
|
package/src/Property/Property.js
CHANGED
|
@@ -405,6 +405,17 @@ export default class Property extends EventEmitter {
|
|
|
405
405
|
return this.__proto__.constructor.className;
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
+
/**
|
|
409
|
+
* Gets the mapped name of this Property.
|
|
410
|
+
* @return {string} name
|
|
411
|
+
*/
|
|
412
|
+
getMapping = () => {
|
|
413
|
+
if (this.isDestroyed) {
|
|
414
|
+
throw Error('this.getMapping is no longer valid. Property has been destroyed.');
|
|
415
|
+
}
|
|
416
|
+
return this.mapping;
|
|
417
|
+
}
|
|
418
|
+
|
|
408
419
|
/**
|
|
409
420
|
* Destroy this object.
|
|
410
421
|
* - Removes all circular references to parent objects
|
|
@@ -847,10 +847,11 @@ export default class Repository extends EventEmitter {
|
|
|
847
847
|
* Creates a single new Entity in storage medium.
|
|
848
848
|
* @param {object} data - Either raw data object or Entity. If raw data, keys are Property names, Values are Property values.
|
|
849
849
|
* @param {boolean} isPersisted - Whether the new entity should be marked as already being persisted in storage medium.
|
|
850
|
+
* @param {boolean} originalIsMapped - Has data already been mapped according to schema?
|
|
850
851
|
* @return {object} entity - new Entity object
|
|
851
852
|
* @fires add
|
|
852
853
|
*/
|
|
853
|
-
add = async (data, isPersisted) => {
|
|
854
|
+
add = async (data, isPersisted = false, originalIsMapped = false) => {
|
|
854
855
|
if (this.isDestroyed) {
|
|
855
856
|
throw Error('this.add is no longer valid. Repository has been destroyed.');
|
|
856
857
|
}
|
|
@@ -858,7 +859,7 @@ export default class Repository extends EventEmitter {
|
|
|
858
859
|
let entity = data;
|
|
859
860
|
if (!(data instanceof Entity)) {
|
|
860
861
|
// Create the new entity
|
|
861
|
-
entity = Repository._createEntity(this.schema, data, this, isPersisted);
|
|
862
|
+
entity = Repository._createEntity(this.schema, data, this, isPersisted, originalIsMapped);
|
|
862
863
|
}
|
|
863
864
|
this._relayEntityEvents(entity);
|
|
864
865
|
this.entities.push(entity);
|
|
@@ -877,20 +878,48 @@ export default class Repository extends EventEmitter {
|
|
|
877
878
|
return entity;
|
|
878
879
|
}
|
|
879
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.createId(); // either a UUID or a temp id
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return entity;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Convenience function to add entity with mapped data.
|
|
904
|
+
*/
|
|
905
|
+
addMapped = (data, isPersisted = false) => {
|
|
906
|
+
return this.add(data, isPersisted, true);
|
|
907
|
+
}
|
|
908
|
+
|
|
880
909
|
/**
|
|
881
910
|
* Convenience function to create multiple new Entities in storage medium.
|
|
882
911
|
* @param {array} data - Array of data objects or Entities.
|
|
883
912
|
* @param {boolean} isPersisted - Whether the new entities should be marked as already being persisted in storage medium.
|
|
884
913
|
* @return {array} entities - new Entity objects
|
|
885
914
|
*/
|
|
886
|
-
addMultiple = async (allData, isPersisted) => {
|
|
915
|
+
addMultiple = async (allData, isPersisted = false, originalIsMapped = false) => {
|
|
887
916
|
|
|
888
917
|
let entities = [],
|
|
889
918
|
i;
|
|
890
919
|
|
|
891
920
|
for (i = 0; i < allData.length; i++) {
|
|
892
921
|
const data = allData[i],
|
|
893
|
-
entity = await this.add(data, isPersisted);
|
|
922
|
+
entity = await this.add(data, isPersisted, originalIsMapped);
|
|
894
923
|
entities.push(entity);
|
|
895
924
|
};
|
|
896
925
|
|
|
@@ -907,8 +936,8 @@ export default class Repository extends EventEmitter {
|
|
|
907
936
|
* @return {object} entity - new Entity object
|
|
908
937
|
* @private
|
|
909
938
|
*/
|
|
910
|
-
static _createEntity = (schema, rawData, repository = null, isPersisted = false) => {
|
|
911
|
-
const entity = new Entity(schema, rawData, repository);
|
|
939
|
+
static _createEntity = (schema, rawData, repository = null, isPersisted = false, originalIsMapped = false) => {
|
|
940
|
+
const entity = new Entity(schema, rawData, repository, originalIsMapped);
|
|
912
941
|
entity.initialize();
|
|
913
942
|
entity.isPersisted = isPersisted;
|
|
914
943
|
return entity;
|