@expo/entity 0.35.0 → 0.36.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 (309) hide show
  1. package/build/AuthorizationResultBasedEntityLoader.d.ts +128 -0
  2. package/build/AuthorizationResultBasedEntityLoader.js +196 -0
  3. package/build/AuthorizationResultBasedEntityLoader.js.map +1 -0
  4. package/build/ComposedEntityCacheAdapter.js +1 -0
  5. package/build/ComposedEntityCacheAdapter.js.map +1 -1
  6. package/build/ComposedSecondaryEntityCache.js +1 -0
  7. package/build/ComposedSecondaryEntityCache.js.map +1 -1
  8. package/build/EnforcingEntityLoader.d.ts +5 -4
  9. package/build/EnforcingEntityLoader.js +4 -2
  10. package/build/EnforcingEntityLoader.js.map +1 -1
  11. package/build/Entity.d.ts +0 -27
  12. package/build/Entity.js +0 -50
  13. package/build/Entity.js.map +1 -1
  14. package/build/EntityAssociationLoader.d.ts +1 -1
  15. package/build/EntityAssociationLoader.js +17 -8
  16. package/build/EntityAssociationLoader.js.map +1 -1
  17. package/build/EntityCompanion.js +9 -1
  18. package/build/EntityCompanion.js.map +1 -1
  19. package/build/EntityCompanionProvider.d.ts +3 -1
  20. package/build/EntityCompanionProvider.js +10 -4
  21. package/build/EntityCompanionProvider.js.map +1 -1
  22. package/build/EntityConfiguration.d.ts +2 -1
  23. package/build/EntityConfiguration.js +19 -1
  24. package/build/EntityConfiguration.js.map +1 -1
  25. package/build/EntityDatabaseAdapter.d.ts +2 -2
  26. package/build/EntityDatabaseAdapter.js +5 -3
  27. package/build/EntityDatabaseAdapter.js.map +1 -1
  28. package/build/EntityFieldDefinition.d.ts +21 -10
  29. package/build/EntityFieldDefinition.js +8 -9
  30. package/build/EntityFieldDefinition.js.map +1 -1
  31. package/build/EntityFields.d.ts +10 -0
  32. package/build/EntityFields.js +15 -1
  33. package/build/EntityFields.js.map +1 -1
  34. package/build/EntityLoader.d.ts +12 -125
  35. package/build/EntityLoader.js +24 -239
  36. package/build/EntityLoader.js.map +1 -1
  37. package/build/EntityLoaderFactory.d.ts +1 -1
  38. package/build/EntityLoaderFactory.js +3 -0
  39. package/build/EntityLoaderFactory.js.map +1 -1
  40. package/build/EntityLoaderUtils.d.ts +58 -0
  41. package/build/EntityLoaderUtils.js +109 -0
  42. package/build/EntityLoaderUtils.js.map +1 -0
  43. package/build/EntityMutator.d.ts +1 -0
  44. package/build/EntityMutator.js +71 -56
  45. package/build/EntityMutator.js.map +1 -1
  46. package/build/EntityMutatorFactory.js +9 -0
  47. package/build/EntityMutatorFactory.js.map +1 -1
  48. package/build/EntityPrivacyPolicy.d.ts +11 -5
  49. package/build/EntityPrivacyPolicy.js +5 -7
  50. package/build/EntityPrivacyPolicy.js.map +1 -1
  51. package/build/EntityQueryContext.d.ts +2 -1
  52. package/build/EntityQueryContext.js +11 -6
  53. package/build/EntityQueryContext.js.map +1 -1
  54. package/build/EntitySecondaryCacheLoader.js +5 -1
  55. package/build/EntitySecondaryCacheLoader.js.map +1 -1
  56. package/build/GenericEntityCacheAdapter.js +1 -0
  57. package/build/GenericEntityCacheAdapter.js.map +1 -1
  58. package/build/GenericSecondaryEntityCache.js +2 -0
  59. package/build/GenericSecondaryEntityCache.js.map +1 -1
  60. package/build/IEntityCacheAdapterProvider.d.ts +1 -1
  61. package/build/IEntityDatabaseAdapterProvider.d.ts +1 -1
  62. package/build/ReadonlyEntity.js +5 -1
  63. package/build/ReadonlyEntity.js.map +1 -1
  64. package/build/ViewerContext.js +2 -0
  65. package/build/ViewerContext.js.map +1 -1
  66. package/build/ViewerScopedEntityCompanion.js +2 -0
  67. package/build/ViewerScopedEntityCompanion.js.map +1 -1
  68. package/build/ViewerScopedEntityCompanionProvider.d.ts +0 -1
  69. package/build/ViewerScopedEntityCompanionProvider.js +2 -1
  70. package/build/ViewerScopedEntityCompanionProvider.js.map +1 -1
  71. package/build/ViewerScopedEntityLoaderFactory.d.ts +1 -1
  72. package/build/ViewerScopedEntityLoaderFactory.js +2 -0
  73. package/build/ViewerScopedEntityLoaderFactory.js.map +1 -1
  74. package/build/ViewerScopedEntityMutatorFactory.js +2 -0
  75. package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
  76. package/build/__tests__/ComposedCacheAdapter-test.js +2 -0
  77. package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -1
  78. package/build/__tests__/ComposedSecondaryEntityCache-test.js +1 -0
  79. package/build/__tests__/ComposedSecondaryEntityCache-test.js.map +1 -1
  80. package/build/__tests__/EnforcingEntityLoader-test.js +101 -113
  81. package/build/__tests__/EnforcingEntityLoader-test.js.map +1 -1
  82. package/build/__tests__/Entity-test.js +0 -132
  83. package/build/__tests__/Entity-test.js.map +1 -1
  84. package/build/__tests__/EntityAssociationLoader-test.js +6 -2
  85. package/build/__tests__/EntityAssociationLoader-test.js.map +1 -1
  86. package/build/__tests__/EntityCommonUseCases-test.js +24 -22
  87. package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
  88. package/build/__tests__/EntityCompanion-test.js +26 -3
  89. package/build/__tests__/EntityCompanion-test.js.map +1 -1
  90. package/build/__tests__/EntityConfiguration-test.js +103 -0
  91. package/build/__tests__/EntityConfiguration-test.js.map +1 -0
  92. package/build/__tests__/EntityDatabaseAdapter-test.js +6 -0
  93. package/build/__tests__/EntityDatabaseAdapter-test.js.map +1 -1
  94. package/build/__tests__/EntityEdges-test.js +61 -20
  95. package/build/__tests__/EntityEdges-test.js.map +1 -1
  96. package/build/__tests__/EntityFields-test.js +6 -0
  97. package/build/__tests__/EntityFields-test.js.map +1 -1
  98. package/build/__tests__/EntityLoader-constructor-test.js +16 -17
  99. package/build/__tests__/EntityLoader-constructor-test.js.map +1 -1
  100. package/build/__tests__/EntityLoader-test.js +74 -22
  101. package/build/__tests__/EntityLoader-test.js.map +1 -1
  102. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js +12 -15
  103. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js.map +1 -1
  104. package/build/__tests__/EntityMutator-test.js +54 -9
  105. package/build/__tests__/EntityMutator-test.js.map +1 -1
  106. package/build/__tests__/EntityPrivacyPolicy-test.js +77 -59
  107. package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
  108. package/build/__tests__/EntityQueryContext-test.js +9 -0
  109. package/build/__tests__/EntityQueryContext-test.js.map +1 -1
  110. package/build/__tests__/EntitySelfReferentialEdges-test.js +42 -25
  111. package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
  112. package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js.map +1 -1
  113. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js +20 -18
  114. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js.map +1 -1
  115. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js +12 -15
  116. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js.map +1 -1
  117. package/build/entityUtils.d.ts +1 -1
  118. package/build/entityUtils.js.map +1 -1
  119. package/build/errors/EntityCacheAdapterError.js +2 -5
  120. package/build/errors/EntityCacheAdapterError.js.map +1 -1
  121. package/build/errors/EntityDatabaseAdapterError.js +14 -35
  122. package/build/errors/EntityDatabaseAdapterError.js.map +1 -1
  123. package/build/errors/EntityError.js +1 -0
  124. package/build/errors/EntityError.js.map +1 -1
  125. package/build/errors/EntityInvalidFieldValueError.js +2 -2
  126. package/build/errors/EntityInvalidFieldValueError.js.map +1 -1
  127. package/build/errors/EntityNotAuthorizedError.js +3 -2
  128. package/build/errors/EntityNotAuthorizedError.js.map +1 -1
  129. package/build/errors/EntityNotFoundError.js +2 -2
  130. package/build/errors/EntityNotFoundError.js.map +1 -1
  131. package/build/index.d.ts +1 -0
  132. package/build/index.js +1 -0
  133. package/build/index.js.map +1 -1
  134. package/build/internal/EntityDataManager.d.ts +1 -1
  135. package/build/internal/EntityDataManager.js +6 -1
  136. package/build/internal/EntityDataManager.js.map +1 -1
  137. package/build/internal/EntityFieldTransformationUtils.d.ts +5 -5
  138. package/build/internal/EntityFieldTransformationUtils.js +5 -8
  139. package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
  140. package/build/internal/EntityTableDataCoordinator.d.ts +1 -1
  141. package/build/internal/EntityTableDataCoordinator.js +5 -0
  142. package/build/internal/EntityTableDataCoordinator.js.map +1 -1
  143. package/build/internal/ReadThroughEntityCache.d.ts +1 -1
  144. package/build/internal/ReadThroughEntityCache.js +2 -0
  145. package/build/internal/ReadThroughEntityCache.js.map +1 -1
  146. package/build/internal/__tests__/EntityFieldTransformationUtils-test.js +6 -2
  147. package/build/internal/__tests__/EntityFieldTransformationUtils-test.js.map +1 -1
  148. package/build/internal/__tests__/ReadThroughEntityCache-test.js +33 -0
  149. package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
  150. package/build/metrics/IEntityMetricsAdapter.d.ts +1 -1
  151. package/build/rules/AlwaysAllowPrivacyPolicyRule.d.ts +1 -1
  152. package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
  153. package/build/rules/AlwaysDenyPrivacyPolicyRule.d.ts +1 -1
  154. package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
  155. package/build/rules/AlwaysSkipPrivacyPolicyRule.d.ts +1 -1
  156. package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
  157. package/build/rules/PrivacyPolicyRule.d.ts +1 -1
  158. package/build/rules/PrivacyPolicyRule.js.map +1 -1
  159. package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js.map +1 -1
  160. package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js.map +1 -1
  161. package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js.map +1 -1
  162. package/build/testfixtures/DateIDTestEntity.js +12 -15
  163. package/build/testfixtures/DateIDTestEntity.js.map +1 -1
  164. package/build/testfixtures/SimpleTestEntity.js +12 -15
  165. package/build/testfixtures/SimpleTestEntity.js.map +1 -1
  166. package/build/testfixtures/TestEntity.js +12 -15
  167. package/build/testfixtures/TestEntity.js.map +1 -1
  168. package/build/testfixtures/TestEntity2.js +12 -15
  169. package/build/testfixtures/TestEntity2.js.map +1 -1
  170. package/build/testfixtures/TestEntityNumberKey.js +12 -15
  171. package/build/testfixtures/TestEntityNumberKey.js.map +1 -1
  172. package/build/testfixtures/TestEntityWithMutationTriggers.d.ts +36 -0
  173. package/build/testfixtures/TestEntityWithMutationTriggers.js +82 -0
  174. package/build/testfixtures/TestEntityWithMutationTriggers.js.map +1 -0
  175. package/build/utils/EntityPrivacyUtils.d.ts +34 -0
  176. package/build/utils/EntityPrivacyUtils.js +160 -0
  177. package/build/utils/EntityPrivacyUtils.js.map +1 -0
  178. package/build/utils/__tests__/EntityPrivacyUtils-test.d.ts +1 -0
  179. package/build/utils/__tests__/EntityPrivacyUtils-test.js +395 -0
  180. package/build/utils/__tests__/EntityPrivacyUtils-test.js.map +1 -0
  181. package/build/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.d.ts +1 -0
  182. package/build/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.js +26 -0
  183. package/build/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.js.map +1 -0
  184. package/build/utils/collections/maps.js.map +1 -1
  185. package/build/utils/mergeEntityMutationTriggerConfigurations.d.ts +4 -0
  186. package/build/utils/mergeEntityMutationTriggerConfigurations.js +28 -0
  187. package/build/utils/mergeEntityMutationTriggerConfigurations.js.map +1 -0
  188. package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +1 -1
  189. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
  190. package/build/utils/testing/StubCacheAdapter.d.ts +3 -3
  191. package/build/utils/testing/StubCacheAdapter.js +3 -3
  192. package/build/utils/testing/StubCacheAdapter.js.map +1 -1
  193. package/build/utils/testing/StubDatabaseAdapter.d.ts +2 -2
  194. package/build/utils/testing/StubDatabaseAdapter.js +4 -2
  195. package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
  196. package/build/utils/testing/StubDatabaseAdapterProvider.d.ts +1 -1
  197. package/build/utils/testing/StubDatabaseAdapterProvider.js +1 -3
  198. package/build/utils/testing/StubDatabaseAdapterProvider.js.map +1 -1
  199. package/build/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.d.ts +1 -0
  200. package/build/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.js +42 -0
  201. package/build/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.js.map +1 -0
  202. package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js +53 -0
  203. package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js.map +1 -1
  204. package/build/utils/testing/describeFieldTestCase.js.map +1 -1
  205. package/package.json +4 -3
  206. package/src/AuthorizationResultBasedEntityLoader.ts +297 -0
  207. package/src/ComposedEntityCacheAdapter.ts +6 -6
  208. package/src/ComposedSecondaryEntityCache.ts +8 -8
  209. package/src/EnforcingEntityLoader.ts +20 -19
  210. package/src/Entity.ts +11 -126
  211. package/src/EntityAssociationLoader.ts +40 -41
  212. package/src/EntityCompanion.ts +8 -4
  213. package/src/EntityCompanionProvider.ts +24 -16
  214. package/src/EntityConfiguration.ts +18 -7
  215. package/src/EntityDatabaseAdapter.ts +41 -41
  216. package/src/EntityFieldDefinition.ts +28 -18
  217. package/src/EntityFields.ts +15 -0
  218. package/src/EntityLoader.ts +63 -357
  219. package/src/EntityLoaderFactory.ts +10 -4
  220. package/src/EntityLoaderUtils.ts +149 -0
  221. package/src/EntityMutationInfo.ts +2 -2
  222. package/src/EntityMutationTriggerConfiguration.ts +5 -5
  223. package/src/EntityMutationValidator.ts +2 -2
  224. package/src/EntityMutator.ts +146 -144
  225. package/src/EntityMutatorFactory.ts +8 -8
  226. package/src/EntityPrivacyPolicy.ts +78 -28
  227. package/src/EntityQueryContext.ts +14 -13
  228. package/src/EntityQueryContextProvider.ts +5 -5
  229. package/src/EntitySecondaryCacheLoader.ts +13 -11
  230. package/src/GenericEntityCacheAdapter.ts +10 -10
  231. package/src/GenericSecondaryEntityCache.ts +6 -6
  232. package/src/IEntityCacheAdapter.ts +4 -4
  233. package/src/IEntityCacheAdapterProvider.ts +2 -2
  234. package/src/IEntityDatabaseAdapterProvider.ts +2 -2
  235. package/src/ReadonlyEntity.ts +5 -5
  236. package/src/ViewerContext.ts +5 -5
  237. package/src/ViewerScopedEntityCompanion.ts +4 -4
  238. package/src/ViewerScopedEntityCompanionProvider.ts +4 -5
  239. package/src/ViewerScopedEntityLoaderFactory.ts +10 -4
  240. package/src/ViewerScopedEntityMutatorFactory.ts +5 -5
  241. package/src/__tests__/ComposedCacheAdapter-test.ts +12 -10
  242. package/src/__tests__/ComposedSecondaryEntityCache-test.ts +8 -8
  243. package/src/__tests__/EnforcingEntityLoader-test.ts +236 -159
  244. package/src/__tests__/Entity-test.ts +0 -202
  245. package/src/__tests__/EntityAssociationLoader-test.ts +29 -25
  246. package/src/__tests__/EntityCommonUseCases-test.ts +29 -13
  247. package/src/__tests__/EntityCompanion-test.ts +57 -5
  248. package/src/__tests__/EntityConfiguration-test.ts +118 -0
  249. package/src/__tests__/EntityDatabaseAdapter-test.ts +11 -11
  250. package/src/__tests__/EntityEdges-test.ts +108 -36
  251. package/src/__tests__/EntityFields-test.ts +14 -2
  252. package/src/__tests__/EntityLoader-constructor-test.ts +20 -7
  253. package/src/__tests__/EntityLoader-test.ts +214 -86
  254. package/src/__tests__/EntityMutator-MutationCacheConsistency-test.ts +2 -2
  255. package/src/__tests__/EntityMutator-test.ts +281 -96
  256. package/src/__tests__/EntityPrivacyPolicy-test.ts +166 -53
  257. package/src/__tests__/EntityQueryContext-test.ts +30 -12
  258. package/src/__tests__/EntitySecondaryCacheLoader-test.ts +7 -7
  259. package/src/__tests__/EntitySelfReferentialEdges-test.ts +46 -26
  260. package/src/__tests__/GenericEntityCacheAdapter-test.ts +2 -2
  261. package/src/__tests__/ViewerContext-test.ts +1 -1
  262. package/src/__tests__/ViewerScopedEntityCompanion-test.ts +2 -2
  263. package/src/__tests__/ViewerScopedEntityCompanionProvider-test.ts +2 -2
  264. package/src/__tests__/ViewerScopedEntityLoaderFactory-test.ts +2 -1
  265. package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +19 -19
  266. package/src/__tests__/entityUtils-test.ts +2 -2
  267. package/src/entityUtils.ts +4 -4
  268. package/src/errors/EntityError.ts +4 -1
  269. package/src/errors/EntityInvalidFieldValueError.ts +2 -2
  270. package/src/errors/EntityNotAuthorizedError.ts +3 -3
  271. package/src/errors/EntityNotFoundError.ts +2 -2
  272. package/src/index.ts +1 -0
  273. package/src/internal/EntityDataManager.ts +24 -24
  274. package/src/internal/EntityFieldTransformationUtils.ts +39 -32
  275. package/src/internal/EntityTableDataCoordinator.ts +3 -3
  276. package/src/internal/ReadThroughEntityCache.ts +9 -9
  277. package/src/internal/__tests__/EntityDataManager-test.ts +51 -51
  278. package/src/internal/__tests__/EntityFieldTransformationUtils-test.ts +14 -10
  279. package/src/internal/__tests__/ReadThroughEntityCache-test.ts +74 -18
  280. package/src/metrics/EntityMetricsUtils.ts +4 -4
  281. package/src/metrics/IEntityMetricsAdapter.ts +1 -1
  282. package/src/rules/AlwaysAllowPrivacyPolicyRule.ts +9 -3
  283. package/src/rules/AlwaysDenyPrivacyPolicyRule.ts +9 -3
  284. package/src/rules/AlwaysSkipPrivacyPolicyRule.ts +9 -3
  285. package/src/rules/PrivacyPolicyRule.ts +9 -3
  286. package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -1
  287. package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -1
  288. package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -1
  289. package/src/testfixtures/TestEntity.ts +1 -1
  290. package/src/testfixtures/TestEntityWithMutationTriggers.ts +156 -0
  291. package/src/utils/EntityPrivacyUtils.ts +325 -0
  292. package/src/utils/__tests__/EntityPrivacyUtils-test.ts +570 -0
  293. package/src/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.ts +29 -0
  294. package/src/utils/collections/__tests__/maps-test.ts +2 -2
  295. package/src/utils/collections/maps.ts +11 -11
  296. package/src/utils/mergeEntityMutationTriggerConfigurations.ts +44 -0
  297. package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +25 -22
  298. package/src/utils/testing/StubCacheAdapter.ts +17 -15
  299. package/src/utils/testing/StubDatabaseAdapter.ts +35 -30
  300. package/src/utils/testing/StubDatabaseAdapterProvider.ts +2 -2
  301. package/src/utils/testing/StubQueryContextProvider.ts +2 -2
  302. package/src/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.ts +42 -0
  303. package/src/utils/testing/__tests__/StubDatabaseAdapter-test.ts +111 -29
  304. package/src/utils/testing/createUnitTestEntityCompanionProvider.ts +2 -2
  305. package/src/utils/testing/describeFieldTestCase.ts +1 -1
  306. package/build/__tests__/EntityDataConfiguration-test.js +0 -68
  307. package/build/__tests__/EntityDataConfiguration-test.js.map +0 -1
  308. package/src/__tests__/EntityDataConfiguration-test.ts +0 -77
  309. /package/build/__tests__/{EntityDataConfiguration-test.d.ts → EntityConfiguration-test.d.ts} +0 -0
