@expo/entity 0.25.3 → 0.27.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.
Files changed (178) hide show
  1. package/LICENSE +21 -0
  2. package/build/ComposedEntityCacheAdapter.d.ts +4 -2
  3. package/build/ComposedEntityCacheAdapter.js +39 -3
  4. package/build/ComposedEntityCacheAdapter.js.map +1 -1
  5. package/build/ComposedSecondaryEntityCache.d.ts +3 -2
  6. package/build/ComposedSecondaryEntityCache.js +3 -2
  7. package/build/ComposedSecondaryEntityCache.js.map +1 -1
  8. package/build/EnforcingEntityLoader.d.ts +10 -10
  9. package/build/EnforcingEntityLoader.js +8 -8
  10. package/build/EnforcingEntityLoader.js.map +1 -1
  11. package/build/Entity.d.ts +5 -5
  12. package/build/Entity.js +9 -9
  13. package/build/Entity.js.map +1 -1
  14. package/build/EntityAssociationLoader.d.ts +9 -4
  15. package/build/EntityAssociationLoader.js +5 -0
  16. package/build/EntityAssociationLoader.js.map +1 -1
  17. package/build/EntityCacheAdapter.d.ts +1 -1
  18. package/build/EntityCompanion.d.ts +1 -1
  19. package/build/EntityCompanion.js +1 -1
  20. package/build/EntityCompanionProvider.d.ts +9 -9
  21. package/build/EntityCompanionProvider.js +3 -3
  22. package/build/EntityConfiguration.d.ts +2 -2
  23. package/build/EntityDatabaseAdapter.d.ts +15 -6
  24. package/build/EntityDatabaseAdapter.js +8 -2
  25. package/build/EntityDatabaseAdapter.js.map +1 -1
  26. package/build/EntityFieldDefinition.d.ts +16 -8
  27. package/build/EntityFieldDefinition.js +12 -5
  28. package/build/EntityFieldDefinition.js.map +1 -1
  29. package/build/EntityFields.d.ts +11 -11
  30. package/build/EntityFields.js +11 -11
  31. package/build/EntityLoader.d.ts +7 -7
  32. package/build/EntityLoader.js +7 -7
  33. package/build/EntityLoader.js.map +1 -1
  34. package/build/EntityMutationInfo.d.ts +2 -0
  35. package/build/EntityMutationTriggerConfiguration.d.ts +1 -1
  36. package/build/EntityMutationTriggerConfiguration.js +1 -1
  37. package/build/EntityMutator.d.ts +7 -9
  38. package/build/EntityMutator.js +59 -47
  39. package/build/EntityMutator.js.map +1 -1
  40. package/build/EntityMutatorFactory.d.ts +4 -4
  41. package/build/EntityMutatorFactory.js +6 -6
  42. package/build/EntityMutatorFactory.js.map +1 -1
  43. package/build/EntityPrivacyPolicy.d.ts +18 -5
  44. package/build/EntityPrivacyPolicy.js +18 -5
  45. package/build/EntityPrivacyPolicy.js.map +1 -1
  46. package/build/EntityQueryContext.d.ts +12 -1
  47. package/build/EntityQueryContext.js +12 -1
  48. package/build/EntityQueryContext.js.map +1 -1
  49. package/build/EntityQueryContextProvider.d.ts +1 -1
  50. package/build/EntitySecondaryCacheLoader.d.ts +1 -1
  51. package/build/EntitySecondaryCacheLoader.js +1 -1
  52. package/build/GenericSecondaryEntityCache.d.ts +1 -1
  53. package/build/GenericSecondaryEntityCache.js +1 -1
  54. package/build/ReadonlyEntity.d.ts +1 -1
  55. package/build/ReadonlyEntity.js +1 -1
  56. package/build/ViewerContext.d.ts +2 -2
  57. package/build/ViewerContext.js +2 -2
  58. package/build/ViewerScopedEntityCompanion.d.ts +2 -2
  59. package/build/ViewerScopedEntityCompanion.js +2 -2
  60. package/build/ViewerScopedEntityLoaderFactory.d.ts +1 -1
  61. package/build/ViewerScopedEntityLoaderFactory.js +1 -1
  62. package/build/ViewerScopedEntityMutatorFactory.d.ts +5 -5
  63. package/build/ViewerScopedEntityMutatorFactory.js +7 -7
  64. package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
  65. package/build/__tests__/ComposedCacheAdapter-test.js +37 -4
  66. package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -1
  67. package/build/__tests__/EntityCommonUseCases-test.js +5 -1
  68. package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
  69. package/build/__tests__/EntityCompanion-test.js +5 -1
  70. package/build/__tests__/EntityCompanion-test.js.map +1 -1
  71. package/build/__tests__/EntityCompanionProvider-test.js +5 -1
  72. package/build/__tests__/EntityCompanionProvider-test.js.map +1 -1
  73. package/build/__tests__/EntityEdges-test.js +199 -33
  74. package/build/__tests__/EntityEdges-test.js.map +1 -1
  75. package/build/__tests__/EntityLoader-test.js +5 -1
  76. package/build/__tests__/EntityLoader-test.js.map +1 -1
  77. package/build/__tests__/EntityMutator-test.js +38 -53
  78. package/build/__tests__/EntityMutator-test.js.map +1 -1
  79. package/build/__tests__/EntityPrivacyPolicy-test.js +5 -1
  80. package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
  81. package/build/__tests__/EntitySelfReferentialEdges-test.js +2 -2
  82. package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
  83. package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js +5 -1
  84. package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js.map +1 -1
  85. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +2 -3
  86. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
  87. package/build/errors/EntityCacheAdapterError.js +5 -1
  88. package/build/errors/EntityCacheAdapterError.js.map +1 -1
  89. package/build/errors/EntityDatabaseAdapterError.js +5 -1
  90. package/build/errors/EntityDatabaseAdapterError.js.map +1 -1
  91. package/build/errors/EntityInvalidFieldValueError.js +6 -2
  92. package/build/errors/EntityInvalidFieldValueError.js.map +1 -1
  93. package/build/errors/EntityNotAuthorizedError.js +5 -1
  94. package/build/errors/EntityNotAuthorizedError.js.map +1 -1
  95. package/build/errors/EntityNotFoundError.js +6 -2
  96. package/build/errors/EntityNotFoundError.js.map +1 -1
  97. package/build/index.js +5 -1
  98. package/build/index.js.map +1 -1
  99. package/build/internal/EntityDataManager.d.ts +5 -5
  100. package/build/internal/EntityDataManager.js +10 -7
  101. package/build/internal/EntityDataManager.js.map +1 -1
  102. package/build/internal/EntityFieldTransformationUtils.js +1 -1
  103. package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
  104. package/build/internal/ReadThroughEntityCache.d.ts +2 -2
  105. package/build/internal/ReadThroughEntityCache.js +3 -3
  106. package/build/internal/ReadThroughEntityCache.js.map +1 -1
  107. package/build/internal/__tests__/EntityDataManager-test.js +12 -7
  108. package/build/internal/__tests__/EntityDataManager-test.js.map +1 -1
  109. package/build/internal/__tests__/ReadThroughEntityCache-test.js +5 -1
  110. package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
  111. package/build/metrics/IEntityMetricsAdapter.d.ts +63 -18
  112. package/build/metrics/IEntityMetricsAdapter.js +17 -1
  113. package/build/metrics/IEntityMetricsAdapter.js.map +1 -1
  114. package/build/metrics/NoOpEntityMetricsAdapter.d.ts +1 -3
  115. package/build/metrics/NoOpEntityMetricsAdapter.js +1 -3
  116. package/build/metrics/NoOpEntityMetricsAdapter.js.map +1 -1
  117. package/build/rules/AlwaysAllowPrivacyPolicyRule.js +5 -1
  118. package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
  119. package/build/rules/AlwaysDenyPrivacyPolicyRule.js +5 -1
  120. package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
  121. package/build/rules/AlwaysSkipPrivacyPolicyRule.js +5 -1
  122. package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
  123. package/build/rules/PrivacyPolicyRule.d.ts +1 -1
  124. package/build/rules/PrivacyPolicyRule.js +1 -1
  125. package/build/utils/collections/maps.d.ts +1 -1
  126. package/build/utils/collections/maps.js +1 -1
  127. package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +1 -2
  128. package/build/utils/testing/StubDatabaseAdapter.js +6 -2
  129. package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
  130. package/build/utils/testing/StubQueryContextProvider.d.ts +1 -1
  131. package/build/utils/testing/StubQueryContextProvider.js +2 -0
  132. package/build/utils/testing/StubQueryContextProvider.js.map +1 -1
  133. package/package.json +3 -2
  134. package/src/ComposedEntityCacheAdapter.ts +44 -3
  135. package/src/ComposedSecondaryEntityCache.ts +3 -2
  136. package/src/EnforcingEntityLoader.ts +14 -10
  137. package/src/Entity.ts +9 -9
  138. package/src/EntityAssociationLoader.ts +9 -4
  139. package/src/EntityCacheAdapter.ts +1 -1
  140. package/src/EntityCompanion.ts +1 -1
  141. package/src/EntityCompanionProvider.ts +9 -9
  142. package/src/EntityConfiguration.ts +2 -2
  143. package/src/EntityDatabaseAdapter.ts +33 -6
  144. package/src/EntityFieldDefinition.ts +15 -6
  145. package/src/EntityFields.ts +11 -11
  146. package/src/EntityLoader.ts +11 -8
  147. package/src/EntityMutationInfo.ts +2 -0
  148. package/src/EntityMutationTriggerConfiguration.ts +1 -1
  149. package/src/EntityMutator.ts +99 -68
  150. package/src/EntityMutatorFactory.ts +4 -10
  151. package/src/EntityPrivacyPolicy.ts +20 -5
  152. package/src/EntityQueryContext.ts +12 -1
  153. package/src/EntityQueryContextProvider.ts +1 -1
  154. package/src/EntitySecondaryCacheLoader.ts +1 -1
  155. package/src/GenericSecondaryEntityCache.ts +1 -1
  156. package/src/ReadonlyEntity.ts +1 -1
  157. package/src/ViewerContext.ts +2 -2
  158. package/src/ViewerScopedEntityCompanion.ts +2 -2
  159. package/src/ViewerScopedEntityLoaderFactory.ts +1 -1
  160. package/src/ViewerScopedEntityMutatorFactory.ts +8 -23
  161. package/src/__tests__/ComposedCacheAdapter-test.ts +43 -4
  162. package/src/__tests__/EntityEdges-test.ts +287 -32
  163. package/src/__tests__/EntityMutator-test.ts +33 -54
  164. package/src/__tests__/EntitySelfReferentialEdges-test.ts +2 -2
  165. package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +2 -6
  166. package/src/errors/EntityInvalidFieldValueError.ts +1 -1
  167. package/src/errors/EntityNotFoundError.ts +1 -1
  168. package/src/internal/EntityDataManager.ts +18 -9
  169. package/src/internal/EntityFieldTransformationUtils.ts +1 -1
  170. package/src/internal/ReadThroughEntityCache.ts +5 -3
  171. package/src/internal/__tests__/EntityDataManager-test.ts +11 -8
  172. package/src/metrics/IEntityMetricsAdapter.ts +73 -20
  173. package/src/metrics/NoOpEntityMetricsAdapter.ts +1 -5
  174. package/src/rules/PrivacyPolicyRule.ts +1 -1
  175. package/src/utils/collections/maps.ts +1 -1
  176. package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +1 -1
  177. package/src/utils/testing/StubDatabaseAdapter.ts +4 -1
  178. package/src/utils/testing/StubQueryContextProvider.ts +1 -1
