@expo/entity 0.16.0 → 0.20.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 (210) hide show
  1. package/build/EnforcingEntityLoader.js +2 -2
  2. package/build/EnforcingEntityLoader.js.map +1 -1
  3. package/build/Entity.js +8 -2
  4. package/build/Entity.js.map +1 -1
  5. package/build/EntityAssociationLoader.js +3 -3
  6. package/build/EntityAssociationLoader.js.map +1 -1
  7. package/build/EntityCompanion.d.ts +5 -0
  8. package/build/EntityCompanion.js +8 -1
  9. package/build/EntityCompanion.js.map +1 -1
  10. package/build/EntityCompanionProvider.d.ts +1 -1
  11. package/build/EntityCompanionProvider.js +5 -5
  12. package/build/EntityCompanionProvider.js.map +1 -1
  13. package/build/EntityConfiguration.d.ts +1 -1
  14. package/build/EntityConfiguration.js +3 -3
  15. package/build/EntityConfiguration.js.map +1 -1
  16. package/build/EntityDatabaseAdapter.d.ts +4 -4
  17. package/build/EntityDatabaseAdapter.js +13 -13
  18. package/build/EntityDatabaseAdapter.js.map +1 -1
  19. package/build/EntityFieldDefinition.d.ts +77 -0
  20. package/build/EntityFieldDefinition.js +53 -0
  21. package/build/EntityFieldDefinition.js.map +1 -0
  22. package/build/EntityFields.d.ts +5 -78
  23. package/build/EntityFields.js +19 -61
  24. package/build/EntityFields.js.map +1 -1
  25. package/build/EntityLoader.d.ts +3 -1
  26. package/build/EntityLoader.js +19 -15
  27. package/build/EntityLoader.js.map +1 -1
  28. package/build/EntityLoaderFactory.d.ts +3 -1
  29. package/build/EntityLoaderFactory.js +3 -2
  30. package/build/EntityLoaderFactory.js.map +1 -1
  31. package/build/EntityMutationInfo.d.ts +26 -0
  32. package/build/EntityMutationInfo.js +10 -0
  33. package/build/EntityMutationInfo.js.map +1 -0
  34. package/build/EntityMutationTriggerConfiguration.d.ts +4 -4
  35. package/build/EntityMutationValidator.d.ts +3 -3
  36. package/build/EntityMutationValidator.js.map +1 -1
  37. package/build/EntityMutator.d.ts +5 -16
  38. package/build/EntityMutator.js +62 -58
  39. package/build/EntityMutator.js.map +1 -1
  40. package/build/EntityPrivacyPolicy.d.ts +5 -4
  41. package/build/EntityPrivacyPolicy.js +60 -12
  42. package/build/EntityPrivacyPolicy.js.map +1 -1
  43. package/build/EntityQueryContext.d.ts +13 -0
  44. package/build/EntityQueryContext.js +18 -0
  45. package/build/EntityQueryContext.js.map +1 -1
  46. package/build/EntitySecondaryCacheLoader.js +2 -2
  47. package/build/EntitySecondaryCacheLoader.js.map +1 -1
  48. package/build/ReadonlyEntity.js +3 -4
  49. package/build/ReadonlyEntity.js.map +1 -1
  50. package/build/ViewerScopedEntityCompanion.d.ts +5 -0
  51. package/build/ViewerScopedEntityCompanion.js +6 -0
  52. package/build/ViewerScopedEntityCompanion.js.map +1 -1
  53. package/build/__tests__/EnforcingEntityLoader-test.js +82 -82
  54. package/build/__tests__/EnforcingEntityLoader-test.js.map +1 -1
  55. package/build/__tests__/Entity-test.js +6 -6
  56. package/build/__tests__/Entity-test.js.map +1 -1
  57. package/build/__tests__/EntityAssociationLoader-test.js +40 -40
  58. package/build/__tests__/EntityAssociationLoader-test.js.map +1 -1
  59. package/build/__tests__/EntityCommonUseCases-test.js +11 -11
  60. package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
  61. package/build/__tests__/EntityCompanion-test.js +3 -3
  62. package/build/__tests__/EntityCompanion-test.js.map +1 -1
  63. package/build/__tests__/EntityCompanionProvider-test.js +1 -1
  64. package/build/__tests__/EntityCompanionProvider-test.js.map +1 -1
  65. package/build/__tests__/EntityDatabaseAdapter-test.js +12 -12
  66. package/build/__tests__/EntityDatabaseAdapter-test.js.map +1 -1
  67. package/build/__tests__/EntityEdges-test.js +103 -6
  68. package/build/__tests__/EntityEdges-test.js.map +1 -1
  69. package/build/__tests__/EntityFields-test.js +18 -26
  70. package/build/__tests__/EntityFields-test.js.map +1 -1
  71. package/build/__tests__/EntityLoader-constructor-test.d.ts +22 -0
  72. package/build/__tests__/EntityLoader-constructor-test.js +111 -0
  73. package/build/__tests__/EntityLoader-constructor-test.js.map +1 -0
  74. package/build/__tests__/EntityLoader-test.js +81 -73
  75. package/build/__tests__/EntityLoader-test.js.map +1 -1
  76. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.d.ts +1 -0
  77. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js +81 -0
  78. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js.map +1 -0
  79. package/build/__tests__/EntityMutator-test.js +138 -136
  80. package/build/__tests__/EntityMutator-test.js.map +1 -1
  81. package/build/__tests__/EntityPrivacyPolicy-test.js +143 -67
  82. package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
  83. package/build/__tests__/EntitySecondaryCacheLoader-test.js +15 -15
  84. package/build/__tests__/EntitySecondaryCacheLoader-test.js.map +1 -1
  85. package/build/__tests__/EntitySelfReferentialEdges-test.js +13 -12
  86. package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
  87. package/build/__tests__/ReadonlyEntity-test.js +12 -12
  88. package/build/__tests__/ReadonlyEntity-test.js.map +1 -1
  89. package/build/__tests__/ViewerContext-test.js +2 -2
  90. package/build/__tests__/ViewerContext-test.js.map +1 -1
  91. package/build/__tests__/ViewerScopedEntityCompanion-test.js +2 -2
  92. package/build/__tests__/ViewerScopedEntityCompanion-test.js.map +1 -1
  93. package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js +2 -2
  94. package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js.map +1 -1
  95. package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js +5 -5
  96. package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js.map +1 -1
  97. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +5 -5
  98. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
  99. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js +5 -5
  100. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js.map +1 -1
  101. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js +2 -2
  102. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js.map +1 -1
  103. package/build/__tests__/entityUtils-test.js +21 -21
  104. package/build/__tests__/entityUtils-test.js.map +1 -1
  105. package/build/index.d.ts +3 -0
  106. package/build/index.js +5 -1
  107. package/build/index.js.map +1 -1
  108. package/build/internal/EntityDataManager.js +8 -7
  109. package/build/internal/EntityDataManager.js.map +1 -1
  110. package/build/internal/EntityFieldTransformationUtils.js +2 -2
  111. package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
  112. package/build/internal/ReadThroughEntityCache.js +4 -4
  113. package/build/internal/ReadThroughEntityCache.js.map +1 -1
  114. package/build/internal/__tests__/EntityDataManager-test.js +21 -21
  115. package/build/internal/__tests__/EntityDataManager-test.js.map +1 -1
  116. package/build/internal/__tests__/EntityFieldTransformationUtils-test.js +8 -8
  117. package/build/internal/__tests__/EntityFieldTransformationUtils-test.js.map +1 -1
  118. package/build/internal/__tests__/ReadThroughEntityCache-test.js +48 -48
  119. package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
  120. package/build/metrics/EntityMetricsUtils.js +1 -1
  121. package/build/metrics/EntityMetricsUtils.js.map +1 -1
  122. package/build/metrics/IEntityMetricsAdapter.d.ts +16 -0
  123. package/build/metrics/IEntityMetricsAdapter.js +6 -1
  124. package/build/metrics/IEntityMetricsAdapter.js.map +1 -1
  125. package/build/metrics/NoOpEntityMetricsAdapter.d.ts +2 -1
  126. package/build/metrics/NoOpEntityMetricsAdapter.js +1 -0
  127. package/build/metrics/NoOpEntityMetricsAdapter.js.map +1 -1
  128. package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js +4 -4
  129. package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js.map +1 -1
  130. package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js +4 -4
  131. package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js.map +1 -1
  132. package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js +4 -4
  133. package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js.map +1 -1
  134. package/build/testfixtures/DateIDTestEntity.js.map +1 -1
  135. package/build/testfixtures/SimpleTestEntity.js.map +1 -1
  136. package/build/testfixtures/TestEntity.d.ts +6 -6
  137. package/build/testfixtures/TestEntity.js +4 -4
  138. package/build/testfixtures/TestEntity.js.map +1 -1
  139. package/build/testfixtures/TestEntity2.d.ts +5 -5
  140. package/build/testfixtures/TestEntity2.js.map +1 -1
  141. package/build/testfixtures/TestEntityNumberKey.js +1 -1
  142. package/build/testfixtures/TestEntityNumberKey.js.map +1 -1
  143. package/build/utils/collections/__tests__/maps-test.js +13 -13
  144. package/build/utils/collections/__tests__/maps-test.js.map +1 -1
  145. package/build/utils/collections/maps.js +1 -1
  146. package/build/utils/collections/maps.js.map +1 -1
  147. package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +6 -6
  148. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js +1 -1
  149. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
  150. package/build/utils/testing/StubCacheAdapter.js +1 -1
  151. package/build/utils/testing/StubCacheAdapter.js.map +1 -1
  152. package/build/utils/testing/StubDatabaseAdapter.js +7 -7
  153. package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
  154. package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js +26 -26
  155. package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js.map +1 -1
  156. package/build/utils/testing/describeFieldTestCase.d.ts +2 -0
  157. package/build/utils/testing/describeFieldTestCase.js +18 -0
  158. package/build/utils/testing/describeFieldTestCase.js.map +1 -0
  159. package/package.json +2 -1
  160. package/src/Entity.ts +10 -2
  161. package/src/EntityAssociationLoader.ts +1 -1
  162. package/src/EntityCompanion.ts +10 -2
  163. package/src/EntityCompanionProvider.ts +5 -9
  164. package/src/EntityConfiguration.ts +1 -1
  165. package/src/EntityDatabaseAdapter.ts +10 -8
  166. package/src/EntityFieldDefinition.ts +124 -0
  167. package/src/EntityFields.ts +11 -126
  168. package/src/EntityLoader.ts +12 -4
  169. package/src/EntityLoaderFactory.ts +5 -2
  170. package/src/EntityMutationInfo.ts +47 -0
  171. package/src/EntityMutationTriggerConfiguration.ts +5 -5
  172. package/src/EntityMutationValidator.ts +10 -4
  173. package/src/EntityMutator.ts +98 -76
  174. package/src/EntityPrivacyPolicy.ts +77 -19
  175. package/src/EntityQueryContext.ts +20 -0
  176. package/src/ReadonlyEntity.ts +3 -2
  177. package/src/ViewerScopedEntityCompanion.ts +8 -0
  178. package/src/__tests__/Entity-test.ts +8 -8
  179. package/src/__tests__/EntityCommonUseCases-test.ts +4 -4
  180. package/src/__tests__/EntityEdges-test.ts +169 -14
  181. package/src/__tests__/EntityFields-test.ts +6 -23
  182. package/src/__tests__/EntityLoader-constructor-test.ts +177 -0
  183. package/src/__tests__/EntityLoader-test.ts +48 -20
  184. package/src/__tests__/EntityMutator-MutationCacheConsistency-test.ts +105 -0
  185. package/src/__tests__/EntityMutator-test.ts +153 -146
  186. package/src/__tests__/EntityPrivacyPolicy-test.ts +215 -78
  187. package/src/__tests__/EntitySecondaryCacheLoader-test.ts +7 -9
  188. package/src/__tests__/EntitySelfReferentialEdges-test.ts +6 -5
  189. package/src/__tests__/ReadonlyEntity-test.ts +1 -1
  190. package/src/__tests__/ViewerContext-test.ts +7 -6
  191. package/src/__tests__/ViewerScopedEntityCompanion-test.ts +11 -10
  192. package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +4 -3
  193. package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +6 -6
  194. package/src/__tests__/cases/TwoEntitySameTableOverlappingRows-test.ts +4 -4
  195. package/src/index.ts +3 -0
  196. package/src/internal/EntityDataManager.ts +2 -1
  197. package/src/internal/__tests__/EntityDataManager-test.ts +6 -6
  198. package/src/internal/__tests__/ReadThroughEntityCache-test.ts +15 -13
  199. package/src/metrics/EntityMetricsUtils.ts +56 -50
  200. package/src/metrics/IEntityMetricsAdapter.ts +23 -0
  201. package/src/metrics/NoOpEntityMetricsAdapter.ts +2 -0
  202. package/src/testfixtures/DateIDTestEntity.ts +4 -4
  203. package/src/testfixtures/SimpleTestEntity.ts +4 -4
  204. package/src/testfixtures/TestEntity.ts +8 -8
  205. package/src/testfixtures/TestEntity2.ts +4 -4
  206. package/src/testfixtures/TestEntityNumberKey.ts +6 -6
  207. package/src/utils/testing/StubDatabaseAdapter.ts +4 -4
  208. package/src/utils/testing/__tests__/StubDatabaseAdapter-test.ts +18 -18
  209. package/src/utils/testing/describeFieldTestCase.ts +21 -0
  210. package/CHANGELOG.md +0 -241
