@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.
@@ -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 definition = {
262
- name: 'foo',
263
- mapping: 'a.b.c',
264
- type: 'int',
265
- },
266
- Property = PropertyTypes[definition.type];
267
- const property = new Property(definition, 'fakeEntity');
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
- property.setValue('47');
296
+ property2.setValue('47');
270
297
 
271
- const reverseMapped = Entity.getReverseMappedRawValue(property),
272
- expected = { a: { b: { c: '47' }, }, };
298
+ const reverseMapped2 = Entity.getReverseMappedRawValue(property2),
299
+ expected2 = { foo: '47' };
273
300
 
274
- expect(_.isEqual(reverseMapped, expected)).to.be.true;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.4.13",
3
+ "version": "1.5.2",
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
@@ -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
@@ -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;