@onehat/data 1.22.48 → 1.23.1
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/e2e/OneHatData.cy.js +102 -0
- package/package.json +2 -1
- package/src/Integration/Browser/Repository/SecureLocalStorage.js +4 -5
- package/src/Integration/Browser/Repository/SecureSessionStorage.js +4 -5
- package/src/Integration/ReactNative/useOneHatData.js +21 -6
- package/src/OneHatData.js +234 -9
- package/src/Repository/Ajax.js +67 -38
- package/src/Repository/Repository.js +98 -43
|
@@ -28,6 +28,17 @@ async function beforeEach(that) {
|
|
|
28
28
|
}, true);
|
|
29
29
|
that.repository = that.oneHatData.getRepositoryById('foo');
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
async function waitForRepositoryInitialization(repository, timeout = 2000) {
|
|
33
|
+
const start = Date.now();
|
|
34
|
+
while (!repository.isInitialized) {
|
|
35
|
+
if (Date.now() - start > timeout) {
|
|
36
|
+
throw new Error('Repository did not initialize in time: ' + repository.name);
|
|
37
|
+
}
|
|
38
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
function afterEach(that) {
|
|
32
43
|
that.oneHatData.destroy();
|
|
33
44
|
}
|
|
@@ -274,6 +285,97 @@ describe('OneHatData', function() {
|
|
|
274
285
|
})();
|
|
275
286
|
});
|
|
276
287
|
|
|
288
|
+
it('getRepositoryAsync returns initialized unique repository', function() {
|
|
289
|
+
(async () => {
|
|
290
|
+
const that = {};
|
|
291
|
+
await beforeEach(that);
|
|
292
|
+
|
|
293
|
+
const repository = await that.oneHatData.getRepositoryAsync('bar', true);
|
|
294
|
+
|
|
295
|
+
expect(repository).to.be.ok;
|
|
296
|
+
expect(repository.isUnique).to.be.true;
|
|
297
|
+
expect(repository.isInitialized).to.be.true;
|
|
298
|
+
|
|
299
|
+
afterEach(that);
|
|
300
|
+
})();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('getRepositoryAsync returns initialized bound repository', function() {
|
|
304
|
+
(async () => {
|
|
305
|
+
const that = {};
|
|
306
|
+
await beforeEach(that);
|
|
307
|
+
|
|
308
|
+
const repository = await that.oneHatData.getRepositoryAsync('bar');
|
|
309
|
+
|
|
310
|
+
expect(repository).to.be.eq(that.repository);
|
|
311
|
+
expect(repository.isInitialized).to.be.true;
|
|
312
|
+
|
|
313
|
+
afterEach(that);
|
|
314
|
+
})();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('getRepository unique keeps filters isolated from bound repository', function() {
|
|
318
|
+
(async () => {
|
|
319
|
+
const that = {};
|
|
320
|
+
await beforeEach(that);
|
|
321
|
+
|
|
322
|
+
const
|
|
323
|
+
boundRepository = that.oneHatData.getRepository('bar'),
|
|
324
|
+
uniqueRepository = that.oneHatData.getRepository('bar', true);
|
|
325
|
+
|
|
326
|
+
await waitForRepositoryInitialization(uniqueRepository);
|
|
327
|
+
|
|
328
|
+
boundRepository.filter('key', 'bound-only');
|
|
329
|
+
expect(boundRepository.hasFilterValue('key', 'bound-only')).to.be.true;
|
|
330
|
+
expect(uniqueRepository.hasFilter('key')).to.be.false;
|
|
331
|
+
|
|
332
|
+
uniqueRepository.filter('key', 'unique-only');
|
|
333
|
+
expect(uniqueRepository.hasFilterValue('key', 'unique-only')).to.be.true;
|
|
334
|
+
expect(boundRepository.hasFilterValue('key', 'bound-only')).to.be.true;
|
|
335
|
+
expect(boundRepository.hasFilterValue('key', 'unique-only')).to.be.false;
|
|
336
|
+
|
|
337
|
+
afterEach(that);
|
|
338
|
+
})();
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('getRepository unique allows pre-init setBaseParams for Ajax repositories', function() {
|
|
342
|
+
(async () => {
|
|
343
|
+
const that = {};
|
|
344
|
+
await beforeEach(that);
|
|
345
|
+
|
|
346
|
+
that.oneHatData.createSchema({
|
|
347
|
+
name: 'meters',
|
|
348
|
+
model: {
|
|
349
|
+
idProperty: 'id',
|
|
350
|
+
displayProperty: 'name',
|
|
351
|
+
properties: [
|
|
352
|
+
{ name: 'id' },
|
|
353
|
+
{ name: 'name' },
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
repository: {
|
|
357
|
+
type: 'ajax',
|
|
358
|
+
api: {
|
|
359
|
+
get: 'meters',
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
await that.oneHatData.createRepository('meters', true);
|
|
364
|
+
|
|
365
|
+
const uniqueRepository = that.oneHatData.getRepository('meters', true);
|
|
366
|
+
|
|
367
|
+
expect(() => {
|
|
368
|
+
uniqueRepository.setBaseParams({
|
|
369
|
+
foo: 'bar',
|
|
370
|
+
});
|
|
371
|
+
}).to.not.throw();
|
|
372
|
+
|
|
373
|
+
expect(uniqueRepository.getBaseParam('foo')).to.be.eq('bar');
|
|
374
|
+
|
|
375
|
+
afterEach(that);
|
|
376
|
+
})();
|
|
377
|
+
});
|
|
378
|
+
|
|
277
379
|
it('getRepositoriesBy', function() {
|
|
278
380
|
(async function() {
|
|
279
381
|
await beforeEach();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onehat/data",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.1",
|
|
4
4
|
"description": "JS data modeling package with adapters for many storage mediums.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"async-wait-until": "^2.0.31",
|
|
42
42
|
"axios": "^1.17.0",
|
|
43
43
|
"chrono-node": "^2.9.1",
|
|
44
|
+
"crypto-es": "^2.1.0",
|
|
44
45
|
"he": "^1.2.0",
|
|
45
46
|
"js-base64": "^3.7.8",
|
|
46
47
|
"lodash": "^4.18.1",
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/** @module Repository */
|
|
2
2
|
|
|
3
3
|
import LocalStorageRepository from '@onehat/data/src/Integration/Browser/Repository/LocalStorage';
|
|
4
|
-
import
|
|
5
|
-
import AES from 'crypto-js/aes';
|
|
4
|
+
import CryptoES from 'crypto-es';
|
|
6
5
|
import _ from 'lodash';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Repository representing an encrypted version of the browser's LocalStorage implementation
|
|
10
|
-
* Requires crypto-
|
|
9
|
+
* Requires crypto-es - https://www.npmjs.com/package/crypto-es
|
|
11
10
|
* @extends LocalStorageRepository
|
|
12
11
|
*/
|
|
13
12
|
class SecureLocalStorageRepository extends LocalStorageRepository {
|
|
@@ -32,7 +31,7 @@ class SecureLocalStorageRepository extends LocalStorageRepository {
|
|
|
32
31
|
// BEGIN MOD
|
|
33
32
|
let result = this._store(name);
|
|
34
33
|
if (!_.isEmpty(result)) {
|
|
35
|
-
result = AES.decrypt(result, this.passphrase).toString(
|
|
34
|
+
result = CryptoES.AES.decrypt(result, this.passphrase).toString(CryptoES.enc.Utf8);
|
|
36
35
|
}
|
|
37
36
|
// END MOD
|
|
38
37
|
|
|
@@ -67,7 +66,7 @@ class SecureLocalStorageRepository extends LocalStorageRepository {
|
|
|
67
66
|
value = JSON.stringify(value);
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
value = AES.encrypt(value, this.passphrase).toString(); // MOD
|
|
69
|
+
value = CryptoES.AES.encrypt(value, this.passphrase).toString(); // MOD
|
|
71
70
|
|
|
72
71
|
const result = this._store(name, value);
|
|
73
72
|
this._broadcastStorageChange(name, 'set');
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/** @module Repository */
|
|
2
2
|
|
|
3
3
|
import SessionStorageRepository from '@onehat/data/src/Integration/Browser/Repository/SessionStorage';
|
|
4
|
-
import
|
|
5
|
-
import AES from 'crypto-js/aes';
|
|
4
|
+
import CryptoES from 'crypto-es';
|
|
6
5
|
import _ from 'lodash';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Repository representing an encrypted version of the browser's SessionStorage implementation
|
|
10
|
-
* Requires crypto-
|
|
9
|
+
* Requires crypto-es - https://www.npmjs.com/package/crypto-es
|
|
11
10
|
* @extends SessionStorageRepository
|
|
12
11
|
*/
|
|
13
12
|
class SecureSessionStorageRepository extends SessionStorageRepository {
|
|
@@ -27,7 +26,7 @@ class SecureSessionStorageRepository extends SessionStorageRepository {
|
|
|
27
26
|
// BEGIN MOD
|
|
28
27
|
let result = this._store.session(name);
|
|
29
28
|
if (!_.isEmpty(result)) {
|
|
30
|
-
result = AES.decrypt(result, this.passphrase).toString(
|
|
29
|
+
result = CryptoES.AES.decrypt(result, this.passphrase).toString(CryptoES.enc.Utf8);
|
|
31
30
|
}
|
|
32
31
|
// END MOD
|
|
33
32
|
|
|
@@ -46,7 +45,7 @@ class SecureSessionStorageRepository extends SessionStorageRepository {
|
|
|
46
45
|
value = JSON.stringify(value);
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
value = AES.encrypt(value, this.passphrase).toString(); // MOD
|
|
48
|
+
value = CryptoES.AES.encrypt(value, this.passphrase).toString(); // MOD
|
|
50
49
|
|
|
51
50
|
const result = this._store.session(name, value);
|
|
52
51
|
this._broadcastStorageChange(name, 'set');
|
|
@@ -28,12 +28,19 @@ export default function useOneHatData(schemaName, uniqueRepository = false) {
|
|
|
28
28
|
useEffect(() => {
|
|
29
29
|
|
|
30
30
|
let repository,
|
|
31
|
+
repositoryIdToDelete = null,
|
|
32
|
+
isMounted = true,
|
|
31
33
|
onChangeData = () => {};
|
|
32
34
|
(async () => {
|
|
33
35
|
|
|
34
36
|
if (uniqueRepository) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
if (_.isString(schemaName)) {
|
|
38
|
+
repository = await oneHatData.getRepositoryAsync(schemaName, true);
|
|
39
|
+
} else {
|
|
40
|
+
repository = await oneHatData.createRepository(schemaName);
|
|
41
|
+
repository.isUnique = true;
|
|
42
|
+
}
|
|
43
|
+
repositoryIdToDelete = repository?.id;
|
|
37
44
|
} else if (_.isObject(schemaName)) {
|
|
38
45
|
if (schemaName.id) {
|
|
39
46
|
repository = oneHatData.getRepositoryById(schemaName.id);
|
|
@@ -42,8 +49,13 @@ export default function useOneHatData(schemaName, uniqueRepository = false) {
|
|
|
42
49
|
repository = await oneHatData.createRepository(schemaName)
|
|
43
50
|
}
|
|
44
51
|
} else {
|
|
45
|
-
repository = oneHatData.
|
|
52
|
+
repository = await oneHatData.getRepositoryAsync(schemaName); // Get initialized bound Repository for this schema
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!isMounted || !repository) {
|
|
56
|
+
return;
|
|
46
57
|
}
|
|
58
|
+
|
|
47
59
|
onChangeData = () => {
|
|
48
60
|
setEntities(repository.entities); // Set new state in component
|
|
49
61
|
};
|
|
@@ -57,9 +69,12 @@ export default function useOneHatData(schemaName, uniqueRepository = false) {
|
|
|
57
69
|
})();
|
|
58
70
|
|
|
59
71
|
return () => {
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
|
|
72
|
+
isMounted = false;
|
|
73
|
+
if (repository) {
|
|
74
|
+
repository.off('changeData', onChangeData);
|
|
75
|
+
}
|
|
76
|
+
if (uniqueRepository && repositoryIdToDelete) {
|
|
77
|
+
oneHatData.deleteRepository(repositoryIdToDelete);
|
|
63
78
|
}
|
|
64
79
|
};
|
|
65
80
|
|
package/src/OneHatData.js
CHANGED
|
@@ -484,19 +484,244 @@ export class OneHatData extends EventEmitter {
|
|
|
484
484
|
return null;
|
|
485
485
|
}
|
|
486
486
|
if (unique) {
|
|
487
|
-
|
|
488
|
-
repoToClone = schema.getBoundRepository(),
|
|
489
|
-
clone = _.cloneDeep(repoToClone);
|
|
490
|
-
|
|
491
|
-
const id = uuid();
|
|
492
|
-
clone.name = clone.name + '-' + id;
|
|
493
|
-
clone.id = id;
|
|
494
|
-
clone.isUnique = true;
|
|
495
|
-
return clone;
|
|
487
|
+
return this._createUniqueRepositorySync(name);
|
|
496
488
|
}
|
|
497
489
|
return schema.getBoundRepository();
|
|
498
490
|
}
|
|
499
491
|
|
|
492
|
+
/**
|
|
493
|
+
* Async variant of getRepository.
|
|
494
|
+
* Useful when unique repositories are needed in frameworks that expect
|
|
495
|
+
* fully initialized objects before first use.
|
|
496
|
+
* @param {string} name - Name of Schema
|
|
497
|
+
* @param {boolean} unique - Whether to create a unique repository
|
|
498
|
+
* @param {number} timeout - Max ms to wait for initialization
|
|
499
|
+
* @return {Promise<Repository>} repository
|
|
500
|
+
*/
|
|
501
|
+
getRepositoryAsync = async (name, unique = false, timeout = 10000) => {
|
|
502
|
+
if (this.isDestroyed) {
|
|
503
|
+
throw new Error('this.getRepositoryAsync is no longer valid. OneHatData has been destroyed.');
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const repository = this.getRepository(name, unique);
|
|
507
|
+
if (!repository) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
await this._waitForRepositoryInitialization(repository, timeout);
|
|
512
|
+
return repository;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Creates an unbound unique Repository synchronously.
|
|
517
|
+
* It returns the instance immediately and initializes in background.
|
|
518
|
+
* @param {string} name - Name of Schema
|
|
519
|
+
* @return {Repository} repository
|
|
520
|
+
*/
|
|
521
|
+
_createUniqueRepositorySync = (name) => {
|
|
522
|
+
if (this.isDestroyed) {
|
|
523
|
+
throw new Error('this._createUniqueRepositorySync is no longer valid. OneHatData has been destroyed.');
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const schema = this.getSchema(name);
|
|
527
|
+
if (!schema) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const boundRepository = schema.getBoundRepository();
|
|
532
|
+
if (!boundRepository) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const id = uuid();
|
|
537
|
+
const schemaRepositoryDef = _.isString(schema.repository) ? { type: schema.repository } : schema.repository;
|
|
538
|
+
|
|
539
|
+
// Keep behavioral overrides while omitting mutable runtime state and nested repo instances.
|
|
540
|
+
const safeOverrides = _.omit(boundRepository.originalConfig || {}, [
|
|
541
|
+
'id',
|
|
542
|
+
'name',
|
|
543
|
+
'isUnique',
|
|
544
|
+
'local',
|
|
545
|
+
'remote',
|
|
546
|
+
'entities',
|
|
547
|
+
'filters',
|
|
548
|
+
'sorters',
|
|
549
|
+
'page',
|
|
550
|
+
'previousPage',
|
|
551
|
+
'pageTotal',
|
|
552
|
+
'pageStart',
|
|
553
|
+
'pageEnd',
|
|
554
|
+
'totalPages',
|
|
555
|
+
'total',
|
|
556
|
+
'isFiltered',
|
|
557
|
+
'isInitialized',
|
|
558
|
+
'isLoaded',
|
|
559
|
+
'isLoading',
|
|
560
|
+
'lastLoaded',
|
|
561
|
+
'hash',
|
|
562
|
+
]);
|
|
563
|
+
|
|
564
|
+
const config = _.merge({}, schemaRepositoryDef, this._repositoryGlobals, safeOverrides, {
|
|
565
|
+
schema,
|
|
566
|
+
id,
|
|
567
|
+
name: boundRepository.name + '-' + id,
|
|
568
|
+
isUnique: true,
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
const repository = this._createRepositorySync(config);
|
|
572
|
+
|
|
573
|
+
this.repositories[repository.id] = repository;
|
|
574
|
+
this._initializeRepositoryInBackground(repository);
|
|
575
|
+
|
|
576
|
+
if (repository.isRegisteredEvent('logout')) { // OneBuild repository emits this
|
|
577
|
+
this.relayEventsFrom(repository, ['logout']);
|
|
578
|
+
}
|
|
579
|
+
if (repository.isRegisteredEvent(CROSS_TAB_EVENT_NAME)) {
|
|
580
|
+
this.relayEventsFrom(repository, [CROSS_TAB_EVENT_NAME]);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
this.emit('createRepository', repository);
|
|
584
|
+
return repository;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Creates a Repository instance synchronously.
|
|
589
|
+
* Used when we must return a repository instance immediately.
|
|
590
|
+
* @param {object} config - Repository config object
|
|
591
|
+
* @return {Repository} repository
|
|
592
|
+
* @private
|
|
593
|
+
*/
|
|
594
|
+
_createRepositorySync = (config) => {
|
|
595
|
+
if (this.isDestroyed) {
|
|
596
|
+
throw new Error('this._createRepositorySync is no longer valid. OneHatData has been destroyed.');
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const workingConfig = _.merge({}, config);
|
|
600
|
+
|
|
601
|
+
if (workingConfig.type === 'lfr') {
|
|
602
|
+
const generalConfig = _.omit(workingConfig, ['type', 'local', 'remote']);
|
|
603
|
+
|
|
604
|
+
let localConfig = workingConfig.local;
|
|
605
|
+
let remoteConfig = workingConfig.remote;
|
|
606
|
+
|
|
607
|
+
if (_.isString(localConfig)) {
|
|
608
|
+
localConfig = {
|
|
609
|
+
type: localConfig,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
if (_.isString(remoteConfig)) {
|
|
613
|
+
remoteConfig = {
|
|
614
|
+
type: remoteConfig,
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (workingConfig.mode === MODE_COMMAND_QUEUE) {
|
|
619
|
+
generalConfig.isPaginated = false;
|
|
620
|
+
remoteConfig.type = 'command';
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
localConfig = _.merge({}, generalConfig, localConfig);
|
|
624
|
+
remoteConfig = _.merge({}, generalConfig, remoteConfig);
|
|
625
|
+
|
|
626
|
+
const localRepository = this._createRepositorySync(localConfig);
|
|
627
|
+
const remoteRepository = this._createRepositorySync(remoteConfig);
|
|
628
|
+
|
|
629
|
+
workingConfig.local = localRepository;
|
|
630
|
+
workingConfig.remote = remoteRepository;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const RepositoryType = this._repositoryTypes[workingConfig.type];
|
|
634
|
+
if (!RepositoryType) {
|
|
635
|
+
throw new Error('Repository type does not exist');
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return new RepositoryType(workingConfig, this);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Initializes repositories in the proper dependency order.
|
|
643
|
+
* @param {Repository} repository - Repository instance
|
|
644
|
+
* @private
|
|
645
|
+
*/
|
|
646
|
+
_initializeRepositoryInBackground = (repository) => {
|
|
647
|
+
repository._initializationError = null;
|
|
648
|
+
repository._initializationPromise = Promise.resolve()
|
|
649
|
+
.then(async () => {
|
|
650
|
+
if (repository && repository.type === 'lfr') {
|
|
651
|
+
if (repository.local && _.isFunction(repository.local.initialize)) {
|
|
652
|
+
await repository.local.initialize();
|
|
653
|
+
}
|
|
654
|
+
if (repository.remote && _.isFunction(repository.remote.initialize)) {
|
|
655
|
+
await repository.remote.initialize();
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
await repository.initialize();
|
|
659
|
+
})
|
|
660
|
+
.catch((error) => {
|
|
661
|
+
repository._initializationError = error;
|
|
662
|
+
repository.emit('error', error);
|
|
663
|
+
});
|
|
664
|
+
return repository._initializationPromise;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Waits for repository initialization if currently pending.
|
|
669
|
+
* @param {Repository} repository - Repository instance
|
|
670
|
+
* @param {number} timeout - Max ms to wait for initialization
|
|
671
|
+
* @return {Promise<Repository>} repository
|
|
672
|
+
* @private
|
|
673
|
+
*/
|
|
674
|
+
_waitForRepositoryInitialization = async (repository, timeout = 10000) => {
|
|
675
|
+
if (!repository) {
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (repository._initializationPromise) {
|
|
680
|
+
await repository._initializationPromise;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (repository._initializationError) {
|
|
684
|
+
throw repository._initializationError;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (repository.isInitialized === true) {
|
|
688
|
+
return repository;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (repository.isInitializing && _.isFunction(repository.on) && _.isFunction(repository.off)) {
|
|
692
|
+
await new Promise((resolve, reject) => {
|
|
693
|
+
const timeoutId = setTimeout(() => {
|
|
694
|
+
repository.off('initialize', handleInitialize);
|
|
695
|
+
repository.off('error', handleError);
|
|
696
|
+
reject(new Error('Timed out waiting for repository initialization: ' + repository.name));
|
|
697
|
+
}, timeout);
|
|
698
|
+
|
|
699
|
+
const handleInitialize = () => {
|
|
700
|
+
clearTimeout(timeoutId);
|
|
701
|
+
repository.off('initialize', handleInitialize);
|
|
702
|
+
repository.off('error', handleError);
|
|
703
|
+
resolve();
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
const handleError = (error) => {
|
|
707
|
+
clearTimeout(timeoutId);
|
|
708
|
+
repository.off('initialize', handleInitialize);
|
|
709
|
+
repository.off('error', handleError);
|
|
710
|
+
reject(error || new Error('Repository initialization failed: ' + repository.name));
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
repository.on('initialize', handleInitialize);
|
|
714
|
+
repository.on('error', handleError);
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if (repository._initializationError) {
|
|
719
|
+
throw repository._initializationError;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
return repository;
|
|
723
|
+
}
|
|
724
|
+
|
|
500
725
|
/**
|
|
501
726
|
* Gets or creates a unique repository with the supplied schemaName and name
|
|
502
727
|
* @param {string} mapName - Name of unique repository (will be internally mapped to an id)
|
package/src/Repository/Ajax.js
CHANGED
|
@@ -141,51 +141,58 @@ class AjaxRepository extends Repository {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
async initialize() {
|
|
144
|
+
this.isInitializing = true;
|
|
145
|
+
try {
|
|
144
146
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
this.registerEvents([
|
|
148
|
+
'beforeLoad',
|
|
149
|
+
]);
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
// Respond to Repository events
|
|
152
|
+
this.on('beforeSave', this._onBeforeSave);
|
|
151
153
|
|
|
152
154
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
155
|
+
// Create Reader
|
|
156
|
+
let readerConfig;
|
|
157
|
+
if (this.reader && this.reader.type) {
|
|
158
|
+
readerConfig = this.reader;
|
|
159
|
+
} else if (_.isString(this.reader)) {
|
|
160
|
+
readerConfig = {
|
|
161
|
+
type: this.reader,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (readerConfig && ReaderTypes[readerConfig.type]) {
|
|
165
|
+
const Reader = ReaderTypes[readerConfig.type];
|
|
166
|
+
this.reader = new Reader(readerConfig);
|
|
167
|
+
} else {
|
|
168
|
+
this.reader = null;
|
|
169
|
+
}
|
|
168
170
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
171
|
+
// Create Writer
|
|
172
|
+
let writerConfig;
|
|
173
|
+
if (this.writer && this.writer.type) {
|
|
174
|
+
writerConfig = this.writer;
|
|
175
|
+
} else if (_.isString(this.writer)) {
|
|
176
|
+
writerConfig = {
|
|
177
|
+
type: this.writer,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (writerConfig && WriterTypes[writerConfig.type]) {
|
|
181
|
+
const Writer = WriterTypes[writerConfig.type];
|
|
182
|
+
this.writer = new Writer(writerConfig);
|
|
183
|
+
} else {
|
|
184
|
+
this.writer = null;
|
|
185
|
+
}
|
|
184
186
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
// Initialize query params
|
|
188
|
+
this._setInitialQueryParams();
|
|
187
189
|
|
|
188
|
-
|
|
190
|
+
await super.initialize();
|
|
191
|
+
} finally {
|
|
192
|
+
if (!this.isInitialized) {
|
|
193
|
+
this.isInitializing = false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
189
196
|
}
|
|
190
197
|
|
|
191
198
|
/**
|
|
@@ -227,6 +234,7 @@ class AjaxRepository extends Repository {
|
|
|
227
234
|
* @param {boolean} isBaseParam - Whether param is a base param (to be sent on every request).
|
|
228
235
|
*/
|
|
229
236
|
setParam(name, value, isBaseParam = false) {
|
|
237
|
+
this._assertInitialized('setParam');
|
|
230
238
|
const
|
|
231
239
|
re = /^([^\[]+)\[([^\]]+)\](.*)$/,
|
|
232
240
|
matches = name.match(re),
|
|
@@ -263,6 +271,7 @@ class AjaxRepository extends Repository {
|
|
|
263
271
|
* @param {boolean} isBaseParam - Whether param is a base param (to be sent on every request).
|
|
264
272
|
*/
|
|
265
273
|
setValuelessParam(name, isBaseParam = false) {
|
|
274
|
+
this._assertInitialized('setValuelessParam');
|
|
266
275
|
const
|
|
267
276
|
re = /^([^\[]+)\[([^\]]+)\](.*)$/,
|
|
268
277
|
matches = name.match(re),
|
|
@@ -290,6 +299,7 @@ class AjaxRepository extends Repository {
|
|
|
290
299
|
* @param {object} params - Params to set. Key is parameter name, value is parameter value
|
|
291
300
|
*/
|
|
292
301
|
setParams(params) {
|
|
302
|
+
this._assertInitialized('setParams');
|
|
293
303
|
const oThis = this;
|
|
294
304
|
_.each(params, (value, name) => {
|
|
295
305
|
oThis.setParam(name, value);
|
|
@@ -301,6 +311,7 @@ class AjaxRepository extends Repository {
|
|
|
301
311
|
* @param {string} name - Param name
|
|
302
312
|
*/
|
|
303
313
|
hasBaseParam(name) {
|
|
314
|
+
this._assertInitialized('hasBaseParam');
|
|
304
315
|
if (this._baseParams.hasOwnProperty(name)) {
|
|
305
316
|
return true;
|
|
306
317
|
}
|
|
@@ -324,6 +335,7 @@ class AjaxRepository extends Repository {
|
|
|
324
335
|
* @param {string} name - Param name
|
|
325
336
|
*/
|
|
326
337
|
getBaseParam(name) {
|
|
338
|
+
this._assertInitialized('getBaseParam');
|
|
327
339
|
if (!this.hasBaseParam(name)) {
|
|
328
340
|
return null;
|
|
329
341
|
}
|
|
@@ -351,6 +363,7 @@ class AjaxRepository extends Repository {
|
|
|
351
363
|
* @param {object} params - Params to set. Key is parameter name, value is parameter value
|
|
352
364
|
*/
|
|
353
365
|
getBaseParams() {
|
|
366
|
+
this._assertInitialized('getBaseParams');
|
|
354
367
|
return this._baseParams;
|
|
355
368
|
}
|
|
356
369
|
|
|
@@ -358,6 +371,7 @@ class AjaxRepository extends Repository {
|
|
|
358
371
|
* Returns current value of any baseParam query conditions
|
|
359
372
|
*/
|
|
360
373
|
getBaseParamConditions() {
|
|
374
|
+
this._assertInitialized('getBaseParamConditions');
|
|
361
375
|
const
|
|
362
376
|
existingConditions = this._baseParams.conditions || {},
|
|
363
377
|
convertedConditions = {};
|
|
@@ -371,6 +385,7 @@ class AjaxRepository extends Repository {
|
|
|
371
385
|
* Returns current value of any param query conditions
|
|
372
386
|
*/
|
|
373
387
|
getParamConditions() {
|
|
388
|
+
this._assertInitialized('getParamConditions');
|
|
374
389
|
const
|
|
375
390
|
existingConditions = this._params.conditions || {},
|
|
376
391
|
convertedConditions = {};
|
|
@@ -385,6 +400,7 @@ class AjaxRepository extends Repository {
|
|
|
385
400
|
* @param {string} name - Param name
|
|
386
401
|
*/
|
|
387
402
|
hasParam(name) {
|
|
403
|
+
this._assertInitialized('hasParam');
|
|
388
404
|
if (this._params.hasOwnProperty(name)) {
|
|
389
405
|
return true;
|
|
390
406
|
}
|
|
@@ -409,6 +425,7 @@ class AjaxRepository extends Repository {
|
|
|
409
425
|
* @param {any} value - Param value to set.
|
|
410
426
|
*/
|
|
411
427
|
setBaseParam(name, value) {
|
|
428
|
+
this._assertInitialized('setBaseParam');
|
|
412
429
|
this.setParam(name, value, true);
|
|
413
430
|
}
|
|
414
431
|
|
|
@@ -417,6 +434,7 @@ class AjaxRepository extends Repository {
|
|
|
417
434
|
* @param {object} params - Base params to set. Key is parameter name, value is parameter value
|
|
418
435
|
*/
|
|
419
436
|
setBaseParams(params) {
|
|
437
|
+
this._assertInitialized('setBaseParams');
|
|
420
438
|
const oThis = this;
|
|
421
439
|
_.each(params, (value, name) => {
|
|
422
440
|
oThis.setBaseParam(name, value);
|
|
@@ -429,6 +447,7 @@ class AjaxRepository extends Repository {
|
|
|
429
447
|
* @param {boolean} reload - Whether to reload repository. Defaults to false.
|
|
430
448
|
*/
|
|
431
449
|
clearParams(reload = false, clearBase = false) {
|
|
450
|
+
this._assertInitialized('clearParams');
|
|
432
451
|
this._params = {};
|
|
433
452
|
if (clearBase) {
|
|
434
453
|
this._baseParams = {};
|
|
@@ -444,6 +463,7 @@ class AjaxRepository extends Repository {
|
|
|
444
463
|
* Refreshes entities.
|
|
445
464
|
*/
|
|
446
465
|
_onChangeSorters() {
|
|
466
|
+
this._assertInitialized('_onChangeSorters');
|
|
447
467
|
const sorter = this.sorters[0];
|
|
448
468
|
this.setBaseParam(this.paramSort, sorter.name);
|
|
449
469
|
this.setBaseParam(this.paramDirection, sorter.direction);
|
|
@@ -458,6 +478,7 @@ class AjaxRepository extends Repository {
|
|
|
458
478
|
* Refreshes entities.
|
|
459
479
|
*/
|
|
460
480
|
_onChangeFilters() {
|
|
481
|
+
this._assertInitialized('_onChangeFilters');
|
|
461
482
|
const oThis = this;
|
|
462
483
|
_.each(this.filters, (value, name) => {
|
|
463
484
|
oThis.setParam(name, value);
|
|
@@ -473,6 +494,7 @@ class AjaxRepository extends Repository {
|
|
|
473
494
|
* Refreshes entities.
|
|
474
495
|
*/
|
|
475
496
|
_onChangePagination() {
|
|
497
|
+
this._assertInitialized('_onChangePagination');
|
|
476
498
|
this.setBaseParam(this.paramPageNum, this.isPaginated ? this.page : null);
|
|
477
499
|
this.setBaseParam(this.paramPageSize, this.isPaginated ? this.pageSize : null);
|
|
478
500
|
|
|
@@ -512,6 +534,7 @@ class AjaxRepository extends Repository {
|
|
|
512
534
|
* @fires beforeLoad,changeData,load,error
|
|
513
535
|
*/
|
|
514
536
|
async load(params, callback = null) {
|
|
537
|
+
this._assertInitialized('load');
|
|
515
538
|
if (this.isTree) {
|
|
516
539
|
return this.loadRootNodes();
|
|
517
540
|
}
|
|
@@ -684,6 +707,7 @@ class AjaxRepository extends Repository {
|
|
|
684
707
|
}
|
|
685
708
|
|
|
686
709
|
showMore(params = {}, callback) {
|
|
710
|
+
this._assertInitialized('showMore');
|
|
687
711
|
params.showMore = true;
|
|
688
712
|
return this.load(params, callback);
|
|
689
713
|
}
|
|
@@ -696,6 +720,7 @@ class AjaxRepository extends Repository {
|
|
|
696
720
|
* @fires reloadEntity,beforeLoad,changeData,load,error
|
|
697
721
|
*/
|
|
698
722
|
async reloadEntity(entity, callback = null) { // use this notation so we can override it in subclasses
|
|
723
|
+
this._assertInitialized('reloadEntity');
|
|
699
724
|
if (this.isDestroyed) {
|
|
700
725
|
this.throwError('this.reloadEntity is no longer valid. Repository has been destroyed.');
|
|
701
726
|
return;
|
|
@@ -759,6 +784,7 @@ class AjaxRepository extends Repository {
|
|
|
759
784
|
* @private
|
|
760
785
|
*/
|
|
761
786
|
_getReloadEntityParams(entity) {
|
|
787
|
+
this._assertInitialized('_getReloadEntityParams');
|
|
762
788
|
const params = {
|
|
763
789
|
id: entity.id,
|
|
764
790
|
};
|
|
@@ -770,6 +796,7 @@ class AjaxRepository extends Repository {
|
|
|
770
796
|
* @private
|
|
771
797
|
*/
|
|
772
798
|
_onBeforeSave() {
|
|
799
|
+
this._assertInitialized('_onBeforeSave');
|
|
773
800
|
this._operations = {
|
|
774
801
|
add: false,
|
|
775
802
|
edit: false,
|
|
@@ -1171,6 +1198,7 @@ class AjaxRepository extends Repository {
|
|
|
1171
1198
|
* @private
|
|
1172
1199
|
*/
|
|
1173
1200
|
_send(method, url, data, options = {}) {
|
|
1201
|
+
this._assertInitialized('_send');
|
|
1174
1202
|
|
|
1175
1203
|
if (!url) {
|
|
1176
1204
|
this.throwError('No url submitted');
|
|
@@ -1251,6 +1279,7 @@ class AjaxRepository extends Repository {
|
|
|
1251
1279
|
* since the server normally sorts, and they haven't yet gone to server.
|
|
1252
1280
|
*/
|
|
1253
1281
|
sortInMemory() {
|
|
1282
|
+
this._assertInitialized('sortInMemory');
|
|
1254
1283
|
const sorters = this.sorters;
|
|
1255
1284
|
let sortNames = [],
|
|
1256
1285
|
sortDirections = [];
|
|
@@ -160,6 +160,7 @@ export default class Repository extends EventEmitter {
|
|
|
160
160
|
* @member {boolean} debugMode - Whether this Repository should output debug messages
|
|
161
161
|
*/
|
|
162
162
|
debugMode: false,
|
|
163
|
+
|
|
163
164
|
};
|
|
164
165
|
|
|
165
166
|
_.merge(this, defaults, config);
|
|
@@ -235,6 +236,11 @@ export default class Repository extends EventEmitter {
|
|
|
235
236
|
*/
|
|
236
237
|
this.isInitialized = false;
|
|
237
238
|
|
|
239
|
+
/**
|
|
240
|
+
* @member {boolean} isInitializing - State: whether initialize() is currently running
|
|
241
|
+
*/
|
|
242
|
+
this.isInitializing = false;
|
|
243
|
+
|
|
238
244
|
/**
|
|
239
245
|
* @member {boolean} isTree - Whether this Repository contains TreeNodes
|
|
240
246
|
* @readonly
|
|
@@ -328,38 +334,66 @@ export default class Repository extends EventEmitter {
|
|
|
328
334
|
* This is async because we may need to wait for loading and sorting.
|
|
329
335
|
*/
|
|
330
336
|
async initialize() {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
// Assign event handlers
|
|
337
|
-
this.on('entity_change', async (entity) => { // Entity changed its value
|
|
338
|
-
if (this.isAutoSave && !this.isRemotePhantomMode) {
|
|
339
|
-
return await this.save(entity);
|
|
337
|
+
this.isInitializing = true;
|
|
338
|
+
try {
|
|
339
|
+
// Create default sorters if none supplied
|
|
340
|
+
if (this.isAutoSort && !this.sorters.length) {
|
|
341
|
+
this.sorters = this.getDefaultSorters();
|
|
340
342
|
}
|
|
341
|
-
|
|
343
|
+
|
|
344
|
+
// Assign event handlers
|
|
345
|
+
this.on('entity_change', async (entity) => { // Entity changed its value
|
|
346
|
+
if (this.isAutoSave && !this.isRemotePhantomMode) {
|
|
347
|
+
return await this.save(entity);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
342
350
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
351
|
+
// Auto load & sort
|
|
352
|
+
if (this.isAutoLoad && !this.isTree) {
|
|
353
|
+
await this.load();
|
|
354
|
+
}
|
|
355
|
+
if (!this.isSorted && this.isAutoSort && !this.isRemoteSort && !this.isTree) { // load may have sorted, in which case this will be skipped.
|
|
356
|
+
await this.sort();
|
|
357
|
+
}
|
|
350
358
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
359
|
+
this._createMethods();
|
|
360
|
+
this._createStatics();
|
|
361
|
+
this._createListeners();
|
|
354
362
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
363
|
+
const init = this.schema.repository.init || this.originalConfig.init; // The latter is mainly for lfr repositories
|
|
364
|
+
if (init) {
|
|
365
|
+
await init.call(this);
|
|
366
|
+
}
|
|
367
|
+
this.rehash();
|
|
368
|
+
|
|
369
|
+
this.isInitialized = true;
|
|
370
|
+
this.emit('initialize');
|
|
371
|
+
} finally {
|
|
372
|
+
this.isInitializing = false;
|
|
358
373
|
}
|
|
359
|
-
|
|
374
|
+
}
|
|
360
375
|
|
|
361
|
-
|
|
362
|
-
|
|
376
|
+
/**
|
|
377
|
+
* Throws when operational methods are used before initialization completes.
|
|
378
|
+
* @param {string} methodName - Name of the method being guarded
|
|
379
|
+
* @private
|
|
380
|
+
*/
|
|
381
|
+
_assertInitialized(methodName) {
|
|
382
|
+
const strictMethods = {
|
|
383
|
+
add: true,
|
|
384
|
+
addMultiple: true,
|
|
385
|
+
save: true,
|
|
386
|
+
delete: true,
|
|
387
|
+
load: true,
|
|
388
|
+
reloadEntity: true,
|
|
389
|
+
_send: true,
|
|
390
|
+
};
|
|
391
|
+
if (!strictMethods[methodName]) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!this.isInitialized && !this.isInitializing) {
|
|
395
|
+
throw new Error('Repository ' + this.name + ' is not initialized. Cannot call ' + methodName + ' yet.');
|
|
396
|
+
}
|
|
363
397
|
}
|
|
364
398
|
|
|
365
399
|
/**
|
|
@@ -588,6 +622,7 @@ export default class Repository extends EventEmitter {
|
|
|
588
622
|
this.throwError('this.sort is no longer valid. Repository has been destroyed.');
|
|
589
623
|
return;
|
|
590
624
|
}
|
|
625
|
+
this._assertInitialized('sort');
|
|
591
626
|
// Assemble sorting definition objects
|
|
592
627
|
let sorters = [];
|
|
593
628
|
if (_.isNil(arg1)) {
|
|
@@ -648,6 +683,7 @@ export default class Repository extends EventEmitter {
|
|
|
648
683
|
this.throwError('this.setSorters is no longer valid. Repository has been destroyed.');
|
|
649
684
|
return;
|
|
650
685
|
}
|
|
686
|
+
this._assertInitialized('setSorters');
|
|
651
687
|
if (!this.allowsMultiSort && sorters.length > 1) {
|
|
652
688
|
this.throwError('Cannot have more than one sorter at a time.');
|
|
653
689
|
return;
|
|
@@ -757,6 +793,7 @@ export default class Repository extends EventEmitter {
|
|
|
757
793
|
this.throwError('this.filter is no longer valid. Repository has been destroyed.');
|
|
758
794
|
return;
|
|
759
795
|
}
|
|
796
|
+
this._assertInitialized('filter');
|
|
760
797
|
|
|
761
798
|
if (_.isNil(arg1)) {
|
|
762
799
|
return this.clearFilters();
|
|
@@ -833,6 +870,7 @@ export default class Repository extends EventEmitter {
|
|
|
833
870
|
* @return this
|
|
834
871
|
*/
|
|
835
872
|
setFilters(filters, clearFirst = true) {
|
|
873
|
+
this._assertInitialized('setFilters');
|
|
836
874
|
const parsed = _.map(filters, (value, name) => {
|
|
837
875
|
return {
|
|
838
876
|
name,
|
|
@@ -853,6 +891,7 @@ export default class Repository extends EventEmitter {
|
|
|
853
891
|
* - repository.clearFilters(['first_name', 'last_name']); // Clear multiple filters
|
|
854
892
|
*/
|
|
855
893
|
clearFilters(filtersToClear) {
|
|
894
|
+
this._assertInitialized('clearFilters');
|
|
856
895
|
let filters = [];
|
|
857
896
|
if (filtersToClear) {
|
|
858
897
|
if (_.isString(filtersToClear)) {
|
|
@@ -875,6 +914,7 @@ export default class Repository extends EventEmitter {
|
|
|
875
914
|
this.throwError('this._setFilters is no longer valid. Repository has been destroyed.');
|
|
876
915
|
return;
|
|
877
916
|
}
|
|
917
|
+
this._assertInitialized('_setFilters');
|
|
878
918
|
if (!_.isEqual(this.filters, filters)) {
|
|
879
919
|
this.filters = filters;
|
|
880
920
|
this.resetPagination();
|
|
@@ -1114,6 +1154,7 @@ export default class Repository extends EventEmitter {
|
|
|
1114
1154
|
this.throwError('this.add is no longer valid. Repository has been destroyed.');
|
|
1115
1155
|
return;
|
|
1116
1156
|
}
|
|
1157
|
+
this._assertInitialized('add');
|
|
1117
1158
|
if (!this.canAdd) {
|
|
1118
1159
|
this.throwError('Adding has been disabled on this repository.');
|
|
1119
1160
|
return;
|
|
@@ -1201,6 +1242,7 @@ export default class Repository extends EventEmitter {
|
|
|
1201
1242
|
* @return {array} entities - new Entity objects
|
|
1202
1243
|
*/
|
|
1203
1244
|
async addMultiple(allData, isPersisted = false) {
|
|
1245
|
+
this._assertInitialized('addMultiple');
|
|
1204
1246
|
|
|
1205
1247
|
if (!this.canAdd) {
|
|
1206
1248
|
this.throwError('Adding has been disabled on this repository.');
|
|
@@ -1386,6 +1428,7 @@ export default class Repository extends EventEmitter {
|
|
|
1386
1428
|
this.throwError('this.getByIx is no longer valid. Repository has been destroyed.');
|
|
1387
1429
|
return;
|
|
1388
1430
|
}
|
|
1431
|
+
this._assertInitialized('getByIx');
|
|
1389
1432
|
return this.entities[ix];
|
|
1390
1433
|
}
|
|
1391
1434
|
|
|
@@ -1401,6 +1444,7 @@ export default class Repository extends EventEmitter {
|
|
|
1401
1444
|
this.throwError('this.getByRange is no longer valid. Repository has been destroyed.');
|
|
1402
1445
|
return;
|
|
1403
1446
|
}
|
|
1447
|
+
this._assertInitialized('getByRange');
|
|
1404
1448
|
return _.slice(this.entities, startIx, endIx+1);
|
|
1405
1449
|
}
|
|
1406
1450
|
|
|
@@ -1414,6 +1458,7 @@ export default class Repository extends EventEmitter {
|
|
|
1414
1458
|
this.throwError('this.getById is no longer valid. Repository has been destroyed.');
|
|
1415
1459
|
return;
|
|
1416
1460
|
}
|
|
1461
|
+
this._assertInitialized('getById');
|
|
1417
1462
|
if (_.isNil(id)) {
|
|
1418
1463
|
return null;
|
|
1419
1464
|
}
|
|
@@ -1430,6 +1475,7 @@ export default class Repository extends EventEmitter {
|
|
|
1430
1475
|
this.throwError('this.getIxById is no longer valid. Repository has been destroyed.');
|
|
1431
1476
|
return;
|
|
1432
1477
|
}
|
|
1478
|
+
this._assertInitialized('getIxById');
|
|
1433
1479
|
if (_.isNil(id)) {
|
|
1434
1480
|
return null;
|
|
1435
1481
|
}
|
|
@@ -1451,6 +1497,7 @@ export default class Repository extends EventEmitter {
|
|
|
1451
1497
|
this.throwError('this.getBy is no longer valid. Repository has been destroyed.');
|
|
1452
1498
|
return;
|
|
1453
1499
|
}
|
|
1500
|
+
this._assertInitialized('getBy');
|
|
1454
1501
|
return _.filter(this.entities, filter);
|
|
1455
1502
|
}
|
|
1456
1503
|
|
|
@@ -1468,6 +1515,7 @@ export default class Repository extends EventEmitter {
|
|
|
1468
1515
|
this.throwError('this.getFirstBy is no longer valid. Repository has been destroyed.');
|
|
1469
1516
|
return;
|
|
1470
1517
|
}
|
|
1518
|
+
this._assertInitialized('getFirstBy');
|
|
1471
1519
|
return _.find(this.entities, filter);
|
|
1472
1520
|
}
|
|
1473
1521
|
|
|
@@ -1513,6 +1561,7 @@ export default class Repository extends EventEmitter {
|
|
|
1513
1561
|
this.throwError('this.getEntities is no longer valid. Repository has been destroyed.');
|
|
1514
1562
|
return;
|
|
1515
1563
|
}
|
|
1564
|
+
this._assertInitialized('getEntities');
|
|
1516
1565
|
return this.entities;
|
|
1517
1566
|
}
|
|
1518
1567
|
/* */
|
|
@@ -1528,6 +1577,7 @@ export default class Repository extends EventEmitter {
|
|
|
1528
1577
|
this.throwError('this.getPagedEntities is no longer valid. Repository has been destroyed.');
|
|
1529
1578
|
return;
|
|
1530
1579
|
}
|
|
1580
|
+
this._assertInitialized('getEntitiesOnPage');
|
|
1531
1581
|
const entities = this.getEntities();
|
|
1532
1582
|
if (!this.isPaginated) {
|
|
1533
1583
|
return entities;
|
|
@@ -1767,6 +1817,7 @@ export default class Repository extends EventEmitter {
|
|
|
1767
1817
|
this.throwError('this.save is no longer valid. Repository has been destroyed.');
|
|
1768
1818
|
return;
|
|
1769
1819
|
}
|
|
1820
|
+
this._assertInitialized('save');
|
|
1770
1821
|
|
|
1771
1822
|
this.emit('beforeSave'); // So subclasses can prep anything needed for saving
|
|
1772
1823
|
|
|
@@ -2011,6 +2062,7 @@ export default class Repository extends EventEmitter {
|
|
|
2011
2062
|
* @fires delete
|
|
2012
2063
|
*/
|
|
2013
2064
|
async delete(entities, moveSubtreeUp = false) {
|
|
2065
|
+
this._assertInitialized('delete');
|
|
2014
2066
|
if (this.isDestroyed) {
|
|
2015
2067
|
this.throwError('this.delete is no longer valid. Repository has been destroyed.');
|
|
2016
2068
|
return;
|
|
@@ -2275,10 +2327,23 @@ export default class Repository extends EventEmitter {
|
|
|
2275
2327
|
* @param {object} data - optional data object to describe the error
|
|
2276
2328
|
*/
|
|
2277
2329
|
throwError(obj, data = null) {
|
|
2330
|
+
let errorObject = obj;
|
|
2331
|
+
if (!(errorObject instanceof Error)) {
|
|
2332
|
+
// standardize the errorObject to be an Error instance
|
|
2333
|
+
if (_.isString(errorObject)) {
|
|
2334
|
+
errorObject = new Error(errorObject);
|
|
2335
|
+
} else if (errorObject && _.isString(errorObject.message)) {
|
|
2336
|
+
errorObject = new Error(errorObject.message);
|
|
2337
|
+
} else {
|
|
2338
|
+
errorObject = new Error('Unknown repository error');
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
errorObject.context = data;
|
|
2342
|
+
|
|
2278
2343
|
if (this.errorHandler) {
|
|
2279
|
-
this.errorHandler(
|
|
2344
|
+
this.errorHandler(errorObject, data);
|
|
2280
2345
|
} else {
|
|
2281
|
-
this.emit('error',
|
|
2346
|
+
this.emit('error', errorObject, data);
|
|
2282
2347
|
}
|
|
2283
2348
|
}
|
|
2284
2349
|
|
|
@@ -2311,11 +2376,7 @@ export default class Repository extends EventEmitter {
|
|
|
2311
2376
|
* @return {string} className
|
|
2312
2377
|
*/
|
|
2313
2378
|
getClassName() {
|
|
2314
|
-
|
|
2315
|
-
this.throwError('this.getClassName is no longer valid. Repository has been destroyed.');
|
|
2316
|
-
return;
|
|
2317
|
-
}
|
|
2318
|
-
return this.__proto__.constructor.className;
|
|
2379
|
+
return this.__proto__.constructor.className || this.constructor.className || 'Repository';
|
|
2319
2380
|
}
|
|
2320
2381
|
|
|
2321
2382
|
get className() {
|
|
@@ -2327,11 +2388,7 @@ export default class Repository extends EventEmitter {
|
|
|
2327
2388
|
* @return {string} className
|
|
2328
2389
|
*/
|
|
2329
2390
|
getType() {
|
|
2330
|
-
|
|
2331
|
-
this.throwError('this.getClassName is no longer valid. Repository has been destroyed.');
|
|
2332
|
-
return;
|
|
2333
|
-
}
|
|
2334
|
-
return this.__proto__.constructor.type;
|
|
2391
|
+
return this.__proto__.constructor.type || this.constructor.type || null;
|
|
2335
2392
|
}
|
|
2336
2393
|
|
|
2337
2394
|
get type() {
|
|
@@ -2339,11 +2396,9 @@ export default class Repository extends EventEmitter {
|
|
|
2339
2396
|
}
|
|
2340
2397
|
|
|
2341
2398
|
toString() {
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
}
|
|
2346
|
-
return this.getClassName() + 'Repository {' + this.name + '} - ' + this.id;
|
|
2399
|
+
const name = this.name || 'destroyed';
|
|
2400
|
+
const id = this.id || 'unknown';
|
|
2401
|
+
return this.getClassName() + 'Repository {' + name + '} - ' + id;
|
|
2347
2402
|
}
|
|
2348
2403
|
|
|
2349
2404
|
};
|