@expo/entity 0.22.0 → 0.25.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/ComposedEntityCacheAdapter.d.ts +19 -0
- package/build/ComposedEntityCacheAdapter.js +66 -0
- package/build/ComposedEntityCacheAdapter.js.map +1 -0
- package/build/ComposedSecondaryEntityCache.d.ts +15 -0
- package/build/ComposedSecondaryEntityCache.js +37 -0
- package/build/ComposedSecondaryEntityCache.js.map +1 -0
- package/build/Entity.js +6 -6
- package/build/Entity.js.map +1 -1
- package/build/EntityAssociationLoader.js +4 -4
- package/build/EntityAssociationLoader.js.map +1 -1
- package/build/EntityCacheAdapter.d.ts +2 -9
- package/build/EntityCacheAdapter.js.map +1 -1
- package/build/EntityFieldDefinition.d.ts +8 -0
- package/build/EntityFieldDefinition.js +5 -0
- package/build/EntityFieldDefinition.js.map +1 -1
- package/build/EntityFields.d.ts +38 -0
- package/build/EntityFields.js +38 -0
- package/build/EntityFields.js.map +1 -1
- package/build/EntityLoader.d.ts +3 -2
- package/build/EntityLoader.js +5 -4
- package/build/EntityLoader.js.map +1 -1
- package/build/EntityLoaderFactory.d.ts +2 -2
- package/build/EntityLoaderFactory.js +2 -2
- package/build/EntityLoaderFactory.js.map +1 -1
- package/build/EntityMutationInfo.d.ts +12 -3
- package/build/EntityMutator.d.ts +5 -4
- package/build/EntityMutator.js +31 -24
- package/build/EntityMutator.js.map +1 -1
- package/build/EntityMutatorFactory.d.ts +4 -4
- package/build/EntityMutatorFactory.js +6 -6
- package/build/EntityMutatorFactory.js.map +1 -1
- package/build/EntityPrivacyPolicy.d.ts +15 -4
- package/build/EntityPrivacyPolicy.js +14 -14
- package/build/EntityPrivacyPolicy.js.map +1 -1
- package/build/GenericSecondaryEntityCache.d.ts +19 -0
- package/build/GenericSecondaryEntityCache.js +74 -0
- package/build/GenericSecondaryEntityCache.js.map +1 -0
- package/build/IEntityGenericCacher.d.ts +11 -0
- package/build/IEntityGenericCacher.js +3 -0
- package/build/IEntityGenericCacher.js.map +1 -0
- package/build/ReadonlyEntity.js +1 -1
- package/build/ReadonlyEntity.js.map +1 -1
- package/build/ViewerScopedEntityLoaderFactory.d.ts +2 -2
- package/build/ViewerScopedEntityLoaderFactory.js +2 -2
- package/build/ViewerScopedEntityLoaderFactory.js.map +1 -1
- package/build/ViewerScopedEntityMutatorFactory.d.ts +4 -4
- package/build/ViewerScopedEntityMutatorFactory.js +6 -6
- package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
- package/build/__tests__/ComposedCacheAdapter-test.d.ts +1 -0
- package/build/__tests__/ComposedCacheAdapter-test.js +198 -0
- package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -0
- package/build/__tests__/ComposedSecondaryEntityCache-test.d.ts +1 -0
- package/build/__tests__/ComposedSecondaryEntityCache-test.js +65 -0
- package/build/__tests__/ComposedSecondaryEntityCache-test.js.map +1 -0
- package/build/__tests__/EntityCommonUseCases-test.js +1 -1
- package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
- package/build/__tests__/EntityEdges-test.js +260 -37
- package/build/__tests__/EntityEdges-test.js.map +1 -1
- package/build/__tests__/EntityLoader-constructor-test.js +2 -1
- package/build/__tests__/EntityLoader-constructor-test.js.map +1 -1
- package/build/__tests__/EntityLoader-test.js +19 -11
- package/build/__tests__/EntityLoader-test.js.map +1 -1
- package/build/__tests__/EntityMutator-test.js +96 -43
- package/build/__tests__/EntityMutator-test.js.map +1 -1
- package/build/__tests__/EntityPrivacyPolicy-test.js +23 -12
- package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
- package/build/__tests__/EntitySecondaryCacheLoader-test.js +1 -1
- package/build/__tests__/EntitySecondaryCacheLoader-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js +3 -2
- package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +3 -2
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
- package/build/index.d.ts +2 -0
- package/build/index.js +3 -1
- package/build/index.js.map +1 -1
- package/build/internal/ReadThroughEntityCache.d.ts +2 -3
- package/build/internal/ReadThroughEntityCache.js +2 -6
- package/build/internal/ReadThroughEntityCache.js.map +1 -1
- package/build/internal/__tests__/ReadThroughEntityCache-test.js +0 -32
- package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.d.ts +2 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js +1 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.d.ts +2 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js +1 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.d.ts +2 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.js +1 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
- package/build/rules/PrivacyPolicyRule.d.ts +2 -1
- package/build/rules/PrivacyPolicyRule.js.map +1 -1
- package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js +1 -0
- package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js.map +1 -1
- package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js +1 -0
- package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js.map +1 -1
- package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js +1 -0
- package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js.map +1 -1
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +2 -0
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.js +6 -6
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
- package/build/utils/testing/StubCacheAdapter.d.ts +6 -9
- package/build/utils/testing/StubCacheAdapter.js +0 -6
- package/build/utils/testing/StubCacheAdapter.js.map +1 -1
- package/package.json +1 -1
- package/src/ComposedEntityCacheAdapter.ts +86 -0
- package/src/ComposedSecondaryEntityCache.ts +63 -0
- package/src/Entity.ts +6 -4
- package/src/EntityAssociationLoader.ts +4 -4
- package/src/EntityCacheAdapter.ts +2 -10
- package/src/EntityFieldDefinition.ts +8 -0
- package/src/EntityFields.ts +45 -0
- package/src/EntityLoader.ts +5 -1
- package/src/EntityLoaderFactory.ts +4 -2
- package/src/EntityMutationInfo.ts +13 -3
- package/src/EntityMutator.ts +44 -21
- package/src/EntityMutatorFactory.ts +10 -4
- package/src/EntityPrivacyPolicy.ts +31 -1
- package/src/GenericSecondaryEntityCache.ts +98 -0
- package/src/IEntityGenericCacher.ts +15 -0
- package/src/ReadonlyEntity.ts +1 -1
- package/src/ViewerScopedEntityLoaderFactory.ts +8 -3
- package/src/ViewerScopedEntityMutatorFactory.ts +22 -7
- package/src/__tests__/ComposedCacheAdapter-test.ts +280 -0
- package/src/__tests__/ComposedSecondaryEntityCache-test.ts +101 -0
- package/src/__tests__/EntityCommonUseCases-test.ts +2 -1
- package/src/__tests__/EntityEdges-test.ts +286 -45
- package/src/__tests__/EntityLoader-constructor-test.ts +3 -1
- package/src/__tests__/EntityLoader-test.ts +26 -1
- package/src/__tests__/EntityMutator-test.ts +99 -37
- package/src/__tests__/EntityPrivacyPolicy-test.ts +66 -7
- package/src/__tests__/EntitySecondaryCacheLoader-test.ts +1 -0
- package/src/__tests__/ViewerScopedEntityLoaderFactory-test.ts +4 -2
- package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +6 -2
- package/src/index.ts +2 -0
- package/src/internal/ReadThroughEntityCache.ts +6 -28
- package/src/internal/__tests__/ReadThroughEntityCache-test.ts +0 -44
- package/src/rules/AlwaysAllowPrivacyPolicyRule.ts +2 -0
- package/src/rules/AlwaysDenyPrivacyPolicyRule.ts +2 -0
- package/src/rules/AlwaysSkipPrivacyPolicyRule.ts +2 -0
- package/src/rules/PrivacyPolicyRule.ts +2 -0
- package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -0
- package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -0
- package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -0
- package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +14 -6
- package/src/utils/testing/StubCacheAdapter.ts +11 -17
package/src/Entity.ts
CHANGED
|
@@ -69,7 +69,7 @@ export default abstract class Entity<
|
|
|
69
69
|
return viewerContext
|
|
70
70
|
.getViewerScopedEntityCompanionForClass(this)
|
|
71
71
|
.getMutatorFactory()
|
|
72
|
-
.forCreate(queryContext);
|
|
72
|
+
.forCreate(queryContext, { cascadingDeleteCause: null });
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
@@ -111,7 +111,7 @@ export default abstract class Entity<
|
|
|
111
111
|
.getViewerContext()
|
|
112
112
|
.getViewerScopedEntityCompanionForClass(this)
|
|
113
113
|
.getMutatorFactory()
|
|
114
|
-
.forUpdate(existingEntity, queryContext);
|
|
114
|
+
.forUpdate(existingEntity, queryContext, { cascadingDeleteCause: null });
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
/**
|
|
@@ -152,7 +152,7 @@ export default abstract class Entity<
|
|
|
152
152
|
.getViewerContext()
|
|
153
153
|
.getViewerScopedEntityCompanionForClass(this)
|
|
154
154
|
.getMutatorFactory()
|
|
155
|
-
.forDelete(existingEntity, queryContext)
|
|
155
|
+
.forDelete(existingEntity, queryContext, { cascadingDeleteCause: null })
|
|
156
156
|
.deleteAsync();
|
|
157
157
|
}
|
|
158
158
|
|
|
@@ -194,7 +194,7 @@ export default abstract class Entity<
|
|
|
194
194
|
.getViewerContext()
|
|
195
195
|
.getViewerScopedEntityCompanionForClass(this)
|
|
196
196
|
.getMutatorFactory()
|
|
197
|
-
.forDelete(existingEntity, queryContext)
|
|
197
|
+
.forDelete(existingEntity, queryContext, { cascadingDeleteCause: null })
|
|
198
198
|
.enforceDeleteAsync();
|
|
199
199
|
}
|
|
200
200
|
|
|
@@ -251,6 +251,7 @@ export default abstract class Entity<
|
|
|
251
251
|
privacyPolicy.authorizeUpdateAsync(
|
|
252
252
|
existingEntity.getViewerContext(),
|
|
253
253
|
queryContext,
|
|
254
|
+
{ cascadingDeleteCause: null },
|
|
254
255
|
existingEntity,
|
|
255
256
|
companion.getMetricsAdapter()
|
|
256
257
|
)
|
|
@@ -304,6 +305,7 @@ export default abstract class Entity<
|
|
|
304
305
|
privacyPolicy.authorizeDeleteAsync(
|
|
305
306
|
existingEntity.getViewerContext(),
|
|
306
307
|
queryContext,
|
|
308
|
+
{ cascadingDeleteCause: null },
|
|
307
309
|
existingEntity,
|
|
308
310
|
companion.getMetricsAdapter()
|
|
309
311
|
)
|
|
@@ -69,7 +69,7 @@ export default class EntityAssociationLoader<
|
|
|
69
69
|
.getViewerContext()
|
|
70
70
|
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
|
|
71
71
|
.getLoaderFactory()
|
|
72
|
-
.forLoad(queryContext);
|
|
72
|
+
.forLoad(queryContext, { cascadingDeleteCause: null });
|
|
73
73
|
|
|
74
74
|
return (await loader.loadByIDAsync(associatedEntityID as unknown as TAssociatedID)) as Result<
|
|
75
75
|
null extends TFields[TIdentifyingField] ? TAssociatedEntity | null : TAssociatedEntity
|
|
@@ -123,7 +123,7 @@ export default class EntityAssociationLoader<
|
|
|
123
123
|
.getViewerContext()
|
|
124
124
|
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
|
|
125
125
|
.getLoaderFactory()
|
|
126
|
-
.forLoad(queryContext);
|
|
126
|
+
.forLoad(queryContext, { cascadingDeleteCause: null });
|
|
127
127
|
return await loader.loadManyByFieldEqualingAsync(
|
|
128
128
|
associatedEntityFieldContainingThisID,
|
|
129
129
|
thisID as any
|
|
@@ -180,7 +180,7 @@ export default class EntityAssociationLoader<
|
|
|
180
180
|
.getViewerContext()
|
|
181
181
|
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
|
|
182
182
|
.getLoaderFactory()
|
|
183
|
-
.forLoad(queryContext);
|
|
183
|
+
.forLoad(queryContext, { cascadingDeleteCause: null });
|
|
184
184
|
return await loader.loadByFieldEqualingAsync(
|
|
185
185
|
associatedEntityLookupByField,
|
|
186
186
|
associatedFieldValue as any
|
|
@@ -238,7 +238,7 @@ export default class EntityAssociationLoader<
|
|
|
238
238
|
.getViewerContext()
|
|
239
239
|
.getViewerScopedEntityCompanionForClass(associatedEntityClass)
|
|
240
240
|
.getLoaderFactory()
|
|
241
|
-
.forLoad(queryContext);
|
|
241
|
+
.forLoad(queryContext, { cascadingDeleteCause: null });
|
|
242
242
|
return await loader.loadManyByFieldEqualingAsync(
|
|
243
243
|
associatedEntityLookupByField,
|
|
244
244
|
associatedFieldValue as any
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import EntityConfiguration from './EntityConfiguration';
|
|
2
|
-
import { FieldTransformerMap } from './internal/EntityFieldTransformationUtils';
|
|
3
2
|
import { CacheLoadResult } from './internal/ReadThroughEntityCache';
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -9,13 +8,6 @@ import { CacheLoadResult } from './internal/ReadThroughEntityCache';
|
|
|
9
8
|
export default abstract class EntityCacheAdapter<TFields> {
|
|
10
9
|
constructor(protected readonly entityConfiguration: EntityConfiguration<TFields>) {}
|
|
11
10
|
|
|
12
|
-
/**
|
|
13
|
-
* Transformer definitions for field types. Used to modify values as they are read from or written to
|
|
14
|
-
* the cache. Override in concrete subclasses to change transformation behavior.
|
|
15
|
-
* If a field type is not present in the map, then fields of that type will not be transformed.
|
|
16
|
-
*/
|
|
17
|
-
public abstract getFieldTransformerMap(): FieldTransformerMap;
|
|
18
|
-
|
|
19
11
|
/**
|
|
20
12
|
* Load many objects from cache.
|
|
21
13
|
* @param fieldName - object field being queried
|
|
@@ -25,7 +17,7 @@ export default abstract class EntityCacheAdapter<TFields> {
|
|
|
25
17
|
public abstract loadManyAsync<N extends keyof TFields>(
|
|
26
18
|
fieldName: N,
|
|
27
19
|
fieldValues: readonly NonNullable<TFields[N]>[]
|
|
28
|
-
): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult
|
|
20
|
+
): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult<TFields>>>;
|
|
29
21
|
|
|
30
22
|
/**
|
|
31
23
|
* Cache many objects fetched from the DB.
|
|
@@ -34,7 +26,7 @@ export default abstract class EntityCacheAdapter<TFields> {
|
|
|
34
26
|
*/
|
|
35
27
|
public abstract cacheManyAsync<N extends keyof TFields>(
|
|
36
28
|
fieldName: N,
|
|
37
|
-
objectMap: ReadonlyMap<NonNullable<TFields[N]>,
|
|
29
|
+
objectMap: ReadonlyMap<NonNullable<TFields[N]>, Readonly<TFields>>
|
|
38
30
|
): Promise<void>;
|
|
39
31
|
|
|
40
32
|
/**
|
|
@@ -28,6 +28,9 @@ export enum EntityEdgeDeletionBehavior {
|
|
|
28
28
|
SET_NULL,
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Defines an association between entities. An association is primarily used to define cascading deletion behavior.
|
|
33
|
+
*/
|
|
31
34
|
export interface EntityAssociationDefinition<
|
|
32
35
|
TViewerContext extends ViewerContext,
|
|
33
36
|
TAssociatedFields,
|
|
@@ -83,6 +86,10 @@ export interface EntityAssociationDefinition<
|
|
|
83
86
|
edgeDeletionBehavior?: EntityEdgeDeletionBehavior;
|
|
84
87
|
}
|
|
85
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Definition for a field referencing a column in the underlying database. Specifies things like
|
|
91
|
+
* cache behavior and associations, and handles input validation.
|
|
92
|
+
*/
|
|
86
93
|
export abstract class EntityFieldDefinition<T> {
|
|
87
94
|
readonly columnName: string;
|
|
88
95
|
readonly cache: boolean;
|
|
@@ -93,6 +100,7 @@ export abstract class EntityFieldDefinition<T> {
|
|
|
93
100
|
* @param cache - Whether or not to cache loaded instances of the entity by this field. The column name is
|
|
94
101
|
* used to derive a cache key for the cache entry. If true, this column must be able uniquely
|
|
95
102
|
* identify the entity.
|
|
103
|
+
* @param association - Defines the association behavior for an entity that this column references.
|
|
96
104
|
*/
|
|
97
105
|
constructor({
|
|
98
106
|
columnName,
|
package/src/EntityFields.ts
CHANGED
|
@@ -2,59 +2,104 @@ import { validate as validateUUID } from 'uuid';
|
|
|
2
2
|
|
|
3
3
|
import { EntityFieldDefinition } from './EntityFieldDefinition';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* {@link EntityFieldDefinition} for a column with a JS string type.
|
|
7
|
+
*/
|
|
5
8
|
export class StringField extends EntityFieldDefinition<string> {
|
|
6
9
|
protected validateInputValueInternal(value: string): boolean {
|
|
7
10
|
return typeof value === 'string';
|
|
8
11
|
}
|
|
9
12
|
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* {@link EntityFieldDefinition} for a column with a JS string type.
|
|
16
|
+
* Enforces that the string is a valid UUID.
|
|
17
|
+
*/
|
|
10
18
|
export class UUIDField extends StringField {
|
|
11
19
|
protected override validateInputValueInternal(value: string): boolean {
|
|
12
20
|
return validateUUID(value);
|
|
13
21
|
}
|
|
14
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* {@link EntityFieldDefinition} for a column with a JS Date type.
|
|
26
|
+
*/
|
|
15
27
|
export class DateField extends EntityFieldDefinition<Date> {
|
|
16
28
|
protected validateInputValueInternal(value: Date): boolean {
|
|
17
29
|
return value instanceof Date;
|
|
18
30
|
}
|
|
19
31
|
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* {@link EntityFieldDefinition} for a column with a JS boolean type.
|
|
35
|
+
*/
|
|
20
36
|
export class BooleanField extends EntityFieldDefinition<boolean> {
|
|
21
37
|
protected validateInputValueInternal(value: boolean): boolean {
|
|
22
38
|
return typeof value === 'boolean';
|
|
23
39
|
}
|
|
24
40
|
}
|
|
25
41
|
|
|
42
|
+
/**
|
|
43
|
+
* {@link EntityFieldDefinition} for a column with a JS number type.
|
|
44
|
+
* Enforces that the number is an integer.
|
|
45
|
+
*/
|
|
26
46
|
export class IntField extends EntityFieldDefinition<number> {
|
|
27
47
|
protected validateInputValueInternal(value: number): boolean {
|
|
28
48
|
return typeof value === 'number' && Number.isInteger(value);
|
|
29
49
|
}
|
|
30
50
|
}
|
|
31
51
|
|
|
52
|
+
/**
|
|
53
|
+
* {@link EntityFieldDefinition} for a column with a JS number type.
|
|
54
|
+
* Enforces that the number is a float (which includes integers in JS).
|
|
55
|
+
*/
|
|
32
56
|
export class FloatField extends EntityFieldDefinition<number> {
|
|
33
57
|
protected validateInputValueInternal(value: number): boolean {
|
|
34
58
|
return typeof value === 'number';
|
|
35
59
|
}
|
|
36
60
|
}
|
|
37
61
|
|
|
62
|
+
/**
|
|
63
|
+
* {@link EntityFieldDefinition} for a column with a JS string array type.
|
|
64
|
+
* Enforces that every member of the string array is a string.
|
|
65
|
+
*/
|
|
38
66
|
export class StringArrayField extends EntityFieldDefinition<string[]> {
|
|
39
67
|
protected validateInputValueInternal(value: string[]): boolean {
|
|
40
68
|
return Array.isArray(value) && value.every((subValue) => typeof subValue === 'string');
|
|
41
69
|
}
|
|
42
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* {@link EntityFieldDefinition} for a column with a JS JSON object type.
|
|
74
|
+
*/
|
|
43
75
|
export class JSONObjectField extends EntityFieldDefinition<object> {
|
|
44
76
|
protected validateInputValueInternal(value: object): boolean {
|
|
45
77
|
return typeof value === 'object' && !Array.isArray(value);
|
|
46
78
|
}
|
|
47
79
|
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* {@link EntityFieldDefinition} for a enum column with a JS string or number type.
|
|
83
|
+
*/
|
|
48
84
|
export class EnumField extends EntityFieldDefinition<string | number> {
|
|
49
85
|
protected validateInputValueInternal(value: string | number): boolean {
|
|
50
86
|
return typeof value === 'number' || typeof value === 'string';
|
|
51
87
|
}
|
|
52
88
|
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* {@link EntityFieldDefinition} for a column with a JS JSON array type.
|
|
92
|
+
*/
|
|
53
93
|
export class JSONArrayField extends EntityFieldDefinition<any[]> {
|
|
54
94
|
protected validateInputValueInternal(value: any[]): boolean {
|
|
55
95
|
return Array.isArray(value);
|
|
56
96
|
}
|
|
57
97
|
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* {@link EntityFieldDefinition} for a column that may be a JS JSON array type.
|
|
101
|
+
* Does not do any validation.
|
|
102
|
+
*/
|
|
58
103
|
export class MaybeJSONArrayField extends EntityFieldDefinition<any | any[]> {
|
|
59
104
|
protected validateInputValueInternal(_value: any): boolean {
|
|
60
105
|
return true;
|
package/src/EntityLoader.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
QuerySelectionModifiers,
|
|
10
10
|
isSingleValueFieldEqualityCondition,
|
|
11
11
|
} from './EntityDatabaseAdapter';
|
|
12
|
-
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
|
|
12
|
+
import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
|
|
13
13
|
import { EntityQueryContext } from './EntityQueryContext';
|
|
14
14
|
import ReadonlyEntity from './ReadonlyEntity';
|
|
15
15
|
import ViewerContext from './ViewerContext';
|
|
@@ -40,6 +40,7 @@ export default class EntityLoader<
|
|
|
40
40
|
constructor(
|
|
41
41
|
private readonly viewerContext: TViewerContext,
|
|
42
42
|
private readonly queryContext: EntityQueryContext,
|
|
43
|
+
private readonly privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
43
44
|
private readonly entityConfiguration: EntityConfiguration<TFields>,
|
|
44
45
|
private readonly entityClass: IEntityClass<
|
|
45
46
|
TFields,
|
|
@@ -218,6 +219,7 @@ export default class EntityLoader<
|
|
|
218
219
|
this.privacyPolicy.authorizeReadAsync(
|
|
219
220
|
this.viewerContext,
|
|
220
221
|
this.queryContext,
|
|
222
|
+
this.privacyPolicyEvaluationContext,
|
|
221
223
|
uncheckedEntityResult.value,
|
|
222
224
|
this.metricsAdapter
|
|
223
225
|
)
|
|
@@ -272,6 +274,7 @@ export default class EntityLoader<
|
|
|
272
274
|
this.privacyPolicy.authorizeReadAsync(
|
|
273
275
|
this.viewerContext,
|
|
274
276
|
this.queryContext,
|
|
277
|
+
this.privacyPolicyEvaluationContext,
|
|
275
278
|
uncheckedEntityResult.value,
|
|
276
279
|
this.metricsAdapter
|
|
277
280
|
)
|
|
@@ -332,6 +335,7 @@ export default class EntityLoader<
|
|
|
332
335
|
this.privacyPolicy.authorizeReadAsync(
|
|
333
336
|
this.viewerContext,
|
|
334
337
|
this.queryContext,
|
|
338
|
+
this.privacyPolicyEvaluationContext,
|
|
335
339
|
uncheckedEntityResult.value,
|
|
336
340
|
this.metricsAdapter
|
|
337
341
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IEntityClass } from './Entity';
|
|
2
2
|
import EntityConfiguration from './EntityConfiguration';
|
|
3
3
|
import EntityLoader from './EntityLoader';
|
|
4
|
-
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
|
|
4
|
+
import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
|
|
5
5
|
import { EntityQueryContext } from './EntityQueryContext';
|
|
6
6
|
import ReadonlyEntity from './ReadonlyEntity';
|
|
7
7
|
import ViewerContext from './ViewerContext';
|
|
@@ -47,11 +47,13 @@ export default class EntityLoaderFactory<
|
|
|
47
47
|
*/
|
|
48
48
|
forLoad(
|
|
49
49
|
viewerContext: TViewerContext,
|
|
50
|
-
queryContext: EntityQueryContext
|
|
50
|
+
queryContext: EntityQueryContext,
|
|
51
|
+
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
51
52
|
): EntityLoader<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
52
53
|
return new EntityLoader(
|
|
53
54
|
viewerContext,
|
|
54
55
|
queryContext,
|
|
56
|
+
privacyPolicyEvaluationContext,
|
|
55
57
|
this.entityConfiguration,
|
|
56
58
|
this.entityClass,
|
|
57
59
|
this.privacyPolicyClass,
|
|
@@ -22,9 +22,19 @@ export type EntityValidatorMutationInfo<
|
|
|
22
22
|
previousValue: TEntity;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Information about a cascading deletion.
|
|
27
|
+
*/
|
|
28
|
+
export type EntityCascadingDeletionInfo = {
|
|
29
|
+
/**
|
|
30
|
+
* The entity that is being mutated at this step in the cascaded deletion.
|
|
31
|
+
*/
|
|
26
32
|
entity: Entity<any, any, any, any>;
|
|
27
|
-
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The cascade deletion that caused this mutation.
|
|
36
|
+
*/
|
|
37
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null;
|
|
28
38
|
};
|
|
29
39
|
|
|
30
40
|
export type EntityTriggerMutationInfo<
|
|
@@ -43,5 +53,5 @@ export type EntityTriggerMutationInfo<
|
|
|
43
53
|
}
|
|
44
54
|
| {
|
|
45
55
|
type: EntityMutationType.DELETE;
|
|
46
|
-
cascadingDeleteCause:
|
|
56
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null;
|
|
47
57
|
};
|
package/src/EntityMutator.ts
CHANGED
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
EntityValidatorMutationInfo,
|
|
12
12
|
EntityMutationType,
|
|
13
13
|
EntityTriggerMutationInfo,
|
|
14
|
-
|
|
14
|
+
EntityCascadingDeletionInfo,
|
|
15
15
|
} from './EntityMutationInfo';
|
|
16
16
|
import EntityMutationTriggerConfiguration, {
|
|
17
17
|
EntityMutationTrigger,
|
|
18
18
|
EntityNonTransactionalMutationTrigger,
|
|
19
19
|
} from './EntityMutationTriggerConfiguration';
|
|
20
20
|
import EntityMutationValidator from './EntityMutationValidator';
|
|
21
|
-
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
|
|
21
|
+
import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
|
|
22
22
|
import { EntityQueryContext, EntityTransactionalQueryContext } from './EntityQueryContext';
|
|
23
23
|
import ReadonlyEntity from './ReadonlyEntity';
|
|
24
24
|
import ViewerContext from './ViewerContext';
|
|
@@ -44,6 +44,7 @@ abstract class BaseMutator<
|
|
|
44
44
|
constructor(
|
|
45
45
|
protected readonly viewerContext: TViewerContext,
|
|
46
46
|
protected readonly queryContext: EntityQueryContext,
|
|
47
|
+
protected readonly privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
47
48
|
protected readonly entityConfiguration: EntityConfiguration<TFields>,
|
|
48
49
|
protected readonly entityClass: IEntityClass<
|
|
49
50
|
TFields,
|
|
@@ -220,6 +221,7 @@ export class CreateMutator<
|
|
|
220
221
|
this.privacyPolicy.authorizeCreateAsync(
|
|
221
222
|
this.viewerContext,
|
|
222
223
|
queryContext,
|
|
224
|
+
this.privacyPolicyEvaluationContext,
|
|
223
225
|
temporaryEntityForPrivacyCheck,
|
|
224
226
|
this.metricsAdapter
|
|
225
227
|
)
|
|
@@ -249,7 +251,11 @@ export class CreateMutator<
|
|
|
249
251
|
|
|
250
252
|
const insertResult = await this.databaseAdapter.insertAsync(queryContext, this.fieldsForEntity);
|
|
251
253
|
|
|
252
|
-
const entityLoader = this.entityLoaderFactory.forLoad(
|
|
254
|
+
const entityLoader = this.entityLoaderFactory.forLoad(
|
|
255
|
+
this.viewerContext,
|
|
256
|
+
queryContext,
|
|
257
|
+
this.privacyPolicyEvaluationContext
|
|
258
|
+
);
|
|
253
259
|
queryContext.appendPostCommitInvalidationCallback(
|
|
254
260
|
entityLoader.invalidateFieldsAsync.bind(entityLoader, insertResult)
|
|
255
261
|
);
|
|
@@ -309,6 +315,7 @@ export class UpdateMutator<
|
|
|
309
315
|
constructor(
|
|
310
316
|
viewerContext: TViewerContext,
|
|
311
317
|
queryContext: EntityQueryContext,
|
|
318
|
+
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
312
319
|
entityConfiguration: EntityConfiguration<TFields>,
|
|
313
320
|
entityClass: IEntityClass<
|
|
314
321
|
TFields,
|
|
@@ -348,6 +355,7 @@ export class UpdateMutator<
|
|
|
348
355
|
super(
|
|
349
356
|
viewerContext,
|
|
350
357
|
queryContext,
|
|
358
|
+
privacyPolicyEvaluationContext,
|
|
351
359
|
entityConfiguration,
|
|
352
360
|
entityClass,
|
|
353
361
|
privacyPolicy,
|
|
@@ -409,6 +417,7 @@ export class UpdateMutator<
|
|
|
409
417
|
this.privacyPolicy.authorizeUpdateAsync(
|
|
410
418
|
this.viewerContext,
|
|
411
419
|
queryContext,
|
|
420
|
+
this.privacyPolicyEvaluationContext,
|
|
412
421
|
entityAboutToBeUpdated,
|
|
413
422
|
this.metricsAdapter
|
|
414
423
|
)
|
|
@@ -443,7 +452,11 @@ export class UpdateMutator<
|
|
|
443
452
|
this.updatedFields
|
|
444
453
|
);
|
|
445
454
|
|
|
446
|
-
const entityLoader = this.entityLoaderFactory.forLoad(
|
|
455
|
+
const entityLoader = this.entityLoaderFactory.forLoad(
|
|
456
|
+
this.viewerContext,
|
|
457
|
+
queryContext,
|
|
458
|
+
this.privacyPolicyEvaluationContext
|
|
459
|
+
);
|
|
447
460
|
|
|
448
461
|
queryContext.appendPostCommitInvalidationCallback(
|
|
449
462
|
entityLoader.invalidateFieldsAsync.bind(
|
|
@@ -506,6 +519,7 @@ export class DeleteMutator<
|
|
|
506
519
|
constructor(
|
|
507
520
|
viewerContext: TViewerContext,
|
|
508
521
|
queryContext: EntityQueryContext,
|
|
522
|
+
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext,
|
|
509
523
|
entityConfiguration: EntityConfiguration<TFields>,
|
|
510
524
|
entityClass: IEntityClass<
|
|
511
525
|
TFields,
|
|
@@ -545,6 +559,7 @@ export class DeleteMutator<
|
|
|
545
559
|
super(
|
|
546
560
|
viewerContext,
|
|
547
561
|
queryContext,
|
|
562
|
+
privacyPolicyEvaluationContext,
|
|
548
563
|
entityConfiguration,
|
|
549
564
|
entityClass,
|
|
550
565
|
privacyPolicy,
|
|
@@ -578,7 +593,7 @@ export class DeleteMutator<
|
|
|
578
593
|
private async deleteInTransactionAsync(
|
|
579
594
|
processedEntityIdentifiersFromTransitiveDeletions: Set<string>,
|
|
580
595
|
skipDatabaseDeletion: boolean,
|
|
581
|
-
cascadingDeleteCause:
|
|
596
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null
|
|
582
597
|
): Promise<Result<void>> {
|
|
583
598
|
return await this.queryContext.runInTransactionIfNotInTransactionAsync((innerQueryContext) =>
|
|
584
599
|
this.deleteInternalAsync(
|
|
@@ -594,12 +609,13 @@ export class DeleteMutator<
|
|
|
594
609
|
queryContext: EntityTransactionalQueryContext,
|
|
595
610
|
processedEntityIdentifiersFromTransitiveDeletions: Set<string>,
|
|
596
611
|
skipDatabaseDeletion: boolean,
|
|
597
|
-
cascadingDeleteCause:
|
|
612
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null
|
|
598
613
|
): Promise<Result<void>> {
|
|
599
614
|
const authorizeDeleteResult = await asyncResult(
|
|
600
615
|
this.privacyPolicy.authorizeDeleteAsync(
|
|
601
616
|
this.viewerContext,
|
|
602
617
|
queryContext,
|
|
618
|
+
{ cascadingDeleteCause },
|
|
603
619
|
this.entity,
|
|
604
620
|
this.metricsAdapter
|
|
605
621
|
)
|
|
@@ -636,7 +652,9 @@ export class DeleteMutator<
|
|
|
636
652
|
);
|
|
637
653
|
}
|
|
638
654
|
|
|
639
|
-
const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext
|
|
655
|
+
const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
|
|
656
|
+
cascadingDeleteCause,
|
|
657
|
+
});
|
|
640
658
|
queryContext.appendPostCommitInvalidationCallback(
|
|
641
659
|
entityLoader.invalidateFieldsAsync.bind(entityLoader, this.entity.getAllDatabaseFields())
|
|
642
660
|
);
|
|
@@ -684,7 +702,7 @@ export class DeleteMutator<
|
|
|
684
702
|
entity: TEntity,
|
|
685
703
|
queryContext: EntityTransactionalQueryContext,
|
|
686
704
|
processedEntityIdentifiers: Set<string>,
|
|
687
|
-
cascadingDeleteCause:
|
|
705
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null
|
|
688
706
|
): Promise<void> {
|
|
689
707
|
// prevent infinite reference cycles by keeping track of entities already processed
|
|
690
708
|
if (processedEntityIdentifiers.has(entity.getUniqueIdentifier())) {
|
|
@@ -732,10 +750,15 @@ export class DeleteMutator<
|
|
|
732
750
|
.getViewerScopedEntityCompanionForClass(entityClass)
|
|
733
751
|
.getMutatorFactory();
|
|
734
752
|
|
|
753
|
+
const newCascadingDeleteCause = {
|
|
754
|
+
entity,
|
|
755
|
+
cascadingDeleteCause,
|
|
756
|
+
};
|
|
757
|
+
|
|
735
758
|
let inboundReferenceEntities: readonly ReadonlyEntity<any, any, any, any>[];
|
|
736
759
|
if (associatedEntityLookupByField) {
|
|
737
760
|
inboundReferenceEntities = await loaderFactory
|
|
738
|
-
.forLoad(queryContext)
|
|
761
|
+
.forLoad(queryContext, { cascadingDeleteCause: newCascadingDeleteCause })
|
|
739
762
|
.enforcing()
|
|
740
763
|
.loadManyByFieldEqualingAsync(
|
|
741
764
|
fieldName,
|
|
@@ -743,7 +766,7 @@ export class DeleteMutator<
|
|
|
743
766
|
);
|
|
744
767
|
} else {
|
|
745
768
|
inboundReferenceEntities = await loaderFactory
|
|
746
|
-
.forLoad(queryContext)
|
|
769
|
+
.forLoad(queryContext, { cascadingDeleteCause: newCascadingDeleteCause })
|
|
747
770
|
.enforcing()
|
|
748
771
|
.loadManyByFieldEqualingAsync(fieldName, entity.getID());
|
|
749
772
|
}
|
|
@@ -754,14 +777,13 @@ export class DeleteMutator<
|
|
|
754
777
|
await Promise.all(
|
|
755
778
|
inboundReferenceEntities.map((inboundReferenceEntity) =>
|
|
756
779
|
mutatorFactory
|
|
757
|
-
.forDelete(inboundReferenceEntity, queryContext
|
|
780
|
+
.forDelete(inboundReferenceEntity, queryContext, {
|
|
781
|
+
cascadingDeleteCause: newCascadingDeleteCause,
|
|
782
|
+
})
|
|
758
783
|
.deleteInTransactionAsync(
|
|
759
784
|
processedEntityIdentifiers,
|
|
760
785
|
/* skipDatabaseDeletion */ true, // deletion is handled by DB
|
|
761
|
-
|
|
762
|
-
entity,
|
|
763
|
-
cascadingDeleteCause,
|
|
764
|
-
}
|
|
786
|
+
newCascadingDeleteCause
|
|
765
787
|
)
|
|
766
788
|
)
|
|
767
789
|
);
|
|
@@ -771,7 +793,9 @@ export class DeleteMutator<
|
|
|
771
793
|
await Promise.all(
|
|
772
794
|
inboundReferenceEntities.map((inboundReferenceEntity) =>
|
|
773
795
|
mutatorFactory
|
|
774
|
-
.forUpdate(inboundReferenceEntity, queryContext
|
|
796
|
+
.forUpdate(inboundReferenceEntity, queryContext, {
|
|
797
|
+
cascadingDeleteCause: newCascadingDeleteCause,
|
|
798
|
+
})
|
|
775
799
|
.setField(fieldName, null)
|
|
776
800
|
.enforceUpdateAsync()
|
|
777
801
|
)
|
|
@@ -782,14 +806,13 @@ export class DeleteMutator<
|
|
|
782
806
|
await Promise.all(
|
|
783
807
|
inboundReferenceEntities.map((inboundReferenceEntity) =>
|
|
784
808
|
mutatorFactory
|
|
785
|
-
.forDelete(inboundReferenceEntity, queryContext
|
|
809
|
+
.forDelete(inboundReferenceEntity, queryContext, {
|
|
810
|
+
cascadingDeleteCause: newCascadingDeleteCause,
|
|
811
|
+
})
|
|
786
812
|
.deleteInTransactionAsync(
|
|
787
813
|
processedEntityIdentifiers,
|
|
788
814
|
/* skipDatabaseDeletion */ false,
|
|
789
|
-
|
|
790
|
-
entity,
|
|
791
|
-
cascadingDeleteCause,
|
|
792
|
-
}
|
|
815
|
+
newCascadingDeleteCause
|
|
793
816
|
)
|
|
794
817
|
)
|
|
795
818
|
);
|
|
@@ -5,7 +5,7 @@ import EntityLoaderFactory from './EntityLoaderFactory';
|
|
|
5
5
|
import EntityMutationTriggerConfiguration from './EntityMutationTriggerConfiguration';
|
|
6
6
|
import EntityMutationValidator from './EntityMutationValidator';
|
|
7
7
|
import { CreateMutator, UpdateMutator, DeleteMutator } from './EntityMutator';
|
|
8
|
-
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
|
|
8
|
+
import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
|
|
9
9
|
import { EntityQueryContext } from './EntityQueryContext';
|
|
10
10
|
import ViewerContext from './ViewerContext';
|
|
11
11
|
import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';
|
|
@@ -72,11 +72,13 @@ export default class EntityMutatorFactory<
|
|
|
72
72
|
*/
|
|
73
73
|
forCreate(
|
|
74
74
|
viewerContext: TViewerContext,
|
|
75
|
-
queryContext: EntityQueryContext
|
|
75
|
+
queryContext: EntityQueryContext,
|
|
76
|
+
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
76
77
|
): CreateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
77
78
|
return new CreateMutator(
|
|
78
79
|
viewerContext,
|
|
79
80
|
queryContext,
|
|
81
|
+
privacyPolicyEvaluationContext,
|
|
80
82
|
this.entityConfiguration,
|
|
81
83
|
this.entityClass,
|
|
82
84
|
this.privacyPolicy,
|
|
@@ -96,11 +98,13 @@ export default class EntityMutatorFactory<
|
|
|
96
98
|
*/
|
|
97
99
|
forUpdate(
|
|
98
100
|
existingEntity: TEntity,
|
|
99
|
-
queryContext: EntityQueryContext
|
|
101
|
+
queryContext: EntityQueryContext,
|
|
102
|
+
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
100
103
|
): UpdateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
101
104
|
return new UpdateMutator(
|
|
102
105
|
existingEntity.getViewerContext(),
|
|
103
106
|
queryContext,
|
|
107
|
+
privacyPolicyEvaluationContext,
|
|
104
108
|
this.entityConfiguration,
|
|
105
109
|
this.entityClass,
|
|
106
110
|
this.privacyPolicy,
|
|
@@ -120,11 +124,13 @@ export default class EntityMutatorFactory<
|
|
|
120
124
|
*/
|
|
121
125
|
forDelete(
|
|
122
126
|
existingEntity: TEntity,
|
|
123
|
-
queryContext: EntityQueryContext
|
|
127
|
+
queryContext: EntityQueryContext,
|
|
128
|
+
privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext
|
|
124
129
|
): DeleteMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
125
130
|
return new DeleteMutator(
|
|
126
131
|
existingEntity.getViewerContext(),
|
|
127
132
|
queryContext,
|
|
133
|
+
privacyPolicyEvaluationContext,
|
|
128
134
|
this.entityConfiguration,
|
|
129
135
|
this.entityClass,
|
|
130
136
|
this.privacyPolicy,
|