@expo/entity 0.22.0 → 0.25.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 (145) hide show
  1. package/build/ComposedEntityCacheAdapter.d.ts +19 -0
  2. package/build/ComposedEntityCacheAdapter.js +66 -0
  3. package/build/ComposedEntityCacheAdapter.js.map +1 -0
  4. package/build/ComposedSecondaryEntityCache.d.ts +15 -0
  5. package/build/ComposedSecondaryEntityCache.js +37 -0
  6. package/build/ComposedSecondaryEntityCache.js.map +1 -0
  7. package/build/Entity.js +6 -6
  8. package/build/Entity.js.map +1 -1
  9. package/build/EntityAssociationLoader.js +4 -4
  10. package/build/EntityAssociationLoader.js.map +1 -1
  11. package/build/EntityCacheAdapter.d.ts +2 -9
  12. package/build/EntityCacheAdapter.js.map +1 -1
  13. package/build/EntityFieldDefinition.d.ts +8 -0
  14. package/build/EntityFieldDefinition.js +5 -0
  15. package/build/EntityFieldDefinition.js.map +1 -1
  16. package/build/EntityFields.d.ts +38 -0
  17. package/build/EntityFields.js +38 -0
  18. package/build/EntityFields.js.map +1 -1
  19. package/build/EntityLoader.d.ts +3 -2
  20. package/build/EntityLoader.js +5 -4
  21. package/build/EntityLoader.js.map +1 -1
  22. package/build/EntityLoaderFactory.d.ts +2 -2
  23. package/build/EntityLoaderFactory.js +2 -2
  24. package/build/EntityLoaderFactory.js.map +1 -1
  25. package/build/EntityMutationInfo.d.ts +12 -3
  26. package/build/EntityMutator.d.ts +5 -4
  27. package/build/EntityMutator.js +31 -24
  28. package/build/EntityMutator.js.map +1 -1
  29. package/build/EntityMutatorFactory.d.ts +4 -4
  30. package/build/EntityMutatorFactory.js +6 -6
  31. package/build/EntityMutatorFactory.js.map +1 -1
  32. package/build/EntityPrivacyPolicy.d.ts +15 -4
  33. package/build/EntityPrivacyPolicy.js +14 -14
  34. package/build/EntityPrivacyPolicy.js.map +1 -1
  35. package/build/GenericSecondaryEntityCache.d.ts +19 -0
  36. package/build/GenericSecondaryEntityCache.js +74 -0
  37. package/build/GenericSecondaryEntityCache.js.map +1 -0
  38. package/build/IEntityGenericCacher.d.ts +11 -0
  39. package/build/IEntityGenericCacher.js +3 -0
  40. package/build/IEntityGenericCacher.js.map +1 -0
  41. package/build/ReadonlyEntity.js +1 -1
  42. package/build/ReadonlyEntity.js.map +1 -1
  43. package/build/ViewerScopedEntityLoaderFactory.d.ts +2 -2
  44. package/build/ViewerScopedEntityLoaderFactory.js +2 -2
  45. package/build/ViewerScopedEntityLoaderFactory.js.map +1 -1
  46. package/build/ViewerScopedEntityMutatorFactory.d.ts +4 -4
  47. package/build/ViewerScopedEntityMutatorFactory.js +6 -6
  48. package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
  49. package/build/__tests__/ComposedCacheAdapter-test.d.ts +1 -0
  50. package/build/__tests__/ComposedCacheAdapter-test.js +198 -0
  51. package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -0
  52. package/build/__tests__/ComposedSecondaryEntityCache-test.d.ts +1 -0
  53. package/build/__tests__/ComposedSecondaryEntityCache-test.js +65 -0
  54. package/build/__tests__/ComposedSecondaryEntityCache-test.js.map +1 -0
  55. package/build/__tests__/EntityCommonUseCases-test.js +1 -1
  56. package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
  57. package/build/__tests__/EntityEdges-test.js +260 -37
  58. package/build/__tests__/EntityEdges-test.js.map +1 -1
  59. package/build/__tests__/EntityLoader-constructor-test.js +2 -1
  60. package/build/__tests__/EntityLoader-constructor-test.js.map +1 -1
  61. package/build/__tests__/EntityLoader-test.js +19 -11
  62. package/build/__tests__/EntityLoader-test.js.map +1 -1
  63. package/build/__tests__/EntityMutator-test.js +96 -43
  64. package/build/__tests__/EntityMutator-test.js.map +1 -1
  65. package/build/__tests__/EntityPrivacyPolicy-test.js +23 -12
  66. package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
  67. package/build/__tests__/EntitySecondaryCacheLoader-test.js +1 -1
  68. package/build/__tests__/EntitySecondaryCacheLoader-test.js.map +1 -1
  69. package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js +3 -2
  70. package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js.map +1 -1
  71. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +3 -2
  72. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
  73. package/build/index.d.ts +2 -0
  74. package/build/index.js +3 -1
  75. package/build/index.js.map +1 -1
  76. package/build/internal/ReadThroughEntityCache.d.ts +2 -3
  77. package/build/internal/ReadThroughEntityCache.js +2 -6
  78. package/build/internal/ReadThroughEntityCache.js.map +1 -1
  79. package/build/internal/__tests__/ReadThroughEntityCache-test.js +0 -32
  80. package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
  81. package/build/rules/AlwaysAllowPrivacyPolicyRule.d.ts +2 -1
  82. package/build/rules/AlwaysAllowPrivacyPolicyRule.js +1 -1
  83. package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
  84. package/build/rules/AlwaysDenyPrivacyPolicyRule.d.ts +2 -1
  85. package/build/rules/AlwaysDenyPrivacyPolicyRule.js +1 -1
  86. package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
  87. package/build/rules/AlwaysSkipPrivacyPolicyRule.d.ts +2 -1
  88. package/build/rules/AlwaysSkipPrivacyPolicyRule.js +1 -1
  89. package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
  90. package/build/rules/PrivacyPolicyRule.d.ts +2 -1
  91. package/build/rules/PrivacyPolicyRule.js.map +1 -1
  92. package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js +1 -0
  93. package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js.map +1 -1
  94. package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js +1 -0
  95. package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js.map +1 -1
  96. package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js +1 -0
  97. package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js.map +1 -1
  98. package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +2 -0
  99. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js +6 -6
  100. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
  101. package/build/utils/testing/StubCacheAdapter.d.ts +6 -9
  102. package/build/utils/testing/StubCacheAdapter.js +0 -6
  103. package/build/utils/testing/StubCacheAdapter.js.map +1 -1
  104. package/package.json +1 -1
  105. package/src/ComposedEntityCacheAdapter.ts +86 -0
  106. package/src/ComposedSecondaryEntityCache.ts +63 -0
  107. package/src/Entity.ts +6 -4
  108. package/src/EntityAssociationLoader.ts +4 -4
  109. package/src/EntityCacheAdapter.ts +2 -10
  110. package/src/EntityFieldDefinition.ts +8 -0
  111. package/src/EntityFields.ts +45 -0
  112. package/src/EntityLoader.ts +5 -1
  113. package/src/EntityLoaderFactory.ts +4 -2
  114. package/src/EntityMutationInfo.ts +13 -3
  115. package/src/EntityMutator.ts +44 -21
  116. package/src/EntityMutatorFactory.ts +10 -4
  117. package/src/EntityPrivacyPolicy.ts +31 -1
  118. package/src/GenericSecondaryEntityCache.ts +98 -0
  119. package/src/IEntityGenericCacher.ts +15 -0
  120. package/src/ReadonlyEntity.ts +1 -1
  121. package/src/ViewerScopedEntityLoaderFactory.ts +8 -3
  122. package/src/ViewerScopedEntityMutatorFactory.ts +22 -7
  123. package/src/__tests__/ComposedCacheAdapter-test.ts +280 -0
  124. package/src/__tests__/ComposedSecondaryEntityCache-test.ts +101 -0
  125. package/src/__tests__/EntityCommonUseCases-test.ts +2 -1
  126. package/src/__tests__/EntityEdges-test.ts +286 -45
  127. package/src/__tests__/EntityLoader-constructor-test.ts +3 -1
  128. package/src/__tests__/EntityLoader-test.ts +26 -1
  129. package/src/__tests__/EntityMutator-test.ts +99 -37
  130. package/src/__tests__/EntityPrivacyPolicy-test.ts +66 -7
  131. package/src/__tests__/EntitySecondaryCacheLoader-test.ts +1 -0
  132. package/src/__tests__/ViewerScopedEntityLoaderFactory-test.ts +4 -2
  133. package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +6 -2
  134. package/src/index.ts +2 -0
  135. package/src/internal/ReadThroughEntityCache.ts +6 -28
  136. package/src/internal/__tests__/ReadThroughEntityCache-test.ts +0 -44
  137. package/src/rules/AlwaysAllowPrivacyPolicyRule.ts +2 -0
  138. package/src/rules/AlwaysDenyPrivacyPolicyRule.ts +2 -0
  139. package/src/rules/AlwaysSkipPrivacyPolicyRule.ts +2 -0
  140. package/src/rules/PrivacyPolicyRule.ts +2 -0
  141. package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -0
  142. package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -0
  143. package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -0
  144. package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +14 -6
  145. package/src/utils/testing/StubCacheAdapter.ts +11 -17
