@onehat/data 1.6.6 → 1.6.10

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.
@@ -42,7 +42,10 @@ const GroupsUsers = {
42
42
  },
43
43
  },
44
44
 
45
- repository: 'onebuild',
45
+ repository: {
46
+ type: 'onebuild',
47
+ autoSave: false,
48
+ },
46
49
 
47
50
  };
48
51
 
@@ -137,6 +137,12 @@ describe('Entity', function() {
137
137
  expect(this.entity.bar).to.be.eq('one');
138
138
  expect(this.entity.isDirty).to.be.false;
139
139
  expect(_.isEqual(this.entity.getParsedValues(), originalData)).to.be.true;
140
+
141
+ this.entity.delete();
142
+ expect(this.entity.isDeleted).to.be.true;
143
+
144
+ this.entity.reset();
145
+ expect(this.entity.isDeleted).to.be.false;
140
146
  });
141
147
 
142
148
  it('getMappedValue', function() {
@@ -533,6 +539,9 @@ describe('Entity', function() {
533
539
  it('markDeleted', function() {
534
540
  this.entity.markDeleted();
535
541
  expect(this.entity.isDeleted).to.be.true;
542
+
543
+ this.entity.markDeleted(false);
544
+ expect(this.entity.isDeleted).to.be.false;
536
545
  });
537
546
 
538
547
  it('delete', function() {
@@ -540,6 +549,11 @@ describe('Entity', function() {
540
549
  expect(this.entity.isDeleted).to.be.true;
541
550
  });
542
551
 
552
+ it('undelete', function() {
553
+ this.entity.delete();
554
+ this.entity.undelete();
555
+ expect(this.entity.isDeleted).to.be.false;
556
+ });
543
557
  });
544
558
 
545
559
  describe('events', function() {
@@ -594,6 +608,20 @@ describe('Entity', function() {
594
608
  expect(didFire).to.be.true;
595
609
  });
596
610
 
611
+ it('undelete', function() {
612
+ this.entity.delete();
613
+ expect(this.entity.isDeleted).to.be.true;
614
+
615
+ let didFire = false;
616
+ this.entity.on('undelete', () => {
617
+ didFire = true;
618
+ });
619
+ this.entity.undelete();
620
+
621
+ expect(didFire).to.be.true;
622
+ expect(this.entity.isDeleted).to.be.false;
623
+ });
624
+
597
625
  it('destroy', function() {
598
626
  let didFire = false;
599
627
  this.entity.on('destroy', () => {
@@ -1,4 +1,10 @@
1
1
  import { OneHatData } from '../../src/OneHatData';
2
+ import GroupsDefinition from '../fixtures/Definitions/Groups';
3
+ import GroupsUsersDefinition from '../fixtures/Definitions/GroupsUsers';
4
+ import UsersDefinition from '../fixtures/Definitions/Users';
5
+ import groupsUserData from '../fixtures/Data/GroupsUser';
6
+ import KeyValues from '../../src/Schema/KeyValues';
7
+
2
8
 
3
9
  // NOTE: Cypress can't handle async functions for beforeEach,
4
10
  // so we have to manually apply it to every test. Ugh!
@@ -110,7 +116,7 @@ describe('OneHatData', function() {
110
116
  (async function() {
111
117
  await beforeEach();
112
118
 
113
- const repository = this.oneHatData.createRepository('bar');
119
+ const repository = await this.oneHatData.createRepository('bar');
114
120
  expect(repository.id).to.be.not.eq(this.repository.id);
115
121
 
116
122
  afterEach();
@@ -128,7 +134,7 @@ describe('OneHatData', function() {
128
134
  { name: 'baz', },
129
135
  ]);
130
136
  const schemas = oneHatData.schemas;
131
- oneHatData.createRepositories(schemas);
137
+ await oneHatData.createRepositories(schemas);
132
138
 
133
139
  const result = oneHatData.getAllRepositories();
134
140
  expect(_.size(result)).to.be.eq(3);
@@ -147,7 +153,7 @@ describe('OneHatData', function() {
147
153
  { name: 'bar', },
148
154
  { name: 'baz', },
149
155
  ]);
150
- oneHatData.createBoundRepositories();
156
+ await oneHatData.createBoundRepositories();
151
157
 
152
158
  const schemas = oneHatData.schemas;
153
159
  let bound = 0;
@@ -183,13 +189,35 @@ describe('OneHatData', function() {
183
189
  oneHatData.createSchemas([
184
190
  { name: 'foo', },
185
191
  ]);
186
- oneHatData.createBoundRepositories();
192
+ await oneHatData.createBoundRepositories();
187
193
  expect(oneHatData.hasRepository('foo')).to.be.true;
188
194
 
189
195
  afterEach();
190
196
  })();
191
197
  });
192
198
 
199
+ it('Entity.getAssociatedRepository', function() {
200
+ (async function() {
201
+ await beforeEach();
202
+
203
+ const oneHatData = new OneHatData();
204
+
205
+ await oneHatData.createSchemas([
206
+ GroupsDefinition,
207
+ GroupsUsersDefinition,
208
+ UsersDefinition,
209
+ ]);
210
+ await oneHatData.createBoundRepositories();
211
+ const GroupsUsers = oneHatData.getRepository('GroupsUsers');
212
+ const groupsUser = await GroupsUsers.add(groupsUserData);
213
+ const Users = groupsUser.getAssociatedRepository('Users');
214
+
215
+ expect(Users).to.be.not.null;
216
+
217
+ afterEach();
218
+ })();
219
+ });
220
+
193
221
  it('hasRepositoryWithId', function() {
194
222
  (async function() {
195
223
  await beforeEach();
@@ -264,9 +292,9 @@ describe('OneHatData', function() {
264
292
  await beforeEach();
265
293
 
266
294
  const oneHatData = this.oneHatData;
267
- oneHatData.createRepository('bar');
268
- oneHatData.createRepository('bar');
269
- oneHatData.createRepository('bar');
295
+ await oneHatData.createRepository('bar');
296
+ await oneHatData.createRepository('bar');
297
+ await oneHatData.createRepository('bar');
270
298
  const result = oneHatData.getRepositoriesBySchema(this.schema);
271
299
  expect(_.size(result)).to.be.eq(4);
272
300
 
@@ -274,22 +302,22 @@ describe('OneHatData', function() {
274
302
  })();
275
303
  });
276
304
 
277
- it('createGlobalErrorHandler', function() {
278
- (async function() {
279
- await beforeEach();
305
+ // it('createGlobalErrorHandler', function() {
306
+ // (async function() {
307
+ // await beforeEach();
280
308
 
281
- let message = '';
282
- const oneHatData = this.oneHatData,
283
- errorHandler = (a, b) => {
284
- debugger;
285
- };
286
- oneHatData.createGlobalErrorHandler(errorHandler);
287
- oneHatData.emitError();
288
- expect(message).to.be.eq('Test here');
309
+ // let message = '';
310
+ // const oneHatData = this.oneHatData,
311
+ // errorHandler = (a, b) => {
312
+ // debugger;
313
+ // };
314
+ // oneHatData.createGlobalErrorHandler(errorHandler);
315
+ // oneHatData.emitError();
316
+ // expect(message).to.be.eq('Test here');
289
317
 
290
- afterEach();
291
- })();
292
- });
318
+ // afterEach();
319
+ // })();
320
+ // });
293
321
 
294
322
  it('setOptionsOnAllRepositories', function() {
295
323
  (async function() {
@@ -323,14 +351,16 @@ describe('OneHatData', function() {
323
351
  await beforeEach();
324
352
 
325
353
  const oneHatData = new OneHatData();
326
- const repositories = oneHatData.createSchemas([
354
+
355
+ await oneHatData.createSchemas([
327
356
  { name: 'foo', },
328
357
  { name: 'bar', },
329
358
  { name: 'baz', },
330
359
  ])
331
- .createBoundRepositories()
332
- .getAllRepositories();
333
-
360
+ .createBoundRepositories();
361
+
362
+ // NOTE: Can't chain getAllRepositories() because we have to wait for createBoundRepositories to finish
363
+ const repositories = oneHatData.getAllRepositories();
334
364
  expect(_.size(repositories)).to.be.eq(3);
335
365
 
336
366
  afterEach();
@@ -349,8 +379,8 @@ describe('OneHatData', function() {
349
379
  { key: '4', value: 'four', },
350
380
  { key: '5', value: 'five', },
351
381
  ],
352
- repository = oneHatData.createRepository({
353
- schema: 'KeyValues',
382
+ repository = await oneHatData.createRepository({
383
+ schema: KeyValues,
354
384
  data,
355
385
  });
356
386
 
@@ -18,12 +18,45 @@ describe('CurrencyProperty', function() {
18
18
  });
19
19
 
20
20
  it('default value', function() {
21
- const property = this.property,
22
- rawValue = property.getDefaultValue();
23
- property.pauseEvents();
24
- property.setValue(rawValue);
25
- property.resumeEvents();
26
21
  expect(this.property.submitValue).to.be.eq('0.00');
22
+
23
+ const rawValue = this.property.getDefaultValue();
24
+ this.property.setValue(rawValue);
25
+ expect(this.property.submitValue).to.be.eq('0.00');
26
+ });
27
+
28
+ it('isZero', function() {
29
+ this.property.setValue('0.00');
30
+ expect(this.property.isZero).to.be.true;
31
+
32
+ this.property.setValue('0.01');
33
+ expect(this.property.isZero).to.be.false;
34
+
35
+ this.property.setValue('-0.01');
36
+ expect(this.property.isZero).to.be.false;
37
+
38
+ this.property.setValue(null);
39
+ expect(this.property.isZero).to.be.false;
40
+
41
+ this.property.setValue();
42
+ expect(this.property.isZero).to.be.false;
43
+ });
44
+
45
+ it('hasValue', function() {
46
+ this.property.setValue('0.00');
47
+ expect(this.property.hasValue).to.be.true;
48
+
49
+ this.property.setValue('0.01');
50
+ expect(this.property.hasValue).to.be.true;
51
+
52
+ this.property.setValue('-0.01');
53
+ expect(this.property.hasValue).to.be.true;
54
+
55
+ this.property.setValue(null);
56
+ expect(this.property.hasValue).to.be.false;
57
+
58
+ this.property.setValue();
59
+ expect(this.property.hasValue).to.be.false;
27
60
  });
28
61
 
29
62
  });
@@ -41,8 +74,8 @@ describe('CurrencyProperty', function() {
41
74
  });
42
75
 
43
76
  it('numeric string', function() {
44
- const parsed = this.property.parse('This is 1234 dollars.');
45
- expect(parsed).to.be.eq(1234);
77
+ const parsed = this.property.parse('This is $1234.56 dollars.');
78
+ expect(parsed).to.be.eq(1234.56);
46
79
  });
47
80
 
48
81
  it('non-numeric string', function() {
@@ -111,6 +111,33 @@ describe('OneBuildRepository', function() {
111
111
  await this.repository.add({ key: 6, value: 'six' });
112
112
  expect(_.size(this.repository.entities)).to.be.eq(1);
113
113
  });
114
+
115
+ it.only('sortInMemory', function() {
116
+
117
+ const repository = this.repository;
118
+
119
+ // Create two phantom records, out of order
120
+ repository.setAutoSave(false);
121
+ repository.add({ key: 5, value: 'Five', });
122
+ repository.add({ key: 4, value: 'One', });
123
+ repository.add({ key: 2, value: 'Two', });
124
+ repository.add({ key: 3, value: 'Three', });
125
+ repository.add({ key: 1, value: 'One', });
126
+
127
+ repository.sorters = [
128
+ { name: 'value', direction: 'DESC', }, // 2,3,4,1,5
129
+ { name: 'key', direction: 'ASC', }, // 2,3,1,4,5
130
+ ];
131
+ repository.sortInMemory();
132
+
133
+ // Check that they are correct order
134
+ const entities = repository.entities;
135
+ expect(entities[0].key).to.be.eq(2);
136
+ expect(entities[1].key).to.be.eq(3);
137
+ expect(entities[2].key).to.be.eq(1);
138
+ expect(entities[3].key).to.be.eq(4);
139
+ expect(entities[4].key).to.be.eq(5);
140
+ });
114
141
 
115
142
  });
116
143
 
@@ -718,6 +718,10 @@ describe('Repository Base', function() {
718
718
  // deleteById
719
719
  // deleteDirty
720
720
  // deletePhantom
721
+ // undeleteByIx
722
+ // undeleteByRange
723
+ // undeleteBy
724
+ // undeleteById
721
725
 
722
726
  });
723
727
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.6.6",
3
+ "version": "1.6.10",
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
@@ -52,6 +52,7 @@ class Entity extends EventEmitter {
52
52
  'reload',
53
53
  'save',
54
54
  'delete',
55
+ 'undelete',
55
56
  'destroy',
56
57
  ]);
57
58
 
@@ -286,7 +287,7 @@ class Entity extends EventEmitter {
286
287
 
287
288
  /**
288
289
  * Resets the Entity to a state as if it had just been created,
289
- * all based on _originalData.
290
+ * Gets data to restore from _originalData.
290
291
  */
291
292
  reset = () => {
292
293
  if (this.isDestroyed) {
@@ -297,6 +298,10 @@ class Entity extends EventEmitter {
297
298
  this._resetPropertyValues();
298
299
  this._originalDataParsed = this.getParsedValues();
299
300
 
301
+ if (this.isDeleted) {
302
+ this.undelete();
303
+ }
304
+
300
305
  this.emit('reset', this._proxy);
301
306
  }
302
307
 
@@ -1047,18 +1052,17 @@ class Entity extends EventEmitter {
1047
1052
 
1048
1053
  /**
1049
1054
  * Marks an entity as having been saved to storage medium.
1055
+ * @param {boolean} bool - How it should be marked. Defaults to true.
1050
1056
  */
1051
- markDeleted = () => {
1057
+ markDeleted = (bool = true) => {
1052
1058
  if (this.isDestroyed) {
1053
1059
  throw Error('this.markDeleted is no longer valid. Entity has been destroyed.');
1054
1060
  }
1055
- this.isDeleted = true;
1061
+ this.isDeleted = bool;
1056
1062
  }
1057
1063
 
1058
1064
  /**
1059
1065
  * Tells the Repository to delete this entity from the storage medium.
1060
- * @param {function} onSuccess - Optional function to execute when save operation successfully completes.
1061
- * @param {function} onFailure - Optional function to execute when save operation successfully completes.
1062
1066
  * @fires delete
1063
1067
  */
1064
1068
  delete = () => {
@@ -1072,6 +1076,23 @@ class Entity extends EventEmitter {
1072
1076
  }
1073
1077
  }
1074
1078
 
1079
+ /**
1080
+ * Marks a deleted entity as undeleted.
1081
+ * Only works when autoSave is off for the containing repository
1082
+ * @fires delete
1083
+ */
1084
+ undelete = () => {
1085
+ if (this.isDestroyed) {
1086
+ throw Error('this.undelete is no longer valid. Entity has been destroyed.');
1087
+ }
1088
+ const repository = this.getRepository();
1089
+ if (repository && repository.autoSave) {
1090
+ throw Error('Cannot undelete entities on an autoSave repository.');
1091
+ }
1092
+ this.markDeleted(false);
1093
+ this.emit('undelete', this._proxy);
1094
+ }
1095
+
1075
1096
  /**
1076
1097
  * Destroy this object.
1077
1098
  * - Removes all circular references to parent objects
@@ -64,6 +64,28 @@ export default class CurrencyProperty extends Property {
64
64
  return this.parsedValue;
65
65
  }
66
66
 
67
+ /**
68
+ * Determines if a currency is equal to 0.00
69
+ * Returns false if no value set.
70
+ */
71
+ get isZero() {
72
+ if (this.isDestroyed) {
73
+ throw Error('this.isZero is no longer valid. Property has been destroyed.');
74
+ }
75
+ return this.parsedValue === 0;
76
+ }
77
+
78
+ /**
79
+ * Determines if a currency has a non-null value
80
+ */
81
+ get hasValue() {
82
+ if (this.isDestroyed) {
83
+ throw Error('this.hasValue is no longer valid. Property has been destroyed.');
84
+ }
85
+ console.log(this.parsedValue);
86
+ return !_.isNil(this.parsedValue);
87
+ }
88
+
67
89
  };
68
90
 
69
91
  CurrencyProperty.className = 'Currency';
@@ -653,6 +653,24 @@ class AjaxRepository extends Repository {
653
653
  return this.reader.read(result);
654
654
  }
655
655
 
656
+ /**
657
+ * Sorts the items in the current page of memory
658
+ * This is mainly used to sort isPhantom entities,
659
+ * since the server normally sorts, and they haven't yet gone to server.
660
+ */
661
+ sortInMemory = () => {
662
+ const sorters = this.sorters;
663
+ let sortNames = [],
664
+ sortDirections = [];
665
+ _.each(sorters, (sorter) => {
666
+ sortNames.push(sorter.name);
667
+ sortDirections.push(sorter.direction.toLowerCase());
668
+ });
669
+ let entities = this.entities;
670
+ entities = _.orderBy(entities, sortNames, sortDirections);
671
+ this.entities = entities;
672
+ }
673
+
656
674
  /**
657
675
  * Helper for save.
658
676
  * Takes an array of Promises from axios. When they are all resolved,
@@ -1478,6 +1478,44 @@ export default class Repository extends EventEmitter {
1478
1478
  await this.delete(this.getPhantom());
1479
1479
  }
1480
1480
 
1481
+ /**
1482
+ * Deletes a single Entity by its index (zero-indexed) on the current page
1483
+ * @param {integer} ix - Index
1484
+ * @return {object} entity - Entity
1485
+ */
1486
+ undeleteByIx = async (ix) => {
1487
+ await this.undelete(this.getByIx(ix));
1488
+ }
1489
+
1490
+ /**
1491
+ * Deletes multiple Entities by their range of indices
1492
+ * (zero-indexed) on the current page
1493
+ * @param {integer} startIx - Index
1494
+ * @param {integer} endIx - Index (inclusive)
1495
+ * @return {array} entities - Array of Entities
1496
+ */
1497
+ undeleteByRange = async (startIx, endIx) => {
1498
+ await this.undelete(this.getByRange(startIx, endIx));
1499
+ }
1500
+
1501
+ /**
1502
+ * Remove multiple Entities by supplied filter function
1503
+ * @param {function} fn - Filter function to apply to all entities
1504
+ * @return {Entity[]} Entities that passed through filter
1505
+ */
1506
+ undeleteBy = async (filter) => {
1507
+ await this.undelete(this.getBy(filter));
1508
+ }
1509
+
1510
+ /**
1511
+ * Remove a single Entity by its id
1512
+ * @param {integer} id - id of record to retrieve
1513
+ * @return {Entity} The Entity with matching id
1514
+ */
1515
+ undeleteById = async (id) => {
1516
+ await this.undelete(this.getById(id));
1517
+ }
1518
+
1481
1519
 
1482
1520
 
1483
1521