@expo/entity 0.38.0 → 0.40.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 (206) hide show
  1. package/build/AuthorizationResultBasedEntityAssociationLoader.d.ts +99 -0
  2. package/build/AuthorizationResultBasedEntityAssociationLoader.js +124 -0
  3. package/build/AuthorizationResultBasedEntityAssociationLoader.js.map +1 -0
  4. package/build/AuthorizationResultBasedEntityLoader.d.ts +1 -1
  5. package/build/AuthorizationResultBasedEntityLoader.js.map +1 -1
  6. package/build/{EntityMutator.d.ts → AuthorizationResultBasedEntityMutator.d.ts} +5 -17
  7. package/build/{EntityMutator.js → AuthorizationResultBasedEntityMutator.js} +22 -48
  8. package/build/AuthorizationResultBasedEntityMutator.js.map +1 -0
  9. package/build/EnforcingEntityAssociationLoader.d.ts +79 -0
  10. package/build/EnforcingEntityAssociationLoader.js +62 -0
  11. package/build/EnforcingEntityAssociationLoader.js.map +1 -0
  12. package/build/EnforcingEntityCreator.d.ts +24 -0
  13. package/build/EnforcingEntityCreator.js +32 -0
  14. package/build/EnforcingEntityCreator.js.map +1 -0
  15. package/build/EnforcingEntityDeleter.d.ts +17 -0
  16. package/build/EnforcingEntityDeleter.js +22 -0
  17. package/build/EnforcingEntityDeleter.js.map +1 -0
  18. package/build/EnforcingEntityUpdater.d.ts +24 -0
  19. package/build/EnforcingEntityUpdater.js +32 -0
  20. package/build/EnforcingEntityUpdater.js.map +1 -0
  21. package/build/Entity.d.ts +8 -12
  22. package/build/Entity.js +9 -34
  23. package/build/Entity.js.map +1 -1
  24. package/build/EntityAssociationLoader.d.ts +12 -91
  25. package/build/EntityAssociationLoader.js +20 -126
  26. package/build/EntityAssociationLoader.js.map +1 -1
  27. package/build/EntityCompanionProvider.d.ts +2 -2
  28. package/build/EntityCompanionProvider.js.map +1 -1
  29. package/build/EntityCreator.d.ts +27 -0
  30. package/build/EntityCreator.js +39 -0
  31. package/build/EntityCreator.js.map +1 -0
  32. package/build/EntityDatabaseAdapter.js +2 -2
  33. package/build/EntityDatabaseAdapter.js.map +1 -1
  34. package/build/EntityDeleter.d.ts +27 -0
  35. package/build/EntityDeleter.js +40 -0
  36. package/build/EntityDeleter.js.map +1 -0
  37. package/build/EntityLoader.d.ts +4 -14
  38. package/build/EntityLoader.js +7 -20
  39. package/build/EntityLoader.js.map +1 -1
  40. package/build/EntityLoaderFactory.d.ts +2 -2
  41. package/build/EntityLoaderFactory.js +4 -2
  42. package/build/EntityLoaderFactory.js.map +1 -1
  43. package/build/EntityMutatorFactory.d.ts +4 -4
  44. package/build/EntityMutatorFactory.js +4 -4
  45. package/build/EntityMutatorFactory.js.map +1 -1
  46. package/build/EntitySecondaryCacheLoader.d.ts +3 -3
  47. package/build/EntitySecondaryCacheLoader.js +1 -3
  48. package/build/EntitySecondaryCacheLoader.js.map +1 -1
  49. package/build/EntityUpdater.d.ts +27 -0
  50. package/build/EntityUpdater.js +40 -0
  51. package/build/EntityUpdater.js.map +1 -0
  52. package/build/ReadonlyEntity.d.ts +2 -2
  53. package/build/ReadonlyEntity.js +4 -6
  54. package/build/ReadonlyEntity.js.map +1 -1
  55. package/build/ViewerScopedEntityLoaderFactory.d.ts +2 -2
  56. package/build/ViewerScopedEntityLoaderFactory.js.map +1 -1
  57. package/build/ViewerScopedEntityMutatorFactory.d.ts +4 -4
  58. package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
  59. package/build/__tests__/AuthorizationResultBasedEntityAssociationLoader-test.d.ts +1 -0
  60. package/build/__tests__/AuthorizationResultBasedEntityAssociationLoader-test.js +273 -0
  61. package/build/__tests__/AuthorizationResultBasedEntityAssociationLoader-test.js.map +1 -0
  62. package/build/__tests__/{EntityLoader-constructor-test.js → AuthorizationResultBasedEntityLoader-constructor-test.js} +11 -11
  63. package/build/__tests__/AuthorizationResultBasedEntityLoader-constructor-test.js.map +1 -0
  64. package/build/__tests__/AuthorizationResultBasedEntityLoader-test.d.ts +1 -0
  65. package/build/__tests__/AuthorizationResultBasedEntityLoader-test.js +401 -0
  66. package/build/__tests__/AuthorizationResultBasedEntityLoader-test.js.map +1 -0
  67. package/build/__tests__/EnforcingEntityAssociationLoader-test.d.ts +1 -0
  68. package/build/__tests__/EnforcingEntityAssociationLoader-test.js +115 -0
  69. package/build/__tests__/EnforcingEntityAssociationLoader-test.js.map +1 -0
  70. package/build/__tests__/Entity-test.js +23 -5
  71. package/build/__tests__/Entity-test.js.map +1 -1
  72. package/build/__tests__/EntityAssociationLoader-test.js +14 -184
  73. package/build/__tests__/EntityAssociationLoader-test.js.map +1 -1
  74. package/build/__tests__/EntityCommonUseCases-test.js +34 -12
  75. package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
  76. package/build/__tests__/EntityCompanion-test.js +17 -7
  77. package/build/__tests__/EntityCompanion-test.js.map +1 -1
  78. package/build/__tests__/EntityDatabaseAdapter-test.js.map +1 -1
  79. package/build/__tests__/EntityEdges-test.js +41 -23
  80. package/build/__tests__/EntityEdges-test.js.map +1 -1
  81. package/build/__tests__/EntityLoader-test.js +22 -386
  82. package/build/__tests__/EntityLoader-test.js.map +1 -1
  83. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js +4 -3
  84. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js.map +1 -1
  85. package/build/__tests__/EntityMutator-test.js +67 -70
  86. package/build/__tests__/EntityMutator-test.js.map +1 -1
  87. package/build/__tests__/EntityPrivacyPolicy-test.js +17 -7
  88. package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
  89. package/build/__tests__/EntitySecondaryCacheLoader-test.js +7 -7
  90. package/build/__tests__/EntitySecondaryCacheLoader-test.js.map +1 -1
  91. package/build/__tests__/EntitySelfReferentialEdges-test.js +36 -24
  92. package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
  93. package/build/__tests__/ReadonlyEntity-test.js +1 -1
  94. package/build/__tests__/ReadonlyEntity-test.js.map +1 -1
  95. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js +4 -2
  96. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js.map +1 -1
  97. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js +7 -4
  98. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js.map +1 -1
  99. package/build/__tests__/entityUtils-test.js +8 -0
  100. package/build/__tests__/entityUtils-test.js.map +1 -1
  101. package/build/entityUtils.d.ts +7 -0
  102. package/build/entityUtils.js +20 -10
  103. package/build/entityUtils.js.map +1 -1
  104. package/build/errors/EntityCacheAdapterError.js +17 -7
  105. package/build/errors/EntityCacheAdapterError.js.map +1 -1
  106. package/build/errors/EntityDatabaseAdapterError.js +17 -7
  107. package/build/errors/EntityDatabaseAdapterError.js.map +1 -1
  108. package/build/errors/EntityInvalidFieldValueError.js +17 -7
  109. package/build/errors/EntityInvalidFieldValueError.js.map +1 -1
  110. package/build/errors/EntityNotAuthorizedError.js +17 -7
  111. package/build/errors/EntityNotAuthorizedError.js.map +1 -1
  112. package/build/errors/EntityNotFoundError.js +17 -7
  113. package/build/errors/EntityNotFoundError.js.map +1 -1
  114. package/build/index.d.ts +19 -11
  115. package/build/index.js +24 -7
  116. package/build/index.js.map +1 -1
  117. package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
  118. package/build/internal/__tests__/EntityDataManager-test.js +42 -32
  119. package/build/internal/__tests__/EntityDataManager-test.js.map +1 -1
  120. package/build/internal/__tests__/ReadThroughEntityCache-test.js +17 -7
  121. package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
  122. package/build/rules/AlwaysAllowPrivacyPolicyRule.js +17 -7
  123. package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
  124. package/build/rules/AlwaysDenyPrivacyPolicyRule.js +17 -7
  125. package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
  126. package/build/rules/AlwaysSkipPrivacyPolicyRule.js +17 -7
  127. package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
  128. package/build/utils/EntityPrivacyUtils.d.ts +32 -4
  129. package/build/utils/EntityPrivacyUtils.js +68 -24
  130. package/build/utils/EntityPrivacyUtils.js.map +1 -1
  131. package/build/utils/__tests__/EntityPrivacyUtils-test.js +148 -23
  132. package/build/utils/__tests__/EntityPrivacyUtils-test.js.map +1 -1
  133. package/build/utils/__tests__/canViewerDeleteAsync-edgeDeletionPermissionInferenceBehavior-test.js +8 -5
  134. package/build/utils/__tests__/canViewerDeleteAsync-edgeDeletionPermissionInferenceBehavior-test.js.map +1 -1
  135. package/build/utils/collections/__tests__/maps-test.js +1 -1
  136. package/build/utils/collections/__tests__/maps-test.js.map +1 -1
  137. package/build/utils/collections/maps.js +2 -2
  138. package/build/utils/collections/maps.js.map +1 -1
  139. package/build/utils/mergeEntityMutationTriggerConfigurations.js +1 -2
  140. package/build/utils/mergeEntityMutationTriggerConfigurations.js.map +1 -1
  141. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js +1 -1
  142. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
  143. package/build/utils/testing/StubDatabaseAdapter.js +17 -7
  144. package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
  145. package/build/utils/testing/StubQueryContextProvider.d.ts +1 -3
  146. package/build/utils/testing/StubQueryContextProvider.js +1 -3
  147. package/build/utils/testing/StubQueryContextProvider.js.map +1 -1
  148. package/build/utils/testing/createUnitTestEntityCompanionProvider.js +2 -1
  149. package/build/utils/testing/createUnitTestEntityCompanionProvider.js.map +1 -1
  150. package/build/utils/testing/describeFieldTestCase.js +1 -1
  151. package/build/utils/testing/describeFieldTestCase.js.map +1 -1
  152. package/package.json +19 -3
  153. package/src/AuthorizationResultBasedEntityAssociationLoader.ts +492 -0
  154. package/src/AuthorizationResultBasedEntityLoader.ts +2 -2
  155. package/src/{EntityMutator.ts → AuthorizationResultBasedEntityMutator.ts} +62 -58
  156. package/src/EnforcingEntityAssociationLoader.ts +390 -0
  157. package/src/EnforcingEntityCreator.ts +55 -0
  158. package/src/EnforcingEntityDeleter.ts +44 -0
  159. package/src/EnforcingEntityUpdater.ts +55 -0
  160. package/src/Entity.ts +20 -65
  161. package/src/EntityAssociationLoader.ts +38 -495
  162. package/src/EntityCompanionProvider.ts +5 -2
  163. package/src/EntityCreator.ts +73 -0
  164. package/src/EntityDeleter.ts +73 -0
  165. package/src/EntityLoader.ts +10 -49
  166. package/src/EntityLoaderFactory.ts +20 -3
  167. package/src/EntityMutatorFactory.ts +32 -7
  168. package/src/EntitySecondaryCacheLoader.ts +5 -7
  169. package/src/EntityUpdater.ts +73 -0
  170. package/src/ReadonlyEntity.ts +14 -13
  171. package/src/ViewerScopedEntityLoaderFactory.ts +9 -2
  172. package/src/ViewerScopedEntityMutatorFactory.ts +29 -4
  173. package/src/__tests__/AuthorizationResultBasedEntityAssociationLoader-test.ts +354 -0
  174. package/src/__tests__/{EntityLoader-constructor-test.ts → AuthorizationResultBasedEntityLoader-constructor-test.ts} +17 -10
  175. package/src/__tests__/AuthorizationResultBasedEntityLoader-test.ts +730 -0
  176. package/src/__tests__/EnforcingEntityAssociationLoader-test.ts +253 -0
  177. package/src/__tests__/Entity-test.ts +24 -5
  178. package/src/__tests__/EntityAssociationLoader-test.ts +16 -259
  179. package/src/__tests__/EntityCommonUseCases-test.ts +20 -8
  180. package/src/__tests__/EntityCompanion-test.ts +1 -1
  181. package/src/__tests__/EntityDatabaseAdapter-test.ts +6 -6
  182. package/src/__tests__/EntityEdges-test.ts +24 -16
  183. package/src/__tests__/EntityLoader-test.ts +25 -675
  184. package/src/__tests__/EntityMutator-MutationCacheConsistency-test.ts +4 -3
  185. package/src/__tests__/EntityMutator-test.ts +116 -103
  186. package/src/__tests__/EntitySecondaryCacheLoader-test.ts +7 -7
  187. package/src/__tests__/EntitySelfReferentialEdges-test.ts +36 -24
  188. package/src/__tests__/ReadonlyEntity-test.ts +1 -1
  189. package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +4 -2
  190. package/src/__tests__/cases/TwoEntitySameTableOverlappingRows-test.ts +7 -4
  191. package/src/__tests__/entityUtils-test.ts +12 -0
  192. package/src/entityUtils.ts +24 -9
  193. package/src/index.ts +19 -11
  194. package/src/internal/EntityFieldTransformationUtils.ts +2 -2
  195. package/src/internal/__tests__/EntityDataManager-test.ts +29 -29
  196. package/src/utils/EntityPrivacyUtils.ts +188 -107
  197. package/src/utils/__tests__/EntityPrivacyUtils-test.ts +169 -29
  198. package/src/utils/__tests__/canViewerDeleteAsync-edgeDeletionPermissionInferenceBehavior-test.ts +8 -5
  199. package/src/utils/collections/__tests__/maps-test.ts +1 -1
  200. package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +1 -1
  201. package/src/utils/testing/StubDatabaseAdapter.ts +1 -1
  202. package/src/utils/testing/StubQueryContextProvider.ts +1 -3
  203. package/src/utils/testing/createUnitTestEntityCompanionProvider.ts +3 -1
  204. package/build/EntityMutator.js.map +0 -1
  205. package/build/__tests__/EntityLoader-constructor-test.js.map +0 -1
  206. /package/build/__tests__/{EntityLoader-constructor-test.d.ts → AuthorizationResultBasedEntityLoader-constructor-test.d.ts} +0 -0
