@expo/entity 0.55.0 → 0.57.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 (163) hide show
  1. package/build/src/AuthorizationResultBasedEntityAssociationLoader.d.ts +1 -1
  2. package/build/src/AuthorizationResultBasedEntityAssociationLoader.js.map +1 -1
  3. package/build/src/AuthorizationResultBasedEntityLoader.d.ts +18 -24
  4. package/build/src/AuthorizationResultBasedEntityLoader.js +37 -56
  5. package/build/src/AuthorizationResultBasedEntityLoader.js.map +1 -1
  6. package/build/src/AuthorizationResultBasedEntityMutator.js +26 -19
  7. package/build/src/AuthorizationResultBasedEntityMutator.js.map +1 -1
  8. package/build/src/EnforcingEntityCreator.d.ts +1 -1
  9. package/build/src/EnforcingEntityCreator.js +1 -1
  10. package/build/src/EnforcingEntityLoader.d.ts +1 -58
  11. package/build/src/EnforcingEntityLoader.js +0 -65
  12. package/build/src/EnforcingEntityLoader.js.map +1 -1
  13. package/build/src/Entity.d.ts +6 -0
  14. package/build/src/Entity.js +6 -0
  15. package/build/src/Entity.js.map +1 -1
  16. package/build/src/EntityCompanion.d.ts +2 -2
  17. package/build/src/EntityCompanion.js.map +1 -1
  18. package/build/src/EntityCompanionProvider.d.ts +1 -1
  19. package/build/src/EntityCompanionProvider.js +4 -4
  20. package/build/src/EntityConfiguration.d.ts +1 -1
  21. package/build/src/EntityConfiguration.js +1 -2
  22. package/build/src/EntityConfiguration.js.map +1 -1
  23. package/build/src/{EntityLoaderUtils.d.ts → EntityConstructionUtils.d.ts} +15 -29
  24. package/build/src/EntityConstructionUtils.js +118 -0
  25. package/build/src/EntityConstructionUtils.js.map +1 -0
  26. package/build/src/EntityDatabaseAdapter.d.ts +10 -108
  27. package/build/src/EntityDatabaseAdapter.js +14 -76
  28. package/build/src/EntityDatabaseAdapter.js.map +1 -1
  29. package/build/src/EntityFieldDefinition.d.ts +1 -1
  30. package/build/src/EntityInvalidationUtils.d.ts +41 -0
  31. package/build/src/EntityInvalidationUtils.js +71 -0
  32. package/build/src/EntityInvalidationUtils.js.map +1 -0
  33. package/build/src/EntityLoader.d.ts +0 -6
  34. package/build/src/EntityLoader.js +0 -7
  35. package/build/src/EntityLoader.js.map +1 -1
  36. package/build/src/EntityLoaderFactory.d.ts +4 -0
  37. package/build/src/EntityLoaderFactory.js +10 -3
  38. package/build/src/EntityLoaderFactory.js.map +1 -1
  39. package/build/src/EntityPrivacyPolicy.d.ts +27 -0
  40. package/build/src/EntityPrivacyPolicy.js +22 -1
  41. package/build/src/EntityPrivacyPolicy.js.map +1 -1
  42. package/build/src/EntitySecondaryCacheLoader.d.ts +14 -3
  43. package/build/src/EntitySecondaryCacheLoader.js +21 -4
  44. package/build/src/EntitySecondaryCacheLoader.js.map +1 -1
  45. package/build/src/ReadonlyEntity.d.ts +4 -5
  46. package/build/src/ReadonlyEntity.js +7 -8
  47. package/build/src/ReadonlyEntity.js.map +1 -1
  48. package/build/src/ViewerContext.d.ts +6 -6
  49. package/build/src/ViewerContext.js +8 -8
  50. package/build/src/ViewerScopedEntityCompanion.d.ts +1 -1
  51. package/build/src/ViewerScopedEntityCompanion.js.map +1 -1
  52. package/build/src/ViewerScopedEntityLoaderFactory.d.ts +4 -0
  53. package/build/src/ViewerScopedEntityLoaderFactory.js +6 -0
  54. package/build/src/ViewerScopedEntityLoaderFactory.js.map +1 -1
  55. package/build/src/errors/EntityDatabaseAdapterError.d.ts +4 -0
  56. package/build/src/errors/EntityDatabaseAdapterError.js +13 -1
  57. package/build/src/errors/EntityDatabaseAdapterError.js.map +1 -1
  58. package/build/src/errors/EntityError.d.ts +2 -1
  59. package/build/src/errors/EntityError.js +1 -0
  60. package/build/src/errors/EntityError.js.map +1 -1
  61. package/build/src/index.d.ts +2 -1
  62. package/build/src/index.js +2 -1
  63. package/build/src/index.js.map +1 -1
  64. package/build/src/internal/EntityDataManager.d.ts +8 -16
  65. package/build/src/internal/EntityDataManager.js +8 -18
  66. package/build/src/internal/EntityDataManager.js.map +1 -1
  67. package/build/src/internal/EntityFieldTransformationUtils.js.map +1 -1
  68. package/build/src/internal/EntityLoadInterfaces.d.ts +2 -0
  69. package/build/src/internal/EntityLoadInterfaces.js +2 -0
  70. package/build/src/internal/EntityLoadInterfaces.js.map +1 -1
  71. package/build/src/internal/EntityTableDataCoordinator.d.ts +2 -0
  72. package/build/src/internal/EntityTableDataCoordinator.js +4 -0
  73. package/build/src/internal/EntityTableDataCoordinator.js.map +1 -1
  74. package/build/src/metrics/EntityMetricsUtils.d.ts +1 -0
  75. package/build/src/metrics/EntityMetricsUtils.js +15 -1
  76. package/build/src/metrics/EntityMetricsUtils.js.map +1 -1
  77. package/build/src/metrics/IEntityMetricsAdapter.d.ts +4 -1
  78. package/build/src/metrics/IEntityMetricsAdapter.js +3 -0
  79. package/build/src/metrics/IEntityMetricsAdapter.js.map +1 -1
  80. package/build/src/rules/AllowIfAllSubRulesAllowPrivacyPolicyRule.d.ts +2 -2
  81. package/build/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.d.ts +2 -2
  82. package/build/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.d.ts +2 -2
  83. package/build/src/rules/PrivacyPolicyRule.d.ts +2 -2
  84. package/build/src/utils/EntityPrivacyUtils.js +11 -20
  85. package/build/src/utils/EntityPrivacyUtils.js.map +1 -1
  86. package/build/src/utils/collections/maps.d.ts +2 -2
  87. package/build/src/utils/collections/maps.js +2 -2
  88. package/package.json +4 -4
  89. package/src/AuthorizationResultBasedEntityAssociationLoader.ts +4 -7
  90. package/src/AuthorizationResultBasedEntityLoader.ts +58 -88
  91. package/src/AuthorizationResultBasedEntityMutator.ts +35 -20
  92. package/src/EnforcingEntityCreator.ts +1 -1
  93. package/src/EnforcingEntityLoader.ts +1 -95
  94. package/src/Entity.ts +6 -0
  95. package/src/EntityCompanion.ts +2 -2
  96. package/src/EntityCompanionProvider.ts +4 -4
  97. package/src/EntityConfiguration.ts +8 -5
  98. package/src/EntityConstructionUtils.ts +168 -0
  99. package/src/EntityDatabaseAdapter.ts +32 -222
  100. package/src/EntityFieldDefinition.ts +1 -1
  101. package/src/{EntityLoaderUtils.ts → EntityInvalidationUtils.ts} +5 -96
  102. package/src/EntityLoader.ts +0 -16
  103. package/src/EntityLoaderFactory.ts +50 -10
  104. package/src/EntityPrivacyPolicy.ts +44 -1
  105. package/src/EntitySecondaryCacheLoader.ts +54 -3
  106. package/src/ReadonlyEntity.ts +9 -11
  107. package/src/ViewerContext.ts +10 -10
  108. package/src/ViewerScopedEntityCompanion.ts +1 -1
  109. package/src/ViewerScopedEntityLoaderFactory.ts +37 -0
  110. package/src/__tests__/AuthorizationResultBasedEntityLoader-constructor-test.ts +3 -5
  111. package/src/__tests__/AuthorizationResultBasedEntityLoader-test.ts +34 -419
  112. package/src/__tests__/ComposedCacheAdapter-test.ts +3 -3
  113. package/src/__tests__/EnforcingEntityLoader-test.ts +2 -134
  114. package/src/__tests__/EntityCompanion-test.ts +18 -0
  115. package/src/__tests__/EntityConfiguration-test.ts +4 -4
  116. package/src/__tests__/EntityDatabaseAdapter-test.ts +33 -68
  117. package/src/__tests__/EntityEdges-test.ts +10 -10
  118. package/src/__tests__/EntityLoader-test.ts +6 -4
  119. package/src/__tests__/EntityMutator-test.ts +27 -15
  120. package/src/__tests__/EntityPrivacyPolicy-test.ts +102 -0
  121. package/src/__tests__/EntityQueryContext-test.ts +11 -11
  122. package/src/__tests__/EntitySecondaryCacheLoader-test.ts +10 -5
  123. package/src/__tests__/EntitySelfReferentialEdges-test.ts +6 -6
  124. package/src/__tests__/GenericEntityCacheAdapter-test.ts +18 -15
  125. package/src/__tests__/GenericSecondaryEntityCache-test.ts +27 -5
  126. package/src/__tests__/ReadonlyEntity-test.ts +6 -4
  127. package/src/__tests__/ViewerContext-test.ts +4 -4
  128. package/src/__tests__/ViewerScopedEntityCompanion-test.ts +1 -0
  129. package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +0 -17
  130. package/src/errors/EntityDatabaseAdapterError.ts +14 -0
  131. package/src/errors/EntityError.ts +1 -0
  132. package/src/errors/__tests__/EntityDatabaseAdapterError-test.ts +9 -0
  133. package/src/errors/__tests__/EntityError-test.ts +13 -5
  134. package/src/index.ts +2 -1
  135. package/src/internal/EntityDataManager.ts +19 -54
  136. package/src/internal/EntityFieldTransformationUtils.ts +5 -5
  137. package/src/internal/EntityLoadInterfaces.ts +2 -0
  138. package/src/internal/EntityTableDataCoordinator.ts +2 -2
  139. package/src/internal/__tests__/CompositeFieldHolder-test.ts +8 -2
  140. package/src/internal/__tests__/EntityDataManager-test.ts +71 -202
  141. package/src/internal/__tests__/ReadThroughEntityCache-test.ts +39 -24
  142. package/src/metrics/EntityMetricsUtils.ts +23 -0
  143. package/src/metrics/IEntityMetricsAdapter.ts +3 -0
  144. package/src/metrics/__tests__/EntityMetricsUtils-test.ts +120 -0
  145. package/src/rules/AllowIfAllSubRulesAllowPrivacyPolicyRule.ts +2 -2
  146. package/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.ts +2 -2
  147. package/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.ts +2 -2
  148. package/src/rules/PrivacyPolicyRule.ts +2 -2
  149. package/src/rules/__tests__/AllowIfAllSubRulesAllowPrivacyPolicyRule-test.ts +4 -4
  150. package/src/rules/__tests__/AllowIfAnySubRuleAllowsPrivacyPolicyRule-test.ts +4 -4
  151. package/src/rules/__tests__/AllowIfInParentCascadeDeletionPrivacyPolicyRule-test.ts +11 -1
  152. package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -2
  153. package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -2
  154. package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -2
  155. package/src/rules/__tests__/EvaluateIfEntityFieldPredicatePrivacyPolicyRule-test.ts +3 -3
  156. package/src/utils/EntityPrivacyUtils.ts +18 -29
  157. package/src/utils/__testfixtures__/PrivacyPolicyRuleTestUtils.ts +2 -2
  158. package/src/utils/__testfixtures__/StubDatabaseAdapter.ts +13 -101
  159. package/src/utils/__tests__/EntityCreationUtils-test.ts +6 -6
  160. package/src/utils/__tests__/EntityPrivacyUtils-test.ts +2 -2
  161. package/src/utils/collections/maps.ts +2 -2
  162. package/build/src/EntityLoaderUtils.js +0 -147
  163. package/build/src/EntityLoaderUtils.js.map +0 -1
