@loopback/repository 1.10.1 → 1.13.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/CHANGELOG.md +55 -0
- package/dist/common-types.d.ts +1 -1
- package/dist/common-types.js.map +1 -1
- package/dist/decorators/model.decorator.js +2 -2
- package/dist/decorators/model.decorator.js.map +1 -1
- package/dist/decorators/repository.decorator.d.ts +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/mixins/repository.mixin.d.ts +1 -1
- package/dist/model.js +3 -3
- package/dist/model.js.map +1 -1
- package/dist/relations/index.d.ts +3 -2
- package/dist/relations/index.js +3 -2
- package/dist/relations/index.js.map +1 -1
- package/dist/relations/relation.decorator.js +15 -5
- package/dist/relations/relation.decorator.js.map +1 -1
- package/dist/relations/relation.helpers.d.ts +23 -0
- package/dist/relations/relation.helpers.js +94 -0
- package/dist/relations/relation.helpers.js.map +1 -0
- package/dist/relations/relation.types.d.ts +22 -0
- package/dist/relations/relation.types.js.map +1 -1
- package/dist/repositories/legacy-juggler-bridge.d.ts +39 -4
- package/dist/repositories/legacy-juggler-bridge.js +63 -6
- package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
- package/dist/repositories/repository.d.ts +23 -0
- package/dist/repositories/repository.js +1 -0
- package/dist/repositories/repository.js.map +1 -1
- package/dist/transaction.d.ts +26 -0
- package/dist/transaction.js +17 -0
- package/dist/transaction.js.map +1 -0
- package/package.json +11 -11
- package/src/common-types.ts +5 -1
- package/src/decorators/model.decorator.ts +2 -0
- package/src/index.ts +6 -5
- package/src/relations/index.ts +3 -2
- package/src/relations/relation.decorator.ts +15 -5
- package/src/relations/relation.helpers.ts +134 -0
- package/src/relations/relation.types.ts +24 -0
- package/src/repositories/legacy-juggler-bridge.ts +106 -9
- package/src/repositories/repository.ts +32 -0
- package/src/transaction.ts +34 -0
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from '../common-types';
|
|
18
18
|
import {EntityNotFoundError} from '../errors';
|
|
19
19
|
import {Entity, Model, PropertyType} from '../model';
|
|
20
|
-
import {Filter, Where} from '../query';
|
|
20
|
+
import {Filter, Inclusion, Where} from '../query';
|
|
21
21
|
import {
|
|
22
22
|
BelongsToAccessor,
|
|
23
23
|
BelongsToDefinition,
|
|
@@ -28,9 +28,15 @@ import {
|
|
|
28
28
|
HasManyRepositoryFactory,
|
|
29
29
|
HasOneDefinition,
|
|
30
30
|
HasOneRepositoryFactory,
|
|
31
|
+
includeRelatedModels,
|
|
32
|
+
InclusionResolver,
|
|
31
33
|
} from '../relations';
|
|
34
|
+
import {IsolationLevel, Transaction} from '../transaction';
|
|
32
35
|
import {isTypeResolver, resolveType} from '../type-resolver';
|
|
33
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
EntityCrudRepository,
|
|
38
|
+
TransactionalEntityRepository,
|
|
39
|
+
} from './repository';
|
|
34
40
|
|
|
35
41
|
export namespace juggler {
|
|
36
42
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
@@ -40,6 +46,10 @@ export namespace juggler {
|
|
|
40
46
|
export import PersistedModel = legacy.PersistedModel;
|
|
41
47
|
export import KeyValueModel = legacy.KeyValueModel;
|
|
42
48
|
export import PersistedModelClass = legacy.PersistedModelClass;
|
|
49
|
+
// eslint-disable-next-line no-shadow
|
|
50
|
+
export import Transaction = legacy.Transaction;
|
|
51
|
+
// eslint-disable-next-line no-shadow
|
|
52
|
+
export import IsolationLevel = legacy.IsolationLevel;
|
|
43
53
|
}
|
|
44
54
|
|
|
45
55
|
function isModelClass(
|
|
@@ -92,6 +102,11 @@ export class DefaultCrudRepository<
|
|
|
92
102
|
> implements EntityCrudRepository<T, ID, Relations> {
|
|
93
103
|
modelClass: juggler.PersistedModelClass;
|
|
94
104
|
|
|
105
|
+
public readonly inclusionResolvers: Map<
|
|
106
|
+
string,
|
|
107
|
+
InclusionResolver<T, Entity>
|
|
108
|
+
> = new Map();
|
|
109
|
+
|
|
95
110
|
/**
|
|
96
111
|
* Constructor of DefaultCrudRepository
|
|
97
112
|
* @param entityClass - Legacy entity class
|
|
@@ -345,18 +360,30 @@ export class DefaultCrudRepository<
|
|
|
345
360
|
filter?: Filter<T>,
|
|
346
361
|
options?: Options,
|
|
347
362
|
): Promise<(T & Relations)[]> {
|
|
363
|
+
const include = filter && filter.include;
|
|
348
364
|
const models = await ensurePromise(
|
|
349
|
-
this.modelClass.find(filter
|
|
365
|
+
this.modelClass.find(this.normalizeFilter(filter), options),
|
|
350
366
|
);
|
|
351
|
-
|
|
367
|
+
const entities = this.toEntities(models);
|
|
368
|
+
return this.includeRelatedModels(entities, include, options);
|
|
352
369
|
}
|
|
353
370
|
|
|
354
|
-
async findOne(
|
|
371
|
+
async findOne(
|
|
372
|
+
filter?: Filter<T>,
|
|
373
|
+
options?: Options,
|
|
374
|
+
): Promise<(T & Relations) | null> {
|
|
355
375
|
const model = await ensurePromise(
|
|
356
|
-
this.modelClass.findOne(filter
|
|
376
|
+
this.modelClass.findOne(this.normalizeFilter(filter), options),
|
|
357
377
|
);
|
|
358
378
|
if (!model) return null;
|
|
359
|
-
|
|
379
|
+
const entity = this.toEntity(model);
|
|
380
|
+
const include = filter && filter.include;
|
|
381
|
+
const resolved = await this.includeRelatedModels(
|
|
382
|
+
[entity],
|
|
383
|
+
include,
|
|
384
|
+
options,
|
|
385
|
+
);
|
|
386
|
+
return resolved[0];
|
|
360
387
|
}
|
|
361
388
|
|
|
362
389
|
async findById(
|
|
@@ -364,13 +391,20 @@ export class DefaultCrudRepository<
|
|
|
364
391
|
filter?: Filter<T>,
|
|
365
392
|
options?: Options,
|
|
366
393
|
): Promise<T & Relations> {
|
|
394
|
+
const include = filter && filter.include;
|
|
367
395
|
const model = await ensurePromise(
|
|
368
|
-
this.modelClass.findById(id, filter
|
|
396
|
+
this.modelClass.findById(id, this.normalizeFilter(filter), options),
|
|
369
397
|
);
|
|
370
398
|
if (!model) {
|
|
371
399
|
throw new EntityNotFoundError(this.entityClass, id);
|
|
372
400
|
}
|
|
373
|
-
|
|
401
|
+
const entity = this.toEntity(model);
|
|
402
|
+
const resolved = await this.includeRelatedModels(
|
|
403
|
+
[entity],
|
|
404
|
+
include,
|
|
405
|
+
options,
|
|
406
|
+
);
|
|
407
|
+
return resolved[0];
|
|
374
408
|
}
|
|
375
409
|
|
|
376
410
|
update(entity: T, options?: Options): Promise<void> {
|
|
@@ -460,4 +494,67 @@ export class DefaultCrudRepository<
|
|
|
460
494
|
protected toEntities<R extends T>(models: juggler.PersistedModel[]): R[] {
|
|
461
495
|
return models.map(m => this.toEntity<R>(m));
|
|
462
496
|
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Register an inclusion resolver for the related model name.
|
|
500
|
+
*
|
|
501
|
+
* @param relationName - Name of the relation defined on the source model
|
|
502
|
+
* @param resolver - Resolver function for getting related model entities
|
|
503
|
+
*/
|
|
504
|
+
registerInclusionResolver(
|
|
505
|
+
relationName: string,
|
|
506
|
+
resolver: InclusionResolver<T, Entity>,
|
|
507
|
+
) {
|
|
508
|
+
this.inclusionResolvers.set(relationName, resolver);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Returns model instances that include related models of this repository
|
|
513
|
+
* that have a registered resolver.
|
|
514
|
+
*
|
|
515
|
+
* @param entities - An array of entity instances or data
|
|
516
|
+
* @param include -Inclusion filter
|
|
517
|
+
* @param options - Options for the operations
|
|
518
|
+
*/
|
|
519
|
+
protected async includeRelatedModels(
|
|
520
|
+
entities: T[],
|
|
521
|
+
include?: Inclusion<T>[],
|
|
522
|
+
options?: Options,
|
|
523
|
+
): Promise<(T & Relations)[]> {
|
|
524
|
+
return includeRelatedModels<T, Relations>(this, entities, include, options);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Removes juggler's "include" filter as it does not apply to LoopBack 4
|
|
529
|
+
* relations.
|
|
530
|
+
*
|
|
531
|
+
* @param filter - Query filter
|
|
532
|
+
*/
|
|
533
|
+
protected normalizeFilter(filter?: Filter<T>): legacy.Filter | undefined {
|
|
534
|
+
if (!filter) return undefined;
|
|
535
|
+
return {...filter, include: undefined} as legacy.Filter;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Default implementation of CRUD repository using legacy juggler model
|
|
541
|
+
* and data source with beginTransaction() method for connectors which
|
|
542
|
+
* support Transactions
|
|
543
|
+
*/
|
|
544
|
+
|
|
545
|
+
export class DefaultTransactionalRepository<
|
|
546
|
+
T extends Entity,
|
|
547
|
+
ID,
|
|
548
|
+
Relations extends object = {}
|
|
549
|
+
> extends DefaultCrudRepository<T, ID, Relations>
|
|
550
|
+
implements TransactionalEntityRepository<T, ID, Relations> {
|
|
551
|
+
async beginTransaction(
|
|
552
|
+
options?: IsolationLevel | Options,
|
|
553
|
+
): Promise<Transaction> {
|
|
554
|
+
const dsOptions: juggler.IsolationLevel | Options = options || {};
|
|
555
|
+
// juggler.Transaction still has the Promise/Callback variants of the
|
|
556
|
+
// Transaction methods
|
|
557
|
+
// so we need it cast it back
|
|
558
|
+
return (await this.dataSource.beginTransaction(dsOptions)) as Transaction;
|
|
559
|
+
}
|
|
463
560
|
}
|
|
@@ -17,6 +17,8 @@ import {DataSource} from '../datasource';
|
|
|
17
17
|
import {EntityNotFoundError} from '../errors';
|
|
18
18
|
import {Entity, Model, ValueObject} from '../model';
|
|
19
19
|
import {Filter, Where} from '../query';
|
|
20
|
+
import {InclusionResolver} from '../relations/relation.types';
|
|
21
|
+
import {IsolationLevel, Transaction} from '../transaction';
|
|
20
22
|
|
|
21
23
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
22
24
|
|
|
@@ -37,6 +39,31 @@ export interface ExecutableRepository<T extends Model> extends Repository<T> {
|
|
|
37
39
|
): Promise<AnyObject>;
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
/**
|
|
43
|
+
* A type for CRUD repositories that are backed by IDs and support
|
|
44
|
+
* Transactions
|
|
45
|
+
*/
|
|
46
|
+
export type TransactionalEntityRepository<
|
|
47
|
+
T extends Entity,
|
|
48
|
+
ID,
|
|
49
|
+
Relations extends object = {}
|
|
50
|
+
> = TransactionalRepository<T> & EntityCrudRepository<T, ID>;
|
|
51
|
+
/**
|
|
52
|
+
* Repository Interface for Repositories that support Transactions
|
|
53
|
+
*
|
|
54
|
+
* @typeParam T Generic type for the Entity
|
|
55
|
+
*/
|
|
56
|
+
export interface TransactionalRepository<T extends Entity>
|
|
57
|
+
extends Repository<T> {
|
|
58
|
+
/**
|
|
59
|
+
* Begin a new Transaction
|
|
60
|
+
* @param options - Options for the operations
|
|
61
|
+
* @returns Promise<Transaction> Promise that resolves to a new Transaction
|
|
62
|
+
* object
|
|
63
|
+
*/
|
|
64
|
+
beginTransaction(options?: IsolationLevel | Options): Promise<Transaction>;
|
|
65
|
+
}
|
|
66
|
+
|
|
40
67
|
/**
|
|
41
68
|
* Basic CRUD operations for ValueObject and Entity. No ID is required.
|
|
42
69
|
*/
|
|
@@ -114,6 +141,7 @@ export interface EntityCrudRepository<
|
|
|
114
141
|
> extends EntityRepository<T, ID>, CrudRepository<T, Relations> {
|
|
115
142
|
// entityClass should have type "typeof T", but that's not supported by TSC
|
|
116
143
|
entityClass: typeof Entity & {prototype: T};
|
|
144
|
+
inclusionResolvers: Map<string, InclusionResolver<T, Entity>>;
|
|
117
145
|
|
|
118
146
|
/**
|
|
119
147
|
* Save an entity. If no id is present, create a new entity
|
|
@@ -220,6 +248,10 @@ export interface EntityCrudRepository<
|
|
|
220
248
|
export class CrudRepositoryImpl<T extends Entity, ID>
|
|
221
249
|
implements EntityCrudRepository<T, ID> {
|
|
222
250
|
private connector: CrudConnector;
|
|
251
|
+
public readonly inclusionResolvers: Map<
|
|
252
|
+
string,
|
|
253
|
+
InclusionResolver<T, Entity>
|
|
254
|
+
> = new Map();
|
|
223
255
|
|
|
224
256
|
constructor(
|
|
225
257
|
public dataSource: DataSource,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Copyright IBM Corp. 2019. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/repository
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Local transaction
|
|
8
|
+
*/
|
|
9
|
+
export interface Transaction {
|
|
10
|
+
/**
|
|
11
|
+
* Commit the transaction
|
|
12
|
+
*/
|
|
13
|
+
commit(): Promise<void>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Rollback the transaction
|
|
17
|
+
*/
|
|
18
|
+
rollback(): Promise<void>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The transaction Identifier
|
|
22
|
+
*/
|
|
23
|
+
id: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Isolation level
|
|
28
|
+
*/
|
|
29
|
+
export enum IsolationLevel {
|
|
30
|
+
READ_COMMITTED = 'READ COMMITTED', // default
|
|
31
|
+
READ_UNCOMMITTED = 'READ UNCOMMITTED',
|
|
32
|
+
SERIALIZABLE = 'SERIALIZABLE',
|
|
33
|
+
REPEATABLE_READ = 'REPEATABLE READ',
|
|
34
|
+
}
|