@@ -21,12 +21,13 @@ import EntityMutationValidator from './EntityMutationValidator';
21
21
  import EntityPrivacyPolicy from './EntityPrivacyPolicy';
22
22
  import { EntityQueryContext, EntityTransactionalQueryContext } from './EntityQueryContext';
23
23
  import ViewerContext from './ViewerContext';
24
+ import { enforceResultsAsync } from './entityUtils';
24
25
  import EntityInvalidFieldValueError from './errors/EntityInvalidFieldValueError';
25
26
  import { timeAndLogMutationEventAsync } from './metrics/EntityMetricsUtils';
26
27
  import IEntityMetricsAdapter, { EntityMetricsMutationType } from './metrics/IEntityMetricsAdapter';
27
28
  import { mapMapAsync } from './utils/collections/maps';
28
29
 
29
- abstract class BaseMutator<
30
+ abstract class AuthorizationResultBasedBaseMutator<
30
31
  TFields extends object,
31
32
  TID extends NonNullable<TFields[TSelectedFields]>,
32
33
  TViewerContext extends ViewerContext,
@@ -154,7 +155,7 @@ abstract class BaseMutator<
154
155
  /**
155
156
  * Mutator for creating a new entity.
156
157
  */
157
- export class CreateMutator<
158
+ export class AuthorizationResultBasedCreateMutator<
158
159
  TFields extends object,