@@ -1,24 +1,19 @@
1
- import { Result, asyncResult, result } from '@expo/results';
2
- import nullthrows from 'nullthrows';
3
-
4
1
  import { IEntityClass } from './Entity';
5
2
  import { EntityConfiguration } from './EntityConfiguration';
6
- import { EntityPrivacyPolicy, EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
7
- import { EntityQueryContext, EntityTransactionalQueryContext } from './EntityQueryContext';
3
+ import { EntityPrivacyPolicy } from './EntityPrivacyPolicy';
4
+ import { EntityTransactionalQueryContext } from './EntityQueryContext';
8
5
  import { ReadonlyEntity } from './ReadonlyEntity';
9
6
  import { ViewerContext } from './ViewerContext';
10
- import { pick } from './entityUtils';
11
7
  import { EntityDataManager } from './internal/EntityDataManager';
12
8
  import { LoadPair } from './internal/EntityLoadInterfaces';
13
9
  import { SingleFieldHolder, SingleFieldValueHolder } from './internal/SingleFieldHolder';
14
10
  import { IEntityMetricsAdapter } from './metrics/IEntityMetricsAdapter';
15
- import { mapMapAsync } from './utils/collections/maps';
16
11
 
17
12
  /**
18
- * Entity loader utilities for things like invalidation, entity construction, and authorization.
13
+ * Entity invalidation utilities.
19
14
  * Methods are exposed publicly since in rare cases they may need to be called manually.
20
15
  */
21
- export class EntityLoaderUtils<
16
+ export class EntityInvalidationUtils<
22
17
  TFields extends Record<string, any>,
23
18
  TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>,
24
19
  TViewerContext extends ViewerContext,
@@ -33,17 +28,8 @@ export class EntityLoaderUtils<
33
28
  TSelectedFields extends keyof TFields,
34
29
  > {
35
30
  constructor(
36
- private readonly viewerContext: TViewerContext,
37
- private readonly queryContext: EntityQueryContext,
38
- private readonly privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext<
39
- TFields,
40
- TIDField,
41
- TViewerContext,
42
- TEntity,
43
- TSelectedFields
44
- >,
45
31
  private readonly entityConfiguration: EntityConfiguration<TFields, TIDField>,
46
- private readonly entityClass: IEntityClass<
32
+ _entityClass: IEntityClass<
47
33
  TFields,
48
34
  TIDField,
49
35
  TViewerContext,
@@ -51,8 +37,6 @@ export class EntityLoaderUtils<
51
37
  TPrivacyPolicy,
52
38
  TSelectedFields
53
39
  >,
54
- private readonly entitySelectedFields: TSelectedFields[] | undefined,
55
- private readonly privacyPolicy: TPrivacyPolicy,
56
40
  private readonly dataManager: EntityDataManager<TFields, TIDField>,
57
41
  protected readonly metricsAdapter: IEntityMetricsAdapter,
58
42
  ) {}
@@ -128,79 +112,4 @@ export class EntityLoaderUtils<
128
112
  ): void {
129
113
  this.invalidateFieldsForTransaction(queryContext, entity.getAllDatabaseFields());
130
114
  }
131
-
132
- /**
133
- * Construct an entity from a fields object (applying field selection if applicable),
134
- * checking that the ID field is specified.
135
- *
136
- * @param fieldsObject - fields object
137
- */
138
- public constructEntity(fieldsObject: TFields): TEntity {
139
- const idField = this.entityConfiguration.idField;
140
- const id = nullthrows(fieldsObject[idField], 'must provide ID to create an entity');
141
- const entitySelectedFields =
142
- this.entitySelectedFields ?? Array.from(this.entityConfiguration.schema.keys());
143
- const selectedFields = pick(fieldsObject, entitySelectedFields);
144
- return new this.entityClass({
145
- viewerContext: this.viewerContext,
146
- id: id as TFields[TIDField],
147
- databaseFields: fieldsObject,
148
- selectedFields,
149
- });
150
- }
151
-
152
- /**
153
- * Construct and authorize entities from fields map, returning error results for entities that fail
154
- * to construct or fail to authorize.
155
- *
156
- * @param map - map from an arbitrary key type to an array of entity field objects
157
- */
158
- public async constructAndAuthorizeEntitiesAsync<K>(
159
- map: ReadonlyMap<K, readonly Readonly<TFields>[]>,
160
- ): Promise<ReadonlyMap<K, readonly Result<TEntity>[]>> {
161
- return await mapMapAsync(map, async (fieldObjects) => {
162
- return await this.constructAndAuthorizeEntitiesArrayAsync(fieldObjects);
163
- });
164
- }
165
-
166
- /**
167
- * Construct and authorize entities from field objects array, returning error results for entities that fail
168
- * to construct or fail to authorize.
169
- *
170
- * @param fieldObjects - array of field objects
171
- */
172
- public async constructAndAuthorizeEntitiesArrayAsync(
173
- fieldObjects: readonly Readonly<TFields>[],
174
- ): Promise<readonly Result<TEntity>[]> {
175
- const uncheckedEntityResults = this.tryConstructEntities(fieldObjects);
176
- return await Promise.all(
177
- uncheckedEntityResults.map(async (uncheckedEntityResult) => {
178
- if (!uncheckedEntityResult.ok) {
179
- return uncheckedEntityResult;
180
- }
181
- return await asyncResult(
182
- this.privacyPolicy.authorizeReadAsync(
183
- this.viewerContext,
184
- this.queryContext,
185
- this.privacyPolicyEvaluationContext,
186
- uncheckedEntityResult.value,
187
- this.metricsAdapter,
188
- ),
189
- );
190
- }),
191
- );
192
- }
193
-
194
- private tryConstructEntities(fieldsObjects: readonly TFields[]): readonly Result<TEntity>[] {
195
- return fieldsObjects.map((fieldsObject) => {
196
- try {
197
- return result(this.constructEntity(fieldsObject));
198
- } catch (e) {
199
- if (!(e instanceof Error)) {
200
- throw e;
201
- }
202
- return result(e);
203
- }
204
- });
205
- }
206
115
  }
