@expo/entity 0.54.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 (173) 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 +6 -1
  62. package/build/src/index.js +6 -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 +10 -0
  81. package/build/src/rules/AllowIfAllSubRulesAllowPrivacyPolicyRule.js +19 -0
  82. package/build/src/rules/AllowIfAllSubRulesAllowPrivacyPolicyRule.js.map +1 -0
  83. package/build/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.d.ts +10 -0
  84. package/build/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.js +19 -0
  85. package/build/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.js.map +1 -0
  86. package/build/src/rules/AllowIfInParentCascadeDeletionPrivacyPolicyRule.d.ts +66 -0
  87. package/build/src/rules/AllowIfInParentCascadeDeletionPrivacyPolicyRule.js +75 -0
  88. package/build/src/rules/AllowIfInParentCascadeDeletionPrivacyPolicyRule.js.map +1 -0
  89. package/build/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.d.ts +12 -0
  90. package/build/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.js +23 -0
  91. package/build/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.js.map +1 -0
  92. package/build/src/rules/PrivacyPolicyRule.d.ts +2 -2
  93. package/build/src/utils/EntityPrivacyUtils.js +11 -20
  94. package/build/src/utils/EntityPrivacyUtils.js.map +1 -1
  95. package/build/src/utils/collections/maps.d.ts +2 -2
  96. package/build/src/utils/collections/maps.js +2 -2
  97. package/package.json +5 -5
  98. package/src/AuthorizationResultBasedEntityAssociationLoader.ts +4 -7
  99. package/src/AuthorizationResultBasedEntityLoader.ts +58 -88
  100. package/src/AuthorizationResultBasedEntityMutator.ts +35 -20
  101. package/src/EnforcingEntityCreator.ts +1 -1
  102. package/src/EnforcingEntityLoader.ts +1 -95
  103. package/src/Entity.ts +6 -0
  104. package/src/EntityCompanion.ts +2 -2
  105. package/src/EntityCompanionProvider.ts +4 -4
  106. package/src/EntityConfiguration.ts +8 -5
  107. package/src/EntityConstructionUtils.ts +168 -0
  108. package/src/EntityDatabaseAdapter.ts +32 -222
  109. package/src/EntityFieldDefinition.ts +1 -1
  110. package/src/{EntityLoaderUtils.ts → EntityInvalidationUtils.ts} +5 -96
  111. package/src/EntityLoader.ts +0 -16
  112. package/src/EntityLoaderFactory.ts +50 -10
  113. package/src/EntityPrivacyPolicy.ts +44 -1
  114. package/src/EntitySecondaryCacheLoader.ts +54 -3
  115. package/src/ReadonlyEntity.ts +9 -11
  116. package/src/ViewerContext.ts +10 -10
  117. package/src/ViewerScopedEntityCompanion.ts +1 -1
  118. package/src/ViewerScopedEntityLoaderFactory.ts +37 -0
  119. package/src/__tests__/AuthorizationResultBasedEntityLoader-constructor-test.ts +3 -5
  120. package/src/__tests__/AuthorizationResultBasedEntityLoader-test.ts +34 -419
  121. package/src/__tests__/ComposedCacheAdapter-test.ts +3 -3
  122. package/src/__tests__/EnforcingEntityLoader-test.ts +2 -134
  123. package/src/__tests__/EntityCompanion-test.ts +18 -0
  124. package/src/__tests__/EntityConfiguration-test.ts +4 -4
  125. package/src/__tests__/EntityDatabaseAdapter-test.ts +33 -68
  126. package/src/__tests__/EntityEdges-test.ts +10 -10
  127. package/src/__tests__/EntityLoader-test.ts +6 -4
  128. package/src/__tests__/EntityMutator-test.ts +27 -15
  129. package/src/__tests__/EntityPrivacyPolicy-test.ts +102 -0
  130. package/src/__tests__/EntityQueryContext-test.ts +11 -11
  131. package/src/__tests__/EntitySecondaryCacheLoader-test.ts +10 -5
  132. package/src/__tests__/EntitySelfReferentialEdges-test.ts +6 -6
  133. package/src/__tests__/GenericEntityCacheAdapter-test.ts +18 -15
  134. package/src/__tests__/GenericSecondaryEntityCache-test.ts +27 -5
  135. package/src/__tests__/ReadonlyEntity-test.ts +6 -4
  136. package/src/__tests__/ViewerContext-test.ts +4 -4
  137. package/src/__tests__/ViewerScopedEntityCompanion-test.ts +1 -0
  138. package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +0 -17
  139. package/src/errors/EntityDatabaseAdapterError.ts +14 -0
  140. package/src/errors/EntityError.ts +1 -0
  141. package/src/errors/__tests__/EntityDatabaseAdapterError-test.ts +9 -0
  142. package/src/errors/__tests__/EntityError-test.ts +13 -5
  143. package/src/index.ts +6 -1
  144. package/src/internal/EntityDataManager.ts +19 -54
  145. package/src/internal/EntityFieldTransformationUtils.ts +5 -5
  146. package/src/internal/EntityLoadInterfaces.ts +2 -0
  147. package/src/internal/EntityTableDataCoordinator.ts +2 -2
  148. package/src/internal/__tests__/CompositeFieldHolder-test.ts +8 -2
  149. package/src/internal/__tests__/EntityDataManager-test.ts +71 -202
  150. package/src/internal/__tests__/ReadThroughEntityCache-test.ts +39 -24
  151. package/src/metrics/EntityMetricsUtils.ts +23 -0
  152. package/src/metrics/IEntityMetricsAdapter.ts +3 -0
  153. package/src/metrics/__tests__/EntityMetricsUtils-test.ts +120 -0
  154. package/src/rules/AllowIfAllSubRulesAllowPrivacyPolicyRule.ts +47 -0
  155. package/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.ts +47 -0
  156. package/src/rules/AllowIfInParentCascadeDeletionPrivacyPolicyRule.ts +177 -0
  157. package/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.ts +46 -0
  158. package/src/rules/PrivacyPolicyRule.ts +2 -2
  159. package/src/rules/__tests__/AllowIfAllSubRulesAllowPrivacyPolicyRule-test.ts +64 -0
  160. package/src/rules/__tests__/AllowIfAnySubRuleAllowsPrivacyPolicyRule-test.ts +64 -0
  161. package/src/rules/__tests__/AllowIfInParentCascadeDeletionPrivacyPolicyRule-test.ts +268 -0
  162. package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -2
  163. package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -2
  164. package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -2
  165. package/src/rules/__tests__/EvaluateIfEntityFieldPredicatePrivacyPolicyRule-test.ts +47 -0
  166. package/src/utils/EntityPrivacyUtils.ts +18 -29
  167. package/src/utils/__testfixtures__/PrivacyPolicyRuleTestUtils.ts +2 -2
  168. package/src/utils/__testfixtures__/StubDatabaseAdapter.ts +13 -101
  169. package/src/utils/__tests__/EntityCreationUtils-test.ts +6 -6
  170. package/src/utils/__tests__/EntityPrivacyUtils-test.ts +2 -2
  171. package/src/utils/collections/maps.ts +2 -2
  172. package/build/src/EntityLoaderUtils.js +0 -147
  173. package/build/src/EntityLoaderUtils.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { EntityPrivacyPolicyEvaluationContext } from '../EntityPrivacyPolicy';