@@ -18,7 +18,7 @@ import EntityMutationTriggerConfiguration, {
18
18
  EntityNonTransactionalMutationTrigger,
19
19
  } from './EntityMutationTriggerConfiguration';
20
20
  import EntityMutationValidator from './EntityMutationValidator';
21
- import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
21
+ import EntityPrivacyPolicy from './EntityPrivacyPolicy';
22
22
  import { EntityQueryContext, EntityTransactionalQueryContext } from './EntityQueryContext';
23
23
  import ReadonlyEntity from './ReadonlyEntity';
24
24
  import ViewerContext from './ViewerContext';
@@ -44,7 +44,6 @@ abstract class BaseMutator<
44
44
  constructor(
45
45
  protected readonly viewerContext: TViewerContext,
46
46
  protected readonly queryContext: EntityQueryContext,
47
- protected readonly privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
48
47
  protected readonly entityConfiguration: EntityConfiguration<TFields>,
49
48
  protected readonly entityClass: IEntityClass<
50
49
  TFields,
@@ -183,7 +182,7 @@ export class CreateMutator<
183
182
 
184
183
  /**
185
184
  * Commit the new entity after authorizing against creation privacy rules. Invalidates all caches for
186
- * queries that would return new entity and caches the new entity if not in a transactional query context.
185
+ * queries that would return new entity.
187
186
  * @returns authorized, cached, newly-created entity result, where result error can be UnauthorizedError
188
187
  */
189
188
  async createAsync(): Promise<Result<TEntity>> {
@@ -221,7 +220,7 @@ export class CreateMutator<
221
220
  this.privacyPolicy.authorizeCreateAsync(
222
221
  this.viewerContext,
223
222
  queryContext,
224
- this.privacyPolicyEvaluationContext,
223
+ { cascadingDeleteCause: null },
225
224
  temporaryEntityForPrivacyCheck,
226
225
  this.metricsAdapter
227
226
  )
@@ -251,11 +250,9 @@ export class CreateMutator<
251
250
 
252
251
  const insertResult = await this.databaseAdapter.insertAsync(queryContext, this.fieldsForEntity);
253
252
 
254
- const entityLoader = this.entityLoaderFactory.forLoad(
255
- this.viewerContext,
256
- queryContext,
257
- this.privacyPolicyEvaluationContext
258
- );
253
+ const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
254
+ cascadingDeleteCause: null,
255
+ });
259
256
  queryContext.appendPostCommitInvalidationCallback(
260
257
  entityLoader.invalidateFieldsAsync.bind(entityLoader, insertResult)
261
258
  );