@@ -1,7 +1,6 @@
1
1
  import { AuthorizationResultBasedEntityLoader } from './AuthorizationResultBasedEntityLoader';
2
2
  import { EnforcingEntityLoader } from './EnforcingEntityLoader';
3
3
  import { IEntityClass } from './Entity';
4
- import { EntityLoaderUtils } from './EntityLoaderUtils';
5
4
  import { EntityPrivacyPolicy } from './EntityPrivacyPolicy';
6
5
  import { EntityQueryContext } from './EntityQueryContext';
7
6
  import { ReadonlyEntity } from './ReadonlyEntity';
@@ -73,19 +72,4 @@ export class EntityLoader<
73
72
  .getLoaderFactory()
74
73
  .forLoad(this.queryContext, { previousValue: null, cascadingDeleteCause: null });
75
74
  }
76
-
77
- /**
78
- * Entity loader utilities for things like cache invalidation, entity construction, and authorization.
79
- * Calling into these should only be necessary in rare cases.
80
- */
81
- public utils(): EntityLoaderUtils<
82
- TFields,
83
- TIDField,
84
- TViewerContext,
85
- TEntity,
86
- TPrivacyPolicy,
87
- TSelectedFields
88
- > {
89
- return this.withAuthorizationResults().utils;
90
- }
91
75
  }
