@onehat/data 1.23.0 → 1.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -28,6 +28,7 @@ async function beforeEach(that) {
|
|
|
28
28
|
}, true);
|
|
29
29
|
that.repository = that.oneHatData.getRepositoryById('foo');
|
|
30
30
|
}
|
|
31
|
+
|
|
31
32
|
function afterEach(that) {
|
|
32
33
|
that.oneHatData.destroy();
|
|
33
34
|
}
|
|
@@ -267,8 +268,130 @@ describe('OneHatData', function() {
|
|
|
267
268
|
|
|
268
269
|
const
|
|
269
270
|
repo1 = that.oneHatData.getRepository('bar'),
|
|
270
|
-
repo2 = that.oneHatData.
|
|
271
|
+
repo2 = await that.oneHatData.getUniqueRepository('bar');
|
|
271
272
|
expect(repo1 !== repo2).to.be.true;
|
|
273
|
+
expect(repo2.isInitialized).to.be.true;
|
|
274
|
+
|
|
275
|
+
afterEach(that);
|
|
276
|
+
})();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('getRepository(name, true) throws migration error', function() {
|
|
280
|
+
(async () => {
|
|
281
|
+
const that = {};
|
|
282
|
+
await beforeEach(that);
|
|
283
|
+
|
|
284
|
+
expect(() => that.oneHatData.getRepository('bar', true)).to.throw('Use await this.getUniqueRepository(name) instead.');
|
|
285
|
+
|
|
286
|
+
afterEach(that);
|
|
287
|
+
})();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('getUniqueRepository returns initialized unique repository', function() {
|
|
291
|
+
(async () => {
|
|
292
|
+
const that = {};
|
|
293
|
+
await beforeEach(that);
|
|
294
|
+
|
|
295
|
+
const repository = await that.oneHatData.getUniqueRepository('bar');
|
|
296
|
+
|
|
297
|
+
expect(repository).to.be.ok;
|
|
298
|
+
expect(repository.isUnique).to.be.true;
|
|
299
|
+
expect(repository.isInitialized).to.be.true;
|
|
300
|
+
|
|
301
|
+
afterEach(that);
|
|
302
|
+
})();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('getRepository unique keeps filters isolated from bound repository', function() {
|
|
306
|
+
(async () => {
|
|
307
|
+
const that = {};
|
|
308
|
+
await beforeEach(that);
|
|
309
|
+
|
|
310
|
+
const
|
|
311
|
+
boundRepository = that.oneHatData.getRepository('bar'),
|
|
312
|
+
uniqueRepository = await that.oneHatData.getUniqueRepository('bar');
|
|
313
|
+
|
|
314
|
+
boundRepository.filter('key', 'bound-only');
|
|
315
|
+
expect(boundRepository.hasFilterValue('key', 'bound-only')).to.be.true;
|
|
316
|
+
expect(uniqueRepository.hasFilter('key')).to.be.false;
|
|
317
|
+
|
|
318
|
+
uniqueRepository.filter('key', 'unique-only');
|
|
319
|
+
expect(uniqueRepository.hasFilterValue('key', 'unique-only')).to.be.true;
|
|
320
|
+
expect(boundRepository.hasFilterValue('key', 'bound-only')).to.be.true;
|
|
321
|
+
expect(boundRepository.hasFilterValue('key', 'unique-only')).to.be.false;
|
|
322
|
+
|
|
323
|
+
afterEach(that);
|
|
324
|
+
})();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('getUniqueRepository allows setBaseParams for Ajax repositories', function() {
|
|
328
|
+
(async () => {
|
|
329
|
+
const that = {};
|
|
330
|
+
await beforeEach(that);
|
|
331
|
+
|
|
332
|
+
that.oneHatData.createSchema({
|
|
333
|
+
name: 'meters',
|
|
334
|
+
model: {
|
|
335
|
+
idProperty: 'id',
|
|
336
|
+
displayProperty: 'name',
|
|
337
|
+
properties: [
|
|
338
|
+
{ name: 'id' },
|
|
339
|
+
{ name: 'name' },
|
|
340
|
+
],
|
|
341
|
+
},
|
|
342
|
+
repository: {
|
|
343
|
+
type: 'ajax',
|
|
344
|
+
api: {
|
|
345
|
+
get: 'meters',
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
await that.oneHatData.createRepository('meters', true);
|
|
350
|
+
|
|
351
|
+
const uniqueRepository = await that.oneHatData.getUniqueRepository('meters');
|
|
352
|
+
|
|
353
|
+
expect(() => {
|
|
354
|
+
uniqueRepository.setBaseParams({
|
|
355
|
+
foo: 'bar',
|
|
356
|
+
});
|
|
357
|
+
}).to.not.throw();
|
|
358
|
+
|
|
359
|
+
expect(uniqueRepository.getBaseParam('foo')).to.be.eq('bar');
|
|
360
|
+
|
|
361
|
+
afterEach(that);
|
|
362
|
+
})();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('getOrCreateUniqueRepository reuses existing mapped repository', function() {
|
|
366
|
+
(async () => {
|
|
367
|
+
const that = {};
|
|
368
|
+
await beforeEach(that);
|
|
369
|
+
|
|
370
|
+
const repository1 = await that.oneHatData.getOrCreateUniqueRepository('partsMap', 'bar');
|
|
371
|
+
const repository2 = await that.oneHatData.getOrCreateUniqueRepository('partsMap', 'bar');
|
|
372
|
+
|
|
373
|
+
expect(repository1).to.be.eq(repository2);
|
|
374
|
+
expect(repository1.isUnique).to.be.true;
|
|
375
|
+
|
|
376
|
+
afterEach(that);
|
|
377
|
+
})();
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('getOrCreateUniqueRepository recreates repository when mapped id is stale', function() {
|
|
381
|
+
(async () => {
|
|
382
|
+
const that = {};
|
|
383
|
+
await beforeEach(that);
|
|
384
|
+
|
|
385
|
+
const repository1 = await that.oneHatData.getOrCreateUniqueRepository('partsMap', 'bar');
|
|
386
|
+
const originalId = repository1.id;
|
|
387
|
+
|
|
388
|
+
that.oneHatData.deleteRepository(originalId);
|
|
389
|
+
|
|
390
|
+
const repository2 = await that.oneHatData.getOrCreateUniqueRepository('partsMap', 'bar');
|
|
391
|
+
|
|
392
|
+
expect(repository2).to.be.ok;
|
|
393
|
+
expect(repository2.id).to.not.eq(originalId);
|
|
394
|
+
expect(that.oneHatData.uniqueRepositoryIdsMap.partsMap).to.be.eq(repository2.id);
|
|
272
395
|
|
|
273
396
|
afterEach(that);
|
|
274
397
|
})();
|
package/package.json
CHANGED
|
@@ -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.getUniqueRepository(schemaName);
|
|
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);
|
|
@@ -44,6 +51,11 @@ export default function useOneHatData(schemaName, uniqueRepository = false) {
|
|
|
44
51
|
} else {
|
|
45
52
|
repository = oneHatData.getRepository(schemaName); // Get bound Repository for this schema
|
|
46
53
|
}
|
|
54
|
+
|
|
55
|
+
if (!isMounted || !repository) {
|
|
56
|
+
return;
|
|
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
|
@@ -479,24 +479,72 @@ export class OneHatData extends EventEmitter {
|
|
|
479
479
|
if (this.isDestroyed) {
|
|
480
480
|
throw new Error('this.getRepository is no longer valid. OneHatData has been destroyed.');
|
|
481
481
|
}
|
|
482
|
+
if (unique) {
|
|
483
|
+
throw new Error('this.getRepository(name, true) is no longer supported. Use await this.getUniqueRepository(name) instead.');
|
|
484
|
+
}
|
|
482
485
|
const schema = this.getSchema(name);
|
|
483
486
|
if (!schema) {
|
|
484
487
|
return null;
|
|
485
488
|
}
|
|
486
|
-
if (unique) {
|
|
487
|
-
const
|
|
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;
|
|
496
|
-
}
|
|
497
489
|
return schema.getBoundRepository();
|
|
498
490
|
}
|
|
499
491
|
|
|
492
|
+
/**
|
|
493
|
+
* Gets a fully initialized unique Repository for the supplied schema.
|
|
494
|
+
* @param {string} name - Name of Schema
|
|
495
|
+
* @return {Promise<Repository>} repository
|
|
496
|
+
*/
|
|
497
|
+
getUniqueRepository = async (name) => {
|
|
498
|
+
if (this.isDestroyed) {
|
|
499
|
+
throw new Error('this.getUniqueRepository is no longer valid. OneHatData has been destroyed.');
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const schema = this.getSchema(name);
|
|
503
|
+
if (!schema) {
|
|
504
|
+
throw new Error('this.getUniqueRepository: Schema not found. Name: ' + name);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const boundRepository = schema.getBoundRepository();
|
|
508
|
+
if (!boundRepository) {
|
|
509
|
+
throw new Error('this.getUniqueRepository: Schema does not have a bound Repository. Name: ' + name);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const
|
|
513
|
+
id = uuid(),
|
|
514
|
+
schemaRepositoryDef = _.isString(schema.repository) ? { type: schema.repository } : schema.repository,
|
|
515
|
+
safeOverrides = _.omit(boundRepository.originalConfig || {}, [ // Keep behavioral overrides while omitting mutable runtime state and nested repo instances.
|
|
516
|
+
'id',
|
|
517
|
+
'name',
|
|
518
|
+
'isUnique',
|
|
519
|
+
'local',
|
|
520
|
+
'remote',
|
|
521
|
+
'entities',
|
|
522
|
+
'filters',
|
|
523
|
+
'sorters',
|
|
524
|
+
'page',
|
|
525
|
+
'previousPage',
|
|
526
|
+
'pageTotal',
|
|
527
|
+
'pageStart',
|
|
528
|
+
'pageEnd',
|
|
529
|
+
'totalPages',
|
|
530
|
+
'total',
|
|
531
|
+
'isFiltered',
|
|
532
|
+
'isInitialized',
|
|
533
|
+
'isLoaded',
|
|
534
|
+
'isLoading',
|
|
535
|
+
'lastLoaded',
|
|
536
|
+
'hash',
|
|
537
|
+
]),
|
|
538
|
+
config = _.merge({}, schemaRepositoryDef, this._repositoryGlobals, safeOverrides, {
|
|
539
|
+
schema,
|
|
540
|
+
id,
|
|
541
|
+
name: boundRepository.name + '-' + id,
|
|
542
|
+
isUnique: true,
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
return await this.createRepository(config);
|
|
546
|
+
}
|
|
547
|
+
|
|
500
548
|
/**
|
|
501
549
|
* Gets or creates a unique repository with the supplied schemaName and name
|
|
502
550
|
* @param {string} mapName - Name of unique repository (will be internally mapped to an id)
|
|
@@ -505,13 +553,17 @@ export class OneHatData extends EventEmitter {
|
|
|
505
553
|
*/
|
|
506
554
|
getOrCreateUniqueRepository = async (mapName, schemaName) => {
|
|
507
555
|
if (this.isDestroyed) {
|
|
508
|
-
throw new Error('this.
|
|
556
|
+
throw new Error('this.getOrCreateUniqueRepository is no longer valid. OneHatData has been destroyed.');
|
|
509
557
|
}
|
|
510
558
|
|
|
511
559
|
// Try to get it
|
|
512
560
|
let id = this.uniqueRepositoryIdsMap[mapName];
|
|
513
561
|
if (id) {
|
|
514
|
-
|
|
562
|
+
const existingRepository = this.getRepositoryById(id);
|
|
563
|
+
if (existingRepository) {
|
|
564
|
+
return existingRepository;
|
|
565
|
+
}
|
|
566
|
+
delete this.uniqueRepositoryIdsMap[mapName];
|
|
515
567
|
}
|
|
516
568
|
|
|
517
569
|
// Try to create it
|
|
@@ -519,11 +571,8 @@ export class OneHatData extends EventEmitter {
|
|
|
519
571
|
if (!schema) {
|
|
520
572
|
return null;
|
|
521
573
|
}
|
|
522
|
-
|
|
523
|
-
const repository = await this.createRepository(schemaName);
|
|
574
|
+
const repository = await this.getUniqueRepository(schemaName);
|
|
524
575
|
id = repository.id;
|
|
525
|
-
repository.name += '-' + id;
|
|
526
|
-
repository.isUnique = true;
|
|
527
576
|
this.uniqueRepositoryIdsMap[mapName] = repository.id;
|
|
528
577
|
return repository;
|
|
529
578
|
}
|
package/src/Repository/Ajax.js
CHANGED
|
@@ -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,43 @@ 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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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();
|
|
360
368
|
|
|
361
|
-
|
|
362
|
-
|
|
369
|
+
this.isInitialized = true;
|
|
370
|
+
this.emit('initialize');
|
|
371
|
+
} finally {
|
|
372
|
+
this.isInitializing = false;
|
|
373
|
+
}
|
|
363
374
|
}
|
|
364
375
|
|
|
365
376
|
/**
|
|
@@ -1201,7 +1212,6 @@ export default class Repository extends EventEmitter {
|
|
|
1201
1212
|
* @return {array} entities - new Entity objects
|
|
1202
1213
|
*/
|
|
1203
1214
|
async addMultiple(allData, isPersisted = false) {
|
|
1204
|
-
|
|
1205
1215
|
if (!this.canAdd) {
|
|
1206
1216
|
this.throwError('Adding has been disabled on this repository.');
|
|
1207
1217
|
return;
|
|
@@ -2275,10 +2285,23 @@ export default class Repository extends EventEmitter {
|
|
|
2275
2285
|
* @param {object} data - optional data object to describe the error
|
|
2276
2286
|
*/
|
|
2277
2287
|
throwError(obj, data = null) {
|
|
2288
|
+
let errorObject = obj;
|
|
2289
|
+
if (!(errorObject instanceof Error)) {
|
|
2290
|
+
// standardize the errorObject to be an Error instance
|
|
2291
|
+
if (_.isString(errorObject)) {
|
|
2292
|
+
errorObject = new Error(errorObject);
|
|
2293
|
+
} else if (errorObject && _.isString(errorObject.message)) {
|
|
2294
|
+
errorObject = new Error(errorObject.message);
|
|
2295
|
+
} else {
|
|
2296
|
+
errorObject = new Error('Unknown repository error');
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
errorObject.context = data;
|
|
2300
|
+
|
|
2278
2301
|
if (this.errorHandler) {
|
|
2279
|
-
this.errorHandler(
|
|
2302
|
+
this.errorHandler(errorObject, data);
|
|
2280
2303
|
} else {
|
|
2281
|
-
this.emit('error',
|
|
2304
|
+
this.emit('error', errorObject, data);
|
|
2282
2305
|
}
|
|
2283
2306
|
}
|
|
2284
2307
|
|
|
@@ -2311,11 +2334,7 @@ export default class Repository extends EventEmitter {
|
|
|
2311
2334
|
* @return {string} className
|
|
2312
2335
|
*/
|
|
2313
2336
|
getClassName() {
|
|
2314
|
-
|
|
2315
|
-
this.throwError('this.getClassName is no longer valid. Repository has been destroyed.');
|
|
2316
|
-
return;
|
|
2317
|
-
}
|
|
2318
|
-
return this.__proto__.constructor.className;
|
|
2337
|
+
return this.__proto__.constructor.className || this.constructor.className || 'Repository';
|
|
2319
2338
|
}
|
|
2320
2339
|
|
|
2321
2340
|
get className() {
|
|
@@ -2327,11 +2346,7 @@ export default class Repository extends EventEmitter {
|
|
|
2327
2346
|
* @return {string} className
|
|
2328
2347
|
*/
|
|
2329
2348
|
getType() {
|
|
2330
|
-
|
|
2331
|
-
this.throwError('this.getClassName is no longer valid. Repository has been destroyed.');
|
|
2332
|
-
return;
|
|
2333
|
-
}
|
|
2334
|
-
return this.__proto__.constructor.type;
|
|
2349
|
+
return this.__proto__.constructor.type || this.constructor.type || null;
|
|
2335
2350
|
}
|
|
2336
2351
|
|
|
2337
2352
|
get type() {
|
|
@@ -2339,11 +2354,9 @@ export default class Repository extends EventEmitter {
|
|
|
2339
2354
|
}
|
|
2340
2355
|
|
|
2341
2356
|
toString() {
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
}
|
|
2346
|
-
return this.getClassName() + 'Repository {' + this.name + '} - ' + this.id;
|
|
2357
|
+
const name = this.name || 'destroyed';
|
|
2358
|
+
const id = this.id || 'unknown';
|
|
2359
|
+
return this.getClassName() + 'Repository {' + name + '} - ' + id;
|
|
2347
2360
|
}
|
|
2348
2361
|
|
|
2349
2362
|
};
|