@@ -315,7 +312,6 @@ export class UpdateMutator<
315
312
  constructor(
316
313
  viewerContext: TViewerContext,
317
314
  queryContext: EntityQueryContext,
318
- privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
319
315
  entityConfiguration: EntityConfiguration<TFields>,
320
316
  entityClass: IEntityClass<
321
317
  TFields,
@@ -355,7 +351,6 @@ export class UpdateMutator<
355
351
  super(
356
352
  viewerContext,
357
353
  queryContext,
358
- privacyPolicyEvaluationContext,
359
354
  entityConfiguration,
360
355
  entityClass,
361
356
  privacyPolicy,
@@ -382,8 +377,7 @@ export class UpdateMutator<
382
377
 
383
378
  /**
384
379
  * Commit the changes to the entity after authorizing against update privacy rules.
385
- * Invalidates all caches for pre-update entity and caches the updated entity if not in a
386
- * transactional query context.
380
+ * Invalidates all caches for pre-update entity.
387
381
  * @returns authorized updated entity result, where result error can be UnauthorizedError
388
382
  */
389
383
  async updateAsync(): Promise<Result<TEntity>> {
@@ -391,7 +385,7 @@ export class UpdateMutator<
391
385
  this.metricsAdapter,
392
386
  EntityMetricsMutationType.UPDATE,
393
387
  this.entityClass.name
394
- )(this.updateInTransactionAsync());
388
+ )(this.updateInTransactionAsync(false, null));
395
389
  }