@@ -1,6 +1,7 @@
1
1
  import { AuthorizationResultBasedEntityLoader } from './AuthorizationResultBasedEntityLoader';
2
2
  import { EntityCompanion } from './EntityCompanion';
3
- import { EntityLoaderUtils } from './EntityLoaderUtils';
3
+ import { EntityConstructionUtils } from './EntityConstructionUtils';
4
+ import { EntityInvalidationUtils } from './EntityInvalidationUtils';
4
5
  import { EntityPrivacyPolicy, EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
5
6
  import { EntityQueryContext } from './EntityQueryContext';
6
7
  import { ReadonlyEntity } from './ReadonlyEntity';
@@ -38,6 +39,52 @@ export class EntityLoaderFactory<
38
39
  protected readonly metricsAdapter: IEntityMetricsAdapter,
39
40
  ) {}
40
41
 
42
+ invalidationUtils(): EntityInvalidationUtils<
43
+ TFields,
44
+ TIDField,
45
+ TViewerContext,
46
+ TEntity,
47
+ TPrivacyPolicy,
48
+ TSelectedFields
49
+ > {
50
+ return new EntityInvalidationUtils(
51
+ this.entityCompanion.entityCompanionDefinition.entityConfiguration,
52
+ this.entityCompanion.entityCompanionDefinition.entityClass,
53
+ this.dataManager,
54
+ this.metricsAdapter,
55
+ );
56
+ }
57
+
58
+ constructionUtils(
59
+ viewerContext: TViewerContext,
60
+ queryContext: EntityQueryContext,
61
+ privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext<
62
+ TFields,
63
+ TIDField,
64
+ TViewerContext,
65
+ TEntity,
66
+ TSelectedFields
67
+ >,
68
+ ): EntityConstructionUtils<
69
+ TFields,
70
+ TIDField,
71
+ TViewerContext,
72
+ TEntity,
73
+ TPrivacyPolicy,
74
+ TSelectedFields
75
+ > {
76
+ return new EntityConstructionUtils(
77
+ viewerContext,
78
+ queryContext,
79
+ privacyPolicyEvaluationContext,
80
+ this.entityCompanion.entityCompanionDefinition.entityConfiguration,
81
+ this.entityCompanion.entityCompanionDefinition.entityClass,
82
+ this.entityCompanion.entityCompanionDefinition.entitySelectedFields,
83
+ this.entityCompanion.privacyPolicy,
84
+ this.metricsAdapter,
85
+ );
86
+ }
87
+
41
88
  /**
42
89
  * Vend loader for loading an entity in a given query context.
43
90
  * @param viewerContext - viewer context of loading user
@@ -61,16 +108,10 @@ export class EntityLoaderFactory<
61
108
  TPrivacyPolicy,
62
109
  TSelectedFields
63
110
  > {
64
- const utils = new EntityLoaderUtils(
111
+ const constructionUtils = this.constructionUtils(
65
112
  viewerContext,
66
113
  queryContext,
67
114
  privacyPolicyEvaluationContext,
68
- this.entityCompanion.entityCompanionDefinition.entityConfiguration,
69
- this.entityCompanion.entityCompanionDefinition.entityClass,
70
- this.entityCompanion.entityCompanionDefinition.entitySelectedFields,
71
- this.entityCompanion.privacyPolicy,
72
- this.dataManager,
73
- this.metricsAdapter,
74
115
  );
75
116
 
76
117
  return new AuthorizationResultBasedEntityLoader(
@@ -78,8 +119,7 @@ export class EntityLoaderFactory<
78
119
  this.entityCompanion.entityCompanionDefinition.entityConfiguration,
79
120
  this.entityCompanion.entityCompanionDefinition.entityClass,
80
121
  this.dataManager,
81
- this.metricsAdapter,
82
- utils,
122
+ constructionUtils,
83
123
  );
84
124
  }
85
125
  }
@@ -32,6 +32,25 @@ export type EntityPrivacyPolicyEvaluationContext<
32
32
  cascadingDeleteCause: EntityCascadingDeletionInfo | null;
33
33
  };
34
34
 
35
+ export type EntityPrivacyPolicyRuleEvaluationContext<
36
+ TFields extends Record<string, any>,
37
+ TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>,
38
+ TViewerContext extends ViewerContext,
39
+ TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>,
40
+ TSelectedFields extends keyof TFields = keyof TFields,
41
+ > = EntityPrivacyPolicyEvaluationContext<
42
+ TFields,
43
+ TIDField,
44
+ TViewerContext,
45
+ TEntity,
46
+ TSelectedFields
47
+ > & {
48
+ /**
49
+ * The CRUD action for which the privacy policy rule is being evaluated.
50
+ */
51
+ action: EntityAuthorizationAction;
52
+ };
53
+
35
54
  /**
36
55
  * Evaluation mode for a EntityPrivacyPolicy. Useful when transitioning to
37
56
  * using Entity for privacy.
@@ -123,6 +142,9 @@ export abstract class EntityPrivacyPolicy<
123
142
  TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>,
124
143
  TSelectedFields extends keyof TFields = keyof TFields,
125
144
  > {
145
+ /**
146
+ * List of rules to evaluate for create authorization.
147
+ */
126
148
  protected readonly createRules: readonly PrivacyPolicyRule<
