@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/common-types.d.ts +1 -1
  3. package/dist/common-types.js.map +1 -1
  4. package/dist/decorators/model.decorator.js +2 -2
  5. package/dist/decorators/model.decorator.js.map +1 -1
  6. package/dist/decorators/repository.decorator.d.ts +1 -1
  7. package/dist/index.d.ts +6 -5
  8. package/dist/index.js +5 -4
  9. package/dist/index.js.map +1 -1
  10. package/dist/mixins/repository.mixin.d.ts +1 -1
  11. package/dist/model.js +3 -3
  12. package/dist/model.js.map +1 -1
  13. package/dist/relations/index.d.ts +3 -2
  14. package/dist/relations/index.js +3 -2
  15. package/dist/relations/index.js.map +1 -1
  16. package/dist/relations/relation.decorator.js +15 -5
  17. package/dist/relations/relation.decorator.js.map +1 -1
  18. package/dist/relations/relation.helpers.d.ts +23 -0
  19. package/dist/relations/relation.helpers.js +94 -0
  20. package/dist/relations/relation.helpers.js.map +1 -0
  21. package/dist/relations/relation.types.d.ts +22 -0
  22. package/dist/relations/relation.types.js.map +1 -1
  23. package/dist/repositories/legacy-juggler-bridge.d.ts +39 -4
  24. package/dist/repositories/legacy-juggler-bridge.js +63 -6
  25. package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
  26. package/dist/repositories/repository.d.ts +23 -0
  27. package/dist/repositories/repository.js +1 -0
  28. package/dist/repositories/repository.js.map +1 -1
  29. package/dist/transaction.d.ts +26 -0
  30. package/dist/transaction.js +17 -0
  31. package/dist/transaction.js.map +1 -0
  32. package/package.json +11 -11
  33. package/src/common-types.ts +5 -1
  34. package/src/decorators/model.decorator.ts +2 -0
  35. package/src/index.ts +6 -5
  36. package/src/relations/index.ts +3 -2
  37. package/src/relations/relation.decorator.ts +15 -5
  38. package/src/relations/relation.helpers.ts +134 -0
  39. package/src/relations/relation.types.ts +24 -0
  40. package/src/repositories/legacy-juggler-bridge.ts +106 -9
  41. package/src/repositories/repository.ts +32 -0
  42. 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 {EntityCrudRepository} from './repository';
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 as legacy.Filter, options),
365
+ this.modelClass.find(this.normalizeFilter(filter), options),
350
366
  );
351
- return this.toEntities(models);
367
+ const entities = this.toEntities(models);
368
+ return this.includeRelatedModels(entities, include, options);
352
369
  }
353
370
 
354
- async findOne(filter?: Filter<T>, options?: Options): Promise<T | null> {
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 as legacy.Filter, options),
376
+ this.modelClass.findOne(this.normalizeFilter(filter), options),
357
377
  );
358
378
  if (!model) return null;
359
- return this.toEntity(model);
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 as legacy.Filter, options),
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
- return this.toEntity<T & Relations>(model);
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
+ }