@@ -102,7 +102,7 @@ class SimpleTestDenyUpdateEntityPrivacyPolicy extends EntityPrivacyPolicy<
102
102
  ViewerContext,
103
103
  SimpleTestDenyUpdateEntity
104
104
  > {
105
- protected readonly readRules = [
105
+ protected override readonly readRules = [
106
106
  new AlwaysAllowPrivacyPolicyRule<
107
107
  TestEntityFields,
108
108
  string,
@@ -110,7 +110,7 @@ class SimpleTestDenyUpdateEntityPrivacyPolicy extends EntityPrivacyPolicy<
110
110
  SimpleTestDenyUpdateEntity
111
111
  >(),
112
112
  ];
113
- protected readonly createRules = [
113
+ protected override readonly createRules = [
114
114
  new AlwaysAllowPrivacyPolicyRule<
115
115
  TestEntityFields,
116
116
  string,
@@ -118,7 +118,7 @@ class SimpleTestDenyUpdateEntityPrivacyPolicy extends EntityPrivacyPolicy<
118
118
  SimpleTestDenyUpdateEntity
119
119
  >(),
120
120
  ];
121
- protected readonly updateRules = [
121
+ protected override readonly updateRules = [
122
122
  new AlwaysDenyPrivacyPolicyRule<
123
123
  TestEntityFields,
124
124
  string,
@@ -126,7 +126,7 @@ class SimpleTestDenyUpdateEntityPrivacyPolicy extends EntityPrivacyPolicy<
126
126
  SimpleTestDenyUpdateEntity
127
127
  >(),
128
128
  ];
129
- protected readonly deleteRules = [
129
+ protected override readonly deleteRules = [
130
130
  new AlwaysAllowPrivacyPolicyRule<
131
131
  TestEntityFields,
132
132
  string,
@@ -142,7 +142,7 @@ class SimpleTestDenyDeleteEntityPrivacyPolicy extends EntityPrivacyPolicy<
142
142
  ViewerContext,
143
143
  SimpleTestDenyDeleteEntity
144
144
  > {
145
- protected readonly readRules = [
145
+ protected override readonly readRules = [
146
146
  new AlwaysAllowPrivacyPolicyRule<
147
147
  TestEntityFields,
148
148
  string,
@@ -150,7 +150,7 @@ class SimpleTestDenyDeleteEntityPrivacyPolicy extends EntityPrivacyPolicy<
150
150
  SimpleTestDenyDeleteEntity
151
151
  >(),
152
152
  ];
153
- protected readonly createRules = [
153
+ protected override readonly createRules = [
154
154
  new AlwaysAllowPrivacyPolicyRule<
155
155
  TestEntityFields,
156
156
  string,
@@ -158,7 +158,7 @@ class SimpleTestDenyDeleteEntityPrivacyPolicy extends EntityPrivacyPolicy<
158
158
  SimpleTestDenyDeleteEntity
159
159
  >(),
160
160
  ];
161
- protected readonly updateRules = [
161
+ protected override readonly updateRules = [
162
162
  new AlwaysAllowPrivacyPolicyRule<
163
163
  TestEntityFields,
164
164
  string,
@@ -166,7 +166,7 @@ class SimpleTestDenyDeleteEntityPrivacyPolicy extends EntityPrivacyPolicy<
166
166
  SimpleTestDenyDeleteEntity
167
167
  >(),
168
168
  ];
169
- protected readonly deleteRules = [
169
+ protected override readonly deleteRules = [
170
170
  new AlwaysDenyPrivacyPolicyRule<
171
171
  TestEntityFields,
172
172
  string,
@@ -66,19 +66,19 @@ class BlahEntityPrivacyPolicy extends EntityPrivacyPolicy<
66
66
  ViewerContext,
67
67
  BlahEntity
68
68
  > {
69
- protected readonly createRules = [
69
+ protected override readonly createRules = [
70
70
  new DenyIfNotOwnerPrivacyPolicyRule(),
71
71
  new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
72
72
  ];
73
- protected readonly readRules = [
73
+ protected override readonly readRules = [
74
74
  new DenyIfNotOwnerPrivacyPolicyRule(),
75
75
  new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
76
76
  ];
77
- protected readonly updateRules = [
77
+ protected override readonly updateRules = [
78
78
  new DenyIfNotOwnerPrivacyPolicyRule(),
79
79
  new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
80
80
  ];
81
- protected readonly deleteRules = [
81
+ protected override readonly deleteRules = [
82
82
  new AlwaysDenyPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
83
83
  ];
84
84
  }
@@ -1,8 +1,12 @@
1
1
  import Entity from '../Entity';
2
2
  import { EntityCompanionDefinition } from '../EntityCompanionProvider';
3
3
  import EntityConfiguration from '../EntityConfiguration';
4
- import { UUIDField, EntityEdgeDeletionBehavior } from '../EntityFields';
4
+ import { EntityEdgeDeletionBehavior } from '../EntityFieldDefinition';
5
+ import { UUIDField } from '../EntityFields';
6
+ import { EntityTriggerMutationInfo, EntityMutationType } from '../EntityMutationInfo';
7
+ import { EntityMutationTrigger } from '../EntityMutationTriggerConfiguration';
5
8
  import EntityPrivacyPolicy from '../EntityPrivacyPolicy';
9
+ import { EntityTransactionalQueryContext } from '../EntityQueryContext';
6
10
  import { CacheStatus } from '../internal/ReadThroughEntityCache';
7
11
  import AlwaysAllowPrivacyPolicyRule from '../rules/AlwaysAllowPrivacyPolicyRule';
8
12
  import TestViewerContext from '../testfixtures/TestViewerContext';
@@ -30,22 +34,141 @@ class TestEntityPrivacyPolicy extends EntityPrivacyPolicy<
30
34
  any,
31
35
  any
32
36
  > {
33
- protected readonly readRules = [
37
+ protected override readonly readRules = [
34
38
  new AlwaysAllowPrivacyPolicyRule<any, string, TestViewerContext, any, any>(),
35
39
  ];
36
- protected readonly createRules = [
40
+ protected override readonly createRules = [
37
41
  new AlwaysAllowPrivacyPolicyRule<any, string, TestViewerContext, any, any>(),
38
42
  ];
39
- protected readonly updateRules = [
43
+ protected override readonly updateRules = [
40
44
  new AlwaysAllowPrivacyPolicyRule<any, string, TestViewerContext, any, any>(),
41
45
  ];
42
- protected readonly deleteRules = [
46
+ protected override readonly deleteRules = [
43
47
  new AlwaysAllowPrivacyPolicyRule<any, string, TestViewerContext, any, any>(),
44
48
  ];
45
49
  }
46
50
 
47
51
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
48
52
  const makeEntityClasses = (edgeDeletionBehavior: EntityEdgeDeletionBehavior) => {
53
+ const triggerExecutionCounts = {
54
+ parent: 0,
55
+ child: 0,
56
+ grandchild: 0,
57
+ };
58
+
59
+ class ParentCheckInfoTrigger extends EntityMutationTrigger<
60
+ ParentFields,
61
+ string,
62
+ TestViewerContext,
63
+ ParentEntity
64
+ > {
65
+ async executeAsync(
66
+ _viewerContext: TestViewerContext,
67
+ _queryContext: EntityTransactionalQueryContext,
68
+ _entity: ParentEntity,
69
+ mutationInfo: EntityTriggerMutationInfo<ParentFields, string, TestViewerContext, ParentEntity>
70
+ ): Promise<void> {
71
+ if (mutationInfo.type !== EntityMutationType.DELETE) {
72
+ return;
73
+ }
74
+
75
+ if (mutationInfo.cascadingDeleteCause !== null) {
76
+ throw new Error('Parent entity should not have casade delete cause');
77
+ }
78
+
79
+ triggerExecutionCounts.parent++;
80
+ }
81
+ }
82
+
83
+ class ChildCheckInfoTrigger extends EntityMutationTrigger<
84
+ ChildFields,
85
+ string,
86
+ TestViewerContext,
87
+ ChildEntity
88
+ > {
89
+ async executeAsync(
90
+ _viewerContext: TestViewerContext,
91
+ _queryContext: EntityTransactionalQueryContext,
92
+ _entity: ChildEntity,
93
+ mutationInfo: EntityTriggerMutationInfo<ChildFields, string, TestViewerContext, ChildEntity>
94
+ ): Promise<void> {
95
+ if (mutationInfo.type !== EntityMutationType.DELETE) {
96
+ return;
97
+ }
98
+
99
+ if (mutationInfo.cascadingDeleteCause === null) {
100
+ throw new Error('Child entity should have casade delete cause');
101
+ }
102
+
103
+ const cascadingDeleteCauseEntity = mutationInfo.cascadingDeleteCause.entity;
104
+ if (!(cascadingDeleteCauseEntity instanceof ParentEntity)) {
105
+ throw new Error('Child entity should have casade delete cause entity of type ParentEntity');
106
+ }
107
+
108
+ const secondLevelCascadingDeleteCause =
109
+ mutationInfo.cascadingDeleteCause.cascadingDeleteCause;
110
+ if (secondLevelCascadingDeleteCause) {
111
+ throw new Error('Child entity should not have two-level casade delete cause');
112
+ }
113
+
114
+ triggerExecutionCounts.child++;
115
+ }
116
+ }
117
+
118
+ class GrandChildCheckInfoTrigger extends EntityMutationTrigger<
119
+ GrandChildFields,
120
+ string,
121
+ TestViewerContext,
122
+ GrandChildEntity
123
+ > {
124
+ async executeAsync(
125
+ _viewerContext: TestViewerContext,
126
+ _queryContext: EntityTransactionalQueryContext,
127
+ _entity: GrandChildEntity,
128
+ mutationInfo: EntityTriggerMutationInfo<
129
+ GrandChildFields,
130
+ string,
131
+ TestViewerContext,
132
+ GrandChildEntity
133
+ >
134
+ ): Promise<void> {
135
+ if (mutationInfo.type !== EntityMutationType.DELETE) {
136
+ return;
137
+ }
138
+
139
+ if (mutationInfo.cascadingDeleteCause === null) {
140
+ throw new Error('GrandChild entity should have cascade delete cause');
141
+ }
142
+
143
+ const cascadingDeleteCauseEntity = mutationInfo.cascadingDeleteCause.entity;
144
+ if (!(cascadingDeleteCauseEntity instanceof ChildEntity)) {
145
+ throw new Error(
146
+ 'GrandChild entity should have cascade delete cause entity of type ChildEntity'
147
+ );
148
+ }
149
+
150
+ const secondLevelCascadingDeleteCause =
151
+ mutationInfo.cascadingDeleteCause.cascadingDeleteCause;
152
+ if (!secondLevelCascadingDeleteCause) {
153
+ throw new Error('GrandChild entity should have two-level casade delete cause');
154
+ }
155
+
156
+ const secondLevelCascadingDeleteCauseEntity = secondLevelCascadingDeleteCause.entity;
157
+ if (!(secondLevelCascadingDeleteCauseEntity instanceof ParentEntity)) {
158
+ throw new Error(
159
+ 'GrandChild entity should have second level casade delete cause entity of type ParentEntity'
160
+ );
161
+ }
162
+
163
+ const thirdLevelCascadingDeleteCause = secondLevelCascadingDeleteCause.cascadingDeleteCause;
164
+ if (thirdLevelCascadingDeleteCause) {
165
+ throw new Error('GrandChild entity should not have three-level casade delete cause');
166
+ }
167
+
168
+ triggerExecutionCounts.grandchild++;
169
+ }
170
+ }
171
+
49
172
  class ParentEntity extends Entity<ParentFields, string, TestViewerContext> {
50
173
  static getCompanionDefinition(): EntityCompanionDefinition<
51
174
  ParentFields,
@@ -143,33 +266,45 @@ const makeEntityClasses = (edgeDeletionBehavior: EntityEdgeDeletionBehavior) =>
143
266
  entityClass: ParentEntity,
144
267
  entityConfiguration: parentEntityConfiguration,
145
268
  privacyPolicyClass: TestEntityPrivacyPolicy,
269
+ mutationTriggers: () => ({
270
+ beforeDelete: [new ParentCheckInfoTrigger()],
271
+ afterDelete: [new ParentCheckInfoTrigger()],
272
+ }),
146
273
  });
147
274
 
148
275
  const childEntityCompanion = new EntityCompanionDefinition({
149
276
  entityClass: ChildEntity,
150
277
  entityConfiguration: childEntityConfiguration,
151
278
  privacyPolicyClass: TestEntityPrivacyPolicy,
279
+ mutationTriggers: () => ({
280
+ beforeDelete: [new ChildCheckInfoTrigger()],
281
+ afterDelete: [new ChildCheckInfoTrigger()],
282
+ }),
152
283
  });
153
284
 
154
285
  const grandChildEntityCompanion = new EntityCompanionDefinition({
155
286
  entityClass: GrandChildEntity,
156
287
  entityConfiguration: grandChildEntityConfiguration,
157
288
  privacyPolicyClass: TestEntityPrivacyPolicy,
289
+ mutationTriggers: () => ({
290
+ beforeDelete: [new GrandChildCheckInfoTrigger()],
291
+ afterDelete: [new GrandChildCheckInfoTrigger()],
292
+ }),
158
293
  });
159
294
 
160
295
  return {
161
296
  ParentEntity,
162
297
  ChildEntity,
163
298
  GrandChildEntity,
299
+ triggerExecutionCounts,
164
300
  };
165
301
  };
166
302
 
167
303
  describe('EntityMutator.processEntityDeletionForInboundEdgesAsync', () => {
168
304
  describe('EntityEdgeDeletionBehavior.CASCADE_DELETE', () => {
169
305
  it('deletes', async () => {
170
- const { ParentEntity, ChildEntity, GrandChildEntity } = makeEntityClasses(
171
- EntityEdgeDeletionBehavior.CASCADE_DELETE
172
- );
306
+ const { ParentEntity, ChildEntity, GrandChildEntity, triggerExecutionCounts } =
307
+ makeEntityClasses(EntityEdgeDeletionBehavior.CASCADE_DELETE);
173
308
  const companionProvider = createUnitTestEntityCompanionProvider();
174
309
  const viewerContext = new TestViewerContext(companionProvider);
175
310
 
@@ -202,14 +337,20 @@ describe('EntityMutator.processEntityDeletionForInboundEdgesAsync', () => {
202
337
  await expect(
203
338
  GrandChildEntity.loader(viewerContext).enforcing().loadByIDNullableAsync(grandchild.getID())
204
339
  ).resolves.toBeNull();
340
+
341
+ // two calls for each trigger, one beforeDelete, one afterDelete
342
+ expect(triggerExecutionCounts).toMatchObject({
343
+ parent: 2,
344
+ child: 2,
345
+ grandchild: 2,
346
+ });
205
347
  });
206
348
  });
207
349
 
208
350
  describe('EntityEdgeDeletionBehavior.SET_NULL', () => {
209
351
  it('sets null', async () => {
210
- const { ParentEntity, ChildEntity, GrandChildEntity } = makeEntityClasses(
211
- EntityEdgeDeletionBehavior.SET_NULL
212
- );
352
+ const { ParentEntity, ChildEntity, GrandChildEntity, triggerExecutionCounts } =
353
+ makeEntityClasses(EntityEdgeDeletionBehavior.SET_NULL);
213
354
 
214
355
  const companionProvider = createUnitTestEntityCompanionProvider();
215
356
  const viewerContext = new TestViewerContext(companionProvider);
@@ -247,14 +388,21 @@ describe('EntityMutator.processEntityDeletionForInboundEdgesAsync', () => {
247
388
  .enforcing()
248
389
  .loadByIDAsync(grandchild.getID());
249
390
  expect(loadedGrandchild.getField('parent_id')).toEqual(loadedChild.getID());
391
+
392
+ // two calls for only parent trigger, one beforeDelete, one afterDelete
393
+ // when using set null the descendants aren't deleted
394
+ expect(triggerExecutionCounts).toMatchObject({
395
+ parent: 2,
396
+ child: 0,
397
+ grandchild: 0,
398
+ });
250
399
  });
251
400
  });
252
401
 
253
402
  describe('EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE', () => {
254
403
  it('invalidates the cache', async () => {
255
- const { ParentEntity, ChildEntity, GrandChildEntity } = makeEntityClasses(
256
- EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE
257
- );
404
+ const { ParentEntity, ChildEntity, GrandChildEntity, triggerExecutionCounts } =
405
+ makeEntityClasses(EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE);
258
406
 
259
407
  const companionProvider = createUnitTestEntityCompanionProvider();
260
408
  const viewerContext = new TestViewerContext(companionProvider);
@@ -318,6 +466,13 @@ describe('EntityMutator.processEntityDeletionForInboundEdgesAsync', () => {
318
466
  await expect(
319
467
  GrandChildEntity.loader(viewerContext).enforcing().loadByIDNullableAsync(grandchild.getID())
320
468
  ).resolves.not.toBeNull();
469
+
470
+ // two calls for each trigger, one beforeDelete, one afterDelete
471
+ expect(triggerExecutionCounts).toMatchObject({
472
+ parent: 2,
473
+ child: 2,
474
+ grandchild: 2,
475
+ });
321
476
  });
322
477
  });
323
478
  });
@@ -1,18 +1,20 @@
1
1
  import { v1 as uuidv1, v3 as uuidv3, v4 as uuidv4, v5 as uuidv5 } from 'uuid';
2
2
 
3
+ import { EntityFieldDefinition } from '../EntityFieldDefinition';
3
4
  import {
4
- EntityFieldDefinition,
5
5
  StringField,
6
6
  UUIDField,
7
7
  DateField,
8
8
  BooleanField,
9
- NumberField,
9
+ IntField,
10
+ FloatField,
10
11
  StringArrayField,
11
12
  JSONObjectField,
12
13
  EnumField,
13
14
  JSONArrayField,
14
15
  MaybeJSONArrayField,
15
16
  } from '../EntityFields';
17
+ import describeFieldTestCase from '../utils/testing/describeFieldTestCase';
16
18
 
17
19
  class TestFieldDefinition extends EntityFieldDefinition<string> {
18
20
  protected validateInputValueInternal(value: string): boolean {
@@ -52,26 +54,6 @@ describe(EntityFieldDefinition, () => {
52
54
  });
53
55
  });
54
56
 
55
- const describeFieldTestCase = <T>(
56
- fieldDefinition: EntityFieldDefinition<T>,
57
- validValues: T[],
58
- invalidValues: any[]
59
- ): void => {
60
- describe(fieldDefinition.constructor.name, () => {
61
- if (validValues.length > 0) {
62
- test.each(validValues)(`${fieldDefinition.constructor.name}.valid %p`, (value) => {
63
- expect(fieldDefinition.validateInputValue(value)).toBe(true);
64
- });
65
- }
66
-
67
- if (invalidValues.length > 0) {
68
- test.each(invalidValues)(`${fieldDefinition.constructor.name}.invalid %p`, (value) => {
69
- expect(fieldDefinition.validateInputValue(value)).toBe(false);
70
- });
71
- }
72
- });
73
- };
74
-
75
57
  describeFieldTestCase(new StringField({ columnName: 'wat' }), ['hello', ''], [1, true, {}, [[]]]);
76
58
  describeFieldTestCase(
77
59
  new UUIDField({ columnName: 'wat' }),
@@ -80,7 +62,8 @@ describeFieldTestCase(
80
62
  );
81
63
  describeFieldTestCase(new DateField({ columnName: 'wat' }), [new Date()], [Date.now()]);
82
64
  describeFieldTestCase(new BooleanField({ columnName: 'wat' }), [true, false], [0, 1, '']);
83
- describeFieldTestCase(new NumberField({ columnName: 'wat' }), [1, 0.5, -0.5], ['1']);
65
+ describeFieldTestCase(new IntField({ columnName: 'wat' }), [1], ['1', 0.5, true]);
66
+ describeFieldTestCase(new FloatField({ columnName: 'wat' }), [1, 0.5, -0.5], ['1']);
84
67
  describeFieldTestCase(
85
68
  new StringArrayField({ columnName: 'wat' }),
86
69
  [[['what']] as any, [[]] as any], // jest test cases need extra wrapping array
@@ -0,0 +1,177 @@
1
+ import { instance, mock } from 'ts-mockito';
2
+
3
+ import Entity from '../Entity';
4
+ import { EntityCompanionDefinition } from '../EntityCompanionProvider';
5
+ import EntityConfiguration from '../EntityConfiguration';
6
+ import { StringField } from '../EntityFields';
7
+ import EntityLoader from '../EntityLoader';
8
+ import EntityPrivacyPolicy from '../EntityPrivacyPolicy';
9
+ import ViewerContext from '../ViewerContext';
10
+ import EntityDataManager from '../internal/EntityDataManager';
11
+ import ReadThroughEntityCache from '../internal/ReadThroughEntityCache';
12
+ import IEntityMetricsAdapter from '../metrics/IEntityMetricsAdapter';
13
+ import AlwaysAllowPrivacyPolicyRule from '../rules/AlwaysAllowPrivacyPolicyRule';
14
+ import { NoCacheStubCacheAdapterProvider } from '../utils/testing/StubCacheAdapter';
15
+ import StubDatabaseAdapter from '../utils/testing/StubDatabaseAdapter';
16
+ import StubQueryContextProvider from '../utils/testing/StubQueryContextProvider';
17
+
18
+ export type TestFields = {
19
+ id: string;
20
+ };
21
+
22
+ export type TestFieldSelection = keyof TestFields;
23
+
24
+ export const testEntityConfiguration = new EntityConfiguration<TestFields>({
25
+ idField: 'id',
26
+ tableName: 'test_entity_should_not_write_to_db',
27
+ schema: {
28
+ id: new StringField({
29
+ columnName: 'id',
30
+ }),
31
+ },
32
+ databaseAdapterFlavor: 'postgres',
33
+ cacheAdapterFlavor: 'redis',
34
+ });
35
+
36
+ export class TestEntityPrivacyPolicy extends EntityPrivacyPolicy<
37
+ TestFields,
38
+ string,
39
+ ViewerContext,
40
+ TestEntity,
41
+ TestFieldSelection
42
+ > {
43
+ protected override readonly readRules = [
44
+ new AlwaysAllowPrivacyPolicyRule<
45
+ TestFields,
46
+ string,
47
+ ViewerContext,
48
+ TestEntity,
49
+ TestFieldSelection
50
+ >(),
51
+ ];
52
+ protected override readonly createRules = [
53
+ new AlwaysAllowPrivacyPolicyRule<
54
+ TestFields,
55
+ string,
56
+ ViewerContext,
57
+ TestEntity,
58
+ TestFieldSelection
59
+ >(),
60
+ ];
61
+ protected override readonly updateRules = [
62
+ new AlwaysAllowPrivacyPolicyRule<
63
+ TestFields,
64
+ string,
65
+ ViewerContext,
66
+ TestEntity,
67
+ TestFieldSelection
68
+ >(),
69
+ ];
70
+ protected override readonly deleteRules = [
71
+ new AlwaysAllowPrivacyPolicyRule<
72
+ TestFields,
73
+ string,
74
+ ViewerContext,
75
+ TestEntity,
76
+ TestFieldSelection
77
+ >(),
78
+ ];
79
+ }
80
+
81
+ const ID_SENTINEL_THROW_LITERAL = 'throw_literal';
82
+ const ID_SENTINEL_THROW_ERROR = 'throw_error';
83
+
84
+ export default class TestEntity extends Entity<
85
+ TestFields,
86
+ string,
87
+ ViewerContext,
88
+ TestFieldSelection
89
+ > {
90
+ constructor(viewerContext: ViewerContext, rawFields: Readonly<TestFields>) {
91
+ if (rawFields.id === ID_SENTINEL_THROW_LITERAL) {
92
+ // eslint-disable-next-line no-throw-literal,@typescript-eslint/no-throw-literal
93
+ throw 'hello';
94
+ } else if (rawFields.id === ID_SENTINEL_THROW_ERROR) {
95
+ throw new Error('world');
96
+ }
97
+ super(viewerContext, rawFields);
98
+ }
99
+
100
+ static getCompanionDefinition(): EntityCompanionDefinition<
101
+ TestFields,
102
+ string,
103
+ ViewerContext,
104
+ TestEntity,
105
+ TestEntityPrivacyPolicy,
106
+ TestFieldSelection
107
+ > {
108
+ return testEntityCompanion;
109
+ }
110
+ }
111
+
112
+ export const testEntityCompanion = new EntityCompanionDefinition({
113
+ entityClass: TestEntity,
114
+ entityConfiguration: testEntityConfiguration,
115
+ privacyPolicyClass: TestEntityPrivacyPolicy,
116
+ });
117
+
118
+ describe(EntityLoader, () => {
119
+ it('handles thrown errors and literals from constructor', async () => {
120
+ const viewerContext = instance(mock(ViewerContext));
121
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
122
+ const queryContext = StubQueryContextProvider.getQueryContext();
123
+
124
+ const databaseAdapter = new StubDatabaseAdapter<TestFields>(
125
+ testEntityConfiguration,
126
+ StubDatabaseAdapter.convertFieldObjectsToDataStore(
127
+ testEntityConfiguration,
128
+ new Map([
129
+ [
130
+ testEntityConfiguration.tableName,
131
+ [
132
+ {
133
+ id: ID_SENTINEL_THROW_LITERAL,
134
+ },
135
+ {
136
+ id: ID_SENTINEL_THROW_ERROR,
137
+ },
138
+ ],
139
+ ],
140
+ ])
141
+ )
142
+ );
143
+ const privacyPolicy = new TestEntityPrivacyPolicy();
144
+ const cacheAdapterProvider = new NoCacheStubCacheAdapterProvider();
145
+ const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
146
+ const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
147
+ const dataManager = new EntityDataManager(
148
+ databaseAdapter,
149
+ entityCache,
150
+ StubQueryContextProvider,
151
+ metricsAdapter,
152
+ TestEntity.name
153
+ );
154
+ const entityLoader = new EntityLoader(
155
+ viewerContext,
156
+ queryContext,
157
+ testEntityConfiguration,
158
+ TestEntity,
159
+ privacyPolicy,
160
+ dataManager,
161
+ metricsAdapter
162
+ );
163
+
164
+ let capturedThrownThing1: any;
165
+ try {
166
+ await entityLoader.loadByIDAsync(ID_SENTINEL_THROW_LITERAL);
167
+ } catch (e) {
168
+ capturedThrownThing1 = e;
169
+ }
170
+ expect(capturedThrownThing1).not.toBeInstanceOf(Error);
171
+ expect(capturedThrownThing1).toEqual('hello');
172
+
173
+ const result = await entityLoader.loadByIDAsync(ID_SENTINEL_THROW_ERROR);
174
+ expect(result.ok).toBe(false);
175
+ expect(result.enforceError().message).toEqual('world');
176
+ });
177
+ });