127
149
  TFields,
128
150
  TIDField,
@@ -130,6 +152,10 @@ export abstract class EntityPrivacyPolicy<
130
152
  TEntity,
131
153
  TSelectedFields
132
154
  >[] = [];
155
+
156
+ /**
157
+ * List of rules to evaluate for read authorization.
158
+ */
133
159
  protected readonly readRules: readonly PrivacyPolicyRule<
134
160
  TFields,
135
161
  TIDField,
@@ -137,6 +163,10 @@ export abstract class EntityPrivacyPolicy<
137
163
  TEntity,
138
164
  TSelectedFields
139
165
  >[] = [];
166
+
167
+ /**
168
+ * List of rules to evaluate for update authorization.
169
+ */
140
170
  protected readonly updateRules: readonly PrivacyPolicyRule<
141
171
  TFields,
142
172
  TIDField,
@@ -144,6 +174,10 @@ export abstract class EntityPrivacyPolicy<
144
174
  TEntity,
145
175
  TSelectedFields
146
176
  >[] = [];
177
+
178
+ /**
179
+ * List of rules to evaluate for delete authorization.
180
+ */
147
181
  protected readonly deleteRules: readonly PrivacyPolicyRule<
148
182
  TFields,
149
183
  TIDField,
@@ -156,6 +190,9 @@ export abstract class EntityPrivacyPolicy<
156
190
  * Get the privacy policy evaluation mode and deny handler for this policy.
