@onehat/data 1.7.11 → 1.7.15
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 +15 -0
- package/package.json +1 -1
- package/src/Entity.js +13 -0
- package/src/Repository/Ajax.js +7 -7
- package/src/Repository/Memory.js +17 -3
- package/src/Repository/Null.js +2 -3
- package/src/Repository/Repository.js +42 -46
|
@@ -580,6 +580,21 @@ describe('Entity', function() {
|
|
|
580
580
|
this.entity.markStaged(false);
|
|
581
581
|
expect(this.entity.isStaged).to.be.false;
|
|
582
582
|
});
|
|
583
|
+
|
|
584
|
+
it('setValue changed lastModified', function() {
|
|
585
|
+
let earlyLastModified,
|
|
586
|
+
lateLastModified;
|
|
587
|
+
|
|
588
|
+
earlyLastModified = this.entity.lastModified;
|
|
589
|
+
this.entity.setValue('foo', '125');
|
|
590
|
+
lateLastModified = this.entity.lastModified;
|
|
591
|
+
expect(earlyLastModified < lateLastModified).to.be.true;
|
|
592
|
+
|
|
593
|
+
earlyLastModified = this.entity.lastModified;
|
|
594
|
+
this.entity.foo = '126';
|
|
595
|
+
lateLastModified = this.entity.lastModified;
|
|
596
|
+
expect(earlyLastModified < lateLastModified).to.be.true;
|
|
597
|
+
});
|
|
583
598
|
});
|
|
584
599
|
|
|
585
600
|
describe('events', function() {
|
package/package.json
CHANGED
package/src/Entity.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import EventEmitter from '@onehat/events';
|
|
4
4
|
import PropertyTypes from './Property';
|
|
5
|
+
import moment from 'moment';
|
|
5
6
|
import _ from 'lodash';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -129,6 +130,11 @@ class Entity extends EventEmitter {
|
|
|
129
130
|
*/
|
|
130
131
|
this.isDestroyed = false;
|
|
131
132
|
|
|
133
|
+
/**
|
|
134
|
+
* @member {string} lastModified - Last time this entity was modified
|
|
135
|
+
*/
|
|
136
|
+
this.lastModified = null;
|
|
137
|
+
|
|
132
138
|
// This ES6 Proxy allows us to create magic getters and setters for all property values.
|
|
133
139
|
// However, these getters and setters are *not* available within the Entity itself.
|
|
134
140
|
this._proxy = new Proxy(this, {
|
|
@@ -325,6 +331,7 @@ class Entity extends EventEmitter {
|
|
|
325
331
|
if (this.isDeleted) {
|
|
326
332
|
this.undelete();
|
|
327
333
|
}
|
|
334
|
+
this.setLastModified();
|
|
328
335
|
|
|
329
336
|
this.emit('reset', this._proxy);
|
|
330
337
|
}
|
|
@@ -399,6 +406,10 @@ class Entity extends EventEmitter {
|
|
|
399
406
|
}
|
|
400
407
|
return value;
|
|
401
408
|
}
|
|
409
|
+
|
|
410
|
+
setLastModified = () => {
|
|
411
|
+
this.lastModified = moment(new Date()).format('YYYY-MM-DD HH:mm:ss.SSSS');
|
|
412
|
+
}
|
|
402
413
|
|
|
403
414
|
|
|
404
415
|
// ______ __ __
|
|
@@ -990,6 +1001,7 @@ class Entity extends EventEmitter {
|
|
|
990
1001
|
}
|
|
991
1002
|
|
|
992
1003
|
idProperty.isTempId = false;
|
|
1004
|
+
this.setLastModified();
|
|
993
1005
|
|
|
994
1006
|
return isChanged;
|
|
995
1007
|
}
|
|
@@ -1034,6 +1046,7 @@ class Entity extends EventEmitter {
|
|
|
1034
1046
|
}
|
|
1035
1047
|
property.resumeEvents();
|
|
1036
1048
|
});
|
|
1049
|
+
this.setLastModified();
|
|
1037
1050
|
|
|
1038
1051
|
if (isChanged) {
|
|
1039
1052
|
this._recalculateDependentProperties();
|
package/src/Repository/Ajax.js
CHANGED
|
@@ -362,7 +362,7 @@ class AjaxRepository extends Repository {
|
|
|
362
362
|
throw new Error('No "get" api endpoint defined.');
|
|
363
363
|
}
|
|
364
364
|
this.emit('beforeLoad'); // TODO: canceling beforeLoad will cancel the load operation
|
|
365
|
-
this.
|
|
365
|
+
this.markLoading();
|
|
366
366
|
|
|
367
367
|
|
|
368
368
|
if (!_.isNil(params) && _.isObject(params)) {
|
|
@@ -407,9 +407,9 @@ class AjaxRepository extends Repository {
|
|
|
407
407
|
// Set the total records that pass filter
|
|
408
408
|
this.total = total;
|
|
409
409
|
this._setPaginationVars();
|
|
410
|
-
|
|
411
|
-
this.
|
|
412
|
-
|
|
410
|
+
|
|
411
|
+
this.markLoaded();
|
|
412
|
+
|
|
413
413
|
this.emit('changeData', this.entities);
|
|
414
414
|
this.emit('load', this);
|
|
415
415
|
})
|
|
@@ -432,7 +432,7 @@ class AjaxRepository extends Repository {
|
|
|
432
432
|
throw new Error('No "get" api endpoint defined.');
|
|
433
433
|
}
|
|
434
434
|
this.emit('beforeLoad'); // TODO: canceling beforeLoad will cancel the load operation
|
|
435
|
-
this.
|
|
435
|
+
this.markLoading();
|
|
436
436
|
|
|
437
437
|
const params = this._getReloadEntityParams(entity);
|
|
438
438
|
if (this.debugMode) {
|
|
@@ -460,8 +460,8 @@ class AjaxRepository extends Repository {
|
|
|
460
460
|
entity.loadOriginalData(updatedData, true);
|
|
461
461
|
entity.emit('reload', entity);
|
|
462
462
|
|
|
463
|
-
this.
|
|
464
|
-
|
|
463
|
+
this.markLoaded();
|
|
464
|
+
|
|
465
465
|
this.emit('changeData', this.entities);
|
|
466
466
|
this.emit('load', this);
|
|
467
467
|
this.emit('reloadEntity', entity);
|
package/src/Repository/Memory.js
CHANGED
|
@@ -95,7 +95,7 @@ class MemoryRepository extends Repository {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
this.emit('beforeLoad');
|
|
98
|
-
this.
|
|
98
|
+
this.markLoading();
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
const isDirectLoad = !_.isNil(data);
|
|
@@ -143,8 +143,7 @@ class MemoryRepository extends Repository {
|
|
|
143
143
|
await this._saveToStorage(entities);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
this.
|
|
147
|
-
this.isLoaded = true;
|
|
146
|
+
this.markLoaded();
|
|
148
147
|
this._recalculate(); // fires changeData if needed
|
|
149
148
|
this.emit('load', entities, this);
|
|
150
149
|
return entities;
|
|
@@ -444,6 +443,21 @@ class MemoryRepository extends Repository {
|
|
|
444
443
|
})
|
|
445
444
|
}
|
|
446
445
|
/* */
|
|
446
|
+
|
|
447
|
+
removeEntity(entity) { // standard function notation
|
|
448
|
+
const id = entity.id;
|
|
449
|
+
|
|
450
|
+
super.removeEntity(entity);
|
|
451
|
+
|
|
452
|
+
if (this.hasSorters) {
|
|
453
|
+
this._applySorters();
|
|
454
|
+
}
|
|
455
|
+
if (this.hasFilters) {
|
|
456
|
+
this._applyFilters();
|
|
457
|
+
}
|
|
458
|
+
delete this._keyedEntities[id];
|
|
459
|
+
}
|
|
460
|
+
|
|
447
461
|
|
|
448
462
|
|
|
449
463
|
|
package/src/Repository/Null.js
CHANGED
|
@@ -46,7 +46,7 @@ class NullRepository extends Repository {
|
|
|
46
46
|
throw Error('this.load is no longer valid. Repository has been destroyed.');
|
|
47
47
|
}
|
|
48
48
|
this.emit('beforeLoad');
|
|
49
|
-
this.
|
|
49
|
+
this.markLoading();
|
|
50
50
|
|
|
51
51
|
if (data) {
|
|
52
52
|
let entities = data;
|
|
@@ -65,8 +65,7 @@ class NullRepository extends Repository {
|
|
|
65
65
|
|
|
66
66
|
this._updateSize();
|
|
67
67
|
|
|
68
|
-
this.
|
|
69
|
-
this.isLoaded = true;
|
|
68
|
+
this.markLoaded();
|
|
70
69
|
this.emit('changeData', this.entities);
|
|
71
70
|
this.emit('load', this);
|
|
72
71
|
}
|
|
@@ -5,6 +5,7 @@ import Entity from '../Entity';
|
|
|
5
5
|
import {
|
|
6
6
|
v4 as uuid,
|
|
7
7
|
} from 'uuid';
|
|
8
|
+
import moment from 'moment';
|
|
8
9
|
import _ from 'lodash';
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -194,6 +195,11 @@ export default class Repository extends EventEmitter {
|
|
|
194
195
|
*/
|
|
195
196
|
this.isLoading = false;
|
|
196
197
|
|
|
198
|
+
/**
|
|
199
|
+
* @member {string} lastLoaded - Last time this repository was loaded
|
|
200
|
+
*/
|
|
201
|
+
this.lastLoaded = null;
|
|
202
|
+
|
|
197
203
|
/**
|
|
198
204
|
* @member {Boolean} isSaving - State: whether or not entities are currently being saved
|
|
199
205
|
*/
|
|
@@ -266,9 +272,10 @@ export default class Repository extends EventEmitter {
|
|
|
266
272
|
}
|
|
267
273
|
|
|
268
274
|
this._createMethods();
|
|
275
|
+
this._createStatics();
|
|
269
276
|
|
|
270
|
-
if (this.schema.repository.
|
|
271
|
-
this.schema.repository.
|
|
277
|
+
if (this.schema.repository.init) {
|
|
278
|
+
await this.schema.repository.init.call(this);
|
|
272
279
|
}
|
|
273
280
|
|
|
274
281
|
this.isInitialized = true;
|
|
@@ -291,6 +298,22 @@ export default class Repository extends EventEmitter {
|
|
|
291
298
|
}
|
|
292
299
|
}
|
|
293
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Creates the static properties for this Repository, based on Schema.
|
|
303
|
+
* @private
|
|
304
|
+
*/
|
|
305
|
+
_createStatics = () => {
|
|
306
|
+
if (this.isDestroyed) {
|
|
307
|
+
throw Error('this._createStatics is no longer valid. Entity has been destroyed.');
|
|
308
|
+
}
|
|
309
|
+
const staticsDefinitions = this.schema.repository.statics;
|
|
310
|
+
if (!_.isEmpty(staticsDefinitions)) {
|
|
311
|
+
_.each(staticsDefinitions, (value, key) => {
|
|
312
|
+
this[key] = value;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
294
317
|
|
|
295
318
|
// __ __
|
|
296
319
|
// / / ____ ____ _____/ /
|
|
@@ -306,6 +329,22 @@ export default class Repository extends EventEmitter {
|
|
|
306
329
|
throw new Error('load must be implemented by Repository subclass');
|
|
307
330
|
}
|
|
308
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Marks this repository as loading
|
|
334
|
+
*/
|
|
335
|
+
markLoading = () => {
|
|
336
|
+
this.isLoading = true;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Marks this repository as loaded
|
|
341
|
+
*/
|
|
342
|
+
markLoaded = () => {
|
|
343
|
+
this.isLoading = false;
|
|
344
|
+
this.isLoaded = true;
|
|
345
|
+
this.lastLoaded = moment(new Date()).format('YYYY-MM-DD HH:mm:ss.SSSS');
|
|
346
|
+
}
|
|
347
|
+
|
|
309
348
|
/**
|
|
310
349
|
* Reload data from storage medium, using previous settings.
|
|
311
350
|
* Subclasses may override this to provide additional
|
|
@@ -1255,49 +1294,6 @@ export default class Repository extends EventEmitter {
|
|
|
1255
1294
|
return associatedRepository;
|
|
1256
1295
|
}
|
|
1257
1296
|
|
|
1258
|
-
/**
|
|
1259
|
-
* Gets the Schema object
|
|
1260
|
-
* @return {Schema} schema
|
|
1261
|
-
*/
|
|
1262
|
-
getSchema = () => {
|
|
1263
|
-
if (this.isDestroyed) {
|
|
1264
|
-
throw Error('this.getSchema is no longer valid. Entity has been destroyed.');
|
|
1265
|
-
}
|
|
1266
|
-
return this.schema;
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
/**
|
|
1270
|
-
* Gets the associated Repository
|
|
1271
|
-
* @param {string} repositoryName - Name of the Repository to retrieve
|
|
1272
|
-
* @return {boolean} hasProperty
|
|
1273
|
-
*/
|
|
1274
|
-
getAssociatedRepository = (repositoryName) => {
|
|
1275
|
-
if (this.isDestroyed) {
|
|
1276
|
-
throw Error('this.getAssociatedRepository is no longer valid. Entity has been destroyed.');
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
const schema = this.getSchema();
|
|
1280
|
-
if (!schema.model.associations.hasOne.includes(repositoryName) &&
|
|
1281
|
-
!schema.model.associations.hasMany.includes(repositoryName) &&
|
|
1282
|
-
!schema.model.associations.belongsTo.includes(repositoryName) &&
|
|
1283
|
-
!schema.model.associations.belongsToMany.includes(repositoryName)
|
|
1284
|
-
) {
|
|
1285
|
-
throw Error(repositoryName + ' is not associated with this schema');
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
const oneHatData = this.oneHatData;
|
|
1289
|
-
if (!oneHatData) {
|
|
1290
|
-
throw Error('No global oneHatData object');
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
const associatedRepository = oneHatData.getRepository(repositoryName);
|
|
1294
|
-
if (!associatedRepository) {
|
|
1295
|
-
throw Error('Repository ' + repositoryName + ' cannot be found');
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
return associatedRepository;
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
1297
|
/**
|
|
1302
1298
|
* Utility function.
|
|
1303
1299
|
* Detects if entity is in the current page of the storage medium.
|
|
@@ -1621,7 +1617,7 @@ export default class Repository extends EventEmitter {
|
|
|
1621
1617
|
* Mainly used for phantom Entities
|
|
1622
1618
|
* Helper for delete()
|
|
1623
1619
|
*/
|
|
1624
|
-
removeEntity
|
|
1620
|
+
removeEntity(entity) { // standard function notation so it can be called by child class
|
|
1625
1621
|
this.entities = _.filter(this.entities, e => e !== entity);
|
|
1626
1622
|
entity.destroy();
|
|
1627
1623
|
}
|