396
390
 
397
391
  /**
@@ -401,14 +395,27 @@ export class UpdateMutator<
401
395
  return await enforceAsyncResult(this.updateAsync());
402
396
  }
403
397
 
404
- private async updateInTransactionAsync(): Promise<Result<TEntity>> {
398
+ private async updateInTransactionAsync(
399
+ skipDatabaseUpdate: true,
400
+ cascadingDeleteCause: EntityCascadingDeletionInfo
401
+ ): Promise<Result<TEntity>>;
402
+ private async updateInTransactionAsync(
403
+ skipDatabaseUpdate: false,
404
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null
405
+ ): Promise<Result<TEntity>>;
406
+ private async updateInTransactionAsync(
407
+ skipDatabaseUpdate: boolean,
408
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null
409
+ ): Promise<Result<TEntity>> {
405
410
  return await this.queryContext.runInTransactionIfNotInTransactionAsync((innerQueryContext) =>
406
- this.updateInternalAsync(innerQueryContext)
411
+ this.updateInternalAsync(innerQueryContext, skipDatabaseUpdate, cascadingDeleteCause)
407
412
  );
408
413
  }
409
414
 
410
415
  private async updateInternalAsync(
411
- queryContext: EntityTransactionalQueryContext
416
+ queryContext: EntityTransactionalQueryContext,
417
+ skipDatabaseUpdate: boolean,
418
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null
412
419
  ): Promise<Result<TEntity>> {
413
420
  this.validateFields(this.updatedFields);
414
421
 
@@ -417,7 +424,7 @@ export class UpdateMutator<
417
424
  this.privacyPolicy.authorizeUpdateAsync(
418
425
  this.viewerContext,
419
426
  queryContext,
420
- this.privacyPolicyEvaluationContext,
427
+ { cascadingDeleteCause },
421
428
  entityAboutToBeUpdated,
422
429
  this.metricsAdapter
423
430
  )
@@ -430,33 +437,34 @@ export class UpdateMutator<
430
437
  this.mutationValidators,
431
438
  queryContext,
432
439
  entityAboutToBeUpdated,
433
- { type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
440
+ { type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
434
441
  );
435
442
  await this.executeMutationTriggersAsync(
436
443
  this.mutationTriggers.beforeAll,
437
444
  queryContext,
438
445
  entityAboutToBeUpdated,
439
- { type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
446
+ { type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
440
447
  );
441
448
  await this.executeMutationTriggersAsync(
442
449
  this.mutationTriggers.beforeUpdate,
443
450
  queryContext,
444
451
  entityAboutToBeUpdated,
445
- { type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
452
+ { type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
446
453
  );
447
454
 
448
- const updateResult = await this.databaseAdapter.updateAsync(
449
- queryContext,
450
- this.entityConfiguration.idField,
451
- entityAboutToBeUpdated.getID(),
452
- this.updatedFields
453
- );
455
+ // skip the database update when specified
456
+ const updateResult = skipDatabaseUpdate
457
+ ? null
458
+ : await this.databaseAdapter.updateAsync(
459
+ queryContext,
460
+ this.entityConfiguration.idField,
461
+ entityAboutToBeUpdated.getID(),
462
+ this.updatedFields
463
+ );
454
464
 
455
- const entityLoader = this.entityLoaderFactory.forLoad(
456
- this.viewerContext,
457
- queryContext,
458
- this.privacyPolicyEvaluationContext
459
- );
465
+ const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
466
+ cascadingDeleteCause,
467
+ });
460
468
 
461
469
  queryContext.appendPostCommitInvalidationCallback(
462
470
  entityLoader.invalidateFieldsAsync.bind(
@@ -468,22 +476,25 @@ export class UpdateMutator<
468
476
  entityLoader.invalidateFieldsAsync.bind(entityLoader, this.fieldsForEntity)
469
477
  );
470
478
 
471
- const unauthorizedEntityAfterUpdate = new this.entityClass(this.viewerContext, updateResult);
472
- const updatedEntity = await entityLoader
473
- .enforcing()
474
- .loadByIDAsync(unauthorizedEntityAfterUpdate.getID());
479
+ // when the database update was skipped, assume it succeeded and use the optimistic
480
+ // entity to execute triggers
481
+ const updatedEntity = updateResult
482
+ ? await entityLoader
483
+ .enforcing()
484
+ .loadByIDAsync(new this.entityClass(this.viewerContext, updateResult).getID())
485
+ : entityAboutToBeUpdated;
475
486
 
476
487
  await this.executeMutationTriggersAsync(
477
488
  this.mutationTriggers.afterUpdate,
478
489
  queryContext,
479
490
  updatedEntity,
480
- { type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
491
+ { type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
481
492
  );
482
493
  await this.executeMutationTriggersAsync(
483
494
  this.mutationTriggers.afterAll,
484
495
  queryContext,
485
496
  updatedEntity,
486
- { type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
497
+ { type: EntityMutationType.UPDATE, previousValue: this.originalEntity, cascadingDeleteCause }
487
498
  );
488
499
 
489
500
  queryContext.appendPostCommitCallback(
@@ -491,7 +502,11 @@ export class UpdateMutator<
491
502
  this,
492
503
  this.mutationTriggers.afterCommit,
493
504
  updatedEntity,
494
- { type: EntityMutationType.UPDATE, previousValue: this.originalEntity }
505
+ {
506
+ type: EntityMutationType.UPDATE,
507
+ previousValue: this.originalEntity,
508
+ cascadingDeleteCause,
509
+ }
495
510
  )
496
511
  );
497
512
 
@@ -519,7 +534,6 @@ export class DeleteMutator<
519
534
  constructor(
520
535
  viewerContext: TViewerContext,
521
536
  queryContext: EntityQueryContext,
522
- privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
523
537
  entityConfiguration: EntityConfiguration<TFields>,
524
538
  entityClass: IEntityClass<
525
539
  TFields,
@@ -559,7 +573,6 @@ export class DeleteMutator<
559
573
  super(
560
574
  viewerContext,
561
575
  queryContext,
562
- privacyPolicyEvaluationContext,
563
576
  entityConfiguration,
564
577
  entityClass,
565
578
  privacyPolicy,
@@ -687,7 +700,7 @@ export class DeleteMutator<
687
700
  /**
688
701
  * Finds all entities referencing the specified entity and either deletes them, nullifies
689
702
  * their references to the specified entity, or invalidates the cache depending on the
690
- * {@link OnDeleteBehavior} of the field referencing the specified entity.
703
+ * OnDeleteBehavior of the field referencing the specified entity.
691
704
  *
692
705
  * @remarks
693
706
  * This works by doing reverse fan-out queries:
@@ -772,19 +785,34 @@ export class DeleteMutator<
772
785
  }
773
786
 
774
787
  switch (association.edgeDeletionBehavior) {
775
- case undefined:
776
- case EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE: {
788
+ case EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE_ONLY: {
789
+ await Promise.all(
790
+ inboundReferenceEntities.map((inboundReferenceEntity) =>
791
+ enforceAsyncResult(
792
+ mutatorFactory
793
+ .forDelete(inboundReferenceEntity, queryContext)
794
+ .deleteInTransactionAsync(
795
+ processedEntityIdentifiers,
796
+ /* skipDatabaseDeletion */ true, // deletion is handled by DB
797
+ newCascadingDeleteCause
798
+ )
799
+ )
800
+ )
801
+ );
802
+ break;
803
+ }
804
+ case EntityEdgeDeletionBehavior.SET_NULL_INVALIDATE_CACHE_ONLY: {
777
805
  await Promise.all(
778
806
  inboundReferenceEntities.map((inboundReferenceEntity) =>
779
- mutatorFactory
780
- .forDelete(inboundReferenceEntity, queryContext, {
781
- cascadingDeleteCause: newCascadingDeleteCause,
782
- })
783
- .deleteInTransactionAsync(
784
- processedEntityIdentifiers,
785
- /* skipDatabaseDeletion */ true, // deletion is handled by DB
786
- newCascadingDeleteCause
787
- )
807
+ enforceAsyncResult(
808
+ mutatorFactory
809
+ .forUpdate(inboundReferenceEntity, queryContext)
810
+ .setField(fieldName, null)
811
+ ['updateInTransactionAsync'](
812
+ /* skipDatabaseUpdate */ true,
813
+ newCascadingDeleteCause
814
+ )
815
+ )
788
816
  )
789
817
  );
790
818
  break;
@@ -792,12 +820,15 @@ export class DeleteMutator<
792
820
  case EntityEdgeDeletionBehavior.SET_NULL: {
793
821
  await Promise.all(
794
822
  inboundReferenceEntities.map((inboundReferenceEntity) =>
795
- mutatorFactory
796
- .forUpdate(inboundReferenceEntity, queryContext, {
797
- cascadingDeleteCause: newCascadingDeleteCause,
798
- })
799
- .setField(fieldName, null)
800
- .enforceUpdateAsync()
823
+ enforceAsyncResult(
824
+ mutatorFactory
825
+ .forUpdate(inboundReferenceEntity, queryContext)
826
+ .setField(fieldName, null)
827
+ ['updateInTransactionAsync'](
828
+ /* skipDatabaseUpdate */ false,
829
+ newCascadingDeleteCause
830
+ )
831
+ )
801
832
  )
802
833
  );
803
834
  break;
@@ -805,15 +836,15 @@ export class DeleteMutator<
805
836
  case EntityEdgeDeletionBehavior.CASCADE_DELETE: {
806
837
  await Promise.all(
807
838
  inboundReferenceEntities.map((inboundReferenceEntity) =>
808
- mutatorFactory
809
- .forDelete(inboundReferenceEntity, queryContext, {
810
- cascadingDeleteCause: newCascadingDeleteCause,
811
- })
812
- .deleteInTransactionAsync(
813
- processedEntityIdentifiers,
814
- /* skipDatabaseDeletion */ false,
815
- newCascadingDeleteCause
816
- )
839
+ enforceAsyncResult(
840
+ mutatorFactory
841
+ .forDelete(inboundReferenceEntity, queryContext)
842
+ .deleteInTransactionAsync(
843
+ processedEntityIdentifiers,
844
+ /* skipDatabaseDeletion */ false,
845
+ newCascadingDeleteCause
846
+ )
847
+ )
817
848
  )
818
849
  );
819
850
  }
@@ -5,7 +5,7 @@ import EntityLoaderFactory from './EntityLoaderFactory';
5
5
  import EntityMutationTriggerConfiguration from './EntityMutationTriggerConfiguration';
6
6
  import EntityMutationValidator from './EntityMutationValidator';
7
7
  import { CreateMutator, UpdateMutator, DeleteMutator } from './EntityMutator';
8
- import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
8
+ import EntityPrivacyPolicy from './EntityPrivacyPolicy';
9
9
  import { EntityQueryContext } from './EntityQueryContext';
10
10
  import ViewerContext from './ViewerContext';
11
11
  import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';
@@ -72,13 +72,11 @@ export default class EntityMutatorFactory<
72
72
  */
73
73
  forCreate(
74
74
  viewerContext: TViewerContext,
75
- queryContext: EntityQueryContext,
76
- privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
75
+ queryContext: EntityQueryContext
77
76
  ): CreateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
78
77
  return new CreateMutator(
79
78
  viewerContext,
80
79
  queryContext,
81
- privacyPolicyEvaluationContext,
82
80
  this.entityConfiguration,
83
81
  this.entityClass,
84
82
  this.privacyPolicy,
@@ -98,13 +96,11 @@ export default class EntityMutatorFactory<
98
96
  */
99
97
  forUpdate(
100
98
  existingEntity: TEntity,
101
- queryContext: EntityQueryContext,
102
- privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
99
+ queryContext: EntityQueryContext
103
100
  ): UpdateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
104
101
  return new UpdateMutator(
105
102
  existingEntity.getViewerContext(),
106
103
  queryContext,
107
- privacyPolicyEvaluationContext,
108
104
  this.entityConfiguration,
109
105
  this.entityClass,
110
106
  this.privacyPolicy,
@@ -124,13 +120,11 @@ export default class EntityMutatorFactory<
124
120
  */
125
121
  forDelete(
126
122
  existingEntity: TEntity,
127
- queryContext: EntityQueryContext,
128
- privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
123
+ queryContext: EntityQueryContext
129
124
  ): DeleteMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
130
125
  return new DeleteMutator(
131
126
  existingEntity.getViewerContext(),
132
127
  queryContext,
133
- privacyPolicyEvaluationContext,
134
128
  this.entityConfiguration,
135
129
  this.entityClass,
136
130
  this.privacyPolicy,
@@ -19,9 +19,24 @@ export type EntityPrivacyPolicyEvaluationContext = {
19
19
  cascadingDeleteCause: EntityCascadingDeletionInfo | null;
20
20
  };
21
21
 
22
+ /**
23
+ * Evaluation mode for a EntityPrivacyPolicy. Useful when transitioning to
24
+ * using Entity for privacy.
25
+ */
22
26
  export enum EntityPrivacyPolicyEvaluationMode {
27
+ /**
28
+ * Enforce this privacy policy. Throw upon denial.
29
+ */
23
30
  ENFORCE,
31
+
32
+ /**
33
+ * Do not enforce this privacy policy. Always allow but log when it would have denied.
34
+ */
24
35
  DRY_RUN,
36
+
37
+ /**
38
+ * Enforce this privacy policy. Throw and log upon denial.
39
+ */
25
40
  ENFORCE_AND_LOG,
26
41
  }
27
42
 
@@ -60,7 +75,7 @@ export enum EntityAuthorizationAction {
60
75
  *
61
76
  * @remarks
62
77
  *
63
- * A privacy policy declares lists of {@link PrivacyPolicyRule} for create, read, update, and delete actions
78
+ * A privacy policy declares lists of PrivacyPolicyRule for create, read, update, and delete actions
64
79
  * for an entity and provides logic for authorizing an entity against rules.
65
80
  *
66
81
  * Evaluation of a list of rules is performed according the following example. This allows constructing of
@@ -134,7 +149,7 @@ export default abstract class EntityPrivacyPolicy<
134
149
  * @param queryContext - query context in which to perform the create authorization
135
150
  * @param entity - entity to authorize
136
151
  * @returns entity if authorized
137
- * @throws {@link EntityNotAuthorizedError} when not authorized
152
+ * @throws EntityNotAuthorizedError when not authorized
138
153
  */
139
154
  async authorizeCreateAsync(
140
155
  viewerContext: TViewerContext,
@@ -160,7 +175,7 @@ export default abstract class EntityPrivacyPolicy<
160
175
  * @param queryContext - query context in which to perform the read authorization
161
176
  * @param entity - entity to authorize
162
177
  * @returns entity if authorized
163
- * @throws {@link EntityNotAuthorizedError} when not authorized
178
+ * @throws EntityNotAuthorizedError when not authorized
164
179
  */
165
180
  async authorizeReadAsync(
166
181
  viewerContext: TViewerContext,
@@ -186,7 +201,7 @@ export default abstract class EntityPrivacyPolicy<
186
201
  * @param queryContext - query context in which to perform the update authorization
187
202
  * @param entity - entity to authorize
188
203
  * @returns entity if authorized
189
- * @throws {@link EntityNotAuthorizedError} when not authorized
204
+ * @throws EntityNotAuthorizedError when not authorized
190
205
  */
191
206
  async authorizeUpdateAsync(
192
207
  viewerContext: TViewerContext,
@@ -212,7 +227,7 @@ export default abstract class EntityPrivacyPolicy<
212
227
  * @param queryContext - query context in which to perform the delete authorization
213
228
  * @param entity - entity to authorize
214
229
  * @returns entity if authorized
215
- * @throws {@link EntityNotAuthorizedError} when not authorized
230
+ * @throws EntityNotAuthorizedError when not authorized
216
231
  */
217
232
  async authorizeDeleteAsync(
218
233
  viewerContext: TViewerContext,
@@ -12,7 +12,7 @@ export type PreCommitCallback = (
12
12
  * Entity framework representation of transactional and non-transactional database
13
13
  * query execution units.
14
14
  *
15
- * The behavior of {@link EntityMutator} and {@link EntityLoader}
15
+ * The behavior of EntityMutator and EntityLoader
16
16
  * differs when in a transactional context.
17
17
  */
18
18
  export abstract class EntityQueryContext {
@@ -29,6 +29,12 @@ export abstract class EntityQueryContext {
29
29
  ): Promise<T>;
30
30
  }
31
31
 
32
+ /**
33
+ * Entity framework representation of a non-transactional query execution unit.
34
+ * When supplied to EntityMutator and EntityLoader methods, they will be
35
+ * run independently of any running transaction (though mutations start their own
36
+ * independent transactions internally when not being run in a transaction).
37
+ */
32
38
  export class EntityNonTransactionalQueryContext extends EntityQueryContext {
33
39
  constructor(
34
40
  queryInterface: any,
@@ -48,6 +54,11 @@ export class EntityNonTransactionalQueryContext extends EntityQueryContext {
48
54
  }
49
55
  }
50
56
 
57
+ /**
58
+ * Entity framework representation of a transactional query execution unit. When supplied
59
+ * to EntityMutator and EntityLoader methods, those methods and their
60
+ * dependent triggers and validators will run within the transaction.
61
+ */
51
62
  export class EntityTransactionalQueryContext extends EntityQueryContext {
52
63
  private readonly postCommitInvalidationCallbacks: PostCommitCallback[] = [];
53
64
  private readonly postCommitCallbacks: PostCommitCallback[] = [];
@@ -20,7 +20,7 @@ export default abstract class EntityQueryContextProvider {
20
20
  protected abstract getQueryInterface(): any;
21
21
 
22
22
  /**
23
- * Vend a transaction runner for use in {@link runInTransactionAsync}.
23
+ * Vend a transaction runner for use in runInTransactionAsync.
24
24
  */
25
25
  protected abstract createTransactionRunner<T>(): (
26
26
  transactionScope: (queryInterface: any) => Promise<T>
@@ -40,7 +40,7 @@ export interface ISecondaryEntityCache<TFields, TLoadParams> {
40
40
  * when the underlying data of a cache key could be stale.
41
41
  *
42
42
  * This is most commonly used to further optimize hot paths that cannot make use of normal entity cache loading
43
- * due to use of a non-unique-field-based {@link EntityLoader} method like `loadManyByFieldEqualityConjunctionAsync` or
43
+ * due to use of a non-unique-field-based EntityLoader method like `loadManyByFieldEqualityConjunctionAsync` or
44
44
  * `loadManyByRawWhereClauseAsync`.
45
45
  */
46
46
  export default abstract class EntitySecondaryCacheLoader<
@@ -8,7 +8,7 @@ import { filterMap, zipToMap } from './utils/collections/maps';
8
8
  /**
9
9
  * A custom secondary read-through entity cache is a way to add a custom second layer of caching for a particular
10
10
  * single entity load. One common way this may be used is to add a second layer of caching in a hot path that makes
11
- * a call to {@link EntityLoader.loadManyByFieldEqualityConjunctionAsync} is guaranteed to return at most one entity.
11
+ * a call to EntityLoader.loadManyByFieldEqualityConjunctionAsync is guaranteed to return at most one entity.
12
12
  */
13
13
  export default abstract class GenericSecondaryEntityCache<TFields, TLoadParams>
14
14
  implements ISecondaryEntityCache<TFields, TLoadParams>
@@ -82,7 +82,7 @@ export default abstract class ReadonlyEntity<
82
82
  }
83
83
 
84
84
  /**
85
- * @returns {@link EntityAssociationLoader} for this entity
85
+ * @returns EntityAssociationLoader for this entity
86
86
  */
87
87
  associationLoader(): EntityAssociationLoader<
88
88
  TFields,
@@ -7,9 +7,9 @@ import ViewerScopedEntityCompanion from './ViewerScopedEntityCompanion';
7
7
  import ViewerScopedEntityCompanionProvider from './ViewerScopedEntityCompanionProvider';
8
8
 
9
9
  /**
10
- * A viewer context encapsulates all information necessary to evaluate an {@link EntityPrivacyPolicy}.
10
+ * A viewer context encapsulates all information necessary to evaluate an EntityPrivacyPolicy.
11
11
  *
12
- * In combination with an {@link EntityCompanionProvider}, a viewer context is the
12
+ * In combination with an EntityCompanionProvider, a viewer context is the
13
13
  * entry point into the Entity framework.
14
14
  */
15
15
  export default class ViewerContext {
@@ -8,7 +8,7 @@ import ViewerScopedEntityMutatorFactory from './ViewerScopedEntityMutatorFactory
8
8
  import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';
9
9
 
10
10
  /**
11
- * Provides a simpler API for loading and mutating entities by injecting the {@link ViewerContext}
11
+ * Provides a simpler API for loading and mutating entities by injecting the ViewerContext
12
12
  * from the viewer-scoped entity companion provider.
13
13
  */
14
14
  export default class ViewerScopedEntityCompanion<
@@ -79,7 +79,7 @@ export default class ViewerScopedEntityCompanion<
79
79
  }
80
80
 
81
81
  /**
82
- * Get the {@link IEntityMetricsAdapter} for this companion.
82
+ * Get the IEntityMetricsAdapter for this companion.
83
83
  */
84
84
  getMetricsAdapter(): IEntityMetricsAdapter {
85
85
  return this.entityCompanion.getMetricsAdapter();
@@ -6,7 +6,7 @@ import ReadonlyEntity from './ReadonlyEntity';
6
6
  import ViewerContext from './ViewerContext';
7
7
 
8
8
  /**
9
- * Provides a cleaner API for loading entities by passing through the {@link ViewerContext}.
9
+ * Provides a cleaner API for loading entities by passing through the ViewerContext.
10
10
  */
11
11
  export default class ViewerScopedEntityLoaderFactory<
12
12
  TFields,
@@ -1,12 +1,12 @@
1
1
  import { CreateMutator, UpdateMutator, DeleteMutator } from './EntityMutator';
2
2
  import EntityMutatorFactory from './EntityMutatorFactory';
3
- import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
3
+ import EntityPrivacyPolicy from './EntityPrivacyPolicy';
4
4
  import { EntityQueryContext } from './EntityQueryContext';
5
5
  import ReadonlyEntity from './ReadonlyEntity';
6
6
  import ViewerContext from './ViewerContext';
7
7
 
8
8
  /**
9
- * Provides a cleaner API for mutating entities by passing through the {@link ViewerContext}.
9
+ * Provides a cleaner API for mutating entities by passing through the ViewerContext.
10
10
  */
11
11
  export default class ViewerScopedEntityMutatorFactory<
12
12
  TFields,
@@ -35,37 +35,22 @@ export default class ViewerScopedEntityMutatorFactory<
35
35
  ) {}
36
36
 
37
37
  forCreate(
38
- queryContext: EntityQueryContext,
39
- privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
38
+ queryContext: EntityQueryContext
40
39
  ): CreateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
41
- return this.entityMutatorFactory.forCreate(
42
- this.viewerContext,
43
- queryContext,
44
- privacyPolicyEvaluationContext
45
- );
40
+ return this.entityMutatorFactory.forCreate(this.viewerContext, queryContext);
46
41
  }
47
42
 
48
43
  forUpdate(
49
44
  existingEntity: TEntity,
50
- queryContext: EntityQueryContext,
51
- privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
45
+ queryContext: EntityQueryContext
52
46
  ): UpdateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
53
- return this.entityMutatorFactory.forUpdate(
54
- existingEntity,
55
- queryContext,
56
- privacyPolicyEvaluationContext
57
- );
47
+ return this.entityMutatorFactory.forUpdate(existingEntity, queryContext);
58
48
  }
59
49
 
60
50
  forDelete(
61
51
  existingEntity: TEntity,
62
- queryContext: EntityQueryContext,
63
- privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
52
+ queryContext: EntityQueryContext
64
53
  ): DeleteMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
65
- return this.entityMutatorFactory.forDelete(
66
- existingEntity,
67
- queryContext,
68
- privacyPolicyEvaluationContext
69
- );
54
+ return this.entityMutatorFactory.forDelete(existingEntity, queryContext);
70
55
  }
71
56
  }