157
191
  * Defaults to normal enforcing policy.
158
192
  *
193
+ * DRY_RUN mode is useful for testing and logging the effects of a policy without actually enforcing it, such as when
194
+ * first rolling out a new policy. Entities that fail the policy will be allowed so caution should be take when using.
195
+ *
159
196
  * @remarks
160
197
  *
161
198
  * Override to enable dry run evaluation of the policy.
@@ -204,7 +241,9 @@ export abstract class EntityPrivacyPolicy<
204
241
  * Authorize an entity against read policy.
205
242
  * @param viewerContext - viewer context of user reading the entity
206
243
  * @param queryContext - query context in which to perform the read authorization
244
+ * @param evaluationContext - context about the reason for this privacy policy evaluation
207
245
  * @param entity - entity to authorize
246
+ * @param metricsAdapter - adapter for logging metrics about this authorization
208
247
  * @returns entity if authorized
209
248
  * @throws EntityNotAuthorizedError when not authorized
210
249
  */
@@ -236,7 +275,9 @@ export abstract class EntityPrivacyPolicy<
236
275
  * Authorize an entity against update policy.
237
276
  * @param viewerContext - viewer context of user updating the entity
238
277
  * @param queryContext - query context in which to perform the update authorization
278
+ * @param evaluationContext - context about the reason for this privacy policy evaluation
239
279
  * @param entity - entity to authorize
280
+ * @param metricsAdapter - adapter for logging metrics about this authorization
240
281
  * @returns entity if authorized
241
282
  * @throws EntityNotAuthorizedError when not authorized
242
283
  */
@@ -268,7 +309,9 @@ export abstract class EntityPrivacyPolicy<
268
309
  * Authorize an entity against deletion policy.
269
310
  * @param viewerContext - viewer context of user deleting the entity
270
311
  * @param queryContext - query context in which to perform the delete authorization
312
+ * @param evaluationContext - context about the reason for this privacy policy evaluation
271
313
  * @param entity - entity to authorize
314
+ * @param metricsAdapter - adapter for logging metrics about this authorization
272
315
  * @returns entity if authorized
273
316
  * @throws EntityNotAuthorizedError when not authorized
274
317
  */
@@ -436,7 +479,7 @@ export abstract class EntityPrivacyPolicy<
436
479
  const ruleEvaluationResult = await rule.evaluateAsync(
437
480
  viewerContext,
438
481
  queryContext,
439
- evaluationContext,
482
+ { ...evaluationContext, action },
440
483
  entity,
441
484
  );
