@expo/entity 0.35.0 → 0.37.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.
- package/build/AuthorizationResultBasedEntityLoader.d.ts +128 -0
- package/build/AuthorizationResultBasedEntityLoader.js +196 -0
- package/build/AuthorizationResultBasedEntityLoader.js.map +1 -0
- package/build/ComposedEntityCacheAdapter.js +1 -0
- package/build/ComposedEntityCacheAdapter.js.map +1 -1
- package/build/ComposedSecondaryEntityCache.js +1 -0
- package/build/ComposedSecondaryEntityCache.js.map +1 -1
- package/build/EnforcingEntityLoader.d.ts +5 -4
- package/build/EnforcingEntityLoader.js +4 -2
- package/build/EnforcingEntityLoader.js.map +1 -1
- package/build/Entity.d.ts +0 -27
- package/build/Entity.js +0 -50
- package/build/Entity.js.map +1 -1
- package/build/EntityAssociationLoader.d.ts +1 -1
- package/build/EntityAssociationLoader.js +17 -8
- package/build/EntityAssociationLoader.js.map +1 -1
- package/build/EntityCompanion.js +9 -1
- package/build/EntityCompanion.js.map +1 -1
- package/build/EntityCompanionProvider.d.ts +3 -1
- package/build/EntityCompanionProvider.js +10 -4
- package/build/EntityCompanionProvider.js.map +1 -1
- package/build/EntityConfiguration.d.ts +2 -1
- package/build/EntityConfiguration.js +19 -1
- package/build/EntityConfiguration.js.map +1 -1
- package/build/EntityDatabaseAdapter.d.ts +2 -2
- package/build/EntityDatabaseAdapter.js +5 -3
- package/build/EntityDatabaseAdapter.js.map +1 -1
- package/build/EntityFieldDefinition.d.ts +21 -10
- package/build/EntityFieldDefinition.js +8 -9
- package/build/EntityFieldDefinition.js.map +1 -1
- package/build/EntityFields.d.ts +10 -0
- package/build/EntityFields.js +15 -1
- package/build/EntityFields.js.map +1 -1
- package/build/EntityLoader.d.ts +12 -125
- package/build/EntityLoader.js +24 -239
- package/build/EntityLoader.js.map +1 -1
- package/build/EntityLoaderFactory.d.ts +1 -1
- package/build/EntityLoaderFactory.js +3 -0
- package/build/EntityLoaderFactory.js.map +1 -1
- package/build/EntityLoaderUtils.d.ts +58 -0
- package/build/EntityLoaderUtils.js +109 -0
- package/build/EntityLoaderUtils.js.map +1 -0
- package/build/EntityMutator.d.ts +1 -0
- package/build/EntityMutator.js +71 -56
- package/build/EntityMutator.js.map +1 -1
- package/build/EntityMutatorFactory.js +9 -0
- package/build/EntityMutatorFactory.js.map +1 -1
- package/build/EntityPrivacyPolicy.d.ts +11 -5
- package/build/EntityPrivacyPolicy.js +5 -7
- package/build/EntityPrivacyPolicy.js.map +1 -1
- package/build/EntityQueryContext.d.ts +2 -1
- package/build/EntityQueryContext.js +11 -6
- package/build/EntityQueryContext.js.map +1 -1
- package/build/EntitySecondaryCacheLoader.js +5 -1
- package/build/EntitySecondaryCacheLoader.js.map +1 -1
- package/build/GenericEntityCacheAdapter.js +1 -0
- package/build/GenericEntityCacheAdapter.js.map +1 -1
- package/build/GenericSecondaryEntityCache.js +2 -0
- package/build/GenericSecondaryEntityCache.js.map +1 -1
- package/build/IEntityCacheAdapterProvider.d.ts +1 -1
- package/build/IEntityDatabaseAdapterProvider.d.ts +1 -1
- package/build/ReadonlyEntity.js +5 -1
- package/build/ReadonlyEntity.js.map +1 -1
- package/build/ViewerContext.js +2 -0
- package/build/ViewerContext.js.map +1 -1
- package/build/ViewerScopedEntityCompanion.js +2 -0
- package/build/ViewerScopedEntityCompanion.js.map +1 -1
- package/build/ViewerScopedEntityCompanionProvider.d.ts +0 -1
- package/build/ViewerScopedEntityCompanionProvider.js +2 -1
- package/build/ViewerScopedEntityCompanionProvider.js.map +1 -1
- package/build/ViewerScopedEntityLoaderFactory.d.ts +1 -1
- package/build/ViewerScopedEntityLoaderFactory.js +2 -0
- package/build/ViewerScopedEntityLoaderFactory.js.map +1 -1
- package/build/ViewerScopedEntityMutatorFactory.js +2 -0
- package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
- package/build/__tests__/ComposedCacheAdapter-test.js +2 -0
- package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -1
- package/build/__tests__/ComposedSecondaryEntityCache-test.js +1 -0
- package/build/__tests__/ComposedSecondaryEntityCache-test.js.map +1 -1
- package/build/__tests__/EnforcingEntityLoader-test.js +101 -113
- package/build/__tests__/EnforcingEntityLoader-test.js.map +1 -1
- package/build/__tests__/Entity-test.js +0 -132
- package/build/__tests__/Entity-test.js.map +1 -1
- package/build/__tests__/EntityAssociationLoader-test.js +6 -2
- package/build/__tests__/EntityAssociationLoader-test.js.map +1 -1
- package/build/__tests__/EntityCommonUseCases-test.js +24 -22
- package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
- package/build/__tests__/EntityCompanion-test.js +26 -3
- package/build/__tests__/EntityCompanion-test.js.map +1 -1
- package/build/__tests__/EntityConfiguration-test.js +103 -0
- package/build/__tests__/EntityConfiguration-test.js.map +1 -0
- package/build/__tests__/EntityDatabaseAdapter-test.js +6 -0
- package/build/__tests__/EntityDatabaseAdapter-test.js.map +1 -1
- package/build/__tests__/EntityEdges-test.js +61 -20
- package/build/__tests__/EntityEdges-test.js.map +1 -1
- package/build/__tests__/EntityFields-test.js +6 -0
- package/build/__tests__/EntityFields-test.js.map +1 -1
- package/build/__tests__/EntityLoader-constructor-test.js +16 -17
- package/build/__tests__/EntityLoader-constructor-test.js.map +1 -1
- package/build/__tests__/EntityLoader-test.js +74 -22
- package/build/__tests__/EntityLoader-test.js.map +1 -1
- package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js +12 -15
- package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js.map +1 -1
- package/build/__tests__/EntityMutator-test.js +54 -9
- package/build/__tests__/EntityMutator-test.js.map +1 -1
- package/build/__tests__/EntityPrivacyPolicy-test.js +77 -59
- package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
- package/build/__tests__/EntityQueryContext-test.js +9 -0
- package/build/__tests__/EntityQueryContext-test.js.map +1 -1
- package/build/__tests__/EntitySelfReferentialEdges-test.js +42 -25
- package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js.map +1 -1
- package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js +20 -18
- package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js.map +1 -1
- package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js +12 -15
- package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js.map +1 -1
- package/build/entityUtils.d.ts +1 -1
- package/build/entityUtils.js.map +1 -1
- package/build/errors/EntityCacheAdapterError.js +2 -5
- package/build/errors/EntityCacheAdapterError.js.map +1 -1
- package/build/errors/EntityDatabaseAdapterError.js +14 -35
- package/build/errors/EntityDatabaseAdapterError.js.map +1 -1
- package/build/errors/EntityError.js +1 -0
- package/build/errors/EntityError.js.map +1 -1
- package/build/errors/EntityInvalidFieldValueError.js +2 -2
- package/build/errors/EntityInvalidFieldValueError.js.map +1 -1
- package/build/errors/EntityNotAuthorizedError.js +3 -2
- package/build/errors/EntityNotAuthorizedError.js.map +1 -1
- package/build/errors/EntityNotFoundError.js +2 -2
- package/build/errors/EntityNotFoundError.js.map +1 -1
- package/build/index.d.ts +26 -20
- package/build/index.js +38 -28
- package/build/index.js.map +1 -1
- package/build/internal/EntityDataManager.d.ts +1 -1
- package/build/internal/EntityDataManager.js +6 -1
- package/build/internal/EntityDataManager.js.map +1 -1
- package/build/internal/EntityFieldTransformationUtils.d.ts +5 -5
- package/build/internal/EntityFieldTransformationUtils.js +5 -8
- package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
- package/build/internal/EntityTableDataCoordinator.d.ts +1 -1
- package/build/internal/EntityTableDataCoordinator.js +5 -0
- package/build/internal/EntityTableDataCoordinator.js.map +1 -1
- package/build/internal/ReadThroughEntityCache.d.ts +1 -1
- package/build/internal/ReadThroughEntityCache.js +2 -0
- package/build/internal/ReadThroughEntityCache.js.map +1 -1
- package/build/internal/__tests__/EntityFieldTransformationUtils-test.js +6 -2
- package/build/internal/__tests__/EntityFieldTransformationUtils-test.js.map +1 -1
- package/build/internal/__tests__/ReadThroughEntityCache-test.js +33 -0
- package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
- package/build/metrics/IEntityMetricsAdapter.d.ts +1 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.d.ts +1 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.d.ts +1 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.d.ts +1 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
- package/build/rules/PrivacyPolicyRule.d.ts +1 -1
- package/build/rules/PrivacyPolicyRule.js.map +1 -1
- package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js.map +1 -1
- package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js.map +1 -1
- package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js.map +1 -1
- package/build/testfixtures/DateIDTestEntity.js +12 -15
- package/build/testfixtures/DateIDTestEntity.js.map +1 -1
- package/build/testfixtures/SimpleTestEntity.js +12 -15
- package/build/testfixtures/SimpleTestEntity.js.map +1 -1
- package/build/testfixtures/TestEntity.js +12 -15
- package/build/testfixtures/TestEntity.js.map +1 -1
- package/build/testfixtures/TestEntity2.js +12 -15
- package/build/testfixtures/TestEntity2.js.map +1 -1
- package/build/testfixtures/TestEntityNumberKey.js +12 -15
- package/build/testfixtures/TestEntityNumberKey.js.map +1 -1
- package/build/testfixtures/TestEntityWithMutationTriggers.d.ts +36 -0
- package/build/testfixtures/TestEntityWithMutationTriggers.js +82 -0
- package/build/testfixtures/TestEntityWithMutationTriggers.js.map +1 -0
- package/build/utils/EntityPrivacyUtils.d.ts +34 -0
- package/build/utils/EntityPrivacyUtils.js +160 -0
- package/build/utils/EntityPrivacyUtils.js.map +1 -0
- package/build/utils/__tests__/EntityPrivacyUtils-test.d.ts +1 -0
- package/build/utils/__tests__/EntityPrivacyUtils-test.js +395 -0
- package/build/utils/__tests__/EntityPrivacyUtils-test.js.map +1 -0
- package/build/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.d.ts +1 -0
- package/build/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.js +26 -0
- package/build/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.js.map +1 -0
- package/build/utils/collections/maps.js.map +1 -1
- package/build/utils/mergeEntityMutationTriggerConfigurations.d.ts +4 -0
- package/build/utils/mergeEntityMutationTriggerConfigurations.js +28 -0
- package/build/utils/mergeEntityMutationTriggerConfigurations.js.map +1 -0
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +1 -1
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
- package/build/utils/testing/StubCacheAdapter.d.ts +3 -3
- package/build/utils/testing/StubCacheAdapter.js +3 -3
- package/build/utils/testing/StubCacheAdapter.js.map +1 -1
- package/build/utils/testing/StubDatabaseAdapter.d.ts +2 -2
- package/build/utils/testing/StubDatabaseAdapter.js +4 -2
- package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
- package/build/utils/testing/StubDatabaseAdapterProvider.d.ts +1 -1
- package/build/utils/testing/StubDatabaseAdapterProvider.js +1 -3
- package/build/utils/testing/StubDatabaseAdapterProvider.js.map +1 -1
- package/build/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.d.ts +1 -0
- package/build/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.js +42 -0
- package/build/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.js.map +1 -0
- package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js +53 -0
- package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js.map +1 -1
- package/build/utils/testing/describeFieldTestCase.js.map +1 -1
- package/package.json +5 -4
- package/src/AuthorizationResultBasedEntityLoader.ts +297 -0
- package/src/ComposedEntityCacheAdapter.ts +6 -6
- package/src/ComposedSecondaryEntityCache.ts +8 -8
- package/src/EnforcingEntityLoader.ts +20 -19
- package/src/Entity.ts +11 -126
- package/src/EntityAssociationLoader.ts +40 -41
- package/src/EntityCompanion.ts +8 -4
- package/src/EntityCompanionProvider.ts +24 -16
- package/src/EntityConfiguration.ts +18 -7
- package/src/EntityDatabaseAdapter.ts +41 -41
- package/src/EntityFieldDefinition.ts +28 -18
- package/src/EntityFields.ts +15 -0
- package/src/EntityLoader.ts +63 -357
- package/src/EntityLoaderFactory.ts +10 -4
- package/src/EntityLoaderUtils.ts +149 -0
- package/src/EntityMutationInfo.ts +2 -2
- package/src/EntityMutationTriggerConfiguration.ts +5 -5
- package/src/EntityMutationValidator.ts +2 -2
- package/src/EntityMutator.ts +146 -144
- package/src/EntityMutatorFactory.ts +8 -8
- package/src/EntityPrivacyPolicy.ts +78 -28
- package/src/EntityQueryContext.ts +14 -13
- package/src/EntityQueryContextProvider.ts +5 -5
- package/src/EntitySecondaryCacheLoader.ts +13 -11
- package/src/GenericEntityCacheAdapter.ts +10 -10
- package/src/GenericSecondaryEntityCache.ts +6 -6
- package/src/IEntityCacheAdapter.ts +4 -4
- package/src/IEntityCacheAdapterProvider.ts +2 -2
- package/src/IEntityDatabaseAdapterProvider.ts +2 -2
- package/src/ReadonlyEntity.ts +5 -5
- package/src/ViewerContext.ts +5 -5
- package/src/ViewerScopedEntityCompanion.ts +4 -4
- package/src/ViewerScopedEntityCompanionProvider.ts +4 -5
- package/src/ViewerScopedEntityLoaderFactory.ts +10 -4
- package/src/ViewerScopedEntityMutatorFactory.ts +5 -5
- package/src/__tests__/ComposedCacheAdapter-test.ts +12 -10
- package/src/__tests__/ComposedSecondaryEntityCache-test.ts +8 -8
- package/src/__tests__/EnforcingEntityLoader-test.ts +236 -159
- package/src/__tests__/Entity-test.ts +0 -202
- package/src/__tests__/EntityAssociationLoader-test.ts +29 -25
- package/src/__tests__/EntityCommonUseCases-test.ts +29 -13
- package/src/__tests__/EntityCompanion-test.ts +57 -5
- package/src/__tests__/EntityConfiguration-test.ts +118 -0
- package/src/__tests__/EntityDatabaseAdapter-test.ts +11 -11
- package/src/__tests__/EntityEdges-test.ts +108 -36
- package/src/__tests__/EntityFields-test.ts +14 -2
- package/src/__tests__/EntityLoader-constructor-test.ts +20 -7
- package/src/__tests__/EntityLoader-test.ts +214 -86
- package/src/__tests__/EntityMutator-MutationCacheConsistency-test.ts +2 -2
- package/src/__tests__/EntityMutator-test.ts +281 -96
- package/src/__tests__/EntityPrivacyPolicy-test.ts +166 -53
- package/src/__tests__/EntityQueryContext-test.ts +30 -12
- package/src/__tests__/EntitySecondaryCacheLoader-test.ts +7 -7
- package/src/__tests__/EntitySelfReferentialEdges-test.ts +46 -26
- package/src/__tests__/GenericEntityCacheAdapter-test.ts +2 -2
- package/src/__tests__/ViewerContext-test.ts +1 -1
- package/src/__tests__/ViewerScopedEntityCompanion-test.ts +2 -2
- package/src/__tests__/ViewerScopedEntityCompanionProvider-test.ts +2 -2
- package/src/__tests__/ViewerScopedEntityLoaderFactory-test.ts +2 -1
- package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +19 -19
- package/src/__tests__/entityUtils-test.ts +2 -2
- package/src/entityUtils.ts +4 -4
- package/src/errors/EntityError.ts +4 -1
- package/src/errors/EntityInvalidFieldValueError.ts +2 -2
- package/src/errors/EntityNotAuthorizedError.ts +3 -3
- package/src/errors/EntityNotFoundError.ts +2 -2
- package/src/index.ts +26 -20
- package/src/internal/EntityDataManager.ts +24 -24
- package/src/internal/EntityFieldTransformationUtils.ts +39 -32
- package/src/internal/EntityTableDataCoordinator.ts +3 -3
- package/src/internal/ReadThroughEntityCache.ts +9 -9
- package/src/internal/__tests__/EntityDataManager-test.ts +51 -51
- package/src/internal/__tests__/EntityFieldTransformationUtils-test.ts +14 -10
- package/src/internal/__tests__/ReadThroughEntityCache-test.ts +74 -18
- package/src/metrics/EntityMetricsUtils.ts +4 -4
- package/src/metrics/IEntityMetricsAdapter.ts +1 -1
- package/src/rules/AlwaysAllowPrivacyPolicyRule.ts +9 -3
- package/src/rules/AlwaysDenyPrivacyPolicyRule.ts +9 -3
- package/src/rules/AlwaysSkipPrivacyPolicyRule.ts +9 -3
- package/src/rules/PrivacyPolicyRule.ts +9 -3
- package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -1
- package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -1
- package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -1
- package/src/testfixtures/TestEntity.ts +1 -1
- package/src/testfixtures/TestEntityWithMutationTriggers.ts +156 -0
- package/src/utils/EntityPrivacyUtils.ts +325 -0
- package/src/utils/__tests__/EntityPrivacyUtils-test.ts +570 -0
- package/src/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.ts +29 -0
- package/src/utils/collections/__tests__/maps-test.ts +2 -2
- package/src/utils/collections/maps.ts +11 -11
- package/src/utils/mergeEntityMutationTriggerConfigurations.ts +44 -0
- package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +25 -22
- package/src/utils/testing/StubCacheAdapter.ts +17 -15
- package/src/utils/testing/StubDatabaseAdapter.ts +35 -30
- package/src/utils/testing/StubDatabaseAdapterProvider.ts +2 -2
- package/src/utils/testing/StubQueryContextProvider.ts +2 -2
- package/src/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.ts +42 -0
- package/src/utils/testing/__tests__/StubDatabaseAdapter-test.ts +111 -29
- package/src/utils/testing/createUnitTestEntityCompanionProvider.ts +2 -2
- package/src/utils/testing/describeFieldTestCase.ts +1 -1
- package/build/__tests__/EntityDataConfiguration-test.js +0 -68
- package/build/__tests__/EntityDataConfiguration-test.js.map +0 -1
- package/src/__tests__/EntityDataConfiguration-test.ts +0 -77
- /package/build/__tests__/{EntityDataConfiguration-test.d.ts → EntityConfiguration-test.d.ts} +0 -0
|
@@ -8,7 +8,7 @@ export const timeAndLogLoadEventAsync =
|
|
|
8
8
|
(
|
|
9
9
|
metricsAdapter: IEntityMetricsAdapter,
|
|
10
10
|
loadType: EntityMetricsLoadType,
|
|
11
|
-
entityClassName: string
|
|
11
|
+
entityClassName: string,
|
|
12
12
|
) =>
|
|
13
13
|
async <TFields>(promise: Promise<readonly Readonly<TFields>[]>) => {
|
|
14
14
|
const startTime = Date.now();
|
|
@@ -29,10 +29,10 @@ export const timeAndLogLoadMapEventAsync =
|
|
|
29
29
|
(
|
|
30
30
|
metricsAdapter: IEntityMetricsAdapter,
|
|
31
31
|
loadType: EntityMetricsLoadType,
|
|
32
|
-
entityClassName: string
|
|
32
|
+
entityClassName: string,
|
|
33
33
|
) =>
|
|
34
34
|
async <TFields, N extends keyof TFields>(
|
|
35
|
-
promise: Promise<ReadonlyMap<NonNullable<TFields[N]>, readonly Readonly<TFields>[]
|
|
35
|
+
promise: Promise<ReadonlyMap<NonNullable<TFields[N]>, readonly Readonly<TFields>[]>>,
|
|
36
36
|
) => {
|
|
37
37
|
const startTime = Date.now();
|
|
38
38
|
const result = await promise;
|
|
@@ -54,7 +54,7 @@ export const timeAndLogMutationEventAsync =
|
|
|
54
54
|
(
|
|
55
55
|
metricsAdapter: IEntityMetricsAdapter,
|
|
56
56
|
mutationType: EntityMetricsMutationType,
|
|
57
|
-
entityClassName: string
|
|
57
|
+
entityClassName: string,
|
|
58
58
|
) =>
|
|
59
59
|
async <T>(promise: Promise<T>) => {
|
|
60
60
|
const startTime = Date.now();
|
|
@@ -140,7 +140,7 @@ export default interface IEntityMetricsAdapter {
|
|
|
140
140
|
* Called when a dataloader, cache, or database load is initiated via the standard
|
|
141
141
|
* load methods (not equality conjunction or raw). Most commonly used for logging
|
|
142
142
|
* a waterfall to determine dataloader and cache hit rates and ratios.
|
|
143
|
-
* @param
|
|
143
|
+
* @param incrementLoadCountEvent - count of field values being loaded for a field
|
|
144
144
|
*/
|
|
145
145
|
incrementDataManagerLoadCount(incrementLoadCountEvent: IncrementLoadCountEvent): void;
|
|
146
146
|
}
|
|
@@ -12,13 +12,19 @@ export default class AlwaysAllowPrivacyPolicyRule<
|
|
|
12
12
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
13
13
|
TViewerContext extends ViewerContext,
|
|
14
14
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
15
|
-
TSelectedFields extends keyof TFields = keyof TFields
|
|
15
|
+
TSelectedFields extends keyof TFields = keyof TFields,
|
|
16
16
|
> extends PrivacyPolicyRule<TFields, TID, TViewerContext, TEntity, TSelectedFields> {
|
|
17
17
|
async evaluateAsync(
|
|
18
18
|
_viewerContext: TViewerContext,
|
|
19
19
|
_queryContext: EntityQueryContext,
|
|
20
|
-
_evaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
21
|
-
|
|
20
|
+
_evaluationContext: EntityPrivacyPolicyEvaluationContext<
|
|
21
|
+
TFields,
|
|
22
|
+
TID,
|
|
23
|
+
TViewerContext,
|
|
24
|
+
TEntity,
|
|
25
|
+
TSelectedFields
|
|
26
|
+
>,
|
|
27
|
+
_entity: TEntity,
|
|
22
28
|
): Promise<RuleEvaluationResult> {
|
|
23
29
|
return RuleEvaluationResult.ALLOW;
|
|
24
30
|
}
|
|
@@ -12,13 +12,19 @@ export default class AlwaysDenyPrivacyPolicyRule<
|
|
|
12
12
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
13
13
|
TViewerContext extends ViewerContext,
|
|
14
14
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
15
|
-
TSelectedFields extends keyof TFields = keyof TFields
|
|
15
|
+
TSelectedFields extends keyof TFields = keyof TFields,
|
|
16
16
|
> extends PrivacyPolicyRule<TFields, TID, TViewerContext, TEntity, TSelectedFields> {
|
|
17
17
|
async evaluateAsync(
|
|
18
18
|
_viewerContext: TViewerContext,
|
|
19
19
|
_queryContext: EntityQueryContext,
|
|
20
|
-
_evaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
21
|
-
|
|
20
|
+
_evaluationContext: EntityPrivacyPolicyEvaluationContext<
|
|
21
|
+
TFields,
|
|
22
|
+
TID,
|
|
23
|
+
TViewerContext,
|
|
24
|
+
TEntity,
|
|
25
|
+
TSelectedFields
|
|
26
|
+
>,
|
|
27
|
+
_entity: TEntity,
|
|
22
28
|
): Promise<RuleEvaluationResult> {
|
|
23
29
|
return RuleEvaluationResult.DENY;
|
|
24
30
|
}
|
|
@@ -12,13 +12,19 @@ export default class AlwaysSkipPrivacyPolicyRule<
|
|
|
12
12
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
13
13
|
TViewerContext extends ViewerContext,
|
|
14
14
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
15
|
-
TSelectedFields extends keyof TFields = keyof TFields
|
|
15
|
+
TSelectedFields extends keyof TFields = keyof TFields,
|
|
16
16
|
> extends PrivacyPolicyRule<TFields, TID, TViewerContext, TEntity, TSelectedFields> {
|
|
17
17
|
async evaluateAsync(
|
|
18
18
|
_viewerContext: TViewerContext,
|
|
19
19
|
_queryContext: EntityQueryContext,
|
|
20
|
-
_evaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
21
|
-
|
|
20
|
+
_evaluationContext: EntityPrivacyPolicyEvaluationContext<
|
|
21
|
+
TFields,
|
|
22
|
+
TID,
|
|
23
|
+
TViewerContext,
|
|
24
|
+
TEntity,
|
|
25
|
+
TSelectedFields
|
|
26
|
+
>,
|
|
27
|
+
_entity: TEntity,
|
|
22
28
|
): Promise<RuleEvaluationResult> {
|
|
23
29
|
return RuleEvaluationResult.SKIP;
|
|
24
30
|
}
|
|
@@ -41,12 +41,18 @@ export default abstract class PrivacyPolicyRule<
|
|
|
41
41
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
42
42
|
TViewerContext extends ViewerContext,
|
|
43
43
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
44
|
-
TSelectedFields extends keyof TFields = keyof TFields
|
|
44
|
+
TSelectedFields extends keyof TFields = keyof TFields,
|
|
45
45
|
> {
|
|
46
46
|
abstract evaluateAsync(
|
|
47
47
|
viewerContext: TViewerContext,
|
|
48
48
|
queryContext: EntityQueryContext,
|
|
49
|
-
evaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
50
|
-
|
|
49
|
+
evaluationContext: EntityPrivacyPolicyEvaluationContext<
|
|
50
|
+
TFields,
|
|
51
|
+
TID,
|
|
52
|
+
TViewerContext,
|
|
53
|
+
TEntity,
|
|
54
|
+
TSelectedFields
|
|
55
|
+
>,
|
|
56
|
+
entity: TEntity,
|
|
51
57
|
): Promise<RuleEvaluationResult>;
|
|
52
58
|
}
|
|
@@ -11,7 +11,8 @@ describePrivacyPolicyRule(new AlwaysAllowPrivacyPolicyRule(), {
|
|
|
11
11
|
{
|
|
12
12
|
viewerContext: instance(mock(ViewerContext)),
|
|
13
13
|
queryContext: instance(mock(EntityQueryContext)),
|
|
14
|
-
evaluationContext:
|
|
14
|
+
evaluationContext:
|
|
15
|
+
instance(mock<EntityPrivacyPolicyEvaluationContext<any, any, any, any, any>>()),
|
|
15
16
|
entity: anything(),
|
|
16
17
|
},
|
|
17
18
|
],
|
|
@@ -11,7 +11,8 @@ describePrivacyPolicyRule(new AlwaysDenyPrivacyPolicyRule(), {
|
|
|
11
11
|
{
|
|
12
12
|
viewerContext: instance(mock(ViewerContext)),
|
|
13
13
|
queryContext: instance(mock(EntityQueryContext)),
|
|
14
|
-
evaluationContext:
|
|
14
|
+
evaluationContext:
|
|
15
|
+
instance(mock<EntityPrivacyPolicyEvaluationContext<any, any, any, any, any>>()),
|
|
15
16
|
entity: anything(),
|
|
16
17
|
},
|
|
17
18
|
],
|
|
@@ -11,7 +11,8 @@ describePrivacyPolicyRule(new AlwaysSkipPrivacyPolicyRule(), {
|
|
|
11
11
|
{
|
|
12
12
|
viewerContext: instance(mock(ViewerContext)),
|
|
13
13
|
queryContext: instance(mock(EntityQueryContext)),
|
|
14
|
-
evaluationContext:
|
|
14
|
+
evaluationContext:
|
|
15
|
+
instance(mock<EntityPrivacyPolicyEvaluationContext<any, any, any, any, any>>()),
|
|
15
16
|
entity: anything(),
|
|
16
17
|
},
|
|
17
18
|
],
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import Entity from '../Entity';
|
|
2
|
+
import { EntityCompanionDefinition } from '../EntityCompanionProvider';
|
|
3
|
+
import EntityConfiguration from '../EntityConfiguration';
|
|
4
|
+
import { StringField, UUIDField } from '../EntityFields';
|
|
5
|
+
import { EntityTriggerMutationInfo } from '../EntityMutationInfo';
|
|
6
|
+
import {
|
|
7
|
+
EntityMutationTrigger,
|
|
8
|
+
EntityNonTransactionalMutationTrigger,
|
|
9
|
+
} from '../EntityMutationTriggerConfiguration';
|
|
10
|
+
import EntityPrivacyPolicy from '../EntityPrivacyPolicy';
|
|
11
|
+
import { EntityQueryContext } from '../EntityQueryContext';
|
|
12
|
+
import ViewerContext from '../ViewerContext';
|
|
13
|
+
import AlwaysAllowPrivacyPolicyRule from '../rules/AlwaysAllowPrivacyPolicyRule';
|
|
14
|
+
|
|
15
|
+
export type TestMTFields = {
|
|
16
|
+
id: string;
|
|
17
|
+
stringField: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const testEntityMTConfiguration = new EntityConfiguration<TestMTFields>({
|
|
21
|
+
idField: 'id',
|
|
22
|
+
tableName: 'test_entity_should_not_write_to_db_3',
|
|
23
|
+
schema: {
|
|
24
|
+
id: new UUIDField({
|
|
25
|
+
columnName: 'id',
|
|
26
|
+
}),
|
|
27
|
+
stringField: new StringField({
|
|
28
|
+
columnName: 'string_field',
|
|
29
|
+
}),
|
|
30
|
+
},
|
|
31
|
+
databaseAdapterFlavor: 'postgres',
|
|
32
|
+
cacheAdapterFlavor: 'redis',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export class TestEntityMTPrivacyPolicy extends EntityPrivacyPolicy<
|
|
36
|
+
TestMTFields,
|
|
37
|
+
string,
|
|
38
|
+
ViewerContext,
|
|
39
|
+
TestEntityWithMutationTriggers
|
|
40
|
+
> {
|
|
41
|
+
protected override readonly readRules = [
|
|
42
|
+
new AlwaysAllowPrivacyPolicyRule<
|
|
43
|
+
TestMTFields,
|
|
44
|
+
string,
|
|
45
|
+
ViewerContext,
|
|
46
|
+
TestEntityWithMutationTriggers
|
|
47
|
+
>(),
|
|
48
|
+
];
|
|
49
|
+
protected override readonly createRules = [
|
|
50
|
+
new AlwaysAllowPrivacyPolicyRule<
|
|
51
|
+
TestMTFields,
|
|
52
|
+
string,
|
|
53
|
+
ViewerContext,
|
|
54
|
+
TestEntityWithMutationTriggers
|
|
55
|
+
>(),
|
|
56
|
+
];
|
|
57
|
+
protected override readonly updateRules = [
|
|
58
|
+
new AlwaysAllowPrivacyPolicyRule<
|
|
59
|
+
TestMTFields,
|
|
60
|
+
string,
|
|
61
|
+
ViewerContext,
|
|
62
|
+
TestEntityWithMutationTriggers
|
|
63
|
+
>(),
|
|
64
|
+
];
|
|
65
|
+
protected override readonly deleteRules = [
|
|
66
|
+
new AlwaysAllowPrivacyPolicyRule<
|
|
67
|
+
TestMTFields,
|
|
68
|
+
string,
|
|
69
|
+
ViewerContext,
|
|
70
|
+
TestEntityWithMutationTriggers
|
|
71
|
+
>(),
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class TestMutationTrigger extends EntityMutationTrigger<
|
|
76
|
+
TestMTFields,
|
|
77
|
+
string,
|
|
78
|
+
ViewerContext,
|
|
79
|
+
TestEntityWithMutationTriggers,
|
|
80
|
+
keyof TestMTFields
|
|
81
|
+
> {
|
|
82
|
+
constructor(
|
|
83
|
+
// @ts-expect-error key is never used but is helpful for debugging
|
|
84
|
+
private readonly key: string,
|
|
85
|
+
) {
|
|
86
|
+
super();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async executeAsync(
|
|
90
|
+
_viewerContext: ViewerContext,
|
|
91
|
+
_queryContext: EntityQueryContext,
|
|
92
|
+
_entity: TestEntityWithMutationTriggers,
|
|
93
|
+
_mutationInfo: EntityTriggerMutationInfo<
|
|
94
|
+
TestMTFields,
|
|
95
|
+
string,
|
|
96
|
+
ViewerContext,
|
|
97
|
+
TestEntityWithMutationTriggers,
|
|
98
|
+
keyof TestMTFields
|
|
99
|
+
>,
|
|
100
|
+
): Promise<void> {}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export class NonTransactionalTestMutationTrigger extends EntityNonTransactionalMutationTrigger<
|
|
104
|
+
TestMTFields,
|
|
105
|
+
string,
|
|
106
|
+
ViewerContext,
|
|
107
|
+
TestEntityWithMutationTriggers,
|
|
108
|
+
keyof TestMTFields
|
|
109
|
+
> {
|
|
110
|
+
constructor(
|
|
111
|
+
// @ts-expect-error key is never used but is helpful for debugging
|
|
112
|
+
private readonly key: string,
|
|
113
|
+
) {
|
|
114
|
+
super();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async executeAsync(
|
|
118
|
+
_viewerContext: ViewerContext,
|
|
119
|
+
_entity: TestEntityWithMutationTriggers,
|
|
120
|
+
_mutationInfo: EntityTriggerMutationInfo<
|
|
121
|
+
TestMTFields,
|
|
122
|
+
string,
|
|
123
|
+
ViewerContext,
|
|
124
|
+
TestEntityWithMutationTriggers,
|
|
125
|
+
keyof TestMTFields
|
|
126
|
+
>,
|
|
127
|
+
): Promise<void> {}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* A test Entity that has one afterCreate and one afterAll trigger
|
|
132
|
+
*/
|
|
133
|
+
export default class TestEntityWithMutationTriggers extends Entity<
|
|
134
|
+
TestMTFields,
|
|
135
|
+
string,
|
|
136
|
+
ViewerContext
|
|
137
|
+
> {
|
|
138
|
+
static defineCompanionDefinition(): EntityCompanionDefinition<
|
|
139
|
+
TestMTFields,
|
|
140
|
+
string,
|
|
141
|
+
ViewerContext,
|
|
142
|
+
TestEntityWithMutationTriggers,
|
|
143
|
+
TestEntityMTPrivacyPolicy
|
|
144
|
+
> {
|
|
145
|
+
return {
|
|
146
|
+
entityClass: TestEntityWithMutationTriggers,
|
|
147
|
+
entityConfiguration: testEntityMTConfiguration,
|
|
148
|
+
privacyPolicyClass: TestEntityMTPrivacyPolicy,
|
|
149
|
+
mutationTriggers: {
|
|
150
|
+
afterCreate: [new TestMutationTrigger('localAfterCreate')],
|
|
151
|
+
afterAll: [new TestMutationTrigger('localAfterAll')],
|
|
152
|
+
afterCommit: [new NonTransactionalTestMutationTrigger('localAfterCommit')],
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -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
|
+
}
|