159
160
  TID extends NonNullable<TFields[TSelectedFields]>,
160
161
  TViewerContext extends ViewerContext,
@@ -167,12 +168,19 @@ export class CreateMutator<
167
168
  TSelectedFields
168
169
  >,
169
170
  TSelectedFields extends keyof TFields,
170
- > extends BaseMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
171
+ > extends AuthorizationResultBasedBaseMutator<
172
+ TFields,
173
+ TID,
174
+ TViewerContext,
175
+ TEntity,
176
+ TPrivacyPolicy,
177
+ TSelectedFields
178
+ > {
171
179
  private readonly fieldsForEntity: Partial<TFields> = {};
172
180
 
173
181
  /**
174
182
  * Set the value for entity field.
175
- * @param fieldName - entity field being updated
183
+ * @param fieldName - entity field being set
176
184
  * @param value - value for entity field
177
185
  */
178
186
  setField<K extends keyof Pick<TFields, TSelectedFields>>(fieldName: K, value: TFields[K]): this {
@@ -193,13 +201,6 @@ export class CreateMutator<
193
201
  )(this.createInTransactionAsync());
194
202
  }
195
203
 
196
- /**
197
- * Convenience method that returns the new entity or throws upon create failure.
198
- */
199
- async enforceCreateAsync(): Promise<TEntity> {
200
- return await enforceAsyncResult(this.createAsync());
201
- }
202
-
203
204
  private async createInTransactionAsync(): Promise<Result<TEntity>> {
204
205
  return await this.queryContext.runInTransactionIfNotInTransactionAsync((innerQueryContext) =>
205
206
  this.createInternalAsync(innerQueryContext),
@@ -216,7 +217,7 @@ export class CreateMutator<
216
217
  cascadingDeleteCause: null,
217
218
  });