442
485
  switch (ruleEvaluationResult) {
@@ -1,7 +1,9 @@
1
1
  import { Result } from '@expo/results';
2
2
 
3
- import { AuthorizationResultBasedEntityLoader } from './AuthorizationResultBasedEntityLoader';
3
+ import { IEntityClass } from './Entity';
4
+ import { EntityConstructionUtils } from './EntityConstructionUtils';
4
5
  import { EntityPrivacyPolicy } from './EntityPrivacyPolicy';
6
+ import { EntityQueryContext } from './EntityQueryContext';
5
7
  import { ReadonlyEntity } from './ReadonlyEntity';
6
8
  import { ViewerContext } from './ViewerContext';
7
9
  import { mapMap } from './utils/collections/maps';
@@ -60,7 +62,7 @@ export abstract class EntitySecondaryCacheLoader<
60
62
  > {
61
63
  constructor(
62
64
  private readonly secondaryEntityCache: ISecondaryEntityCache<TFields, TLoadParams>,
63
- protected readonly entityLoader: AuthorizationResultBasedEntityLoader<
65
+ private readonly constructionUtils: EntityConstructionUtils<
64
66
  TFields,
65
67
  TIDField,
66
68
  TViewerContext,
@@ -84,7 +86,7 @@ export abstract class EntitySecondaryCacheLoader<
84
86
  );
85
87
 
86
88
  // convert value to and from array to reuse complex code
87
- const entitiesMap = await this.entityLoader.utils.constructAndAuthorizeEntitiesAsync(
89
+ const entitiesMap = await this.constructionUtils.constructAndAuthorizeEntitiesAsync(
88
90
  mapMap(loadParamsToFieldObjects, (fieldObject) => (fieldObject ? [fieldObject] : [])),
89
91
  );
90
92
  return mapMap(entitiesMap, (fieldObjects) => fieldObjects[0] ?? null);
@@ -109,4 +111,53 @@ export abstract class EntitySecondaryCacheLoader<
109
111
  protected abstract fetchObjectsFromDatabaseAsync(
110
112
  loadParamsArray: readonly Readonly<TLoadParams>[],
111
113
  ): Promise<ReadonlyMap<Readonly<Readonly<TLoadParams>>, Readonly<TFields> | null>>;
114
+
115
+ /**
116
+ * Helper to get construction utils for instantiating a EntitySecondaryCacheLoader.
117
+ *
118
+ * @param entityClass - the entity class for which to get construction utils for
119
+ * @param viewerContext - the viewer context to use for construction utils
120
+ * @param queryContext - query context to use for construction utils
121
+ * @returns construction utils for the given entity class and viewer context
122
+ */
123
+ public static getConstructionUtilsForEntityClass<
124
+ TFields extends Record<string, any>,
125
+ TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>,
126
+ TViewerContext extends ViewerContext,
127
+ TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>,
128
+ TPrivacyPolicy extends EntityPrivacyPolicy<
129
+ TFields,
130
+ TIDField,
131
+ TViewerContext,
132
+ TEntity,
133
+ TSelectedFields
134
+ >,
135
+ TSelectedFields extends keyof TFields = keyof TFields,
136
+ >(
137
+ entityClass: IEntityClass<
138
+ TFields,
139
+ TIDField,
140
+ TViewerContext,
141
+ TEntity,
142
+ TPrivacyPolicy,
143
+ TSelectedFields
144
+ >,
145
+ viewerContext: TViewerContext,
146
+ queryContext: EntityQueryContext = viewerContext
147
+ .getViewerScopedEntityCompanionForClass(entityClass)
148
+ .getQueryContextProvider()
149
+ .getQueryContext(),
150
+ ): EntityConstructionUtils<
151
+ TFields,
152
+ TIDField,
153
+ TViewerContext,
154
+ TEntity,
155
+ TPrivacyPolicy,
156
+ TSelectedFields
157
+ > {
158
+ return viewerContext
159
+ .getViewerScopedEntityCompanionForClass(entityClass)
160
+ .getLoaderFactory()
161
+ .constructionUtils(queryContext, { previousValue: null, cascadingDeleteCause: null });
162
+ }
112
163
  }
@@ -6,8 +6,8 @@ import { EnforcingEntityAssociationLoader } from './EnforcingEntityAssociationLo
6
6
  import { EnforcingEntityLoader } from './EnforcingEntityLoader';
7
7
  import { IEntityClass } from './Entity';
8
8
  import { EntityAssociationLoader } from './EntityAssociationLoader';
9
+ import { EntityInvalidationUtils } from './EntityInvalidationUtils';
9
10
  import { EntityLoader } from './EntityLoader';
10
- import { EntityLoaderUtils } from './EntityLoaderUtils';
11
11
  import { EntityPrivacyPolicy } from './EntityPrivacyPolicy';
12
12
  import { EntityQueryContext } from './EntityQueryContext';
13
13
  import { ViewerContext } from './ViewerContext';
@@ -230,11 +230,10 @@ export abstract class ReadonlyEntity<
230
230
  }
231
231
 
232
232
  /**
233
- * Vend loader for loading an entity in a given query context.
234
- * @param viewerContext - viewer context of loading user
235
- * @param queryContext - query context in which to perform the load
233
+ * Utilities for entity invalidation.
234
+ * Call these manually to keep entity cache consistent when performing operations outside of the entity framework.
236
235
  */
237
- static loaderUtils<
236
+ static invalidationUtils<
238
237
  TMFields extends object,
239
238
  TMIDField extends keyof NonNullable<Pick<TMFields, TMSelectedFields>>,
240
239
  TMViewerContext extends ViewerContext,
@@ -258,11 +257,7 @@ export abstract class ReadonlyEntity<
258
257
  TMSelectedFields
259
258
  >,
260
259
  viewerContext: TMViewerContext2,
261
- queryContext: EntityQueryContext = viewerContext
262
- .getViewerScopedEntityCompanionForClass(this)
263
- .getQueryContextProvider()
264
- .getQueryContext(),
265
- ): EntityLoaderUtils<
260
+ ): EntityInvalidationUtils<
266
261
  TMFields,
267
262
  TMIDField,
268
263
  TMViewerContext,
@@ -270,6 +265,9 @@ export abstract class ReadonlyEntity<
270
265
  TMPrivacyPolicy,
271
266
  TMSelectedFields
272
267
  > {
273
- return new EntityLoader(viewerContext, queryContext, this).utils();
268
+ return viewerContext
269
+ .getViewerScopedEntityCompanionForClass(this)
270
+ .getLoaderFactory()
271
+ .invalidationUtils();
274
272
  }
275
273
  }
@@ -64,30 +64,30 @@ export class ViewerContext {
64
64
  }
65
65
 
66
66
  /**
67
- * Get the regular (non-transactional) query context for a database adaptor flavor.
68
- * @param databaseAdaptorFlavor - database adaptor flavor
67
+ * Get the regular (non-transactional) query context for a database adapter flavor.
68
+ * @param databaseAdapterFlavor - database adapter flavor
69
69
  */
