@onehat/data 1.13.5 → 1.14.0

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.
@@ -358,6 +358,22 @@ describe('OneHatData', function() {
358
358
  })();
359
359
  });
360
360
 
361
+ it('setIsOnline', function() {
362
+ (async function() {
363
+ await beforeEach();
364
+
365
+ const oneHatData = this.oneHatData;
366
+
367
+ oneHatData.setIsOnline(true);
368
+ expect(oneHatData.isOnline).to.be.true;
369
+
370
+ oneHatData.setIsOnline(false);
371
+ expect(oneHatData.isOnline).to.be.false;
372
+
373
+ afterEach();
374
+ })();
375
+ });
376
+
361
377
  it('isEntity', async function() {
362
378
  (async function() {
363
379
  await beforeEach();
@@ -112,7 +112,7 @@ describe('OneBuildRepository', function() {
112
112
  expect(_.size(this.repository.entities)).to.be.eq(1);
113
113
  });
114
114
 
115
- it.only('sortInMemory', function() {
115
+ it('sortInMemory', function() {
116
116
 
117
117
  const repository = this.repository;
118
118
 
@@ -138,6 +138,17 @@ describe('OneBuildRepository', function() {
138
138
  expect(entities[3].key).to.be.eq(4);
139
139
  expect(entities[4].key).to.be.eq(5);
140
140
  });
141
+
142
+ it('setIsOnline', function() {
143
+
144
+ const repository = this.repository;
145
+
146
+ repository.setIsOnline(true);
147
+ expect(repository.isOnline).to.be.true;
148
+
149
+ repository.setIsOnline(false);
150
+ expect(repository.isOnline).to.be.false;
151
+ });
141
152
 
142
153
  });
143
154
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.13.5",
3
+ "version": "1.14.0",
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/OneHatData.js CHANGED
@@ -67,6 +67,14 @@ export class OneHatData extends EventEmitter {
67
67
  */
68
68
  this.isDestroyed = false;
69
69
 
70
+ /**
71
+ * @member {boolean} isOnline - Whether the remote Internet connection is active.
72
+ * This must be managed by outside software, calling setIsOnline at appropriate times.
73
+ * @private
74
+ */
75
+ this.isOnline = true;
76
+
77
+
70
78
  this.registerEvents([
71
79
  'createRepository',
72
80
  'deleteRepository',
@@ -114,6 +122,23 @@ export class OneHatData extends EventEmitter {
114
122
  return this;
115
123
  }
116
124
 
125
+ /**
126
+ * Sets global error handler on all Repositories.
127
+ * Chainable.
128
+ * @param {function} handler - The global error handler
129
+ * @return this
130
+ */
131
+ setGlobalErrorHandler = (handler) => {
132
+ if (this.isDestroyed) {
133
+ throw new Error('this.setGlobalErrorHandler is no longer valid. OneHatData has been destroyed.');
134
+ }
135
+ const repositories = this.getAllRepositories();
136
+ _.each(repositories, (repository) => {
137
+ repository.setErrorHandler(handler);
138
+ });
139
+ return this;
140
+ }
141
+
117
142
 
118
143
  // ______ __
119
144
  // / ____/_______ ____ _/ /____
@@ -588,6 +613,18 @@ export class OneHatData extends EventEmitter {
588
613
  return this.repositories && this.repositories.hasOwnProperty(id);
589
614
  }
590
615
 
616
+ /**
617
+ * Sets isOnline for all remote repositories.
618
+ * Remote repositories won't submit queries if !isOnline
619
+ */
620
+ setIsOnline = (isOnline) => {
621
+ this.isOnline = !!isOnline;
622
+ const remoteRepositories = oneHatData.getRepositoriesBy((repository) => !!repository.setIsOnline);
623
+ _.each(remoteRepositories, (repository) => {
624
+ repository.setIsOnline(isOnline);
625
+ });
626
+ }
627
+
591
628
 
592
629
 
593
630
  // ____ __ __
@@ -93,6 +93,13 @@ class AjaxRepository extends Repository {
93
93
  * @member {object} _baseParams - Params that will be applied to every request
94
94
  */
95
95
  _baseParams: {},
96
+
97
+ /**
98
+ * @member {boolean} isOnline - Whether the remote storage medium is available.
99
+ * This must be managed by outside software, calling setIsOnline at appropriate times.
100
+ * @private
101
+ */
102
+ isOnline: true,
96
103
 
97
104
  };
98
105
  _.merge(this, defaults, config);
@@ -362,10 +369,12 @@ class AjaxRepository extends Repository {
362
369
  */
363
370
  load = async (params, callback = null) => {
364
371
  if (this.isDestroyed) {
365
- throw Error('this.load is no longer valid. Repository has been destroyed.');
372
+ this.throwError('this.load is no longer valid. Repository has been destroyed.');
373
+ return;
366
374
  }
367
375
  if (!this.api.get) {
368
- throw new Error('No "get" api endpoint defined.');
376
+ this.throwError('No "get" api endpoint defined.');
377
+ return;
369
378
  }
370
379
  this.emit('beforeLoad'); // TODO: canceling beforeLoad will cancel the load operation
371
380
  this.markLoading();
@@ -433,10 +442,12 @@ class AjaxRepository extends Repository {
433
442
  */
434
443
  reloadEntity = async (entity, callback = null) => {
435
444
  if (this.isDestroyed) {
436
- throw Error('this.reloadEntity is no longer valid. Repository has been destroyed.');
445
+ this.throwError('this.reloadEntity is no longer valid. Repository has been destroyed.');
446
+ return;
437
447
  }
438
448
  if (!this.api.get) {
439
- throw new Error('No "get" api endpoint defined.');
449
+ this.throwError('No "get" api endpoint defined.');
450
+ return;
440
451
  }
441
452
  this.emit('beforeLoad'); // TODO: canceling beforeLoad will cancel the load operation
442
453
  this.markLoading();
@@ -460,7 +471,8 @@ class AjaxRepository extends Repository {
460
471
  } = this._processServerResponse(result);
461
472
 
462
473
  if (!success) {
463
- throw Error(message);
474
+ this.throwError(message);
475
+ return;
464
476
  }
465
477
 
466
478
  const updatedData = root[0];
@@ -515,7 +527,8 @@ class AjaxRepository extends Repository {
515
527
  */
516
528
  _doAdd(entity) { // standard function notation
517
529
  if (!this.api.add) {
518
- throw new Error('No "add" api endpoint defined.');
530
+ this.throwError('No "add" api endpoint defined.');
531
+ return;
519
532
  }
520
533
 
521
534
  this._operations.add = true;
@@ -537,7 +550,8 @@ class AjaxRepository extends Repository {
537
550
  } = this._processServerResponse(result);
538
551
 
539
552
  if (!success) {
540
- throw new Error(message);
553
+ this.throwError(message);
554
+ return;
541
555
  }
542
556
 
543
557
  entity.loadOriginalData(root[0]);
@@ -553,7 +567,8 @@ class AjaxRepository extends Repository {
553
567
  */
554
568
  _doBatchAdd(entities) { // standard function notation
555
569
  if (!this.api.batchAdd) {
556
- throw new Error('No "batchAdd" api endpoint defined.');
570
+ this.throwError('No "batchAdd" api endpoint defined.');
571
+ return;
557
572
  }
558
573
 
559
574
  this._operations.add = true;
@@ -577,7 +592,8 @@ class AjaxRepository extends Repository {
577
592
  } = this._processServerResponse(result);
578
593
 
579
594
  if (!success) {
580
- throw new Error(message);
595
+ this.throwError(message);
596
+ return;
581
597
  }
582
598
 
583
599
  // Reload each entity with new data
@@ -595,7 +611,8 @@ class AjaxRepository extends Repository {
595
611
  */
596
612
  _doEdit(entity) { // standard function notation
597
613
  if (!this.api.edit) {
598
- throw new Error('No "edit" api endpoint defined.');
614
+ this.throwError('No "edit" api endpoint defined.');
615
+ return;
599
616
  }
600
617
 
601
618
  this._operations.edit = true;
@@ -617,7 +634,8 @@ class AjaxRepository extends Repository {
617
634
  } = this._processServerResponse(result);
618
635
 
619
636
  if (!success) {
620
- throw new Error(message);
637
+ this.throwError(message);
638
+ return;
621
639
  }
622
640
 
623
641
  entity.loadOriginalData(root[0]);
@@ -633,7 +651,8 @@ class AjaxRepository extends Repository {
633
651
  */
634
652
  _doBatchEdit(entities) { // standard function notation
635
653
  if (!this.api.batchEdit) {
636
- throw new Error('No "batchEdit" api endpoint defined.');
654
+ this.throwError('No "batchEdit" api endpoint defined.');
655
+ return;
637
656
  }
638
657
 
639
658
  this._operations.edit = true;
@@ -657,7 +676,8 @@ class AjaxRepository extends Repository {
657
676
  } = this._processServerResponse(result);
658
677
 
659
678
  if (!success) {
660
- throw new Error(message);
679
+ this.throwError(message);
680
+ return;
661
681
  }
662
682
 
663
683
  // Reload each entity with new data
@@ -675,7 +695,8 @@ class AjaxRepository extends Repository {
675
695
  */
676
696
  _doDelete(entity) { // standard function notation
677
697
  if (!this.api.delete) {
678
- throw new Error('No "delete" api endpoint defined.');
698
+ this.throwError('No "delete" api endpoint defined.');
699
+ return;
679
700
  }
680
701
 
681
702
  this._operations.delete = true;
@@ -697,7 +718,8 @@ class AjaxRepository extends Repository {
697
718
  } = this._processServerResponse(result);
698
719
 
699
720
  if (!success) {
700
- throw new Error(message);
721
+ this.throwError(message);
722
+ return;
701
723
  }
702
724
 
703
725
  // Delete it from this.entities
@@ -716,7 +738,8 @@ class AjaxRepository extends Repository {
716
738
  */
717
739
  _doBatchDelete(entities) { // standard function notation
718
740
  if (!this.api.batchDelete) {
719
- throw new Error('No "batchDelete" api endpoint defined.');
741
+ this.throwError('No "batchDelete" api endpoint defined.');
742
+ return;
720
743
  }
721
744
 
722
745
  this._operations.delete = true;
@@ -739,7 +762,8 @@ class AjaxRepository extends Repository {
739
762
  } = this._processServerResponse(result);
740
763
 
741
764
  if (!success) {
742
- throw new Error(message);
765
+ this.throwError(message);
766
+ return;
743
767
  }
744
768
 
745
769
  // Delete it from this.entities
@@ -779,7 +803,13 @@ class AjaxRepository extends Repository {
779
803
  _send = (method, url, data) => {
780
804
 
781
805
  if (!url) {
782
- throw new Error('No url submitted');
806
+ this.throwError('No url submitted');
807
+ return;
808
+ }
809
+
810
+ if (!this.isOnline) {
811
+ this.throwError('Offline');
812
+ return;
783
813
  }
784
814
 
785
815
  const options = {
@@ -866,6 +896,10 @@ class AjaxRepository extends Repository {
866
896
  }));
867
897
  }
868
898
 
899
+ setIsOnline = (isOnline) => {
900
+ this.isOnline = !!isOnline; // force convert type to boolean
901
+ }
902
+
869
903
  }
870
904
 
871
905
  AjaxRepository.className = 'Ajax';
@@ -68,16 +68,20 @@ class LocalFromRemoteRepository extends EventEmitter {
68
68
  super(...arguments);
69
69
 
70
70
  if (!config.local || !(config.local instanceof Repository)) {
71
- throw new Error('No local repository defined.');
71
+ this.throwError('No local repository defined.');
72
+ return;
72
73
  }
73
74
  if (!config.local.isLocal) {
74
- throw new Error('Local repository is not configured to be a local type.');
75
+ this.throwError('Local repository is not configured to be a local type.');
76
+ return;
75
77
  }
76
78
  if (!config.remote || !(config.remote instanceof Repository)) {
77
- throw new Error('No remote repository defined.');
79
+ this.throwError('No remote repository defined.');
80
+ return;
78
81
  }
79
82
  if (!config.remote.isRemote) {
80
- throw new Error('Remote repository is not configured to be a remote type.');
83
+ this.throwError('Remote repository is not configured to be a remote type.');
84
+ return;
81
85
  }
82
86
 
83
87
  const defaults = {
@@ -139,7 +143,8 @@ class LocalFromRemoteRepository extends EventEmitter {
139
143
  if (this.mode !== MODE_LOCAL_MIRROR &&
140
144
  this.mode !== MODE_REMOTE_WITH_OFFLINE &&
141
145
  this.mode !== MODE_COMMAND_QUEUE) {
142
- throw new Error('Mode not recognized.');
146
+ this.throwError('Mode not recognized.');
147
+ return;
143
148
  }
144
149
 
145
150
  this.registerEvents([
@@ -326,10 +331,12 @@ class LocalFromRemoteRepository extends EventEmitter {
326
331
  let command = this.commands[localItem.command];
327
332
 
328
333
  if (!command) {
329
- throw new Error('Command ' + localItem.command + ' not registered');
334
+ this.throwError('Command ' + localItem.command + ' not registered');
335
+ return;
330
336
  }
331
337
  if (!command.hasHandlers()) {
332
- throw new Error('No command handler registered for ' + localItem.command);
338
+ this.throwError('No command handler registered for ' + localItem.command);
339
+ return;
333
340
  }
334
341
 
335
342
  // local --> remote
@@ -365,7 +372,8 @@ class LocalFromRemoteRepository extends EventEmitter {
365
372
 
366
373
  case MODE_REMOTE_WITH_OFFLINE:
367
374
 
368
- throw new Error('Not implemented');
375
+ this.throwError('Not implemented');
376
+ return;
369
377
 
370
378
  // Load remote data into local
371
379
  // Local <-- Remote
@@ -92,7 +92,8 @@ class MemoryRepository extends Repository {
92
92
  load = async (data = null) => {
93
93
 
94
94
  if (this.isDestroyed) {
95
- throw Error('this.load is no longer valid. Repository has been destroyed.');
95
+ this.throwError('this.load is no longer valid. Repository has been destroyed.');
96
+ return;
96
97
  }
97
98
 
98
99
  this.emit('beforeLoad');
@@ -200,7 +201,8 @@ class MemoryRepository extends Repository {
200
201
  */
201
202
  _applySorters = () => {
202
203
  if (this.isDestroyed) {
203
- throw Error('this._applySorters is no longer valid. Repository has been destroyed.');
204
+ this.throwError('this._applySorters is no longer valid. Repository has been destroyed.');
205
+ return;
204
206
  }
205
207
  if (!this.hasSorters) {
206
208
  this.isSorted = false;
@@ -309,7 +311,8 @@ class MemoryRepository extends Repository {
309
311
  */
310
312
  _applyFilters = () => {
311
313
  if (this.isDestroyed) {
312
- throw Error('this._applyFilters is no longer valid. Repository has been destroyed.');
314
+ this.throwError('this._applyFilters is no longer valid. Repository has been destroyed.');
315
+ return;
313
316
  }
314
317
  if (!this.hasFilters) {
315
318
  this._filteredEntities = [];
@@ -358,7 +361,8 @@ class MemoryRepository extends Repository {
358
361
  */
359
362
  _doAdd(entity) { // standard function notation
360
363
  if (this.isDestroyed) {
361
- throw Error('this._doAdd is no longer valid. Repository has been destroyed.');
364
+ this.throwError('this._doAdd is no longer valid. Repository has been destroyed.');
365
+ return;
362
366
  }
363
367
 
364
368
  // Give it a non-temporary ID, if needed
@@ -407,7 +411,8 @@ class MemoryRepository extends Repository {
407
411
  */
408
412
  _doEdit(entity) { // standard function notation
409
413
  if (this.isDestroyed) {
410
- throw Error('this._doEdit is no longer valid. Repository has been destroyed.');
414
+ this.throwError('this._doEdit is no longer valid. Repository has been destroyed.');
415
+ return;
411
416
  }
412
417
 
413
418
  entity.markSaved();
@@ -422,7 +427,8 @@ class MemoryRepository extends Repository {
422
427
  */
423
428
  _doDelete(entity) { // standard function notation
424
429
  if (this.isDestroyed) {
425
- throw Error('this._doDelete is no longer valid. Repository has been destroyed.');
430
+ this.throwError('this._doDelete is no longer valid. Repository has been destroyed.');
431
+ return;
426
432
  }
427
433
  delete this._keyedEntities[entity.id];
428
434
  this.entities = _.filter(this.entities, (x) => x.id !== entity.id);
@@ -445,7 +451,8 @@ class MemoryRepository extends Repository {
445
451
  */
446
452
  getById = (id) => {
447
453
  if (this.isDestroyed) {
448
- throw Error('this.getById is no longer valid. Repository has been destroyed.');
454
+ this.throwError('this.getById is no longer valid. Repository has been destroyed.');
455
+ return;
449
456
  }
450
457
  return this._keyedEntities[id] ? this._keyedEntities[id] : null;
451
458
  }
@@ -458,7 +465,8 @@ class MemoryRepository extends Repository {
458
465
  */
459
466
  getIxById = (id) => {
460
467
  if (this.isDestroyed) {
461
- throw Error('this.getIxById is no longer valid. Repository has been destroyed.');
468
+ this.throwError('this.getIxById is no longer valid. Repository has been destroyed.');
469
+ return;
462
470
  }
463
471
 
464
472
  const ix = this.getEntitiesOnPage().findIndex((entity) => entity.id === id);
@@ -475,7 +483,8 @@ class MemoryRepository extends Repository {
475
483
  */
476
484
  getEntities = () => {
477
485
  if (this.isDestroyed) {
478
- throw Error('this.getEntities is no longer valid. Repository has been destroyed.');
486
+ this.throwError('this.getEntities is no longer valid. Repository has been destroyed.');
487
+ return;
479
488
  }
480
489
  return this._getActiveEntities();
481
490
  }
@@ -487,7 +496,8 @@ class MemoryRepository extends Repository {
487
496
  * /
488
497
  getAllData = () => {
489
498
  if (this.isDestroyed) {
490
- throw Error('this.getAllData is no longer valid. Repository has been destroyed.');
499
+ this.throwError('this.getAllData is no longer valid. Repository has been destroyed.');
500
+ return;
491
501
  }
492
502
  const entities = this._getActiveEntities();
493
503
  return _.map(entities, (entity) => {
@@ -528,7 +538,8 @@ class MemoryRepository extends Repository {
528
538
  */
529
539
  _paginate = (entities) => {
530
540
  if (this.isDestroyed) {
531
- throw Error('this._paginate is no longer valid. Repository has been destroyed.');
541
+ this.throwError('this._paginate is no longer valid. Repository has been destroyed.');
542
+ return;
532
543
  }
533
544
  if (!this.isPaginated) {
534
545
  return entities;
@@ -552,7 +563,8 @@ class MemoryRepository extends Repository {
552
563
  */
553
564
  _recalculate = () => {
554
565
  if (this.isDestroyed) {
555
- throw Error('this._recalculate is no longer valid. Repository has been destroyed.');
566
+ this.throwError('this._recalculate is no longer valid. Repository has been destroyed.');
567
+ return;
556
568
  }
557
569
  this._unsortedEntities = _.map(this._keyedEntities, item => item);
558
570
  this._sortedEntities = null;
@@ -589,7 +601,8 @@ class MemoryRepository extends Repository {
589
601
  */
590
602
  getGrandTotal = () => {
591
603
  if (this.isDestroyed) {
592
- throw Error('this.getGrandTotal is no longer valid. Repository has been destroyed.');
604
+ this.throwError('this.getGrandTotal is no longer valid. Repository has been destroyed.');
605
+ return;
593
606
  }
594
607
  return _.size(this._unsortedEntities);
595
608
  }
@@ -603,7 +616,8 @@ class MemoryRepository extends Repository {
603
616
  */
604
617
  _getActiveEntities = () => {
605
618
  if (this.isDestroyed) {
606
- throw Error('this._getActiveEntities is no longer valid. Repository has been destroyed.');
619
+ this.throwError('this._getActiveEntities is no longer valid. Repository has been destroyed.');
620
+ return;
607
621
  }
608
622
  if (this.hasFilters) {
609
623
  return this._filteredEntities;
@@ -43,7 +43,8 @@ class NullRepository extends Repository {
43
43
  */
44
44
  load = (data = this.data) => {
45
45
  if (this.isDestroyed) {
46
- throw Error('this.load is no longer valid. Repository has been destroyed.');
46
+ this.throwError('this.load is no longer valid. Repository has been destroyed.');
47
+ return;
47
48
  }
48
49
  this.emit('beforeLoad');
49
50
  this.markLoading();
@@ -96,7 +97,8 @@ class NullRepository extends Repository {
96
97
  */
97
98
  _doEdit(entity) { // standard function notation
98
99
  if (this.isDestroyed) {
99
- throw Error('this._doEdit is no longer valid. Repository has been destroyed.');
100
+ this.throwError('this._doEdit is no longer valid. Repository has been destroyed.');
101
+ return;
100
102
  }
101
103
  entity.markSaved();
102
104
  return {
@@ -303,7 +303,8 @@ class OfflineRepository extends MemoryRepository {
303
303
  */
304
304
  _getKeys = async () => {
305
305
  if (!this._getAllKeys) {
306
- throw new Error('Storage medium does not support _getAllKeys');
306
+ this.throwError('Storage medium does not support _getAllKeys');
307
+ return;
307
308
  }
308
309
  const re = new RegExp('^' + this.name + '-');
309
310
  let keys = await this._getAllKeys();
@@ -334,7 +335,8 @@ class OfflineRepository extends MemoryRepository {
334
335
  * @abstract
335
336
  */
336
337
  _storageGetValue = async (name) => {
337
- throw new Error('this._storageGetValue must be implemented by OfflineRepository subclass.');
338
+ this.throwError('this._storageGetValue must be implemented by OfflineRepository subclass.');
339
+ return;
338
340
  }
339
341
 
340
342
  /**
@@ -345,7 +347,8 @@ class OfflineRepository extends MemoryRepository {
345
347
  * @abstract
346
348
  */
347
349
  _storageSetValue = async (name, value) => {
348
- throw new Error('this._storageSetValue must be implemented by OfflineRepository subclass.');
350
+ this.throwError('this._storageSetValue must be implemented by OfflineRepository subclass.');
351
+ return;
349
352
  }
350
353
 
351
354
  /**
@@ -355,7 +358,8 @@ class OfflineRepository extends MemoryRepository {
355
358
  * @abstract
356
359
  */
357
360
  _storageDeleteValue = async (name) => {
358
- throw new Error('this._storageDeleteValue must be implemented by OfflineRepository subclass.');
361
+ this.throwError('this._storageDeleteValue must be implemented by OfflineRepository subclass.');
362
+ return;
359
363
  }
360
364
 
361
365
  /**
@@ -95,7 +95,13 @@ class OneBuildRepository extends AjaxRepository {
95
95
  _send = (method, url, data) => {
96
96
 
97
97
  if (!url) {
98
- throw new Error('No url submitted');
98
+ this.throwError('No url submitted');
99
+ return;
100
+ }
101
+
102
+ if (!this.isOnline) {
103
+ this.throwError('Offline');
104
+ return;
99
105
  }
100
106
 
101
107
  const options = {
@@ -241,6 +247,12 @@ class OneBuildRepository extends AjaxRepository {
241
247
  * @return {Promise}
242
248
  */
243
249
  reorder = (dragRecord, dropRecord, dropPosition) => {
250
+
251
+ if (!this.isOnline) {
252
+ this.throwError('Offline');
253
+ return;
254
+ }
255
+
244
256
  const data = {
245
257
  url: 'reorder',
246
258
  data: qs.stringify({
@@ -264,7 +276,8 @@ class OneBuildRepository extends AjaxRepository {
264
276
 
265
277
  const response = result.data;
266
278
  if (!response.success) {
267
- throw new Error(response.data);
279
+ this.throwError(response.data);
280
+ return;
268
281
  }
269
282
 
270
283
  // Reload the repository, so updated sort_order values can be retrieved
@@ -282,6 +295,11 @@ class OneBuildRepository extends AjaxRepository {
282
295
  */
283
296
  login = (creds) => {
284
297
 
298
+ if (!this.isOnline) {
299
+ this.throwError('Offline');
300
+ return;
301
+ }
302
+
285
303
  const data = {
286
304
  url: 'apiLogin',
287
305
  data: qs.stringify(creds),
@@ -301,7 +319,8 @@ class OneBuildRepository extends AjaxRepository {
301
319
 
302
320
  const response = result.data;
303
321
  if (!response.success) {
304
- throw new Error(response.data); // TODO: Fix back-end, so OneBuild submits the error message on response.message, not response.data
322
+ this.throwError(response.data); // TODO: Fix back-end, so OneBuild submits the error message on response.message, not response.data
323
+ return;
305
324
  }
306
325
 
307
326
  const userData = response.data;
@@ -314,9 +333,16 @@ class OneBuildRepository extends AjaxRepository {
314
333
  * @return {Promise}
315
334
  */
316
335
  logout = () => {
336
+
337
+ if (!this.isOnline) {
338
+ this.throwError('Offline');
339
+ return;
340
+ }
341
+
317
342
  if (this.debugMode) {
318
343
  console.log('logout');
319
344
  }
345
+
320
346
  return this.axios({
321
347
  url: 'Users/apiLogout',
322
348
  method: 'POST',
@@ -330,13 +356,20 @@ class OneBuildRepository extends AjaxRepository {
330
356
  }
331
357
  const response = result.data;
332
358
  if (!response.success) {
333
- throw new Error(response.data);
359
+ this.throwError(response.data);
360
+ return;
334
361
  }
335
362
  return true;
336
363
  });
337
364
  }
338
365
 
339
366
  forgotPassword = (email = null, username = null) => {
367
+
368
+ if (!this.isOnline) {
369
+ this.throwError('Offline');
370
+ return;
371
+ }
372
+
340
373
  const data = {
341
374
  url: 'forgotPassword',
342
375
  data: qs.stringify({
@@ -359,7 +392,8 @@ class OneBuildRepository extends AjaxRepository {
359
392
 
360
393
  const response = result.data;
361
394
  if (!response.success) {
362
- throw new Error(response.data);
395
+ this.throwError(response.data);
396
+ return;
363
397
  }
364
398
 
365
399
  return response;
@@ -31,7 +31,8 @@ export default class Repository extends EventEmitter {
31
31
  const { schema } = config;
32
32
 
33
33
  if (!schema || !schema.model) {
34
- throw new Error('Schema cannot be empty');
34
+ this.throwError('Schema cannot be empty');
35
+ return;
35
36
  }
36
37
 
37
38
  const defaults = {
@@ -290,7 +291,8 @@ export default class Repository extends EventEmitter {
290
291
  */
291
292
  _createMethods = () => {
292
293
  if (this.isDestroyed) {
293
- throw Error('this._createMethods is no longer valid. Repository has been destroyed.');
294
+ this.throwError('this._createMethods is no longer valid. Repository has been destroyed.');
295
+ return;
294
296
  }
295
297
  const methodDefinitions = this.schema.repository.methods || this.originalConfig.methods; // The latter is mainly for lfr repositories
296
298
  if (!_.isEmpty(methodDefinitions)) {
@@ -306,7 +308,8 @@ export default class Repository extends EventEmitter {
306
308
  */
307
309
  _createStatics = () => {
308
310
  if (this.isDestroyed) {
309
- throw Error('this._createStatics is no longer valid. Repository has been destroyed.');
311
+ this.throwError('this._createStatics is no longer valid. Repository has been destroyed.');
312
+ return;
310
313
  }
311
314
  const staticsDefinitions = this.schema.repository.statics || this.originalConfig.statics; // The latter is mainly for lfr repositories
312
315
  if (!_.isEmpty(staticsDefinitions)) {
@@ -328,7 +331,8 @@ export default class Repository extends EventEmitter {
328
331
  * @abstract
329
332
  */
330
333
  load = async () => {
331
- throw new Error('load must be implemented by Repository subclass');
334
+ this.throwError('load must be implemented by Repository subclass');
335
+ return;
332
336
  }
333
337
 
334
338
  /**
@@ -361,7 +365,8 @@ export default class Repository extends EventEmitter {
361
365
  * @abstract
362
366
  */
363
367
  reloadEntity = async (entity) => {
364
- throw new Error('reloadEntity must be implemented by Repository subclass');
368
+ this.throwError('reloadEntity must be implemented by Repository subclass');
369
+ return;
365
370
  }
366
371
 
367
372
  /**
@@ -370,7 +375,8 @@ export default class Repository extends EventEmitter {
370
375
  */
371
376
  setAutoSave = (isAutoSave) => {
372
377
  if (this.isDestroyed) {
373
- throw Error('this.setAutoSave is no longer valid. Repository has been destroyed.');
378
+ this.throwError('this.setAutoSave is no longer valid. Repository has been destroyed.');
379
+ return;
374
380
  }
375
381
  this.isAutoSave = isAutoSave
376
382
  }
@@ -381,7 +387,8 @@ export default class Repository extends EventEmitter {
381
387
  */
382
388
  setAutoLoad = (isAutoLoad) => {
383
389
  if (this.isDestroyed) {
384
- throw Error('this.setAutoLoad is no longer valid. Repository has been destroyed.');
390
+ this.throwError('this.setAutoLoad is no longer valid. Repository has been destroyed.');
391
+ return;
385
392
  }
386
393
  this.isAutoLoad = isAutoLoad
387
394
  }
@@ -398,7 +405,8 @@ export default class Repository extends EventEmitter {
398
405
  */
399
406
  get hasSorters() {
400
407
  if (this.isDestroyed) {
401
- throw Error('this.hasSorters is no longer valid. Repository has been destroyed.');
408
+ this.throwError('this.hasSorters is no longer valid. Repository has been destroyed.');
409
+ return;
402
410
  }
403
411
  return this.sorters.length > 0;
404
412
  }
@@ -408,7 +416,8 @@ export default class Repository extends EventEmitter {
408
416
  */
409
417
  clearSort = () => {
410
418
  if (this.isDestroyed) {
411
- throw Error('this.clearSort is no longer valid. Repository has been destroyed.');
419
+ this.throwError('this.clearSort is no longer valid. Repository has been destroyed.');
420
+ return;
412
421
  }
413
422
  this.setSorters([])
414
423
  }
@@ -445,7 +454,8 @@ export default class Repository extends EventEmitter {
445
454
  */
446
455
  sort = (arg1 = null, arg2 = 'ASC', arg3 = null) => {
447
456
  if (this.isDestroyed) {
448
- throw Error('this.sort is no longer valid. Repository has been destroyed.');
457
+ this.throwError('this.sort is no longer valid. Repository has been destroyed.');
458
+ return;
449
459
  }
450
460
  // Assemble sorting definition objects
451
461
  let sorters = [];
@@ -476,7 +486,8 @@ export default class Repository extends EventEmitter {
476
486
  */
477
487
  getDefaultSorters = () => {
478
488
  if (this.isDestroyed) {
479
- throw Error('this.getDefaultSorters is no longer valid. Repository has been destroyed.');
489
+ this.throwError('this.getDefaultSorters is no longer valid. Repository has been destroyed.');
490
+ return;
480
491
  }
481
492
  let sorters = [];
482
493
  if (this.schema?.model) {
@@ -505,10 +516,12 @@ export default class Repository extends EventEmitter {
505
516
  */
506
517
  setSorters = (sorters) => {
507
518
  if (this.isDestroyed) {
508
- throw Error('this.setSorters is no longer valid. Repository has been destroyed.');
519
+ this.throwError('this.setSorters is no longer valid. Repository has been destroyed.');
520
+ return;
509
521
  }
510
522
  if (!this.allowsMultiSort && sorters.length > 1) {
511
- throw Error('Cannot have more than one sorter at a time.');
523
+ this.throwError('Cannot have more than one sorter at a time.');
524
+ return;
512
525
  }
513
526
 
514
527
  let isChanged = false;
@@ -522,13 +535,15 @@ export default class Repository extends EventEmitter {
522
535
  }
523
536
  const propertyDefinition = _.find(this.schema.model.properties, (property) => property.name === sorter.name);
524
537
  if (!propertyDefinition) {
525
- throw new Error('Sorting property does not exist.');
538
+ this.throwError('Sorting property does not exist.');
539
+ return;
526
540
  }
527
541
  const propertyType = propertyDefinition.type;
528
542
  if (propertyType && PropertyTypes[propertyType]) {
529
543
  const propertyInstance = new PropertyTypes[propertyType]();
530
544
  if (!propertyInstance.isSortable) {
531
- throw new Error('Sorting property type is not sortable.');
545
+ this.throwError('Sorting property type is not sortable.');
546
+ return;
532
547
  }
533
548
  }
534
549
  });
@@ -554,7 +569,8 @@ export default class Repository extends EventEmitter {
554
569
  */
555
570
  get hasFilters() {
556
571
  if (this.isDestroyed) {
557
- throw Error('this.hasFilters is no longer valid. Repository has been destroyed.');
572
+ this.throwError('this.hasFilters is no longer valid. Repository has been destroyed.');
573
+ return;
558
574
  }
559
575
  return this.filters.length > 0;
560
576
  }
@@ -614,7 +630,8 @@ export default class Repository extends EventEmitter {
614
630
  */
615
631
  filter = (arg1 = null, arg2 = null, clearFirst = true) => {
616
632
  if (this.isDestroyed) {
617
- throw Error('this.filter is no longer valid. Repository has been destroyed.');
633
+ this.throwError('this.filter is no longer valid. Repository has been destroyed.');
634
+ return;
618
635
  }
619
636
 
620
637
  if (_.isNil(arg1)) {
@@ -719,7 +736,8 @@ export default class Repository extends EventEmitter {
719
736
  */
720
737
  _setFilters = (filters) => {
721
738
  if (this.isDestroyed) {
722
- throw Error('this._setFilters is no longer valid. Repository has been destroyed.');
739
+ this.throwError('this._setFilters is no longer valid. Repository has been destroyed.');
740
+ return;
723
741
  }
724
742
  let isChanged = false;
725
743
  if (!_.isEqual(this.filters, filters)) {
@@ -752,7 +770,8 @@ export default class Repository extends EventEmitter {
752
770
  */
753
771
  resetPagination = () => {
754
772
  if (this.isDestroyed) {
755
- throw Error('this.resetPagination is no longer valid. Repository has been destroyed.');
773
+ this.throwError('this.resetPagination is no longer valid. Repository has been destroyed.');
774
+ return;
756
775
  }
757
776
  this.setPage(1);
758
777
  }
@@ -763,7 +782,8 @@ export default class Repository extends EventEmitter {
763
782
  */
764
783
  setPageSize = (pageSize) => {
765
784
  if (this.isDestroyed) {
766
- throw Error('this.setPageSize is no longer valid. Repository has been destroyed.');
785
+ this.throwError('this.setPageSize is no longer valid. Repository has been destroyed.');
786
+ return;
767
787
  }
768
788
  if (!this.isPaginated) {
769
789
  return false;
@@ -792,7 +812,8 @@ export default class Repository extends EventEmitter {
792
812
  */
793
813
  setPage = (page) => {
794
814
  if (this.isDestroyed) {
795
- throw Error('this.setPage is no longer valid. Repository has been destroyed.');
815
+ this.throwError('this.setPage is no longer valid. Repository has been destroyed.');
816
+ return;
796
817
  }
797
818
  if (!this.isPaginated) {
798
819
  return false;
@@ -839,7 +860,8 @@ export default class Repository extends EventEmitter {
839
860
  */
840
861
  _setPaginationVars = () => {
841
862
  if (this.isDestroyed) {
842
- throw Error('this._setPaginationVars is no longer valid. Repository has been destroyed.');
863
+ this.throwError('this._setPaginationVars is no longer valid. Repository has been destroyed.');
864
+ return;
843
865
  }
844
866
  if (!this.isPaginated) {
845
867
  this.totalPages = 1;
@@ -935,7 +957,8 @@ export default class Repository extends EventEmitter {
935
957
  */
936
958
  add = async (data, isPersisted = false, originalIsMapped = false, isDelayedSave = false) => {
937
959
  if (this.isDestroyed) {
938
- throw Error('this.add is no longer valid. Repository has been destroyed.');
960
+ this.throwError('this.add is no longer valid. Repository has been destroyed.');
961
+ return;
939
962
  }
940
963
 
941
964
  // Does it already exist? If so, edit the existing
@@ -982,7 +1005,8 @@ export default class Repository extends EventEmitter {
982
1005
  */
983
1006
  createStandaloneEntity = async (data, isPersisted = false, originalIsMapped = false, isDelayedSave = false) => {
984
1007
  if (this.isDestroyed) {
985
- throw Error('this.createStandaloneEntity is no longer valid. Repository has been destroyed.');
1008
+ this.throwError('this.createStandaloneEntity is no longer valid. Repository has been destroyed.');
1009
+ return;
986
1010
  }
987
1011
 
988
1012
  const entity = Repository._createEntity(this.schema, data, this, isPersisted, originalIsMapped, isDelayedSave);
@@ -1046,7 +1070,8 @@ export default class Repository extends EventEmitter {
1046
1070
  */
1047
1071
  _relayEntityEvents = (entity) => {
1048
1072
  if (this.isDestroyed) {
1049
- throw Error('this._relayEntityEvents is no longer valid. Repository has been destroyed.');
1073
+ this.throwError('this._relayEntityEvents is no longer valid. Repository has been destroyed.');
1074
+ return;
1050
1075
  }
1051
1076
  this.relayEventsFrom(entity, [
1052
1077
  'change',
@@ -1146,7 +1171,8 @@ export default class Repository extends EventEmitter {
1146
1171
  */
1147
1172
  getByIx = (ix) => {
1148
1173
  if (this.isDestroyed) {
1149
- throw Error('this.getByIx is no longer valid. Repository has been destroyed.');
1174
+ this.throwError('this.getByIx is no longer valid. Repository has been destroyed.');
1175
+ return;
1150
1176
  }
1151
1177
  return this.entities[ix];
1152
1178
  }
@@ -1160,7 +1186,8 @@ export default class Repository extends EventEmitter {
1160
1186
  */
1161
1187
  getByRange = (startIx, endIx) => {
1162
1188
  if (this.isDestroyed) {
1163
- throw Error('this.getByRange is no longer valid. Repository has been destroyed.');
1189
+ this.throwError('this.getByRange is no longer valid. Repository has been destroyed.');
1190
+ return;
1164
1191
  }
1165
1192
  return _.slice(this.entities, startIx, endIx+1);
1166
1193
  }
@@ -1172,7 +1199,8 @@ export default class Repository extends EventEmitter {
1172
1199
  */
1173
1200
  getById = (id) => {
1174
1201
  if (this.isDestroyed) {
1175
- throw Error('this.getById is no longer valid. Repository has been destroyed.');
1202
+ this.throwError('this.getById is no longer valid. Repository has been destroyed.');
1203
+ return;
1176
1204
  }
1177
1205
  return this.getFirstBy(entity => entity.id === id);
1178
1206
  }
@@ -1184,7 +1212,8 @@ export default class Repository extends EventEmitter {
1184
1212
  */
1185
1213
  getIxById = (id) => {
1186
1214
  if (this.isDestroyed) {
1187
- throw Error('this.getIxById is no longer valid. Repository has been destroyed.');
1215
+ this.throwError('this.getIxById is no longer valid. Repository has been destroyed.');
1216
+ return;
1188
1217
  }
1189
1218
 
1190
1219
  const ix = this.entities.findIndex((entity) => entity.id === id);
@@ -1201,7 +1230,8 @@ export default class Repository extends EventEmitter {
1201
1230
  */
1202
1231
  getBy = (filter) => {
1203
1232
  if (this.isDestroyed) {
1204
- throw Error('this.getBy is no longer valid. Repository has been destroyed.');
1233
+ this.throwError('this.getBy is no longer valid. Repository has been destroyed.');
1234
+ return;
1205
1235
  }
1206
1236
  return _.filter(this.entities, filter);
1207
1237
  }
@@ -1217,7 +1247,8 @@ export default class Repository extends EventEmitter {
1217
1247
  */
1218
1248
  getFirstBy = (filter) => {
1219
1249
  if (this.isDestroyed) {
1220
- throw Error('this.getFirstBy is no longer valid. Repository has been destroyed.');
1250
+ this.throwError('this.getFirstBy is no longer valid. Repository has been destroyed.');
1251
+ return;
1221
1252
  }
1222
1253
  return _.find(this.entities, filter);
1223
1254
  }
@@ -1229,7 +1260,8 @@ export default class Repository extends EventEmitter {
1229
1260
  */
1230
1261
  getPhantom = (entities = null) => {
1231
1262
  if (this.isDestroyed) {
1232
- throw Error('this.getPhantom is no longer valid. Repository has been destroyed.');
1263
+ this.throwError('this.getPhantom is no longer valid. Repository has been destroyed.');
1264
+ return;
1233
1265
  }
1234
1266
  if (!entities) {
1235
1267
  entities = this.entities;
@@ -1244,7 +1276,8 @@ export default class Repository extends EventEmitter {
1244
1276
  */
1245
1277
  getNonPersisted = (entities = null) => {
1246
1278
  if (this.isDestroyed) {
1247
- throw Error('this.getDirty is no longer valid. Repository has been destroyed.');
1279
+ this.throwError('this.getDirty is no longer valid. Repository has been destroyed.');
1280
+ return;
1248
1281
  }
1249
1282
  if (!entities) {
1250
1283
  entities = this.entities;
@@ -1259,7 +1292,8 @@ export default class Repository extends EventEmitter {
1259
1292
  */
1260
1293
  getEntities = () => {
1261
1294
  if (this.isDestroyed) {
1262
- throw Error('this.getEntities is no longer valid. Repository has been destroyed.');
1295
+ this.throwError('this.getEntities is no longer valid. Repository has been destroyed.');
1296
+ return;
1263
1297
  }
1264
1298
  return this.entities;
1265
1299
  }
@@ -1273,7 +1307,8 @@ export default class Repository extends EventEmitter {
1273
1307
  */
1274
1308
  getEntitiesOnPage = () => {
1275
1309
  if (this.isDestroyed) {
1276
- throw Error('this.getPagedEntities is no longer valid. Repository has been destroyed.');
1310
+ this.throwError('this.getPagedEntities is no longer valid. Repository has been destroyed.');
1311
+ return;
1277
1312
  }
1278
1313
  const entities = this.getEntities();
1279
1314
  if (!this.isPaginated) {
@@ -1294,7 +1329,8 @@ export default class Repository extends EventEmitter {
1294
1329
  */
1295
1330
  getDirty = (entities = null) => {
1296
1331
  if (this.isDestroyed) {
1297
- throw Error('this.getDirty is no longer valid. Repository has been destroyed.');
1332
+ this.throwError('this.getDirty is no longer valid. Repository has been destroyed.');
1333
+ return;
1298
1334
  }
1299
1335
  if (!entities) {
1300
1336
  entities = this.entities;
@@ -1309,7 +1345,8 @@ export default class Repository extends EventEmitter {
1309
1345
  */
1310
1346
  getDeleted = (entities = null) => {
1311
1347
  if (this.isDestroyed) {
1312
- throw Error('this.getDeleted is no longer valid. Repository has been destroyed.');
1348
+ this.throwError('this.getDeleted is no longer valid. Repository has been destroyed.');
1349
+ return;
1313
1350
  }
1314
1351
  if (!entities) {
1315
1352
  entities = this.entities;
@@ -1324,7 +1361,8 @@ export default class Repository extends EventEmitter {
1324
1361
  */
1325
1362
  getStaged = (entities = null) => {
1326
1363
  if (this.isDestroyed) {
1327
- throw Error('this.getStaged is no longer valid. Repository has been destroyed.');
1364
+ this.throwError('this.getStaged is no longer valid. Repository has been destroyed.');
1365
+ return;
1328
1366
  }
1329
1367
  if (!entities) {
1330
1368
  entities = this.entities;
@@ -1338,7 +1376,8 @@ export default class Repository extends EventEmitter {
1338
1376
  */
1339
1377
  getSchema = () => {
1340
1378
  if (this.isDestroyed) {
1341
- throw Error('this.getSchema is no longer valid. Repository has been destroyed.');
1379
+ this.throwError('this.getSchema is no longer valid. Repository has been destroyed.');
1380
+ return;
1342
1381
  }
1343
1382
  return this.schema;
1344
1383
  }
@@ -1349,7 +1388,8 @@ export default class Repository extends EventEmitter {
1349
1388
  */
1350
1389
  getSortField = () => {
1351
1390
  if (this.isDestroyed) {
1352
- throw Error('this.getSortField is no longer valid. Repository has been destroyed.');
1391
+ this.throwError('this.getSortField is no longer valid. Repository has been destroyed.');
1392
+ return;
1353
1393
  }
1354
1394
  if (!this.allowsMultiSort || this.sorters.length < 1) {
1355
1395
  return null;
@@ -1363,7 +1403,8 @@ export default class Repository extends EventEmitter {
1363
1403
  */
1364
1404
  getSortDirection = () => {
1365
1405
  if (this.isDestroyed) {
1366
- throw Error('this.getSortDirection is no longer valid. Repository has been destroyed.');
1406
+ this.throwError('this.getSortDirection is no longer valid. Repository has been destroyed.');
1407
+ return;
1367
1408
  }
1368
1409
  if (!this.allowsMultiSort || this.sorters.length < 1) {
1369
1410
  return null;
@@ -1377,7 +1418,8 @@ export default class Repository extends EventEmitter {
1377
1418
  */
1378
1419
  getSortFn = () => {
1379
1420
  if (this.isDestroyed) {
1380
- throw Error('this.getSortDirection is no longer valid. Repository has been destroyed.');
1421
+ this.throwError('this.getSortDirection is no longer valid. Repository has been destroyed.');
1422
+ return;
1381
1423
  }
1382
1424
  if (!this.allowsMultiSort || this.sorters.length < 1) {
1383
1425
  return null;
@@ -1392,7 +1434,8 @@ export default class Repository extends EventEmitter {
1392
1434
  */
1393
1435
  getAssociatedRepository = (repositoryName) => {
1394
1436
  if (this.isDestroyed) {
1395
- throw Error('this.getAssociatedRepository is no longer valid. Repository has been destroyed.');
1437
+ this.throwError('this.getAssociatedRepository is no longer valid. Repository has been destroyed.');
1438
+ return;
1396
1439
  }
1397
1440
 
1398
1441
  const schema = this.getSchema();
@@ -1401,17 +1444,20 @@ export default class Repository extends EventEmitter {
1401
1444
  !schema.model.associations.belongsTo.includes(repositoryName) &&
1402
1445
  !schema.model.associations.belongsToMany.includes(repositoryName)
1403
1446
  ) {
1404
- throw Error(repositoryName + ' is not associated with this schema');
1447
+ this.throwError(repositoryName + ' is not associated with this schema');
1448
+ return;
1405
1449
  }
1406
1450
 
1407
1451
  const oneHatData = this.oneHatData;
1408
1452
  if (!oneHatData) {
1409
- throw Error('No global oneHatData object');
1453
+ this.throwError('No global oneHatData object');
1454
+ return;
1410
1455
  }
1411
1456
 
1412
1457
  const associatedRepository = oneHatData.getRepository(repositoryName);
1413
1458
  if (!associatedRepository) {
1414
- throw Error('Repository ' + repositoryName + ' cannot be found');
1459
+ this.throwError('Repository ' + repositoryName + ' cannot be found');
1460
+ return;
1415
1461
  }
1416
1462
 
1417
1463
  return associatedRepository;
@@ -1425,7 +1471,8 @@ export default class Repository extends EventEmitter {
1425
1471
  */
1426
1472
  isInRepository(idOrEntity) {
1427
1473
  if (this.isDestroyed) {
1428
- throw Error('this.isInRepository is no longer valid. Repository has been destroyed.');
1474
+ this.throwError('this.isInRepository is no longer valid. Repository has been destroyed.');
1475
+ return;
1429
1476
  }
1430
1477
  if (idOrEntity instanceof Entity) {
1431
1478
  return this.entities.indexOf(idOrEntity) !== -1;
@@ -1440,7 +1487,8 @@ export default class Repository extends EventEmitter {
1440
1487
  */
1441
1488
  get isDirty() {
1442
1489
  if (this.isDestroyed) {
1443
- throw Error('this.isDirty is no longer valid. Repository has been destroyed.');
1490
+ this.throwError('this.isDirty is no longer valid. Repository has been destroyed.');
1491
+ return;
1444
1492
  }
1445
1493
  return !!this.getDirty().length;
1446
1494
  }
@@ -1473,7 +1521,8 @@ export default class Repository extends EventEmitter {
1473
1521
  */
1474
1522
  save = async (entity = null, useStaged = false) => {
1475
1523
  if (this.isDestroyed) {
1476
- throw Error('this.save is no longer valid. Repository has been destroyed.');
1524
+ this.throwError('this.save is no longer valid. Repository has been destroyed.');
1525
+ return;
1477
1526
  }
1478
1527
 
1479
1528
  this.emit('beforeSave'); // So subclasses can prep anything needed for saving
@@ -1619,7 +1668,8 @@ export default class Repository extends EventEmitter {
1619
1668
  * @abstract
1620
1669
  */
1621
1670
  _doBatchAdd(entities) { // standard function notation
1622
- throw new Error('_doBatchAdd must be implemented by Repository subclass');
1671
+ this.throwError('_doBatchAdd must be implemented by Repository subclass');
1672
+ return;
1623
1673
  }
1624
1674
 
1625
1675
  /**
@@ -1630,7 +1680,8 @@ export default class Repository extends EventEmitter {
1630
1680
  * @abstract
1631
1681
  */
1632
1682
  _doAdd(entity) { // standard function notation
1633
- throw new Error('_doAdd must be implemented by Repository subclass');
1683
+ this.throwError('_doAdd must be implemented by Repository subclass');
1684
+ return;
1634
1685
  }
1635
1686
 
1636
1687
  /**
@@ -1641,7 +1692,8 @@ export default class Repository extends EventEmitter {
1641
1692
  * @abstract
1642
1693
  */
1643
1694
  _doBatchEdit(entities) { // standard function notation
1644
- throw new Error('_doBatchEdit must be implemented by Repository subclass');
1695
+ this.throwError('_doBatchEdit must be implemented by Repository subclass');
1696
+ return;
1645
1697
  }
1646
1698
 
1647
1699
  /**
@@ -1652,7 +1704,8 @@ export default class Repository extends EventEmitter {
1652
1704
  * @abstract
1653
1705
  */
1654
1706
  _doEdit(entity) { // standard function notation
1655
- throw new Error('_doEdit must be implemented by Repository subclass');
1707
+ this.throwError('_doEdit must be implemented by Repository subclass');
1708
+ return;
1656
1709
  }
1657
1710
 
1658
1711
  /**
@@ -1663,7 +1716,8 @@ export default class Repository extends EventEmitter {
1663
1716
  * @abstract
1664
1717
  */
1665
1718
  _doBatchDelete(entities) { // standard function notation
1666
- throw new Error('_doBatchDelete must be implemented by Repository subclass');
1719
+ this.throwError('_doBatchDelete must be implemented by Repository subclass');
1720
+ return;
1667
1721
  }
1668
1722
 
1669
1723
  /**
@@ -1674,7 +1728,8 @@ export default class Repository extends EventEmitter {
1674
1728
  * @abstract
1675
1729
  */
1676
1730
  _doDelete(entity) { // standard function notation
1677
- throw new Error('_doDelete must be implemented by Repository subclass');
1731
+ this.throwError('_doDelete must be implemented by Repository subclass');
1732
+ return;
1678
1733
  }
1679
1734
 
1680
1735
  /**
@@ -1710,7 +1765,8 @@ export default class Repository extends EventEmitter {
1710
1765
  */
1711
1766
  delete = async (entities) => {
1712
1767
  if (this.isDestroyed) {
1713
- throw Error('this.delete is no longer valid. Repository has been destroyed.');
1768
+ this.throwError('this.delete is no longer valid. Repository has been destroyed.');
1769
+ return;
1714
1770
  }
1715
1771
  if (!entities) {
1716
1772
  return false;
@@ -1873,6 +1929,26 @@ export default class Repository extends EventEmitter {
1873
1929
  _.merge(this, options);
1874
1930
  }
1875
1931
 
1932
+ /**
1933
+ * Set error handler for this repository
1934
+ * @param {function} handler - the error handler
1935
+ */
1936
+ setErrorHandler = (handler) => {
1937
+ this.errorHandler = handler;
1938
+ }
1939
+
1940
+
1941
+ /**
1942
+ * Either generates an exception, or handles it with the repository's errorHandler
1943
+ * @param {string|object|bool} error - the error message
1944
+ */
1945
+ throwError(obj) {
1946
+ if (this.errorHandler) {
1947
+ this.errorHandler(obj);
1948
+ } else {
1949
+ throw Error(obj);
1950
+ }
1951
+ }
1876
1952
 
1877
1953
  /**
1878
1954
  * Destroy this object.
@@ -1904,7 +1980,8 @@ export default class Repository extends EventEmitter {
1904
1980
  */
1905
1981
  getClassName = () => {
1906
1982
  if (this.isDestroyed) {
1907
- throw Error('this.getClassName is no longer valid. Repository has been destroyed.');
1983
+ this.throwError('this.getClassName is no longer valid. Repository has been destroyed.');
1984
+ return;
1908
1985
  }
1909
1986
  return this.__proto__.constructor.className;
1910
1987
  }
@@ -1919,7 +1996,8 @@ export default class Repository extends EventEmitter {
1919
1996
  */
1920
1997
  getType = () => {
1921
1998
  if (this.isDestroyed) {
1922
- throw Error('this.getClassName is no longer valid. Repository has been destroyed.');
1999
+ this.throwError('this.getClassName is no longer valid. Repository has been destroyed.');
2000
+ return;
1923
2001
  }
1924
2002
  return this.__proto__.constructor.type;
1925
2003
  }
@@ -1930,7 +2008,8 @@ export default class Repository extends EventEmitter {
1930
2008
 
1931
2009
  toString = () => {
1932
2010
  if (this.isDestroyed) {
1933
- throw Error('this.toString is no longer valid. Repository has been destroyed.');
2011
+ this.throwError('this.toString is no longer valid. Repository has been destroyed.');
2012
+ return;
1934
2013
  }
1935
2014
  return this.getClassName() + 'Repository {' + this.name + '} - ' + this.id;
1936
2015
  }