package/src/Entity.ts CHANGED
@@ -69,7 +69,7 @@ export default abstract class Entity<
69
69
  return viewerContext
70
70
  .getViewerScopedEntityCompanionForClass(this)
71
71
  .getMutatorFactory()
72
- .forCreate(queryContext);
72
+ .forCreate(queryContext, { cascadingDeleteCause: null });
73
73
  }
74
74
 
75
75
  /**
@@ -111,7 +111,7 @@ export default abstract class Entity<
111
111
  .getViewerContext()
112
112
  .getViewerScopedEntityCompanionForClass(this)
113
113
  .getMutatorFactory()
114
- .forUpdate(existingEntity, queryContext);
114
+ .forUpdate(existingEntity, queryContext, { cascadingDeleteCause: null });
115
115
  }
116
116
 
117
117
  /**
@@ -152,7 +152,7 @@ export default abstract class Entity<
152
152
  .getViewerContext()
153
153
  .getViewerScopedEntityCompanionForClass(this)
154
154
  .getMutatorFactory()
155
- .forDelete(existingEntity, queryContext)
155
+ .forDelete(existingEntity, queryContext, { cascadingDeleteCause: null })
156
156
  .deleteAsync();
157
157
  }
158
158
 
@@ -194,7 +194,7 @@ export default abstract class Entity<
194
194
  .getViewerContext()
195
195
  .getViewerScopedEntityCompanionForClass(this)
196
196
  .getMutatorFactory()
197
- .forDelete(existingEntity, queryContext)
197
+ .forDelete(existingEntity, queryContext, { cascadingDeleteCause: null })
198
198
  .enforceDeleteAsync();
199
199
  }
200
200
 
@@ -251,6 +251,7 @@ export default abstract class Entity<
251
251
  privacyPolicy.authorizeUpdateAsync(
252
252
  existingEntity.getViewerContext(),
253
253
  queryContext,
254
+ { cascadingDeleteCause: null },
254
255
  existingEntity,
255
256
  companion.getMetricsAdapter()
256
257
  )
@@ -304,6 +305,7 @@ export default abstract class Entity<
304
305
  privacyPolicy.authorizeDeleteAsync(
305
306
  existingEntity.getViewerContext(),
306
307
  queryContext,
308
+ { cascadingDeleteCause: null },
307
309
  existingEntity,
308
310
  companion.getMetricsAdapter()
309
311
  )
@@ -69,7 +69,7 @@ export default class EntityAssociationLoader<
69
69
  .getViewerContext()
70
70
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
71
71
  .getLoaderFactory()
72
- .forLoad(queryContext);
72
+ .forLoad(queryContext, { cascadingDeleteCause: null });
73
73
 
74
74
  return (await loader.loadByIDAsync(associatedEntityID as unknown as TAssociatedID)) as Result<
75
75
  null extends TFields[TIdentifyingField] ? TAssociatedEntity | null : TAssociatedEntity
@@ -123,7 +123,7 @@ export default class EntityAssociationLoader<
123
123
  .getViewerContext()
124
124
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
125
125
  .getLoaderFactory()
126
- .forLoad(queryContext);
126
+ .forLoad(queryContext, { cascadingDeleteCause: null });
127
127
  return await loader.loadManyByFieldEqualingAsync(
128
128
  associatedEntityFieldContainingThisID,
129
129
  thisID as any
@@ -180,7 +180,7 @@ export default class EntityAssociationLoader<
180
180
  .getViewerContext()
181
181
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
182
182
  .getLoaderFactory()
183
- .forLoad(queryContext);
183
+ .forLoad(queryContext, { cascadingDeleteCause: null });
184
184
  return await loader.loadByFieldEqualingAsync(
185
185
  associatedEntityLookupByField,
186
186
  associatedFieldValue as any
@@ -238,7 +238,7 @@ export default class EntityAssociationLoader<
238
238
  .getViewerContext()
239
239
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
240
240
  .getLoaderFactory()
241
- .forLoad(queryContext);
241
+ .forLoad(queryContext, { cascadingDeleteCause: null });
242
242
  return await loader.loadManyByFieldEqualingAsync(
243
243
  associatedEntityLookupByField,
244
244
  associatedFieldValue as any
@@ -1,5 +1,4 @@
1
1
  import EntityConfiguration from './EntityConfiguration';
2
- import { FieldTransformerMap } from './internal/EntityFieldTransformationUtils';
3
2
  import { CacheLoadResult } from './internal/ReadThroughEntityCache';
4
3
 
5
4
  /**
@@ -9,13 +8,6 @@ import { CacheLoadResult } from './internal/ReadThroughEntityCache';
9
8
  export default abstract class EntityCacheAdapter<TFields> {
10
9
  constructor(protected readonly entityConfiguration: EntityConfiguration<TFields>) {}
11
10
 
12
- /**
13
- * Transformer definitions for field types. Used to modify values as they are read from or written to
14
- * the cache. Override in concrete subclasses to change transformation behavior.
15
- * If a field type is not present in the map, then fields of that type will not be transformed.
16
- */
17
- public abstract getFieldTransformerMap(): FieldTransformerMap;
18
-
19
11
  /**
20
12
  * Load many objects from cache.
21
13
  * @param fieldName - object field being queried
@@ -25,7 +17,7 @@ export default abstract class EntityCacheAdapter<TFields> {
25
17
  public abstract loadManyAsync<N extends keyof TFields>(
26
18
  fieldName: N,
27
19
  fieldValues: readonly NonNullable<TFields[N]>[]
28
- ): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult>>;
20
+ ): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult<TFields>>>;
29
21
 
30
22
  /**
31
23
  * Cache many objects fetched from the DB.
@@ -34,7 +26,7 @@ export default abstract class EntityCacheAdapter<TFields> {
34
26
  */