70
- getQueryContextForDatabaseAdaptorFlavor(
71
- databaseAdaptorFlavor: DatabaseAdapterFlavor,
70
+ getQueryContextForDatabaseAdapterFlavor(
71
+ databaseAdapterFlavor: DatabaseAdapterFlavor,
72
72
  ): EntityQueryContext {
73
73
  return this.entityCompanionProvider
74
- .getQueryContextProviderForDatabaseAdaptorFlavor(databaseAdaptorFlavor)
74
+ .getQueryContextProviderForDatabaseAdapterFlavor(databaseAdapterFlavor)
75
75
  .getQueryContext();
76
76
  }
77
77
 
78
78
  /**
79
- * Run a transaction of specified database adaptor flavor and execute the provided
79
+ * Run a transaction of specified database adapter flavor and execute the provided
80
80
  * transaction-scoped closure within the transaction.
81
- * @param databaseAdaptorFlavor - databaseAdaptorFlavor
81
+ * @param databaseAdapterFlavor - databaseAdapterFlavor
82
82
  * @param transactionScope - async callback to execute within the transaction
83
83
  */
84
- async runInTransactionForDatabaseAdaptorFlavorAsync<TResult>(
85
- databaseAdaptorFlavor: DatabaseAdapterFlavor,
84
+ async runInTransactionForDatabaseAdapterFlavorAsync<TResult>(
85
+ databaseAdapterFlavor: DatabaseAdapterFlavor,
86
86
  transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<TResult>,
87
87
  transactionConfig?: TransactionConfig,
88
88
  ): Promise<TResult> {
89
89
  return await this.entityCompanionProvider
90
- .getQueryContextProviderForDatabaseAdaptorFlavor(databaseAdaptorFlavor)
90
+ .getQueryContextProviderForDatabaseAdapterFlavor(databaseAdapterFlavor)
91
91
  .getQueryContext()
92
92
  .runInTransactionIfNotInTransactionAsync(transactionScope, transactionConfig);
93
93
  }
@@ -34,7 +34,7 @@ export class ViewerScopedEntityCompanion<
34
34
  TPrivacyPolicy,
35
35
  TSelectedFields
36
36
  >,
37
- private readonly viewerContext: TViewerContext,
37
+ public readonly viewerContext: TViewerContext,
38
38
  ) {}
39
39
 
40
40
  /**
@@ -1,4 +1,6 @@
1
1
  import { AuthorizationResultBasedEntityLoader } from './AuthorizationResultBasedEntityLoader';
2
+ import { EntityConstructionUtils } from './EntityConstructionUtils';
3
+ import { EntityInvalidationUtils } from './EntityInvalidationUtils';
2
4
  import { EntityLoaderFactory } from './EntityLoaderFactory';
3
5
  import { EntityPrivacyPolicy, EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
4
6
  import { EntityQueryContext } from './EntityQueryContext';
@@ -34,6 +36,41 @@ export class ViewerScopedEntityLoaderFactory<
34
36
  private readonly viewerContext: TViewerContext,
35
37
  ) {}
36
38
 
39
+ invalidationUtils(): EntityInvalidationUtils<
40
+ TFields,
41
+ TIDField,
42
+ TViewerContext,
43
+ TEntity,
44
+ TPrivacyPolicy,
45
+ TSelectedFields
46
+ > {
47
+ return this.entityLoaderFactory.invalidationUtils();
48
+ }
49
+
50
+ constructionUtils(
51
+ queryContext: EntityQueryContext,
52
+ privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext<
53
+ TFields,
54
+ TIDField,
55
+ TViewerContext,
56
+ TEntity,
57
+ TSelectedFields
58
+ >,
59
+ ): EntityConstructionUtils<
60
+ TFields,
61
+ TIDField,
62
+ TViewerContext,
63
+ TEntity,
64
+ TPrivacyPolicy,
65
+ TSelectedFields
66
+ > {
67
+ return this.entityLoaderFactory.constructionUtils(
68
+ this.viewerContext,
69
+ queryContext,
70
+ privacyPolicyEvaluationContext,
71
+ );
72
+ }
73
+
37
74
  forLoad(
38
75
  queryContext: EntityQueryContext,
39
76
  privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext<
@@ -5,8 +5,8 @@ import { AuthorizationResultBasedEntityLoader } from '../AuthorizationResultBase
5
5
  import { Entity } from '../Entity';
6
6
  import { EntityCompanionDefinition } from '../EntityCompanionProvider';
7
7
  import { EntityConfiguration } from '../EntityConfiguration';
8
+ import { EntityConstructionUtils } from '../EntityConstructionUtils';
8
9
  import { StringField } from '../EntityFields';
9
- import { EntityLoaderUtils } from '../EntityLoaderUtils';
10
10
  import { EntityPrivacyPolicy, EntityPrivacyPolicyEvaluationContext } from '../EntityPrivacyPolicy';
11
11
  import { ViewerContext } from '../ViewerContext';
12
12
  import { EntityDataManager } from '../internal/EntityDataManager';
@@ -164,7 +164,7 @@ describe(AuthorizationResultBasedEntityLoader, () => {
164
164
  metricsAdapter,
165
165
  TestEntity.name,
166
166
  );
167
- const utils = new EntityLoaderUtils(
167
+ const constructionUtils = new EntityConstructionUtils(
168
168
  viewerContext,
169
169
  queryContext,
170
170
  privacyPolicyEvaluationContext,
@@ -172,7 +172,6 @@ describe(AuthorizationResultBasedEntityLoader, () => {
172
172
  TestEntity,
173
173
  /* entitySelectedFields */ undefined,
174
174
  privacyPolicy,
175
- dataManager,
176
175
  metricsAdapter,
177
176
  );
178
177
  const entityLoader = new AuthorizationResultBasedEntityLoader(
@@ -180,8 +179,7 @@ describe(AuthorizationResultBasedEntityLoader, () => {
180
179
  testEntityConfiguration,
181
180
  TestEntity,
182
181
  dataManager,
183
- metricsAdapter,
184
- utils,
182
+ constructionUtils,
185
183
  );
186
184
 
187
185
  let capturedThrownThing1: any;