1
+ import { EntityPrivacyPolicyRuleEvaluationContext } from '../EntityPrivacyPolicy';
2
2
  import { EntityQueryContext } from '../EntityQueryContext';
3
3
  import { ReadonlyEntity } from '../ReadonlyEntity';
4
4
  import { ViewerContext } from '../ViewerContext';
@@ -46,7 +46,7 @@ export abstract class PrivacyPolicyRule<
46
46
  abstract evaluateAsync(
47
47
  viewerContext: TViewerContext,
48
48
  queryContext: EntityQueryContext,
49
- evaluationContext: EntityPrivacyPolicyEvaluationContext<
49
+ evaluationContext: EntityPrivacyPolicyRuleEvaluationContext<
50
50
  TFields,
51
51
  TIDField,
52
52
  TViewerContext,
@@ -0,0 +1,64 @@
1
+ import { anything, instance, mock } from 'ts-mockito';
2
+
3
+ import { EntityPrivacyPolicyRuleEvaluationContext } from '../../EntityPrivacyPolicy';
4
+ import { EntityQueryContext } from '../../EntityQueryContext';
5
+ import { ViewerContext } from '../../ViewerContext';
6
+ import { describePrivacyPolicyRule } from '../../utils/__testfixtures__/PrivacyPolicyRuleTestUtils';
7
+ import { AllowIfAllSubRulesAllowPrivacyPolicyRule } from '../AllowIfAllSubRulesAllowPrivacyPolicyRule';
8
+ import { AlwaysAllowPrivacyPolicyRule } from '../AlwaysAllowPrivacyPolicyRule';
9
+ import { AlwaysDenyPrivacyPolicyRule } from '../AlwaysDenyPrivacyPolicyRule';
10
+ import { AlwaysSkipPrivacyPolicyRule } from '../AlwaysSkipPrivacyPolicyRule';
11
+
12
+ describePrivacyPolicyRule(
13
+ new AllowIfAllSubRulesAllowPrivacyPolicyRule([
14
+ new AlwaysAllowPrivacyPolicyRule(),
15
+ new AlwaysSkipPrivacyPolicyRule(),
16
+ ]),
17
+ {
18
+ skipCases: [
19
+ {
20
+ viewerContext: instance(mock(ViewerContext)),
21
+ queryContext: instance(mock(EntityQueryContext)),
22
+ evaluationContext:
23
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
24
+ entity: anything(),
25
+ },
26
+ ],
27
+ },
28
+ );
29
+
30
+ describePrivacyPolicyRule(
31
+ new AllowIfAllSubRulesAllowPrivacyPolicyRule([
32
+ new AlwaysAllowPrivacyPolicyRule(),
33
+ new AlwaysDenyPrivacyPolicyRule(),
34
+ ]),
35
+ {
36
+ skipCases: [
37
+ {
38
+ viewerContext: instance(mock(ViewerContext)),
39
+ queryContext: instance(mock(EntityQueryContext)),
40
+ evaluationContext:
41
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
42
+ entity: anything(),
43
+ },
44
+ ],
45
+ },
46
+ );
47
+
48
+ describePrivacyPolicyRule(
49
+ new AllowIfAllSubRulesAllowPrivacyPolicyRule([
50
+ new AlwaysAllowPrivacyPolicyRule(),
51
+ new AlwaysAllowPrivacyPolicyRule(),
52
+ ]),
53
+ {
54
+ allowCases: [
55
+ {
56
+ viewerContext: instance(mock(ViewerContext)),
57
+ queryContext: instance(mock(EntityQueryContext)),
58
+ evaluationContext:
59
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
60
+ entity: anything(),
61
+ },
62
+ ],
63
+ },
64
+ );
@@ -0,0 +1,64 @@
1
+ import { anything, instance, mock } from 'ts-mockito';
2
+
3
+ import { EntityPrivacyPolicyRuleEvaluationContext } from '../../EntityPrivacyPolicy';
4
+ import { EntityQueryContext } from '../../EntityQueryContext';
5
+ import { ViewerContext } from '../../ViewerContext';
6
+ import { describePrivacyPolicyRule } from '../../utils/__testfixtures__/PrivacyPolicyRuleTestUtils';
7
+ import { AllowIfAnySubRuleAllowsPrivacyPolicyRule } from '../AllowIfAnySubRuleAllowsPrivacyPolicyRule';
8
+ import { AlwaysAllowPrivacyPolicyRule } from '../AlwaysAllowPrivacyPolicyRule';
9
+ import { AlwaysDenyPrivacyPolicyRule } from '../AlwaysDenyPrivacyPolicyRule';
10
+ import { AlwaysSkipPrivacyPolicyRule } from '../AlwaysSkipPrivacyPolicyRule';
11
+
12
+ describePrivacyPolicyRule(
13
+ new AllowIfAnySubRuleAllowsPrivacyPolicyRule([
14
+ new AlwaysAllowPrivacyPolicyRule(),
15
+ new AlwaysSkipPrivacyPolicyRule(),
16
+ ]),
17
+ {
18
+ allowCases: [
19
+ {
20
+ viewerContext: instance(mock(ViewerContext)),
21
+ queryContext: instance(mock(EntityQueryContext)),
22
+ evaluationContext:
23
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
24
+ entity: anything(),
25
+ },
26
+ ],
27
+ },
28
+ );
29
+
30
+ describePrivacyPolicyRule(
31
+ new AllowIfAnySubRuleAllowsPrivacyPolicyRule([
32
+ new AlwaysAllowPrivacyPolicyRule(),
33
+ new AlwaysDenyPrivacyPolicyRule(),
34
+ ]),
35
+ {
36
+ allowCases: [
37
+ {
38
+ viewerContext: instance(mock(ViewerContext)),
39
+ queryContext: instance(mock(EntityQueryContext)),
40
+ evaluationContext:
41
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
42
+ entity: anything(),
43
+ },
44
+ ],
45
+ },
46
+ );
47
+
48
+ describePrivacyPolicyRule(
49
+ new AllowIfAnySubRuleAllowsPrivacyPolicyRule([
50
+ new AlwaysSkipPrivacyPolicyRule(),
51
+ new AlwaysSkipPrivacyPolicyRule(),
52
+ ]),
53
+ {
54
+ skipCases: [
55
+ {
56
+ viewerContext: instance(mock(ViewerContext)),
57
+ queryContext: instance(mock(EntityQueryContext)),
58
+ evaluationContext:
59
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
60
+ entity: anything(),
61
+ },
62
+ ],
63
+ },
64
+ );
@@ -0,0 +1,268 @@
1
+ import { anything, instance, mock, when } from 'ts-mockito';
2
+
3
+ import { EntityCompanionDefinition } from '../../EntityCompanionProvider';
4
+ import { EntityPrivacyPolicy } from '../../EntityPrivacyPolicy';
5
+ import { EntityQueryContext } from '../../EntityQueryContext';
6
+ import { ReadonlyEntity } from '../../ReadonlyEntity';
7
+ import { ViewerContext } from '../../ViewerContext';
8
+ import { describePrivacyPolicyRule } from '../../utils/__testfixtures__/PrivacyPolicyRuleTestUtils';
9
+ import { AllowIfInParentCascadeDeletionPrivacyPolicyRule } from '../AllowIfInParentCascadeDeletionPrivacyPolicyRule';
10
+
11
+ // Define test field types
12
+ type ParentFields = {
13
+ id: string;
14
+ name: string;
15
+ };
16
+
17
+ type ChildFields = {
18
+ id: string;
19
+ parent_id: string | null;
20
+ parent_name: string | null;
21
+ };
22
+
23
+ // Create a mock privacy policy for the parent entity
24
+ class TestParentPrivacyPolicy extends EntityPrivacyPolicy<
25
+ ParentFields,
26
+ 'id',
27
+ ViewerContext,
28
+ TestParentEntity
29
+ > {
30
+ protected override readonly readRules = [];
31
+ protected override readonly createRules = [];
32
+ protected override readonly updateRules = [];
33
+ protected override readonly deleteRules = [];
34
+ }
35
+
36
+ // Create a mock parent entity class with required static method
37
+ class TestParentEntity extends ReadonlyEntity<ParentFields, 'id', ViewerContext> {
38
+ static defineCompanionDefinition(): EntityCompanionDefinition<
39
+ ParentFields,
40
+ 'id',
41
+ ViewerContext,
42
+ TestParentEntity,
43
+ TestParentPrivacyPolicy
44
+ > {
45
+ throw new Error('Not implemented for test');
46
+ }
47
+ }
48
+
49
+ // Create a mock child entity class
50
+ class TestChildEntity extends ReadonlyEntity<ChildFields, 'id', ViewerContext> {}
51
+
52
+ // Mock parent entities
53
+ const parentEntityMock = mock(TestParentEntity);
54
+ when(parentEntityMock.getID()).thenReturn('5');
55
+ const parentEntity = instance(parentEntityMock);
56
+ Object.setPrototypeOf(parentEntity, TestParentEntity.prototype);
57
+
58
+ const otherParentEntityMock = mock(TestParentEntity);
59
+ when(otherParentEntityMock.getID()).thenReturn('6');
60
+ const otherParentEntity = instance(otherParentEntityMock);
61
+ Object.setPrototypeOf(otherParentEntity, TestParentEntity.prototype);
62
+
63
+ // Mock a non-parent entity (different class)
64
+ class UnrelatedOtherEntity extends ReadonlyEntity<{ id: string }, 'id', ViewerContext> {}
65
+ const unrelatedOtherEntityMock = mock(UnrelatedOtherEntity);
66
+ Object.setPrototypeOf(unrelatedOtherEntityMock, UnrelatedOtherEntity.prototype);
67
+
68
+ // Mock child entities
69
+ const childEntityMock = mock(TestChildEntity);
70
+ when(childEntityMock.getField('parent_id')).thenReturn('5');
71
+
72
+ const childEntityMockWithNullifiedField = mock(TestChildEntity);
73
+ when(childEntityMockWithNullifiedField.getField('parent_id')).thenReturn(null);
74
+
75
+ const childEntityDifferentParentMock = mock(TestChildEntity);
76
+ when(childEntityDifferentParentMock.getField('parent_id')).thenReturn('6');
77
+
78
+ describePrivacyPolicyRule(
79
+ new AllowIfInParentCascadeDeletionPrivacyPolicyRule<
80
+ ChildFields,
81
+ 'id',
82
+ ViewerContext,
83
+ TestChildEntity,
84
+ ParentFields,
85
+ 'id',
86
+ TestParentEntity,
87
+ TestParentPrivacyPolicy
88
+ >({
89
+ fieldIdentifyingParentEntity: 'parent_id',
90
+ parentEntityClass: TestParentEntity,
91
+ }),
92
+ {
93
+ allowCases: [
94
+ // parent id matches parent being deleted, field not yet nullified
95
+ {
96
+ viewerContext: instance(mock(ViewerContext)),
97
+ queryContext: instance(mock(EntityQueryContext)),
98
+ evaluationContext: {
99
+ action: anything(),
100
+ previousValue: instance(childEntityMock),
101
+ cascadingDeleteCause: {
102
+ entity: parentEntity,
103
+ cascadingDeleteCause: null,
104
+ },
105
+ },
106
+ entity: instance(childEntityMock),
107
+ },
108
+ // parent id matches parent being deleted, field null in current version but filled in previous version
109
+ {
110
+ viewerContext: instance(mock(ViewerContext)),
111
+ queryContext: instance(mock(EntityQueryContext)),
112
+ evaluationContext: {
113
+ action: anything(),
114
+ previousValue: instance(childEntityMock),
115
+ cascadingDeleteCause: {
116
+ entity: parentEntity,
117
+ cascadingDeleteCause: null,
118
+ },
119
+ },
120
+ entity: instance(childEntityMockWithNullifiedField),
121
+ },
122
+ ],
123
+ skipCases: [
124
+ // no cascading delete
125
+ {
126
+ viewerContext: instance(mock(ViewerContext)),
127
+ queryContext: instance(mock(EntityQueryContext)),
128
+ evaluationContext: {
129
+ action: anything(),
130
+ previousValue: null,
131
+ cascadingDeleteCause: null,
132
+ },
133
+ entity: instance(childEntityMock),
134
+ },
135
+ // cascading delete not from parent entity class
136
+ {
137
+ viewerContext: instance(mock(ViewerContext)),
138
+ queryContext: instance(mock(EntityQueryContext)),
139
+ evaluationContext: {
140
+ action: anything(),
141
+ previousValue: null,
142
+ cascadingDeleteCause: {
143
+ entity: instance(unrelatedOtherEntityMock),
144
+ cascadingDeleteCause: null,
145
+ },
146
+ },
147
+ entity: instance(childEntityMock),
148
+ },
149
+ // cascading delete from different parent, field not nullified
150
+ {
151
+ viewerContext: instance(mock(ViewerContext)),
152
+ queryContext: instance(mock(EntityQueryContext)),
153
+ evaluationContext: {
154
+ action: anything(),
155
+ previousValue: null,
156
+ cascadingDeleteCause: {
157
+ entity: otherParentEntity,
158
+ cascadingDeleteCause: null,
159
+ },
160
+ },
161
+ entity: instance(childEntityMock),
162
+ },
163
+ // entity belongs to different parent
164
+ {
165
+ viewerContext: instance(mock(ViewerContext)),
166
+ queryContext: instance(mock(EntityQueryContext)),
167
+ evaluationContext: {
168
+ action: anything(),
169
+ previousValue: null,
170
+ cascadingDeleteCause: {
171
+ entity: parentEntity,
172
+ cascadingDeleteCause: null,
173
+ },
174
+ },
175
+ entity: instance(childEntityDifferentParentMock),
176
+ },
177
+ // parent id field undefined (null) and no previous value
178
+ {
179
+ viewerContext: instance(mock(ViewerContext)),
180
+ queryContext: instance(mock(EntityQueryContext)),
181
+ evaluationContext: {
182
+ action: anything(),
183
+ previousValue: null,
184
+ cascadingDeleteCause: {
185
+ entity: parentEntity,
186
+ cascadingDeleteCause: null,
187
+ },
188
+ },
189
+ entity: instance(childEntityMockWithNullifiedField),
190
+ },
191
+ // parent id now null but previous value different parent
192
+ {
193
+ viewerContext: instance(mock(ViewerContext)),
194
+ queryContext: instance(mock(EntityQueryContext)),
195
+ evaluationContext: {
196
+ action: anything(),
197
+ previousValue: instance(childEntityDifferentParentMock),
198
+ cascadingDeleteCause: {
199
+ entity: parentEntity,
200
+ cascadingDeleteCause: null,
201
+ },
202
+ },
203
+ entity: instance(childEntityMockWithNullifiedField),
204
+ },
205
+ ],
206
+ },
207
+ );
208
+
209
+ // Test with custom lookup field (parentEntityLookupByField)
210
+ const parentEntityWithNameMock = mock(TestParentEntity);
211
+ when(parentEntityWithNameMock.getField('name')).thenReturn('test-name');
212
+ const parentEntityWithName = instance(parentEntityWithNameMock);
213
+ Object.setPrototypeOf(parentEntityWithName, TestParentEntity.prototype);
214
+
215
+ const childEntityWithNameRefMock = mock(TestChildEntity);
216
+ when(childEntityWithNameRefMock.getField('parent_name')).thenReturn('test-name');
217
+
218
+ const childEntityWithNameRefMockWithNullifiedField = mock(TestChildEntity);
219
+ when(childEntityWithNameRefMockWithNullifiedField.getField('parent_name')).thenReturn(null);
220
+
221
+ describePrivacyPolicyRule(
222
+ new AllowIfInParentCascadeDeletionPrivacyPolicyRule<
223
+ ChildFields,
224
+ 'id',
225
+ ViewerContext,
226
+ TestChildEntity,
227
+ ParentFields,
228
+ 'id',
229
+ TestParentEntity,
230
+ TestParentPrivacyPolicy
231
+ >({
232
+ fieldIdentifyingParentEntity: 'parent_name',
233
+ parentEntityClass: TestParentEntity,
234
+ parentEntityLookupByField: 'name',
235
+ }),
236
+ {
237
+ allowCases: [
238
+ // parent name matches parent being deleted, field not yet nullified
239
+ {
240
+ viewerContext: instance(mock(ViewerContext)),
241
+ queryContext: instance(mock(EntityQueryContext)),
242
+ evaluationContext: {
243
+ action: anything(),
244
+ previousValue: instance(childEntityWithNameRefMock),
245
+ cascadingDeleteCause: {
246
+ entity: parentEntityWithName,
247
+ cascadingDeleteCause: null,
248
+ },
249
+ },
250
+ entity: instance(childEntityWithNameRefMock),
251
+ },
252
+ // parent name matches parent being deleted, field null in current version but filled in previous version
253
+ {
254
+ viewerContext: instance(mock(ViewerContext)),
255
+ queryContext: instance(mock(EntityQueryContext)),
256
+ evaluationContext: {
257
+ action: anything(),
258
+ previousValue: instance(childEntityWithNameRefMock),
259
+ cascadingDeleteCause: {
260
+ entity: parentEntityWithName,
261
+ cascadingDeleteCause: null,
262
+ },
263
+ },
264
+ entity: instance(childEntityWithNameRefMockWithNullifiedField),
265
+ },
266
+ ],
267
+ },
268
+ );
@@ -1,6 +1,6 @@
1
1
  import { anything, instance, mock } from 'ts-mockito';
2
2
 
3
- import { EntityPrivacyPolicyEvaluationContext } from '../../EntityPrivacyPolicy';
3
+ import { EntityPrivacyPolicyRuleEvaluationContext } from '../../EntityPrivacyPolicy';
4
4
  import { EntityQueryContext } from '../../EntityQueryContext';
5
5
  import { ViewerContext } from '../../ViewerContext';
6
6
  import { describePrivacyPolicyRule } from '../../utils/__testfixtures__/PrivacyPolicyRuleTestUtils';
@@ -12,7 +12,7 @@ describePrivacyPolicyRule(new AlwaysAllowPrivacyPolicyRule(), {
12
12
  viewerContext: instance(mock(ViewerContext)),
13
13
  queryContext: instance(mock(EntityQueryContext)),
14
14
  evaluationContext:
15
- instance(mock<EntityPrivacyPolicyEvaluationContext<any, any, any, any, any>>()),
15
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
16
16
  entity: anything(),
17
17
  },
18
18
  ],
@@ -1,6 +1,6 @@
1
1
  import { anything, instance, mock } from 'ts-mockito';
2
2
 
3
- import { EntityPrivacyPolicyEvaluationContext } from '../../EntityPrivacyPolicy';
3
+ import { EntityPrivacyPolicyRuleEvaluationContext } from '../../EntityPrivacyPolicy';
4
4
  import { EntityQueryContext } from '../../EntityQueryContext';
5
5
  import { ViewerContext } from '../../ViewerContext';
6
6
  import { describePrivacyPolicyRule } from '../../utils/__testfixtures__/PrivacyPolicyRuleTestUtils';
@@ -12,7 +12,7 @@ describePrivacyPolicyRule(new AlwaysDenyPrivacyPolicyRule(), {
12
12
  viewerContext: instance(mock(ViewerContext)),
13
13
  queryContext: instance(mock(EntityQueryContext)),
14
14
  evaluationContext:
15
- instance(mock<EntityPrivacyPolicyEvaluationContext<any, any, any, any, any>>()),
15
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
16
16
  entity: anything(),
17
17
  },
18
18
  ],
@@ -1,6 +1,6 @@
1
1
  import { anything, instance, mock } from 'ts-mockito';
2
2
 
3
- import { EntityPrivacyPolicyEvaluationContext } from '../../EntityPrivacyPolicy';
3
+ import { EntityPrivacyPolicyRuleEvaluationContext } from '../../EntityPrivacyPolicy';
4
4
  import { EntityQueryContext } from '../../EntityQueryContext';
5
5
  import { ViewerContext } from '../../ViewerContext';
6
6
  import { describePrivacyPolicyRule } from '../../utils/__testfixtures__/PrivacyPolicyRuleTestUtils';
@@ -12,7 +12,7 @@ describePrivacyPolicyRule(new AlwaysSkipPrivacyPolicyRule(), {
12
12
  viewerContext: instance(mock(ViewerContext)),
13
13
  queryContext: instance(mock(EntityQueryContext)),
14
14
  evaluationContext:
15
- instance(mock<EntityPrivacyPolicyEvaluationContext<any, any, any, any, any>>()),
15
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
16
16
  entity: anything(),
17
17
  },
18
18
  ],
@@ -0,0 +1,47 @@
1
+ import { mock, instance, when } from 'ts-mockito';
2
+
3
+ import { EntityPrivacyPolicyRuleEvaluationContext } from '../../EntityPrivacyPolicy';
4
+ import { EntityQueryContext } from '../../EntityQueryContext';
5
+ import { ViewerContext } from '../../ViewerContext';
6
+ import { describePrivacyPolicyRule } from '../../utils/__testfixtures__/PrivacyPolicyRuleTestUtils';
7
+ import { TestEntity, TestFields } from '../../utils/__testfixtures__/TestEntity';
8
+ import { AlwaysAllowPrivacyPolicyRule } from '../AlwaysAllowPrivacyPolicyRule';
9
+ import { EvaluateIfEntityFieldPredicatePrivacyPolicyRule } from '../EvaluateIfEntityFieldPredicatePrivacyPolicyRule';
10
+
11
+ const entityBlahMock = mock(TestEntity);
12
+ when(entityBlahMock.getField('testIndexedField')).thenReturn('1');
13
+ const entityBlah = instance(entityBlahMock);
14
+
15
+ const entityFooMock = mock(TestEntity);
16
+ when(entityFooMock.getField('testIndexedField')).thenReturn('2');
17
+ const entityFoo = instance(entityFooMock);
18
+
19
+ describePrivacyPolicyRule<TestFields, 'customIdField', ViewerContext, TestEntity>(
20
+ new EvaluateIfEntityFieldPredicatePrivacyPolicyRule<
21
+ TestFields,
22
+ 'customIdField',
23
+ ViewerContext,
24
+ TestEntity,
25
+ 'testIndexedField'
26
+ >('testIndexedField', (val) => val === '1', new AlwaysAllowPrivacyPolicyRule()),
27
+ {
28
+ allowCases: [
29
+ {
30
+ viewerContext: instance(mock(ViewerContext)),
31
+ queryContext: instance(mock(EntityQueryContext)),
32
+ evaluationContext:
33
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
34
+ entity: entityBlah,
35
+ },
36
+ ],
37
+ skipCases: [
38
+ {
39
+ viewerContext: instance(mock(ViewerContext)),
40
+ queryContext: instance(mock(EntityQueryContext)),
41
+ evaluationContext:
42
+ instance(mock<EntityPrivacyPolicyRuleEvaluationContext<any, any, any, any, any>>()),
43
+ entity: entityFoo,
44
+ },
45
+ ],
46
+ },
47
+ );
@@ -354,13 +354,16 @@ async function canViewerDeleteInternalAsync<
354
354
  entityCompanionProvider.getCompanionForEntity(inboundEdge).entityCompanionDefinition
355
355
  .entityConfiguration;
356
356
 
357
- const loader = viewerContext
358
- .getViewerScopedEntityCompanionForClass(inboundEdge)
359
- .getLoaderFactory()
360
- .forLoad(queryContext, {
361
- previousValue: null,
362
- cascadingDeleteCause: newCascadingDeleteCause,
363
- });
357
+ const entityCompanion = viewerContext.getViewerScopedEntityCompanionForClass(inboundEdge);
358
+ const loaderFactory = entityCompanion.getLoaderFactory();
359
+ const loader = loaderFactory.forLoad(queryContext, {
360
+ previousValue: null,
361
+ cascadingDeleteCause: newCascadingDeleteCause,
362
+ });
363
+ const constructionUtils = loaderFactory.constructionUtils(queryContext, {
364
+ previousValue: null,
365
+ cascadingDeleteCause: newCascadingDeleteCause,
366
+ });
364
367
 
365
368
  for (const [fieldName, fieldDefinition] of configurationForInboundEdge.schema) {
366
369
  const association = fieldDefinition.association;
@@ -384,18 +387,12 @@ async function canViewerDeleteInternalAsync<
384
387
  edgeDeletionPermissionInferenceBehavior ===
385
388
  EntityEdgeDeletionAuthorizationInferenceBehavior.ONE_IMPLIES_ALL
386
389
  ) {
387
- const singleEntityResultToTestForInboundEdge =
388
- await loader.loadFirstByFieldEqualityConjunctionAsync(
389
- [
390
- {
391
- fieldName,
392
- fieldValue: association.associatedEntityLookupByField
393
- ? sourceEntity.getField(association.associatedEntityLookupByField as any)
394
- : sourceEntity.getID(),
395
- },
396
- ],
397
- { orderBy: [] },
398
- );
390
+ const singleEntityResultToTestForInboundEdge = await loader['loadOneByFieldEqualingAsync'](
391
+ fieldName,
392
+ association.associatedEntityLookupByField
393
+ ? sourceEntity.getField(association.associatedEntityLookupByField)
394
+ : sourceEntity.getID(),
395
+ );
399
396
  entityResultsToCheckForInboundEdge = singleEntityResultToTestForInboundEdge
400
397
  ? [singleEntityResultToTestForInboundEdge]
401
398
  : [];
@@ -403,7 +400,7 @@ async function canViewerDeleteInternalAsync<
403
400
  const entityResultsForInboundEdge = await loader.loadManyByFieldEqualingAsync(
404
401
  fieldName,
405
402
  association.associatedEntityLookupByField
406
- ? sourceEntity.getField(association.associatedEntityLookupByField as any)
403
+ ? sourceEntity.getField(association.associatedEntityLookupByField)
407
404
  : sourceEntity.getID(),
408
405
  );
409
406
  entityResultsToCheckForInboundEdge = entityResultsForInboundEdge;
@@ -451,14 +448,6 @@ async function canViewerDeleteInternalAsync<
451
448
  // privacy policy as it would be after the cascading SET NULL operation
452
449
  const previousAndSyntheticEntitiesForInboundEdge = entitiesForInboundEdge.map(
453
450
  (entity) => {
454
- const entityLoader = viewerContext
455
- .getViewerScopedEntityCompanionForClass(inboundEdge)
456
- .getLoaderFactory()
457
- .forLoad(queryContext, {
458
- previousValue: entity,
459
- cascadingDeleteCause: newCascadingDeleteCause,
460
- });
461
-
462
451
  const allFields = entity.getAllDatabaseFields();
463
452
  const syntheticFields = {
464
453
  ...allFields,
@@ -467,7 +456,7 @@ async function canViewerDeleteInternalAsync<
467
456
 
468
457
  return {
469
458
  previousValue: entity,
470
- syntheticallyUpdatedValue: entityLoader.utils.constructEntity(syntheticFields),
459
+ syntheticallyUpdatedValue: constructionUtils.constructEntity(syntheticFields),
471
460
  };
472
461
  },
473
462
  );
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test } from '@jest/globals';
2
2
 
3
- import { EntityPrivacyPolicyEvaluationContext } from '../../EntityPrivacyPolicy';
3
+ import { EntityPrivacyPolicyRuleEvaluationContext } from '../../EntityPrivacyPolicy';
4
4
  import { EntityQueryContext } from '../../EntityQueryContext';
5
5
  import { ReadonlyEntity } from '../../ReadonlyEntity';
6
6
  import { ViewerContext } from '../../ViewerContext';
@@ -15,7 +15,7 @@ export interface Case<
15
15
  > {
16
16
  viewerContext: TViewerContext;
17
17
  queryContext: EntityQueryContext;
18
- evaluationContext: EntityPrivacyPolicyEvaluationContext<
18
+ evaluationContext: EntityPrivacyPolicyRuleEvaluationContext<
19
19
  TFields,
20
20
  TIDField,
21
21
  TViewerContext,