@@ -0,0 +1,325 @@
1
+ import { asyncResult } from '@expo/results';
2
+
3
+ import Entity, { IEntityClass } from '../Entity';
4
+ import { EntityEdgeDeletionBehavior } from '../EntityFieldDefinition';
5
+ import { EntityCascadingDeletionInfo } from '../EntityMutationInfo';
6
+ import EntityPrivacyPolicy from '../EntityPrivacyPolicy';
7
+ import { EntityQueryContext } from '../EntityQueryContext';
8
+ import ViewerContext from '../ViewerContext';
9
+ import { failedResults } from '../entityUtils';
10
+ import EntityNotAuthorizedError from '../errors/EntityNotAuthorizedError';
11
+
12
+ /**
13
+ * Check whether an entity loaded by a viewer can be updated by that same viewer.
14
+ *
15
+ * @remarks
16
+ *
17
+ * This may be useful in situations relying upon the thrown privacy policy thrown authorization error
18
+ * is insufficient for the task at hand. When dealing with purely a sequence of mutations it is easy
19
+ * to roll back all mutations given a single authorization error by wrapping them in a single transaction.
20
+ * When certain portions of a mutation cannot be rolled back transactionally (third pary calls,
21
+ * legacy code, etc), using this method can help decide whether the sequence of mutations will fail before
22
+ * attempting them. Note that if any privacy policy rules use a piece of data being updated in the mutations
23
+ * the result of this method and the update mutation itself may differ.
24
+ *
25
+ * @param entityClass - class of entity
26
+ * @param sourceEntity - entity loaded by viewer
27
+ * @param queryContext - query context in which to perform the check
28
+ */
29
+ export async function canViewerUpdateAsync<
30
+ TMFields extends object,
31
+ TMID extends NonNullable<TMFields[TMSelectedFields]>,
32
+ TMViewerContext extends ViewerContext,
33
+ TMEntity extends Entity<TMFields, TMID, TMViewerContext, TMSelectedFields>,
34
+ TMPrivacyPolicy extends EntityPrivacyPolicy<
35
+ TMFields,
36
+ TMID,
37
+ TMViewerContext,
38
+ TMEntity,
39
+ TMSelectedFields
40
+ >,
41
+ TMSelectedFields extends keyof TMFields = keyof TMFields,
42
+ >(
43
+ entityClass: IEntityClass<
44
+ TMFields,
45
+ TMID,
46
+ TMViewerContext,
47
+ TMEntity,
48
+ TMPrivacyPolicy,
49
+ TMSelectedFields
50
+ >,
51
+ sourceEntity: TMEntity,
52
+ queryContext: EntityQueryContext = sourceEntity
53
+ .getViewerContext()
54
+ .getViewerScopedEntityCompanionForClass(entityClass)
55
+ .getQueryContextProvider()
56
+ .getQueryContext(),
57
+ ): Promise<boolean> {
58
+ return await canViewerUpdateInternalAsync(
59
+ entityClass,
60
+ sourceEntity,
61
+ /* cascadingDeleteCause */ null,
62
+ queryContext,
63
+ );
64
+ }
65
+
66
+ async function canViewerUpdateInternalAsync<
67
+ TMFields extends object,
68
+ TMID extends NonNullable<TMFields[TMSelectedFields]>,
69
+ TMViewerContext extends ViewerContext,
70
+ TMEntity extends Entity<TMFields, TMID, TMViewerContext, TMSelectedFields>,
71
+ TMPrivacyPolicy extends EntityPrivacyPolicy<
72
+ TMFields,
73
+ TMID,
74
+ TMViewerContext,
75
+ TMEntity,
76
+ TMSelectedFields
77
+ >,
78
+ TMSelectedFields extends keyof TMFields = keyof TMFields,
79
+ >(
80
+ entityClass: IEntityClass<
81
+ TMFields,
82
+ TMID,
83
+ TMViewerContext,
84
+ TMEntity,
85
+ TMPrivacyPolicy,
86
+ TMSelectedFields
87
+ >,
88
+ sourceEntity: TMEntity,
89
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null,
90
+ queryContext: EntityQueryContext,
91
+ ): Promise<boolean> {
92
+ const companion = sourceEntity
93
+ .getViewerContext()
94
+ .getViewerScopedEntityCompanionForClass(entityClass);
95
+ const privacyPolicy = companion.entityCompanion.privacyPolicy;
96
+ const evaluationResult = await asyncResult(
97
+ privacyPolicy.authorizeUpdateAsync(
98
+ sourceEntity.getViewerContext(),
99
+ queryContext,
100
+ { previousValue: null, cascadingDeleteCause },
101
+ sourceEntity,
102
+ companion.getMetricsAdapter(),
103
+ ),
104
+ );
105
+ if (!evaluationResult.ok) {
106
+ if (evaluationResult.reason instanceof EntityNotAuthorizedError) {
107
+ return false;
108
+ } else {
109
+ throw evaluationResult.reason;
110
+ }
111
+ }
112
+ return evaluationResult.ok;
113
+ }
114
+
115
+ /**
116
+ * Check whether a single entity loaded by a viewer can be deleted by that same viewer.
117
+ * This recursively checks edge cascade permissions (EntityEdgeDeletionBehavior) as well.
118
+ *
119
+ * @remarks
120
+ * See remarks for canViewerUpdate.
121
+ *
122
+ * @param entityClass - class of entity
123
+ * @param sourceEntity - entity loaded by viewer
124
+ * @param queryContext - query context in which to perform the check
125
+ */
126
+ export async function canViewerDeleteAsync<
127
+ TFields extends object,
128
+ TID extends NonNullable<TFields[TSelectedFields]>,
129
+ TMViewerContext extends ViewerContext,
130
+ TEntity extends Entity<TFields, TID, TMViewerContext, TSelectedFields>,
131
+ TPrivacyPolicy extends EntityPrivacyPolicy<
132
+ TFields,
133
+ TID,
134
+ TMViewerContext,
135
+ TEntity,
136
+ TSelectedFields
137
+ >,
138
+ TSelectedFields extends keyof TFields = keyof TFields,
139
+ >(
140
+ entityClass: IEntityClass<
141
+ TFields,
142
+ TID,
143
+ TMViewerContext,
144
+ TEntity,
145
+ TPrivacyPolicy,
146
+ TSelectedFields
147
+ >,
148
+ sourceEntity: TEntity,
149
+ queryContext: EntityQueryContext = sourceEntity
150
+ .getViewerContext()
151
+ .getViewerScopedEntityCompanionForClass(entityClass)
152
+ .getQueryContextProvider()
153
+ .getQueryContext(),
154
+ ): Promise<boolean> {
155
+ return await canViewerDeleteInternalAsync(
156
+ entityClass,
157
+ sourceEntity,
158
+ /* cascadingDeleteCause */ null,
159
+ queryContext,
160
+ );
161
+ }
162
+
163
+ async function canViewerDeleteInternalAsync<
164
+ TFields extends object,
165
+ TID extends NonNullable<TFields[TSelectedFields]>,
166
+ TMViewerContext extends ViewerContext,
167
+ TEntity extends Entity<TFields, TID, TMViewerContext, TSelectedFields>,
168
+ TPrivacyPolicy extends EntityPrivacyPolicy<
169
+ TFields,
170
+ TID,
171
+ TMViewerContext,
172
+ TEntity,
173
+ TSelectedFields
174
+ >,
175
+ TSelectedFields extends keyof TFields = keyof TFields,
176
+ >(
177
+ entityClass: IEntityClass<
178
+ TFields,
179
+ TID,
180
+ TMViewerContext,
181
+ TEntity,
182
+ TPrivacyPolicy,
183
+ TSelectedFields
184
+ >,
185
+ sourceEntity: TEntity,
186
+ cascadingDeleteCause: EntityCascadingDeletionInfo | null,
187
+ queryContext: EntityQueryContext,
188
+ ): Promise<boolean> {
189
+ const viewerContext = sourceEntity.getViewerContext();
190
+ const entityCompanionProvider = viewerContext.entityCompanionProvider;
191
+ const viewerScopedCompanion = sourceEntity
192
+ .getViewerContext()
193
+ .getViewerScopedEntityCompanionForClass(entityClass);
194
+
195
+ const privacyPolicy = viewerScopedCompanion.entityCompanion.privacyPolicy;
196
+ const evaluationResult = await asyncResult(
197
+ privacyPolicy.authorizeDeleteAsync(
198
+ sourceEntity.getViewerContext(),
199
+ queryContext,
200
+ { previousValue: null, cascadingDeleteCause },
201
+ sourceEntity,
202
+ viewerScopedCompanion.getMetricsAdapter(),
203
+ ),
204
+ );
205
+ if (!evaluationResult.ok) {
206
+ if (evaluationResult.reason instanceof EntityNotAuthorizedError) {
207
+ return false;
208
+ } else {
209
+ throw evaluationResult.reason;
210
+ }
211
+ }
212
+
213
+ const newCascadingDeleteCause = {
214
+ entity: sourceEntity,
215
+ cascadingDeleteCause,
216
+ };
217
+
218
+ // Take entity X which is proposed to be deleted, look at inbound edges (entities that reference X).
219
+ // These inbound edges are the entities that will either get deleted or have their references
220
+ // to X nullified based on the EntityEdgeDeletionBehavior when entity X is deleted.
221
+ // For each of these inboundEdge entities Y, look at the field(s) on Y that reference X.
222
+ // For each of the field(s) on Y that reference X,
223
+ // - if EntityEdgeDeletionBehavior is cascade set null, check if user can update Y
224
+ // - if EntityEdgeDeletionBehavior is cascade delete, recursively run canViewerDeleteAsync on Y
225
+ // Return the conjunction (returning eagerly when false) of all checks recursively.
226
+
227
+ const entityConfiguration =
228
+ viewerScopedCompanion.entityCompanion.entityCompanionDefinition.entityConfiguration;
229
+ const inboundEdges = entityConfiguration.inboundEdges;
230
+
231
+ for (const inboundEdge of inboundEdges) {
232
+ const configurationForInboundEdge =
233
+ entityCompanionProvider.getCompanionForEntity(inboundEdge).entityCompanionDefinition
234
+ .entityConfiguration;
235
+
236
+ const loader = viewerContext
237
+ .getViewerScopedEntityCompanionForClass(inboundEdge)
238
+ .getLoaderFactory()
239
+ .forLoad(queryContext, {
240
+ previousValue: null,
241
+ cascadingDeleteCause: newCascadingDeleteCause,
242
+ });
243
+
244
+ for (const [fieldName, fieldDefinition] of configurationForInboundEdge.schema) {
245
+ const association = fieldDefinition.association;
246
+ if (!association) {
247
+ continue;
248
+ }
249
+
250
+ const associatedConfiguration = entityCompanionProvider.getCompanionForEntity(
251
+ association.associatedEntityClass,
252
+ ).entityCompanionDefinition.entityConfiguration;
253
+ if (associatedConfiguration !== entityConfiguration) {
254
+ continue;
255
+ }
256
+
257
+ const entityResultsForInboundEdge = await loader
258
+ .withAuthorizationResults()
259
+ .loadManyByFieldEqualingAsync(
260
+ fieldName,
261
+ association.associatedEntityLookupByField
262
+ ? sourceEntity.getField(association.associatedEntityLookupByField as any)
263
+ : sourceEntity.getID(),
264
+ );
265
+
266
+ const failedEntityLoadResults = failedResults(entityResultsForInboundEdge);
267
+ for (const failedResult of failedEntityLoadResults) {
268
+ if (failedResult.reason instanceof EntityNotAuthorizedError) {
269
+ return false;
270
+ } else {
271
+ throw failedResult.reason;
272
+ }
273
+ }
274
+
275
+ // all results should be success at this point due to check above
276
+ const entitiesForInboundEdge = entityResultsForInboundEdge.map((r) => r.enforceValue());
277
+
278
+ switch (association.edgeDeletionBehavior) {
279
+ case EntityEdgeDeletionBehavior.CASCADE_DELETE:
280
+ case EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE_ONLY: {
281
+ const canDeleteAll = (
282
+ await Promise.all(
283
+ entitiesForInboundEdge.map((entity) =>
284
+ canViewerDeleteInternalAsync(
285
+ inboundEdge,
286
+ entity,
287
+ newCascadingDeleteCause,
288
+ queryContext,
289
+ ),
290
+ ),
291
+ )
292
+ ).every((b) => b);
293
+
294
+ if (!canDeleteAll) {
295
+ return false;
296
+ }
297
+ break;
298
+ }
299
+
300
+ case EntityEdgeDeletionBehavior.SET_NULL:
301
+ case EntityEdgeDeletionBehavior.SET_NULL_INVALIDATE_CACHE_ONLY: {
302
+ const canUpdateAll = (
303
+ await Promise.all(
304
+ entitiesForInboundEdge.map((entity) =>
305
+ canViewerUpdateInternalAsync(
306
+ inboundEdge,
307
+ entity,
308
+ newCascadingDeleteCause,
309
+ queryContext,
310
+ ),
311
+ ),
312
+ )
313
+ ).every((b) => b);
314
+
315
+ if (!canUpdateAll) {
316
+ return false;
317
+ }
318
+ break;
319
+ }
320
+ }
321
+ }
322
+ }
323
+
324
+ return true;
325
+ }