35
27
  public abstract cacheManyAsync<N extends keyof TFields>(
36
28
  fieldName: N,
37
- objectMap: ReadonlyMap<NonNullable<TFields[N]>, object>
29
+ objectMap: ReadonlyMap<NonNullable<TFields[N]>, Readonly<TFields>>
38
30
  ): Promise<void>;
39
31
 
40
32
  /**
@@ -28,6 +28,9 @@ export enum EntityEdgeDeletionBehavior {
28
28
  SET_NULL,
29
29
  }
30
30
 
31
+ /**
32
+ * Defines an association between entities. An association is primarily used to define cascading deletion behavior.
33
+ */
31
34
  export interface EntityAssociationDefinition<
32
35
  TViewerContext extends ViewerContext,
33
36
  TAssociatedFields,
@@ -83,6 +86,10 @@ export interface EntityAssociationDefinition<
83
86
  edgeDeletionBehavior?: EntityEdgeDeletionBehavior;
84
87
  }
85
88
 
89
+ /**
90
+ * Definition for a field referencing a column in the underlying database. Specifies things like
91
+ * cache behavior and associations, and handles input validation.
92
+ */
86
93
  export abstract class EntityFieldDefinition<T> {
87
94
  readonly columnName: string;
88
95
  readonly cache: boolean;
@@ -93,6 +100,7 @@ export abstract class EntityFieldDefinition<T> {
93
100
  * @param cache - Whether or not to cache loaded instances of the entity by this field. The column name is
94
101
  * used to derive a cache key for the cache entry. If true, this column must be able uniquely
95
102
  * identify the entity.
103
+ * @param association - Defines the association behavior for an entity that this column references.
96
104
  */
97
105
  constructor({
98
106
  columnName,
@@ -2,59 +2,104 @@ import { validate as validateUUID } from 'uuid';
2
2
 
3
3
  import { EntityFieldDefinition } from './EntityFieldDefinition';
4
4
 
5
+ /**
6
+ * {@link EntityFieldDefinition} for a column with a JS string type.
7
+ */
5
8
  export class StringField extends EntityFieldDefinition<string> {
6
9
  protected validateInputValueInternal(value: string): boolean {
7
10
  return typeof value === 'string';
8
11
  }
9
12
  }
13
+
14
+ /**
15
+ * {@link EntityFieldDefinition} for a column with a JS string type.
16
+ * Enforces that the string is a valid UUID.
17
+ */
10
18
  export class UUIDField extends StringField {
11
19
  protected override validateInputValueInternal(value: string): boolean {
12
20
  return validateUUID(value);
13
21
  }
14
22
  }
23
+
24
+ /**
25
+ * {@link EntityFieldDefinition} for a column with a JS Date type.
26
+ */
15
27
  export class DateField extends EntityFieldDefinition<Date> {
16
28
  protected validateInputValueInternal(value: Date): boolean {
17
29
  return value instanceof Date;
18
30
  }
19
31
  }
32
+
33
+ /**
34
+ * {@link EntityFieldDefinition} for a column with a JS boolean type.
35
+ */
20
36
  export class BooleanField extends EntityFieldDefinition<boolean> {
21
37
  protected validateInputValueInternal(value: boolean): boolean {
22
38
  return typeof value === 'boolean';
23
39
  }
24
40
  }
25
41
 
42
+ /**
43
+ * {@link EntityFieldDefinition} for a column with a JS number type.
44
+ * Enforces that the number is an integer.
45
+ */
26
46
  export class IntField extends EntityFieldDefinition<number> {
27
47
  protected validateInputValueInternal(value: number): boolean {
28
48
  return typeof value === 'number' && Number.isInteger(value);
29
49
  }
30
50
  }
31
51
 
52
+ /**
53
+ * {@link EntityFieldDefinition} for a column with a JS number type.
54
+ * Enforces that the number is a float (which includes integers in JS).
55
+ */
32
56
  export class FloatField extends EntityFieldDefinition<number> {
33
57
  protected validateInputValueInternal(value: number): boolean {
34
58
  return typeof value === 'number';
35
59
  }
36
60
  }
37
61
 
62
+ /**
63
+ * {@link EntityFieldDefinition} for a column with a JS string array type.
64
+ * Enforces that every member of the string array is a string.
65
+ */
38
66
  export class StringArrayField extends EntityFieldDefinition<string[]> {
39
67
  protected validateInputValueInternal(value: string[]): boolean {
40
68
  return Array.isArray(value) && value.every((subValue) => typeof subValue === 'string');
41
69
  }
42
70
  }
71
+
72
+ /**
73
+ * {@link EntityFieldDefinition} for a column with a JS JSON object type.
74
+ */
43
75
  export class JSONObjectField extends EntityFieldDefinition<object> {
44
76
  protected validateInputValueInternal(value: object): boolean {
45
77
  return typeof value === 'object' && !Array.isArray(value);
46
78
  }
47
79
  }
80
+
81
+ /**
82
+ * {@link EntityFieldDefinition} for a enum column with a JS string or number type.
83
+ */
48
84
  export class EnumField extends EntityFieldDefinition<string | number> {
49
85
  protected validateInputValueInternal(value: string | number): boolean {
50
86
  return typeof value === 'number' || typeof value === 'string';
51
87
  }
52
88
  }
89
+
90
+ /**
91
+ * {@link EntityFieldDefinition} for a column with a JS JSON array type.
92
+ */
53
93
  export class JSONArrayField extends EntityFieldDefinition<any[]> {
54
94
  protected validateInputValueInternal(value: any[]): boolean {
55
95
  return Array.isArray(value);
56
96
  }
57
97
  }
98
+
99
+ /**
100
+ * {@link EntityFieldDefinition} for a column that may be a JS JSON array type.
101
+ * Does not do any validation.
102
+ */
58
103
  export class MaybeJSONArrayField extends EntityFieldDefinition<any | any[]> {
59
104
  protected validateInputValueInternal(_value: any): boolean {
60
105
  return true;
@@ -9,7 +9,7 @@ import {
9
9
  QuerySelectionModifiers,
10
10
  isSingleValueFieldEqualityCondition,
11
11
  } from './EntityDatabaseAdapter';
12
- import EntityPrivacyPolicy from './EntityPrivacyPolicy';
12
+ import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
13
13
  import { EntityQueryContext } from './EntityQueryContext';
14
14
  import ReadonlyEntity from './ReadonlyEntity';
15
15
  import ViewerContext from './ViewerContext';
@@ -40,6 +40,7 @@ export default class EntityLoader<
40
40
  constructor(
41
41
  private readonly viewerContext: TViewerContext,
42
42
  private readonly queryContext: EntityQueryContext,
43
+ private readonly privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
43
44
  private readonly entityConfiguration: EntityConfiguration<TFields>,
44
45
  private readonly entityClass: IEntityClass<
45
46
  TFields,
@@ -218,6 +219,7 @@ export default class EntityLoader<
218
219
  this.privacyPolicy.authorizeReadAsync(
219
220
  this.viewerContext,
220
221
  this.queryContext,
222
+ this.privacyPolicyEvaluationContext,
221
223
  uncheckedEntityResult.value,
222
224
  this.metricsAdapter
223
225
  )
@@ -272,6 +274,7 @@ export default class EntityLoader<
272
274
  this.privacyPolicy.authorizeReadAsync(
273
275
  this.viewerContext,
274
276
  this.queryContext,
277
+ this.privacyPolicyEvaluationContext,
275
278
  uncheckedEntityResult.value,
276
279
  this.metricsAdapter
277
280
  )
@@ -332,6 +335,7 @@ export default class EntityLoader<
332
335
  this.privacyPolicy.authorizeReadAsync(
333
336
  this.viewerContext,
334
337
  this.queryContext,
338
+ this.privacyPolicyEvaluationContext,
335
339
  uncheckedEntityResult.value,
336
340
  this.metricsAdapter
337
341
  )
@@ -1,7 +1,7 @@
1
1
  import { IEntityClass } from './Entity';
2
2
  import EntityConfiguration from './EntityConfiguration';
3
3
  import EntityLoader from './EntityLoader';
4
- import EntityPrivacyPolicy from './EntityPrivacyPolicy';
4
+ import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
5
5
  import { EntityQueryContext } from './EntityQueryContext';
6
6
  import ReadonlyEntity from './ReadonlyEntity';
7
7
  import ViewerContext from './ViewerContext';
@@ -47,11 +47,13 @@ export default class EntityLoaderFactory<
47
47
  */
48
48
  forLoad(
49
49
  viewerContext: TViewerContext,
50
- queryContext: EntityQueryContext
50
+ queryContext: EntityQueryContext,
51
+ privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
51
52
  ): EntityLoader<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
52
53
  return new EntityLoader(
53
54
  viewerContext,
54
55
  queryContext,
56
+ privacyPolicyEvaluationContext,
55
57
  this.entityConfiguration,
56
58
  this.entityClass,
57
59
  this.privacyPolicyClass,
@@ -22,9 +22,19 @@ export type EntityValidatorMutationInfo<
22
22
  previousValue: TEntity;
23
23
  };
24
24
 
25
- export type EntityMutationTriggerDeleteCascadeInfo<> = {
25
+ /**
26
+ * Information about a cascading deletion.
27
+ */
28
+ export type EntityCascadingDeletionInfo = {
29
+ /**
30
+ * The entity that is being mutated at this step in the cascaded deletion.
31
+ */
26
32
  entity: Entity<any, any, any, any>;
27
- cascadingDeleteCause: EntityMutationTriggerDeleteCascadeInfo | null;
33
+
34
+ /**
35
+ * The cascade deletion that caused this mutation.
36
+ */
37
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null;
28
38
  };
29
39
 
30
40
  export type EntityTriggerMutationInfo<
@@ -43,5 +53,5 @@ export type EntityTriggerMutationInfo<
43
53
  }
44
54
  | {
45
55
  type: EntityMutationType.DELETE;
46
- cascadingDeleteCause: EntityMutationTriggerDeleteCascadeInfo | null;
56
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null;
47
57
  };
@@ -11,14 +11,14 @@ import {
11
11
  EntityValidatorMutationInfo,
12
12
  EntityMutationType,
13
13
  EntityTriggerMutationInfo,
14
- EntityMutationTriggerDeleteCascadeInfo,
14
+ EntityCascadingDeletionInfo,
15
15
  } from './EntityMutationInfo';
16
16
  import EntityMutationTriggerConfiguration, {
17
17
  EntityMutationTrigger,
18
18
  EntityNonTransactionalMutationTrigger,
19
19
  } from './EntityMutationTriggerConfiguration';
20
20
  import EntityMutationValidator from './EntityMutationValidator';
21
- import EntityPrivacyPolicy from './EntityPrivacyPolicy';
21
+ import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
22
22
  import { EntityQueryContext, EntityTransactionalQueryContext } from './EntityQueryContext';
23
23
  import ReadonlyEntity from './ReadonlyEntity';
24
24
  import ViewerContext from './ViewerContext';
@@ -44,6 +44,7 @@ abstract class BaseMutator<
44
44
  constructor(
45
45
  protected readonly viewerContext: TViewerContext,
46
46
  protected readonly queryContext: EntityQueryContext,
47
+ protected readonly privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
47
48
  protected readonly entityConfiguration: EntityConfiguration<TFields>,
48
49
  protected readonly entityClass: IEntityClass<
49
50
  TFields,
@@ -220,6 +221,7 @@ export class CreateMutator<
220
221
  this.privacyPolicy.authorizeCreateAsync(
221
222
  this.viewerContext,
222
223
  queryContext,
224
+ this.privacyPolicyEvaluationContext,
223
225
  temporaryEntityForPrivacyCheck,
224
226
  this.metricsAdapter
225
227
  )
@@ -249,7 +251,11 @@ export class CreateMutator<
249
251
 
250
252
  const insertResult = await this.databaseAdapter.insertAsync(queryContext, this.fieldsForEntity);
251
253
 
252
- const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext);
254
+ const entityLoader = this.entityLoaderFactory.forLoad(
255
+ this.viewerContext,
256
+ queryContext,
257
+ this.privacyPolicyEvaluationContext
258
+ );
253
259
  queryContext.appendPostCommitInvalidationCallback(
254
260
  entityLoader.invalidateFieldsAsync.bind(entityLoader, insertResult)
255
261
  );
@@ -309,6 +315,7 @@ export class UpdateMutator<
309
315
  constructor(
310
316
  viewerContext: TViewerContext,
311
317
  queryContext: EntityQueryContext,
318
+ privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
312
319
  entityConfiguration: EntityConfiguration<TFields>,
313
320
  entityClass: IEntityClass<
314
321
  TFields,
@@ -348,6 +355,7 @@ export class UpdateMutator<
348
355
  super(
349
356
  viewerContext,
350
357
  queryContext,
358
+ privacyPolicyEvaluationContext,
351
359
  entityConfiguration,
352
360
  entityClass,
353
361
  privacyPolicy,
@@ -409,6 +417,7 @@ export class UpdateMutator<
409
417
  this.privacyPolicy.authorizeUpdateAsync(
410
418
  this.viewerContext,
411
419
  queryContext,
420
+ this.privacyPolicyEvaluationContext,
412
421
  entityAboutToBeUpdated,
413
422
  this.metricsAdapter
414
423
  )
@@ -443,7 +452,11 @@ export class UpdateMutator<
443
452
  this.updatedFields
444
453
  );
445
454
 
446
- const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext);
455
+ const entityLoader = this.entityLoaderFactory.forLoad(
456
+ this.viewerContext,
457
+ queryContext,
458
+ this.privacyPolicyEvaluationContext
459
+ );
447
460
 
448
461
  queryContext.appendPostCommitInvalidationCallback(
449
462
  entityLoader.invalidateFieldsAsync.bind(
@@ -506,6 +519,7 @@ export class DeleteMutator<
506
519
  constructor(
507
520
  viewerContext: TViewerContext,
508
521
  queryContext: EntityQueryContext,
522
+ privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
509
523
  entityConfiguration: EntityConfiguration<TFields>,
510
524
  entityClass: IEntityClass<
511
525
  TFields,
@@ -545,6 +559,7 @@ export class DeleteMutator<
545
559
  super(
546
560
  viewerContext,
547
561
  queryContext,
562
+ privacyPolicyEvaluationContext,
548
563
  entityConfiguration,
549
564
  entityClass,
550
565
  privacyPolicy,
@@ -578,7 +593,7 @@ export class DeleteMutator<
578
593
  private async deleteInTransactionAsync(
579
594
  processedEntityIdentifiersFromTransitiveDeletions: Set<string>,
580
595
  skipDatabaseDeletion: boolean,
581
- cascadingDeleteCause: EntityMutationTriggerDeleteCascadeInfo | null
596
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null
582
597
  ): Promise<Result<void>> {
583
598
  return await this.queryContext.runInTransactionIfNotInTransactionAsync((innerQueryContext) =>
584
599
  this.deleteInternalAsync(
@@ -594,12 +609,13 @@ export class DeleteMutator<
594
609
  queryContext: EntityTransactionalQueryContext,
595
610
  processedEntityIdentifiersFromTransitiveDeletions: Set<string>,
596
611
  skipDatabaseDeletion: boolean,
597
- cascadingDeleteCause: EntityMutationTriggerDeleteCascadeInfo | null
612
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null
598
613
  ): Promise<Result<void>> {
599
614
  const authorizeDeleteResult = await asyncResult(
600
615
  this.privacyPolicy.authorizeDeleteAsync(
601
616
  this.viewerContext,
602
617
  queryContext,
618
+ { cascadingDeleteCause },
603
619
  this.entity,
604
620
  this.metricsAdapter
605
621
  )
@@ -636,7 +652,9 @@ export class DeleteMutator<
636
652
  );
637
653
  }
638
654
 
639
- const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext);
655
+ const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
656
+ cascadingDeleteCause,
657
+ });
640
658
  queryContext.appendPostCommitInvalidationCallback(
641
659
  entityLoader.invalidateFieldsAsync.bind(entityLoader, this.entity.getAllDatabaseFields())
642
660
  );
@@ -684,7 +702,7 @@ export class DeleteMutator<
684
702
  entity: TEntity,
685
703
  queryContext: EntityTransactionalQueryContext,
686
704
  processedEntityIdentifiers: Set<string>,
687
- cascadingDeleteCause: EntityMutationTriggerDeleteCascadeInfo | null
705
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null
688
706
  ): Promise<void> {
689
707
  // prevent infinite reference cycles by keeping track of entities already processed
690
708
  if (processedEntityIdentifiers.has(entity.getUniqueIdentifier())) {
@@ -732,10 +750,15 @@ export class DeleteMutator<
732
750
  .getViewerScopedEntityCompanionForClass(entityClass)
733
751
  .getMutatorFactory();
734
752
 
753
+ const newCascadingDeleteCause = {
754
+ entity,
755
+ cascadingDeleteCause,
756
+ };
757
+
735
758
  let inboundReferenceEntities: readonly ReadonlyEntity<any, any, any, any>[];
736
759
  if (associatedEntityLookupByField) {
737
760
  inboundReferenceEntities = await loaderFactory
738
- .forLoad(queryContext)
761
+ .forLoad(queryContext, { cascadingDeleteCause: newCascadingDeleteCause })
739
762
  .enforcing()
740
763
  .loadManyByFieldEqualingAsync(
741
764
  fieldName,
@@ -743,7 +766,7 @@ export class DeleteMutator<
743
766
  );
744
767
  } else {
745
768
  inboundReferenceEntities = await loaderFactory
746
- .forLoad(queryContext)
769
+ .forLoad(queryContext, { cascadingDeleteCause: newCascadingDeleteCause })
747
770
  .enforcing()
748
771
  .loadManyByFieldEqualingAsync(fieldName, entity.getID());
749
772
  }
@@ -754,14 +777,13 @@ export class DeleteMutator<
754
777
  await Promise.all(
755
778
  inboundReferenceEntities.map((inboundReferenceEntity) =>
756
779
  mutatorFactory
757
- .forDelete(inboundReferenceEntity, queryContext)
780
+ .forDelete(inboundReferenceEntity, queryContext, {
781
+ cascadingDeleteCause: newCascadingDeleteCause,
782
+ })
758
783
  .deleteInTransactionAsync(
759
784
  processedEntityIdentifiers,
760
785
  /* skipDatabaseDeletion */ true, // deletion is handled by DB
761
- {
762
- entity,
763
- cascadingDeleteCause,
764
- }
786
+ newCascadingDeleteCause
765
787
  )
766
788
  )
767
789
  );
@@ -771,7 +793,9 @@ export class DeleteMutator<
771
793
  await Promise.all(
772
794
  inboundReferenceEntities.map((inboundReferenceEntity) =>
773
795
  mutatorFactory
774
- .forUpdate(inboundReferenceEntity, queryContext)
796
+ .forUpdate(inboundReferenceEntity, queryContext, {
797
+ cascadingDeleteCause: newCascadingDeleteCause,
798
+ })
775
799
  .setField(fieldName, null)
776
800
  .enforceUpdateAsync()
777
801
  )
@@ -782,14 +806,13 @@ export class DeleteMutator<
782
806
  await Promise.all(
783
807
  inboundReferenceEntities.map((inboundReferenceEntity) =>
784
808
  mutatorFactory
785
- .forDelete(inboundReferenceEntity, queryContext)
809
+ .forDelete(inboundReferenceEntity, queryContext, {
810
+ cascadingDeleteCause: newCascadingDeleteCause,
811
+ })
786
812
  .deleteInTransactionAsync(
787
813
  processedEntityIdentifiers,
788
814
  /* skipDatabaseDeletion */ false,
789
- {
790
- entity,
791
- cascadingDeleteCause,
792
- }
815
+ newCascadingDeleteCause
793
816
  )
794
817
  )
795
818
  );
@@ -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 from './EntityPrivacyPolicy';
8
+ import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
9
9
  import { EntityQueryContext } from './EntityQueryContext';
10
10
  import ViewerContext from './ViewerContext';
11
11
  import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';
@@ -72,11 +72,13 @@ export default class EntityMutatorFactory<
72
72
  */
73
73
  forCreate(
74
74
  viewerContext: TViewerContext,
75
- queryContext: EntityQueryContext
75
+ queryContext: EntityQueryContext,
76
+ privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
76
77
  ): CreateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
77
78
  return new CreateMutator(
78
79
  viewerContext,
79
80
  queryContext,
81
+ privacyPolicyEvaluationContext,
80
82
  this.entityConfiguration,
81
83
  this.entityClass,
82
84
  this.privacyPolicy,
@@ -96,11 +98,13 @@ export default class EntityMutatorFactory<
96
98
  */
97
99
  forUpdate(
98
100
  existingEntity: TEntity,
99
- queryContext: EntityQueryContext
101
+ queryContext: EntityQueryContext,
102
+ privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
100
103
  ): UpdateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
101
104
  return new UpdateMutator(
102
105
  existingEntity.getViewerContext(),
103
106
  queryContext,
107
+ privacyPolicyEvaluationContext,
104
108
  this.entityConfiguration,
105
109
  this.entityClass,
106
110
  this.privacyPolicy,
@@ -120,11 +124,13 @@ export default class EntityMutatorFactory<
120
124
  */
121
125
  forDelete(
122
126
  existingEntity: TEntity,
123
- queryContext: EntityQueryContext
127
+ queryContext: EntityQueryContext,
128
+ privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
124
129
  ): DeleteMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
125
130
  return new DeleteMutator(
126
131
  existingEntity.getViewerContext(),
127
132
  queryContext,
133
+ privacyPolicyEvaluationContext,
128
134
  this.entityConfiguration,
129
135
  this.entityClass,
130
136
  this.privacyPolicy,