218
219
 
219
- const temporaryEntityForPrivacyCheck = entityLoader.utils().constructEntity({
220
+ const temporaryEntityForPrivacyCheck = entityLoader.utils.constructEntity({
220
221
  [this.entityConfiguration.idField]: '00000000-0000-0000-0000-000000000000', // zero UUID
221
222
  ...this.fieldsForEntity,
222
223
  } as unknown as TFields);
@@ -256,13 +257,13 @@ export class CreateMutator<
256
257
  const insertResult = await this.databaseAdapter.insertAsync(queryContext, this.fieldsForEntity);
257
258
 
258
259
  queryContext.appendPostCommitInvalidationCallback(
259
- entityLoader.utils().invalidateFieldsAsync.bind(entityLoader, insertResult),
260
+ entityLoader.utils.invalidateFieldsAsync.bind(entityLoader, insertResult),
260
261
  );
261
262
 
262
- const unauthorizedEntityAfterInsert = entityLoader.utils().constructEntity(insertResult);
263
- const newEntity = await entityLoader
264
- .enforcing()
265
- .loadByIDAsync(unauthorizedEntityAfterInsert.getID());
263
+ const unauthorizedEntityAfterInsert = entityLoader.utils.constructEntity(insertResult);
264
+ const newEntity = await enforceAsyncResult(
265
+ entityLoader.loadByIDAsync(unauthorizedEntityAfterInsert.getID()),
266
+ );
266
267
 
267
268
  await this.executeMutationTriggersAsync(
268
269
  this.mutationTriggers.afterCreate,
@@ -293,7 +294,7 @@ export class CreateMutator<
293
294
  /**
294
295
  * Mutator for updating an existing entity.
295
296
  */
296
- export class UpdateMutator<
297
+ export class AuthorizationResultBasedUpdateMutator<
297
298
  TFields extends object,
298
299
  TID extends NonNullable<TFields[TSelectedFields]>,
299
300
  TViewerContext extends ViewerContext,
@@ -306,7 +307,14 @@ export class UpdateMutator<
306
307
  TSelectedFields
307
308
  >,
308
309
  TSelectedFields extends keyof TFields,
309
- > extends BaseMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
310
+ > extends AuthorizationResultBasedBaseMutator<
311
+ TFields,
312
+ TID,
313
+ TViewerContext,
314
+ TEntity,
315
+ TPrivacyPolicy,
316
+ TSelectedFields
317
+ > {
310
318
  private readonly originalEntity: TEntity;
311
319
  private readonly fieldsForEntity: TFields;
312
320
  private readonly updatedFields: Partial<TFields> = {};
@@ -392,13 +400,6 @@ export class UpdateMutator<
392
400
  )(this.updateInTransactionAsync(false, null));
393
401
  }
394
402
 
395
- /**
396
- * Convenience method that returns the updated entity or throws upon update failure.
397
- */
398
- async enforceUpdateAsync(): Promise<TEntity> {
399
- return await enforceAsyncResult(this.updateAsync());
400
- }
401
-
402
403
  private async updateInTransactionAsync(
403
404
  skipDatabaseUpdate: true,
404
405
  cascadingDeleteCause: EntityCascadingDeletionInfo,
@@ -429,7 +430,7 @@ export class UpdateMutator<
429
430
  cascadingDeleteCause,
430
431
  });
431
432
 
432
- const entityAboutToBeUpdated = entityLoader.utils().constructEntity(this.fieldsForEntity);
433
+ const entityAboutToBeUpdated = entityLoader.utils.constructEntity(this.fieldsForEntity);
433
434
  const authorizeUpdateResult = await asyncResult(
434
435
  this.privacyPolicy.authorizeUpdateAsync(
435
436
  this.viewerContext,
@@ -473,17 +474,18 @@ export class UpdateMutator<
473
474
  }
474
475
 
475
476
  queryContext.appendPostCommitInvalidationCallback(
476
- entityLoader
477
- .utils()
478
- .invalidateFieldsAsync.bind(entityLoader, this.originalEntity.getAllDatabaseFields()),
477
+ entityLoader.utils.invalidateFieldsAsync.bind(
478
+ entityLoader,
479
+ this.originalEntity.getAllDatabaseFields(),
480
+ ),
479
481
  );
480
482
  queryContext.appendPostCommitInvalidationCallback(
481
- entityLoader.utils().invalidateFieldsAsync.bind(entityLoader, this.fieldsForEntity),
483
+ entityLoader.utils.invalidateFieldsAsync.bind(entityLoader, this.fieldsForEntity),
482
484
  );
483
485
 
484
- const updatedEntity = await entityLoader
485
- .enforcing()
486
- .loadByIDAsync(entityAboutToBeUpdated.getID()); // ID is guaranteed to be stable by ensureStableIDField
486
+ const updatedEntity = await enforceAsyncResult(
487
+ entityLoader.loadByIDAsync(entityAboutToBeUpdated.getID()),
488
+ ); // ID is guaranteed to be stable by ensureStableIDField
487
489
 
488
490
  await this.executeMutationTriggersAsync(
489
491
  this.mutationTriggers.afterUpdate,
@@ -526,7 +528,7 @@ export class UpdateMutator<
526
528
  /**
527
529
  * Mutator for deleting an existing entity.
528
530
  */
529
- export class DeleteMutator<
531
+ export class AuthorizationResultBasedDeleteMutator<
530
532
  TFields extends object,
531
533
  TID extends NonNullable<TFields[TSelectedFields]>,
532
534
  TViewerContext extends ViewerContext,
@@ -539,7 +541,14 @@ export class DeleteMutator<
539
541
  TSelectedFields
540
542
  >,
541
543
  TSelectedFields extends keyof TFields,
542
- > extends BaseMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
544
+ > extends AuthorizationResultBasedBaseMutator<
545
+ TFields,
546
+ TID,
547
+ TViewerContext,
548
+ TEntity,
549
+ TPrivacyPolicy,
550
+ TSelectedFields
551
+ > {
543
552
  constructor(
544
553
  companionProvider: EntityCompanionProvider,
545
554
  viewerContext: TViewerContext,
@@ -607,13 +616,6 @@ export class DeleteMutator<
607
616
  )(this.deleteInTransactionAsync(new Set(), false, null));
608
617
  }
609
618
 
610
- /**
611
- * Convenience method that throws upon delete failure.
612
- */
613
- async enforceDeleteAsync(): Promise<void> {
614
- return await enforceAsyncResult(this.deleteAsync());
615
- }
616
-
617
619
  private async deleteInTransactionAsync(
618
620
  processedEntityIdentifiersFromTransitiveDeletions: Set<string>,
619
621
  skipDatabaseDeletion: boolean,
@@ -681,9 +683,10 @@ export class DeleteMutator<
681
683
  cascadingDeleteCause,
682
684
  });
683
685
  queryContext.appendPostCommitInvalidationCallback(
684
- entityLoader
685
- .utils()
686
- .invalidateFieldsAsync.bind(entityLoader, this.entity.getAllDatabaseFields()),
686
+ entityLoader.utils.invalidateFieldsAsync.bind(
687
+ entityLoader,
688
+ this.entity.getAllDatabaseFields(),
689
+ ),
687
690
  );
688
691
 
689
692
  await this.executeMutationTriggersAsync(
@@ -782,18 +785,19 @@ export class DeleteMutator<
782
785
  return;
783
786
  }
784
787
 
785
- const inboundReferenceEntities = await loaderFactory
786
- .forLoad(queryContext, {
787
- previousValue: null,
788
- cascadingDeleteCause: newCascadingDeleteCause,
789
- })
790
- .enforcing()
791
- .loadManyByFieldEqualingAsync(
792
- fieldName,
793
- association.associatedEntityLookupByField
794
- ? entity.getField(association.associatedEntityLookupByField as any)
795
- : entity.getID(),
796
- );
788
+ const inboundReferenceEntities = await enforceResultsAsync(
789
+ loaderFactory
790
+ .forLoad(queryContext, {
791
+ previousValue: null,
792
+ cascadingDeleteCause: newCascadingDeleteCause,
793
+ })
794
+ .loadManyByFieldEqualingAsync(
795
+ fieldName,
796
+ association.associatedEntityLookupByField
797
+ ? entity.getField(association.associatedEntityLookupByField as any)
798
+ : entity.getID(),
799
+ ),
800
+ );
797
801
 
798
802
  switch (association.edgeDeletionBehavior) {
799
803
  case EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE_ONLY: {
@@ -0,0 +1,390 @@
1
+ import { enforceAsyncResult } from '@expo/results';
2
+
3
+ import AuthorizationResultBasedEntityAssociationLoader, {
4
+ EntityLoadThroughDirective,
5
+ } from './AuthorizationResultBasedEntityAssociationLoader';
6
+ import { IEntityClass } from './Entity';
7
+ import EntityPrivacyPolicy from './EntityPrivacyPolicy';
8
+ import ReadonlyEntity from './ReadonlyEntity';
9
+ import ViewerContext from './ViewerContext';
10
+ import { enforceResultsAsync } from './entityUtils';
11
+
12
+ /**
13
+ * An association loader is a set of convenience methods for loading entities
14
+ * associated with an entity. In relational databases, these entities are often referenced
15
+ * by foreign keys.
16
+ */
17
+ export default class EnforcingEntityAssociationLoader<
18
+ TFields extends object,
19
+ TID extends NonNullable<TFields[TSelectedFields]>,
20
+ TViewerContext extends ViewerContext,
21
+ TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
22
+ TSelectedFields extends keyof TFields,
23
+ > {
24
+ constructor(
25
+ private readonly authorizationResultBasedEntityAssociationLoader: AuthorizationResultBasedEntityAssociationLoader<
26
+ TFields,
27
+ TID,
28
+ TViewerContext,
29
+ TEntity,
30
+ TSelectedFields
31
+ >,
32
+ ) {}
33
+
34
+ /**
35
+ * Load an associated entity identified by a field value of this entity. In a relational database,
36
+ * the field in this entity is a foreign key to the ID of the associated entity.
37
+ * @param fieldIdentifyingAssociatedEntity - field of this entity containing the ID of the associated entity
38
+ * @param associatedEntityClass - class of the associated entity
39
+ */
40
+ async loadAssociatedEntityAsync<
41
+ TIdentifyingField extends keyof Pick<TFields, TSelectedFields>,
42
+ TAssociatedFields extends object,
43
+ TAssociatedID extends NonNullable<TAssociatedFields[TAssociatedSelectedFields]>,
44
+ TAssociatedEntity extends ReadonlyEntity<
45
+ TAssociatedFields,
46
+ TAssociatedID,
47
+ TViewerContext,
48
+ TAssociatedSelectedFields
49
+ >,
50
+ TAssociatedPrivacyPolicy extends EntityPrivacyPolicy<
51
+ TAssociatedFields,
52
+ TAssociatedID,
53
+ TViewerContext,
54
+ TAssociatedEntity,
55
+ TAssociatedSelectedFields
56
+ >,
57
+ TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields,
58
+ >(
59
+ fieldIdentifyingAssociatedEntity: TIdentifyingField,
60
+ associatedEntityClass: IEntityClass<
61
+ TAssociatedFields,
62
+ TAssociatedID,
63
+ TViewerContext,
64
+ TAssociatedEntity,
65
+ TAssociatedPrivacyPolicy,
66
+ TAssociatedSelectedFields
67
+ >,
68
+ ): Promise<
69
+ null extends TFields[TIdentifyingField] ? TAssociatedEntity | null : TAssociatedEntity
70
+ > {
71
+ return await enforceAsyncResult(
72
+ this.authorizationResultBasedEntityAssociationLoader.loadAssociatedEntityAsync(
73
+ fieldIdentifyingAssociatedEntity,
74
+ associatedEntityClass,
75
+ ),
76
+ );
77
+ }
78
+
79
+ /**
80
+ * Load many entities associated with this entity, often referred to as entites belonging
81
+ * to this entity. In a relational database, the field in the foreign entity is a
82
+ * foreign key to the ID of this entity. Also commonly referred to as a has many relationship,
83
+ * where this entity has many associated entities.
84
+ * @param associatedEntityClass - class of the associated entities
85
+ * @param associatedEntityFieldContainingThisID - field of associated entity which contains the ID of this entity
86
+ */
87
+ async loadManyAssociatedEntitiesAsync<
88
+ TAssociatedFields extends object,
89
+ TAssociatedID extends NonNullable<TAssociatedFields[TAssociatedSelectedFields]>,
90
+ TAssociatedEntity extends ReadonlyEntity<
91
+ TAssociatedFields,
92
+ TAssociatedID,
93
+ TViewerContext,
94
+ TAssociatedSelectedFields
95
+ >,
96
+ TAssociatedPrivacyPolicy extends EntityPrivacyPolicy<
97
+ TAssociatedFields,
98
+ TAssociatedID,
99
+ TViewerContext,
100
+ TAssociatedEntity,
101
+ TAssociatedSelectedFields
102
+ >,
103
+ TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields,
104
+ >(
105
+ associatedEntityClass: IEntityClass<
106
+ TAssociatedFields,
107
+ TAssociatedID,
108
+ TViewerContext,
109
+ TAssociatedEntity,
110
+ TAssociatedPrivacyPolicy,
111
+ TAssociatedSelectedFields
112
+ >,
113
+ associatedEntityFieldContainingThisID: keyof Pick<TAssociatedFields, TAssociatedSelectedFields>,
114
+ ): Promise<readonly TAssociatedEntity[]> {
115
+ return await enforceResultsAsync(
116
+ this.authorizationResultBasedEntityAssociationLoader.loadManyAssociatedEntitiesAsync(
117
+ associatedEntityClass,
118
+ associatedEntityFieldContainingThisID,
119
+ ),
120
+ );
121
+ }
122
+
123
+ /**
124
+ * Load an associated entity identified by a field value of this entity. In a relational database,
125
+ * the field in this entity is a foreign key to a unique field of the associated entity.
126
+ * @param fieldIdentifyingAssociatedEntity - field of this entity containing the value with which to look up associated entity
127
+ * @param associatedEntityClass - class of the associated entity
128
+ * @param associatedEntityLookupByField - field of associated entity with which to look up the associated entity
129
+ */
130
+ async loadAssociatedEntityByFieldEqualingAsync<
131
+ TAssociatedFields extends object,
132
+ TAssociatedID extends NonNullable<TAssociatedFields[TAssociatedSelectedFields]>,
133
+ TAssociatedEntity extends ReadonlyEntity<
134
+ TAssociatedFields,
135
+ TAssociatedID,
136
+ TViewerContext,
137
+ TAssociatedSelectedFields
138
+ >,
139
+ TAssociatedPrivacyPolicy extends EntityPrivacyPolicy<
140
+ TAssociatedFields,
141
+ TAssociatedID,
142
+ TViewerContext,
143
+ TAssociatedEntity,
144
+ TAssociatedSelectedFields
145
+ >,
146
+ TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields,
147
+ >(
148
+ fieldIdentifyingAssociatedEntity: keyof Pick<TFields, TSelectedFields>,
149
+ associatedEntityClass: IEntityClass<
150
+ TAssociatedFields,
151
+ TAssociatedID,
152
+ TViewerContext,
153
+ TAssociatedEntity,
154
+ TAssociatedPrivacyPolicy,
155
+ TAssociatedSelectedFields
156
+ >,
157
+ associatedEntityLookupByField: keyof Pick<TAssociatedFields, TAssociatedSelectedFields>,
158
+ ): Promise<TAssociatedEntity | null> {
159
+ const result =
160
+ await this.authorizationResultBasedEntityAssociationLoader.loadAssociatedEntityByFieldEqualingAsync(
161
+ fieldIdentifyingAssociatedEntity,
162
+ associatedEntityClass,
163
+ associatedEntityLookupByField,
164
+ );
165
+ return result?.enforceValue() ?? null;
166
+ }
167
+
168
+ /**
169
+ * Load many associated entities identified by a field value of this entity. In a relational database,
170
+ * the field in this entity refers to a field of the associated entity.
171
+ * @param fieldIdentifyingAssociatedEntity - field of this entity containing the value with which to look up associated entities
172
+ * @param associatedEntityClass - class of the associated entities
173
+ * @param associatedEntityLookupByField - field of associated entities with which to look up the associated entities
174
+ */
175
+ async loadManyAssociatedEntitiesByFieldEqualingAsync<
176
+ TAssociatedFields extends object,
177
+ TAssociatedID extends NonNullable<TAssociatedFields[TAssociatedSelectedFields]>,
178
+ TAssociatedEntity extends ReadonlyEntity<
179
+ TAssociatedFields,
180
+ TAssociatedID,
181
+ TViewerContext,
182
+ TAssociatedSelectedFields
183
+ >,
184
+ TAssociatedPrivacyPolicy extends EntityPrivacyPolicy<
185
+ TAssociatedFields,
186
+ TAssociatedID,
187
+ TViewerContext,
188
+ TAssociatedEntity,
189
+ TAssociatedSelectedFields
190
+ >,
191
+ TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields,
192
+ >(
193
+ fieldIdentifyingAssociatedEntity: keyof Pick<TFields, TSelectedFields>,
194
+ associatedEntityClass: IEntityClass<
195
+ TAssociatedFields,
196
+ TAssociatedID,
197
+ TViewerContext,
198
+ TAssociatedEntity,
199
+ TAssociatedPrivacyPolicy,
200
+ TAssociatedSelectedFields
201
+ >,
202
+ associatedEntityLookupByField: keyof Pick<TAssociatedFields, TAssociatedSelectedFields>,
203
+ ): Promise<readonly TAssociatedEntity[]> {
204
+ return await enforceResultsAsync(
205
+ this.authorizationResultBasedEntityAssociationLoader.loadManyAssociatedEntitiesByFieldEqualingAsync(
206
+ fieldIdentifyingAssociatedEntity,
207
+ associatedEntityClass,
208
+ associatedEntityLookupByField,
209
+ ),
210
+ );
211
+ }
212
+
213
+ /**
214
+ * Load an associated entity by folding a sequence of EntityLoadThroughDirective. At each
215
+ * fold step, load an associated entity identified by a field value of the current fold value.
216
+ * @param loadDirectives - associated entity load directives instructing each step of the folds
217
+ */
218
+ async loadAssociatedEntityThroughAsync<
219
+ TFields2 extends object,
220
+ TID2 extends NonNullable<TFields2[TSelectedFields2]>,
221
+ TEntity2 extends ReadonlyEntity<TFields2, TID2, TViewerContext, TSelectedFields2>,
222
+ TPrivacyPolicy2 extends EntityPrivacyPolicy<
223
+ TFields2,
224
+ TID2,
225
+ TViewerContext,
226
+ TEntity2,
227
+ TSelectedFields2
228
+ >,
229
+ TSelectedFields2 extends keyof TFields2 = keyof TFields2,
230
+ >(
231
+ loadDirectives: [
232
+ EntityLoadThroughDirective<
233
+ TViewerContext,
234
+ TFields,
235
+ TFields2,
236
+ TID2,
237
+ TEntity2,
238
+ TPrivacyPolicy2,
239
+ TSelectedFields,
240
+ TSelectedFields2
241
+ >,
242
+ ],
243
+ ): Promise<TEntity2 | null>;
244
+
245
+ /**
246
+ * Load an associated entity by folding a sequence of EntityLoadThroughDirective. At each
247
+ * fold step, load an associated entity identified by a field value of the current fold value.
248
+ * @param loadDirectives - associated entity load directives instructing each step of the folds
249
+ */
250
+ async loadAssociatedEntityThroughAsync<
251
+ TFields2 extends object,
252
+ TID2 extends NonNullable<TFields2[TSelectedFields2]>,
253
+ TEntity2 extends ReadonlyEntity<TFields2, TID2, TViewerContext, TSelectedFields2>,
254
+ TPrivacyPolicy2 extends EntityPrivacyPolicy<
255
+ TFields2,
256
+ TID2,
257
+ TViewerContext,
258
+ TEntity2,
259
+ TSelectedFields2
260
+ >,
261
+ TFields3 extends object,
262
+ TID3 extends NonNullable<TFields3[TSelectedFields3]>,
263
+ TEntity3 extends ReadonlyEntity<TFields3, TID3, TViewerContext, TSelectedFields3>,
264
+ TPrivacyPolicy3 extends EntityPrivacyPolicy<
265
+ TFields3,
266
+ TID3,
267
+ TViewerContext,
268
+ TEntity3,
269
+ TSelectedFields3
270
+ >,
271
+ TSelectedFields2 extends keyof TFields2 = keyof TFields2,
272
+ TSelectedFields3 extends keyof TFields3 = keyof TFields3,
273
+ >(
274
+ loadDirectives: [
275
+ EntityLoadThroughDirective<
276
+ TViewerContext,
277
+ TFields,
278
+ TFields2,
279
+ TID2,
280
+ TEntity2,
281
+ TPrivacyPolicy2,
282
+ TSelectedFields,
283
+ TSelectedFields2
284
+ >,
285
+ EntityLoadThroughDirective<
286
+ TViewerContext,
287
+ TFields2,
288
+ TFields3,
289
+ TID3,
290
+ TEntity3,
291
+ TPrivacyPolicy3,
292
+ TSelectedFields2,
293
+ TSelectedFields3
294
+ >,
295
+ ],
296
+ ): Promise<TEntity3 | null>;
297
+
298
+ /**
299
+ * Load an associated entity by folding a sequence of EntityLoadThroughDirective. At each
300
+ * fold step, load an associated entity identified by a field value of the current fold value.
301
+ * @param loadDirectives - associated entity load directives instructing each step of the folds
302
+ */
303
+ async loadAssociatedEntityThroughAsync<
304
+ TFields2 extends object,
305
+ TID2 extends NonNullable<TFields2[TSelectedFields2]>,
306
+ TEntity2 extends ReadonlyEntity<TFields2, TID2, TViewerContext, TSelectedFields2>,
307
+ TPrivacyPolicy2 extends EntityPrivacyPolicy<
308
+ TFields2,
309
+ TID2,
310
+ TViewerContext,
311
+ TEntity2,
312
+ TSelectedFields2
313
+ >,
314
+ TFields3 extends object,
315
+ TID3 extends NonNullable<TFields3[TSelectedFields3]>,
316
+ TEntity3 extends ReadonlyEntity<TFields3, TID3, TViewerContext, TSelectedFields3>,
317
+ TPrivacyPolicy3 extends EntityPrivacyPolicy<
318
+ TFields3,
319
+ TID3,
320
+ TViewerContext,
321
+ TEntity3,
322
+ TSelectedFields3
323
+ >,
324
+ TFields4 extends object,
325
+ TID4 extends NonNullable<TFields4[TSelectedFields4]>,
326
+ TEntity4 extends ReadonlyEntity<TFields4, TID4, TViewerContext, TSelectedFields4>,
327
+ TPrivacyPolicy4 extends EntityPrivacyPolicy<
328
+ TFields4,
329
+ TID4,
330
+ TViewerContext,
331
+ TEntity4,
332
+ TSelectedFields4
333
+ >,
334
+ TSelectedFields2 extends keyof TFields2 = keyof TFields2,
335
+ TSelectedFields3 extends keyof TFields3 = keyof TFields3,
336
+ TSelectedFields4 extends keyof TFields4 = keyof TFields4,
337
+ >(
338
+ loadDirectives: [
339
+ EntityLoadThroughDirective<
340
+ TViewerContext,
341
+ TFields,
342
+ TFields2,
343
+ TID2,
344
+ TEntity2,
345
+ TPrivacyPolicy2,
346
+ TSelectedFields,
347
+ TSelectedFields2
348
+ >,
349
+ EntityLoadThroughDirective<
350
+ TViewerContext,
351
+ TFields2,
352
+ TFields3,
353
+ TID3,
354
+ TEntity3,
355
+ TPrivacyPolicy3,
356
+ TSelectedFields2,
357
+ TSelectedFields3
358
+ >,
359
+ EntityLoadThroughDirective<
360
+ TViewerContext,
361
+ TFields3,
362
+ TFields4,
363
+ TID4,
364
+ TEntity4,
365
+ TPrivacyPolicy4,
366
+ TSelectedFields3,
367
+ TSelectedFields4
368
+ >,
369
+ ],
370
+ ): Promise<TEntity4 | null>;
371
+
372
+ /**
373
+ * Load an associated entity by folding a sequence of EntityLoadThroughDirective. At each
374
+ * fold step, load an associated entity identified by a field value of the current fold value.
375
+ * @param loadDirectives - associated entity load directives instructing each step of the folds
376
+ */
377
+ async loadAssociatedEntityThroughAsync(
378
+ loadDirectives: EntityLoadThroughDirective<TViewerContext, any, any, any, any, any, any, any>[],
379
+ ): Promise<ReadonlyEntity<any, any, any, any> | null>;
380
+
381
+ async loadAssociatedEntityThroughAsync(
382
+ loadDirectives: EntityLoadThroughDirective<TViewerContext, any, any, any, any, any, any, any>[],
383
+ ): Promise<ReadonlyEntity<any, any, any, any> | null> {
384
+ const result =
385
+ await this.authorizationResultBasedEntityAssociationLoader.loadAssociatedEntityThroughAsync(
386
+ loadDirectives,
387
+ );
388
+ return result?.enforceValue() ?? null;
389
+ }
390
+ }
@@ -0,0 +1,55 @@
1
+ import { enforceAsyncResult } from '@expo/results';
2
+
3
+ import { AuthorizationResultBasedCreateMutator } from './AuthorizationResultBasedEntityMutator';
4
+ import EntityPrivacyPolicy from './EntityPrivacyPolicy';
5
+ import ReadonlyEntity from './ReadonlyEntity';
6
+ import ViewerContext from './ViewerContext';
7
+
8
+ /**
9
+ * Enforcing entity creator. All updates
10
+ * through this creator will throw if authorization is not successful.
11
+ */
12
+ export default class EnforcingEntityCreator<
13
+ TFields extends object,
14
+ TID extends NonNullable<TFields[TSelectedFields]>,
15
+ TViewerContext extends ViewerContext,
16
+ TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
17
+ TPrivacyPolicy extends EntityPrivacyPolicy<
18
+ TFields,
19
+ TID,
20
+ TViewerContext,
21
+ TEntity,
22
+ TSelectedFields
23
+ >,
24
+ TSelectedFields extends keyof TFields,
25
+ > {
26
+ constructor(
27
+ private readonly entityCreator: AuthorizationResultBasedCreateMutator<
28
+ TFields,
29
+ TID,
30
+ TViewerContext,
31
+ TEntity,
32
+ TPrivacyPolicy,
33
+ TSelectedFields
34
+ >,
35
+ ) {}
36
+
37
+ /**
38
+ * Set the value for entity field.
39
+ * @param fieldName - entity field being updated
40
+ * @param value - value for entity field
41
+ */
42
+ setField<K extends keyof Pick<TFields, TSelectedFields>>(fieldName: K, value: TFields[K]): this {
43
+ this.entityCreator.setField(fieldName, value);
44
+ return this;
45
+ }
46
+
47
+ /**
48
+ * Commit the new entity after authorizing against creation privacy rules. Invalidates all caches for
49
+ * queries that would return new entity.
50
+ * @returns authorized, cached, newly-created entity, throwing if unsuccessful
51
+ */
52
+ async createAsync(): Promise<TEntity> {
53
+ return await enforceAsyncResult(this.entityCreator.createAsync());
54
+ }
55
+ }