@expo/entity 0.25.3 → 0.27.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/LICENSE +21 -0
- package/build/ComposedEntityCacheAdapter.d.ts +4 -2
- package/build/ComposedEntityCacheAdapter.js +39 -3
- package/build/ComposedEntityCacheAdapter.js.map +1 -1
- package/build/ComposedSecondaryEntityCache.d.ts +3 -2
- package/build/ComposedSecondaryEntityCache.js +3 -2
- package/build/ComposedSecondaryEntityCache.js.map +1 -1
- package/build/EnforcingEntityLoader.d.ts +10 -10
- package/build/EnforcingEntityLoader.js +8 -8
- package/build/EnforcingEntityLoader.js.map +1 -1
- package/build/Entity.d.ts +5 -5
- package/build/Entity.js +9 -9
- package/build/Entity.js.map +1 -1
- package/build/EntityAssociationLoader.d.ts +9 -4
- package/build/EntityAssociationLoader.js +5 -0
- package/build/EntityAssociationLoader.js.map +1 -1
- package/build/EntityCacheAdapter.d.ts +1 -1
- package/build/EntityCompanion.d.ts +1 -1
- package/build/EntityCompanion.js +1 -1
- package/build/EntityCompanionProvider.d.ts +9 -9
- package/build/EntityCompanionProvider.js +3 -3
- package/build/EntityConfiguration.d.ts +2 -2
- package/build/EntityDatabaseAdapter.d.ts +15 -6
- package/build/EntityDatabaseAdapter.js +8 -2
- package/build/EntityDatabaseAdapter.js.map +1 -1
- package/build/EntityFieldDefinition.d.ts +16 -8
- package/build/EntityFieldDefinition.js +12 -5
- package/build/EntityFieldDefinition.js.map +1 -1
- package/build/EntityFields.d.ts +11 -11
- package/build/EntityFields.js +11 -11
- package/build/EntityLoader.d.ts +7 -7
- package/build/EntityLoader.js +7 -7
- package/build/EntityLoader.js.map +1 -1
- package/build/EntityMutationInfo.d.ts +2 -0
- package/build/EntityMutationTriggerConfiguration.d.ts +1 -1
- package/build/EntityMutationTriggerConfiguration.js +1 -1
- package/build/EntityMutator.d.ts +7 -9
- package/build/EntityMutator.js +59 -47
- 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 +18 -5
- package/build/EntityPrivacyPolicy.js +18 -5
- package/build/EntityPrivacyPolicy.js.map +1 -1
- package/build/EntityQueryContext.d.ts +12 -1
- package/build/EntityQueryContext.js +12 -1
- package/build/EntityQueryContext.js.map +1 -1
- package/build/EntityQueryContextProvider.d.ts +1 -1
- package/build/EntitySecondaryCacheLoader.d.ts +1 -1
- package/build/EntitySecondaryCacheLoader.js +1 -1
- package/build/GenericSecondaryEntityCache.d.ts +1 -1
- package/build/GenericSecondaryEntityCache.js +1 -1
- package/build/ReadonlyEntity.d.ts +1 -1
- package/build/ReadonlyEntity.js +1 -1
- package/build/ViewerContext.d.ts +2 -2
- package/build/ViewerContext.js +2 -2
- package/build/ViewerScopedEntityCompanion.d.ts +2 -2
- package/build/ViewerScopedEntityCompanion.js +2 -2
- package/build/ViewerScopedEntityLoaderFactory.d.ts +1 -1
- package/build/ViewerScopedEntityLoaderFactory.js +1 -1
- package/build/ViewerScopedEntityMutatorFactory.d.ts +5 -5
- package/build/ViewerScopedEntityMutatorFactory.js +7 -7
- package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
- package/build/__tests__/ComposedCacheAdapter-test.js +37 -4
- package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -1
- package/build/__tests__/EntityCommonUseCases-test.js +5 -1
- package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
- package/build/__tests__/EntityCompanion-test.js +5 -1
- package/build/__tests__/EntityCompanion-test.js.map +1 -1
- package/build/__tests__/EntityCompanionProvider-test.js +5 -1
- package/build/__tests__/EntityCompanionProvider-test.js.map +1 -1
- package/build/__tests__/EntityEdges-test.js +199 -33
- package/build/__tests__/EntityEdges-test.js.map +1 -1
- package/build/__tests__/EntityLoader-test.js +5 -1
- package/build/__tests__/EntityLoader-test.js.map +1 -1
- package/build/__tests__/EntityMutator-test.js +38 -53
- package/build/__tests__/EntityMutator-test.js.map +1 -1
- package/build/__tests__/EntityPrivacyPolicy-test.js +5 -1
- package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
- package/build/__tests__/EntitySelfReferentialEdges-test.js +2 -2
- package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js +5 -1
- package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +2 -3
- package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
- package/build/errors/EntityCacheAdapterError.js +5 -1
- package/build/errors/EntityCacheAdapterError.js.map +1 -1
- package/build/errors/EntityDatabaseAdapterError.js +5 -1
- package/build/errors/EntityDatabaseAdapterError.js.map +1 -1
- package/build/errors/EntityInvalidFieldValueError.js +6 -2
- package/build/errors/EntityInvalidFieldValueError.js.map +1 -1
- package/build/errors/EntityNotAuthorizedError.js +5 -1
- package/build/errors/EntityNotAuthorizedError.js.map +1 -1
- package/build/errors/EntityNotFoundError.js +6 -2
- package/build/errors/EntityNotFoundError.js.map +1 -1
- package/build/index.js +5 -1
- package/build/index.js.map +1 -1
- package/build/internal/EntityDataManager.d.ts +5 -5
- package/build/internal/EntityDataManager.js +10 -7
- package/build/internal/EntityDataManager.js.map +1 -1
- package/build/internal/EntityFieldTransformationUtils.js +1 -1
- package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
- package/build/internal/ReadThroughEntityCache.d.ts +2 -2
- package/build/internal/ReadThroughEntityCache.js +3 -3
- package/build/internal/ReadThroughEntityCache.js.map +1 -1
- package/build/internal/__tests__/EntityDataManager-test.js +12 -7
- package/build/internal/__tests__/EntityDataManager-test.js.map +1 -1
- package/build/internal/__tests__/ReadThroughEntityCache-test.js +5 -1
- package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
- package/build/metrics/IEntityMetricsAdapter.d.ts +63 -18
- package/build/metrics/IEntityMetricsAdapter.js +17 -1
- package/build/metrics/IEntityMetricsAdapter.js.map +1 -1
- package/build/metrics/NoOpEntityMetricsAdapter.d.ts +1 -3
- package/build/metrics/NoOpEntityMetricsAdapter.js +1 -3
- package/build/metrics/NoOpEntityMetricsAdapter.js.map +1 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js +5 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js +5 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.js +5 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
- package/build/rules/PrivacyPolicyRule.d.ts +1 -1
- package/build/rules/PrivacyPolicyRule.js +1 -1
- package/build/utils/collections/maps.d.ts +1 -1
- package/build/utils/collections/maps.js +1 -1
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +1 -2
- package/build/utils/testing/StubDatabaseAdapter.js +6 -2
- package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
- package/build/utils/testing/StubQueryContextProvider.d.ts +1 -1
- package/build/utils/testing/StubQueryContextProvider.js +2 -0
- package/build/utils/testing/StubQueryContextProvider.js.map +1 -1
- package/package.json +3 -2
- package/src/ComposedEntityCacheAdapter.ts +44 -3
- package/src/ComposedSecondaryEntityCache.ts +3 -2
- package/src/EnforcingEntityLoader.ts +14 -10
- package/src/Entity.ts +9 -9
- package/src/EntityAssociationLoader.ts +9 -4
- package/src/EntityCacheAdapter.ts +1 -1
- package/src/EntityCompanion.ts +1 -1
- package/src/EntityCompanionProvider.ts +9 -9
- package/src/EntityConfiguration.ts +2 -2
- package/src/EntityDatabaseAdapter.ts +33 -6
- package/src/EntityFieldDefinition.ts +15 -6
- package/src/EntityFields.ts +11 -11
- package/src/EntityLoader.ts +11 -8
- package/src/EntityMutationInfo.ts +2 -0
- package/src/EntityMutationTriggerConfiguration.ts +1 -1
- package/src/EntityMutator.ts +99 -68
- package/src/EntityMutatorFactory.ts +4 -10
- package/src/EntityPrivacyPolicy.ts +20 -5
- package/src/EntityQueryContext.ts +12 -1
- package/src/EntityQueryContextProvider.ts +1 -1
- package/src/EntitySecondaryCacheLoader.ts +1 -1
- package/src/GenericSecondaryEntityCache.ts +1 -1
- package/src/ReadonlyEntity.ts +1 -1
- package/src/ViewerContext.ts +2 -2
- package/src/ViewerScopedEntityCompanion.ts +2 -2
- package/src/ViewerScopedEntityLoaderFactory.ts +1 -1
- package/src/ViewerScopedEntityMutatorFactory.ts +8 -23
- package/src/__tests__/ComposedCacheAdapter-test.ts +43 -4
- package/src/__tests__/EntityEdges-test.ts +287 -32
- package/src/__tests__/EntityMutator-test.ts +33 -54
- package/src/__tests__/EntitySelfReferentialEdges-test.ts +2 -2
- package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +2 -6
- package/src/errors/EntityInvalidFieldValueError.ts +1 -1
- package/src/errors/EntityNotFoundError.ts +1 -1
- package/src/internal/EntityDataManager.ts +18 -9
- package/src/internal/EntityFieldTransformationUtils.ts +1 -1
- package/src/internal/ReadThroughEntityCache.ts +5 -3
- package/src/internal/__tests__/EntityDataManager-test.ts +11 -8
- package/src/metrics/IEntityMetricsAdapter.ts +73 -20
- package/src/metrics/NoOpEntityMetricsAdapter.ts +1 -5
- package/src/rules/PrivacyPolicyRule.ts +1 -1
- package/src/utils/collections/maps.ts +1 -1
- package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +1 -1
- package/src/utils/testing/StubDatabaseAdapter.ts +4 -1
- package/src/utils/testing/StubQueryContextProvider.ts +1 -1
package/src/Entity.ts
CHANGED
|
@@ -11,19 +11,19 @@ import ViewerContext from './ViewerContext';
|
|
|
11
11
|
* Entity is a privacy-first data model.
|
|
12
12
|
*
|
|
13
13
|
* A instance of an entity represents a single "row" of persisted data in a database that a
|
|
14
|
-
* viewer, represented by the corresponding
|
|
14
|
+
* viewer, represented by the corresponding ViewerContext, has permission to read.
|
|
15
15
|
*
|
|
16
16
|
* Create, read, update, and delete permissions for an entity are declaratively defined using an
|
|
17
|
-
*
|
|
17
|
+
* EntityPrivacyPolicy.
|
|
18
18
|
*
|
|
19
|
-
* Entites are loaded through an
|
|
19
|
+
* Entites are loaded through an EntityLoader, which is responsible for
|
|
20
20
|
* orchestrating fetching, caching, and authorization of reading "rows".
|
|
21
21
|
*
|
|
22
|
-
* Entities are mutated and deleted through an
|
|
22
|
+
* Entities are mutated and deleted through an EntityMutator, which is responsible for
|
|
23
23
|
* orchestrating database writes, cache invalidation, and authorization of writing "rows".
|
|
24
24
|
*
|
|
25
25
|
* All concrete entity implementations should extend this class and provide their
|
|
26
|
-
* own
|
|
26
|
+
* own EntityCompanionDefinition.
|
|
27
27
|
*/
|
|
28
28
|
export default abstract class Entity<
|
|
29
29
|
TFields,
|
|
@@ -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);
|
|
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);
|
|
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)
|
|
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)
|
|
198
198
|
.enforceDeleteAsync();
|
|
199
199
|
}
|
|
200
200
|
|
|
@@ -6,6 +6,11 @@ import { EntityQueryContext } from './EntityQueryContext';
|
|
|
6
6
|
import ReadonlyEntity from './ReadonlyEntity';
|
|
7
7
|
import ViewerContext from './ViewerContext';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* An association loader is a set of convenience methods for loading entities
|
|
11
|
+
* associated with an entity. In relational databases, these entities are often referenced
|
|
12
|
+
* by foreign keys.
|
|
13
|
+
*/
|
|
9
14
|
export default class EntityAssociationLoader<
|
|
10
15
|
TFields,
|
|
11
16
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
@@ -246,7 +251,7 @@ export default class EntityAssociationLoader<
|
|
|
246
251
|
}
|
|
247
252
|
|
|
248
253
|
/**
|
|
249
|
-
* Load an associated entity by folding a sequence of
|
|
254
|
+
* Load an associated entity by folding a sequence of EntityLoadThroughDirective. At each
|
|
250
255
|
* fold step, load an associated entity identified by a field value of the current fold value.
|
|
251
256
|
* @param loadDirectives - associated entity load directives instructing each step of the fold
|
|
252
257
|
* @param queryContext - query context in which to perform the loads
|
|
@@ -280,7 +285,7 @@ export default class EntityAssociationLoader<
|
|
|
280
285
|
): Promise<Result<TEntity2> | null>;
|
|
281
286
|
|
|
282
287
|
/**
|
|
283
|
-
* Load an associated entity by folding a sequence of
|
|
288
|
+
* Load an associated entity by folding a sequence of EntityLoadThroughDirective. At each
|
|
284
289
|
* fold step, load an associated entity identified by a field value of the current fold value.
|
|
285
290
|
* @param loadDirectives - associated entity load directives instructing each step of the fold
|
|
286
291
|
* @param queryContext - query context in which to perform the loads
|
|
@@ -335,7 +340,7 @@ export default class EntityAssociationLoader<
|
|
|
335
340
|
): Promise<Result<TEntity3> | null>;
|
|
336
341
|
|
|
337
342
|
/**
|
|
338
|
-
* Load an associated entity by folding a sequence of
|
|
343
|
+
* Load an associated entity by folding a sequence of EntityLoadThroughDirective. At each
|
|
339
344
|
* fold step, load an associated entity identified by a field value of the current fold value.
|
|
340
345
|
* @param loadDirectives - associated entity load directives instructing each step of the fold
|
|
341
346
|
* @param queryContext - query context in which to perform the loads
|
|
@@ -411,7 +416,7 @@ export default class EntityAssociationLoader<
|
|
|
411
416
|
): Promise<Result<TEntity4> | null>;
|
|
412
417
|
|
|
413
418
|
/**
|
|
414
|
-
* Load an associated entity by folding a sequence of
|
|
419
|
+
* Load an associated entity by folding a sequence of EntityLoadThroughDirective. At each
|
|
415
420
|
* fold step, load an associated entity identified by a field value of the current fold value.
|
|
416
421
|
* @param loadDirectives - associated entity load directives instructing each step of the fold
|
|
417
422
|
* @param queryContext - query context in which to perform the loads
|
|
@@ -32,7 +32,7 @@ export default abstract class EntityCacheAdapter<TFields> {
|
|
|
32
32
|
/**
|
|
33
33
|
* Negatively cache objects that could not be found in the cache or DB.
|
|
34
34
|
* @param fieldName - object field being queried
|
|
35
|
-
* @param fieldValues - fieldValues for objects reported as
|
|
35
|
+
* @param fieldValues - fieldValues for objects reported as CacheStatus.NEGATIVE
|
|
36
36
|
* in the cache and not found in the DB.
|
|
37
37
|
*/
|
|
38
38
|
public abstract cacheDBMissesAsync<N extends keyof TFields>(
|
package/src/EntityCompanion.ts
CHANGED
|
@@ -132,7 +132,7 @@ export default class EntityCompanion<
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
|
-
* Get the
|
|
135
|
+
* Get the IEntityMetricsAdapter for this companion.
|
|
136
136
|
*/
|
|
137
137
|
getMetricsAdapter(): IEntityMetricsAdapter {
|
|
138
138
|
return this.metricsAdapter;
|
|
@@ -109,15 +109,15 @@ export class EntityCompanionDefinition<
|
|
|
109
109
|
TSelectedFields
|
|
110
110
|
>;
|
|
111
111
|
/**
|
|
112
|
-
* The
|
|
112
|
+
* The EntityConfiguration for this entity.
|
|
113
113
|
*/
|
|
114
114
|
entityConfiguration: EntityConfiguration<TFields>;
|
|
115
115
|
/**
|
|
116
|
-
* The
|
|
116
|
+
* The EntityPrivacyPolicy class for this entity.
|
|
117
117
|
*/
|
|
118
118
|
privacyPolicyClass: IPrivacyPolicyClass<TPrivacyPolicy>;
|
|
119
119
|
/**
|
|
120
|
-
* An optional list of
|
|
120
|
+
* An optional list of EntityMutationValidator for this entity.
|
|
121
121
|
*/
|
|
122
122
|
mutationValidators?: () => EntityMutationValidator<
|
|
123
123
|
TFields,
|
|
@@ -127,7 +127,7 @@ export class EntityCompanionDefinition<
|
|
|
127
127
|
TSelectedFields
|
|
128
128
|
>[];
|
|
129
129
|
/**
|
|
130
|
-
* An optional list of
|
|
130
|
+
* An optional list of EntityMutationTrigger for this entity.
|
|
131
131
|
*/
|
|
132
132
|
mutationTriggers?: () => EntityMutationTriggerConfiguration<
|
|
133
133
|
TFields,
|
|
@@ -137,8 +137,8 @@ export class EntityCompanionDefinition<
|
|
|
137
137
|
TSelectedFields
|
|
138
138
|
>;
|
|
139
139
|
/**
|
|
140
|
-
* An optional subset of fields defined in the
|
|
141
|
-
* For use when multiple types of entities are backed by a single table (
|
|
140
|
+
* An optional subset of fields defined in the EntityConfiguration which belong to this entity.
|
|
141
|
+
* For use when multiple types of entities are backed by a single table (EntityConfiguration) yet
|
|
142
142
|
* only expose a subset of the fields.
|
|
143
143
|
*/
|
|
144
144
|
entitySelectedFields?: TSelectedFields[];
|
|
@@ -155,11 +155,11 @@ export class EntityCompanionDefinition<
|
|
|
155
155
|
/**
|
|
156
156
|
* An instance of the Entity framework.
|
|
157
157
|
*
|
|
158
|
-
* Required to create a
|
|
158
|
+
* Required to create a ViewerContext, which is the application entry point
|
|
159
159
|
* into the framework.
|
|
160
160
|
*
|
|
161
161
|
* Internally, this is a lazy entity companion factory that instantiates and caches one
|
|
162
|
-
*
|
|
162
|
+
* EntityCompanion for each type of Entity.
|
|
163
163
|
*/
|
|
164
164
|
export default class EntityCompanionProvider {
|
|
165
165
|
private readonly companionMap: Map<string, EntityCompanion<any, any, any, any, any, any>> =
|
|
@@ -169,7 +169,7 @@ export default class EntityCompanionProvider {
|
|
|
169
169
|
|
|
170
170
|
/**
|
|
171
171
|
* Instantiate an Entity framework.
|
|
172
|
-
* @param metricsAdapter - An
|
|
172
|
+
* @param metricsAdapter - An IEntityMetricsAdapter for collecting metrics on this instance
|
|
173
173
|
* @param databaseAdapterFlavors - Database adapter configurations for this instance
|
|
174
174
|
* @param cacheAdapterFlavors - Cache adapter configurations for this instance
|
|
175
175
|
*/
|
|
@@ -41,12 +41,12 @@ export default class EntityConfiguration<TFields> {
|
|
|
41
41
|
tableName: string;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
* Map from each entity field to an
|
|
44
|
+
* Map from each entity field to an EntityFieldDefinition specifying information about the field.
|
|
45
45
|
*/
|
|
46
46
|
schema: Record<keyof TFields, EntityFieldDefinition<any>>;
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* List of other entity types that reference this type in
|
|
49
|
+
* List of other entity types that reference this type in EntityFieldDefinition associations.
|
|
50
50
|
*/
|
|
51
51
|
getInboundEdges?: () => IEntityClass<any, any, any, any, any, any>[];
|
|
52
52
|
|
|
@@ -7,12 +7,18 @@ import {
|
|
|
7
7
|
FieldTransformerMap,
|
|
8
8
|
} from './internal/EntityFieldTransformationUtils';
|
|
9
9
|
|
|
10
|
-
interface SingleValueFieldEqualityCondition<
|
|
10
|
+
export interface SingleValueFieldEqualityCondition<
|
|
11
|
+
TFields,
|
|
12
|
+
N extends keyof TFields = keyof TFields
|
|
13
|
+
> {
|
|
11
14
|
fieldName: N;
|
|
12
15
|
fieldValue: TFields[N];
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
interface MultiValueFieldEqualityCondition<
|
|
18
|
+
export interface MultiValueFieldEqualityCondition<
|
|
19
|
+
TFields,
|
|
20
|
+
N extends keyof TFields = keyof TFields
|
|
21
|
+
> {
|
|
16
22
|
fieldName: N;
|
|
17
23
|
fieldValues: readonly TFields[N][];
|
|
18
24
|
}
|
|
@@ -68,6 +74,14 @@ export interface QuerySelectionModifiers<TFields> {
|
|
|
68
74
|
limit?: number;
|
|
69
75
|
}
|
|
70
76
|
|
|
77
|
+
export interface QuerySelectionModifiersWithOrderByRaw<TFields>
|
|
78
|
+
extends QuerySelectionModifiers<TFields> {
|
|
79
|
+
/**
|
|
80
|
+
* Order the entities by a raw SQL `ORDER BY` clause.
|
|
81
|
+
*/
|
|
82
|
+
orderByRaw?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
71
85
|
export interface TableQuerySelectionModifiers {
|
|
72
86
|
orderBy:
|
|
73
87
|
| {
|
|
@@ -79,6 +93,10 @@ export interface TableQuerySelectionModifiers {
|
|
|
79
93
|
limit: number | undefined;
|
|
80
94
|
}
|
|
81
95
|
|
|
96
|
+
export interface TableQuerySelectionModifiersWithOrderByRaw extends TableQuerySelectionModifiers {
|
|
97
|
+
orderByRaw: string | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
82
100
|
/**
|
|
83
101
|
* A database adapter is an interface by which entity objects can be
|
|
84
102
|
* fetched, inserted, updated, and deleted from a database. This base class
|
|
@@ -149,7 +167,7 @@ export default abstract class EntityDatabaseAdapter<TFields> {
|
|
|
149
167
|
*
|
|
150
168
|
* @param queryContext - query context with which to perform the fetch
|
|
151
169
|
* @param fieldEqualityOperands - list of field equality where clause operand specifications
|
|
152
|
-
* @param querySelectionModifiers - limit, offset, and
|
|
170
|
+
* @param querySelectionModifiers - limit, offset, orderBy, and orderByRaw for the query
|
|
153
171
|
* @returns array of objects matching the query
|
|
154
172
|
*/
|
|
155
173
|
async fetchManyByFieldEqualityConjunctionAsync<N extends keyof TFields>(
|
|
@@ -207,14 +225,14 @@ export default abstract class EntityDatabaseAdapter<TFields> {
|
|
|
207
225
|
queryContext: EntityQueryContext,
|
|
208
226
|
rawWhereClause: string,
|
|
209
227
|
bindings: any[] | object,
|
|
210
|
-
querySelectionModifiers:
|
|
228
|
+
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>
|
|
211
229
|
): Promise<readonly Readonly<TFields>[]> {
|
|
212
230
|
const results = await this.fetchManyByRawWhereClauseInternalAsync(
|
|
213
231
|
queryContext.getQueryInterface(),
|
|
214
232
|
this.entityConfiguration.tableName,
|
|
215
233
|
rawWhereClause,
|
|
216
234
|
bindings,
|
|
217
|
-
this.
|
|
235
|
+
this.convertToTableQueryModifiersWithOrderByRaw(querySelectionModifiers)
|
|
218
236
|
);
|
|
219
237
|
|
|
220
238
|
return results.map((result) =>
|
|
@@ -227,7 +245,7 @@ export default abstract class EntityDatabaseAdapter<TFields> {
|
|
|
227
245
|
tableName: string,
|
|
228
246
|
rawWhereClause: string,
|
|
229
247
|
bindings: any[] | object,
|
|
230
|
-
querySelectionModifiers:
|
|
248
|
+
querySelectionModifiers: TableQuerySelectionModifiersWithOrderByRaw
|
|
231
249
|
): Promise<object[]>;
|
|
232
250
|
|
|
233
251
|
/**
|
|
@@ -363,6 +381,15 @@ export default abstract class EntityDatabaseAdapter<TFields> {
|
|
|
363
381
|
id: any
|
|
364
382
|
): Promise<number>;
|
|
365
383
|
|
|
384
|
+
private convertToTableQueryModifiersWithOrderByRaw(
|
|
385
|
+
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>
|
|
386
|
+
): TableQuerySelectionModifiersWithOrderByRaw {
|
|
387
|
+
return {
|
|
388
|
+
...this.convertToTableQueryModifiers(querySelectionModifiers),
|
|
389
|
+
orderByRaw: querySelectionModifiers.orderByRaw,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
366
393
|
private convertToTableQueryModifiers(
|
|
367
394
|
querySelectionModifiers: QuerySelectionModifiers<TFields>
|
|
368
395
|
): TableQuerySelectionModifiers {
|
|
@@ -5,13 +5,21 @@ import ViewerContext from './ViewerContext';
|
|
|
5
5
|
|
|
6
6
|
export enum EntityEdgeDeletionBehavior {
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Invalidate the cache for all entities that reference the entity
|
|
9
9
|
* being deleted through this field, and transitively run deletions on those entities.
|
|
10
10
|
* This is most useful when the database itself expresses foreign
|
|
11
|
-
* keys and cascading deletes
|
|
11
|
+
* keys and cascading deletes and the entity framework just needs to
|
|
12
12
|
* be kept consistent with the state of the database.
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
CASCADE_DELETE_INVALIDATE_CACHE_ONLY,
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Invalidate the cache for all entities that reference the entity
|
|
18
|
+
* being deleted through this field. This is most useful when the database itself expresses
|
|
19
|
+
* foreign keys and cascading "SET NULL"s and the entity framework just needs to be
|
|
20
|
+
* kept consistent with the state of the database.
|
|
21
|
+
*/
|
|
22
|
+
SET_NULL_INVALIDATE_CACHE_ONLY,
|
|
15
23
|
|
|
16
24
|
/**
|
|
17
25
|
* Delete all entities that reference the entity being deleted through this field. This is very similar
|
|
@@ -74,16 +82,17 @@ export interface EntityAssociationDefinition<
|
|
|
74
82
|
*
|
|
75
83
|
* @remarks
|
|
76
84
|
* The entity framework doesn't prescribe a one-size-fits-all solution for referential
|
|
77
|
-
* integrity; instead it exposes mechanisms that
|
|
85
|
+
* integrity; instead it exposes mechanisms that support both database foreign key constraints
|
|
78
86
|
* and implicit entity-specified foreign keys. Choosing which approach to use often depends on
|
|
79
87
|
* application requirements, and sometimes even a mix-and-match is the right choice.
|
|
80
88
|
*
|
|
81
89
|
* - If referential integrity is critical to your application, database foreign key constraints
|
|
82
|
-
* combined with
|
|
90
|
+
* combined with EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE_ONLY or
|
|
91
|
+
* EntityEdgeDeletionBehavior.SET_NULL_INVALIDATE_CACHE_ONLY are recommended.
|
|
83
92
|
* - If the database being used doesn't support foreign keys, then using the entity framework for referential
|
|
84
93
|
* integrity is recommended.
|
|
85
94
|
*/
|
|
86
|
-
edgeDeletionBehavior
|
|
95
|
+
edgeDeletionBehavior: EntityEdgeDeletionBehavior;
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
/**
|
package/src/EntityFields.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { validate as validateUUID } from 'uuid';
|
|
|
3
3
|
import { EntityFieldDefinition } from './EntityFieldDefinition';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* EntityFieldDefinition for a column with a JS string type.
|
|
7
7
|
*/
|
|
8
8
|
export class StringField extends EntityFieldDefinition<string> {
|
|
9
9
|
protected validateInputValueInternal(value: string): boolean {
|
|
@@ -12,7 +12,7 @@ export class StringField extends EntityFieldDefinition<string> {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* EntityFieldDefinition for a column with a JS string type.
|
|
16
16
|
* Enforces that the string is a valid UUID.
|
|
17
17
|
*/
|
|
18
18
|
export class UUIDField extends StringField {
|
|
@@ -22,7 +22,7 @@ export class UUIDField extends StringField {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* EntityFieldDefinition for a column with a JS Date type.
|
|
26
26
|
*/
|
|
27
27
|
export class DateField extends EntityFieldDefinition<Date> {
|
|
28
28
|
protected validateInputValueInternal(value: Date): boolean {
|
|
@@ -31,7 +31,7 @@ export class DateField extends EntityFieldDefinition<Date> {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* EntityFieldDefinition for a column with a JS boolean type.
|
|
35
35
|
*/
|
|
36
36
|
export class BooleanField extends EntityFieldDefinition<boolean> {
|
|
37
37
|
protected validateInputValueInternal(value: boolean): boolean {
|
|
@@ -40,7 +40,7 @@ export class BooleanField extends EntityFieldDefinition<boolean> {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
43
|
+
* EntityFieldDefinition for a column with a JS number type.
|
|
44
44
|
* Enforces that the number is an integer.
|
|
45
45
|
*/
|
|
46
46
|
export class IntField extends EntityFieldDefinition<number> {
|
|
@@ -50,7 +50,7 @@ export class IntField extends EntityFieldDefinition<number> {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
53
|
+
* EntityFieldDefinition for a column with a JS number type.
|
|
54
54
|
* Enforces that the number is a float (which includes integers in JS).
|
|
55
55
|
*/
|
|
56
56
|
export class FloatField extends EntityFieldDefinition<number> {
|
|
@@ -60,7 +60,7 @@ export class FloatField extends EntityFieldDefinition<number> {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* EntityFieldDefinition for a column with a JS string array type.
|
|
64
64
|
* Enforces that every member of the string array is a string.
|
|
65
65
|
*/
|
|
66
66
|
export class StringArrayField extends EntityFieldDefinition<string[]> {
|
|
@@ -70,7 +70,7 @@ export class StringArrayField extends EntityFieldDefinition<string[]> {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* EntityFieldDefinition for a column with a JS JSON object type.
|
|
74
74
|
*/
|
|
75
75
|
export class JSONObjectField extends EntityFieldDefinition<object> {
|
|
76
76
|
protected validateInputValueInternal(value: object): boolean {
|
|
@@ -79,7 +79,7 @@ export class JSONObjectField extends EntityFieldDefinition<object> {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
82
|
+
* EntityFieldDefinition for a enum column with a JS string or number type.
|
|
83
83
|
*/
|
|
84
84
|
export class EnumField extends EntityFieldDefinition<string | number> {
|
|
85
85
|
protected validateInputValueInternal(value: string | number): boolean {
|
|
@@ -88,7 +88,7 @@ export class EnumField extends EntityFieldDefinition<string | number> {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
91
|
+
* EntityFieldDefinition for a column with a JS JSON array type.
|
|
92
92
|
*/
|
|
93
93
|
export class JSONArrayField extends EntityFieldDefinition<any[]> {
|
|
94
94
|
protected validateInputValueInternal(value: any[]): boolean {
|
|
@@ -97,7 +97,7 @@ export class JSONArrayField extends EntityFieldDefinition<any[]> {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
-
*
|
|
100
|
+
* EntityFieldDefinition for a column that may be a JS JSON array type.
|
|
101
101
|
* Does not do any validation.
|
|
102
102
|
*/
|
|
103
103
|
export class MaybeJSONArrayField extends EntityFieldDefinition<any | any[]> {
|
package/src/EntityLoader.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
FieldEqualityCondition,
|
|
9
9
|
QuerySelectionModifiers,
|
|
10
10
|
isSingleValueFieldEqualityCondition,
|
|
11
|
+
QuerySelectionModifiersWithOrderByRaw,
|
|
11
12
|
} from './EntityDatabaseAdapter';
|
|
12
13
|
import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
|
|
13
14
|
import { EntityQueryContext } from './EntityQueryContext';
|
|
@@ -21,7 +22,7 @@ import { mapMap, mapMapAsync } from './utils/collections/maps';
|
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* The primary interface for loading entities. All normal loads are batched,
|
|
24
|
-
* cached, and authorized against the entity's
|
|
25
|
+
* cached, and authorized against the entity's EntityPrivacyPolicy.
|
|
25
26
|
*/
|
|
26
27
|
export default class EntityLoader<
|
|
27
28
|
TFields,
|
|
@@ -126,7 +127,9 @@ export default class EntityLoader<
|
|
|
126
127
|
const entityResults = await this.loadManyByFieldEqualingAsync(uniqueFieldName, fieldValue);
|
|
127
128
|
invariant(
|
|
128
129
|
entityResults.length <= 1,
|
|
129
|
-
`loadByFieldEqualing: Multiple entities of type ${this.entityClass.name} found for ${
|
|
130
|
+
`loadByFieldEqualing: Multiple entities of type ${this.entityClass.name} found for ${String(
|
|
131
|
+
uniqueFieldName
|
|
132
|
+
)}=${fieldValue}`
|
|
130
133
|
);
|
|
131
134
|
return entityResults[0] ?? null;
|
|
132
135
|
}
|
|
@@ -242,12 +245,12 @@ export default class EntityLoader<
|
|
|
242
245
|
* bindings: `[1]`
|
|
243
246
|
* Entites returned `WHERE id = 1`
|
|
244
247
|
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
248
|
+
* http://knexjs.org/#Builder-whereRaw
|
|
249
|
+
* http://knexjs.org/#Raw-Bindings
|
|
247
250
|
*
|
|
248
251
|
* @param rawWhereClause - parameterized SQL WHERE clause with positional binding placeholders or named binding placeholders
|
|
249
252
|
* @param bindings - array of positional bindings or object of named bindings
|
|
250
|
-
* @param querySelectionModifiers - limit, offset, and
|
|
253
|
+
* @param querySelectionModifiers - limit, offset, orderBy, and orderByRaw for the query
|
|
251
254
|
* @returns array of entity results that match the query, where result error can be UnauthorizedError
|
|
252
255
|
* @throws Error when rawWhereClause or bindings are invalid
|
|
253
256
|
*
|
|
@@ -256,7 +259,7 @@ export default class EntityLoader<
|
|
|
256
259
|
async loadManyByRawWhereClauseAsync(
|
|
257
260
|
rawWhereClause: string,
|
|
258
261
|
bindings: any[] | object,
|
|
259
|
-
querySelectionModifiers:
|
|
262
|
+
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields> = {}
|
|
260
263
|
): Promise<readonly Result<TEntity>[]> {
|
|
261
264
|
const fieldObjects = await this.dataManager.loadManyByRawWhereClauseAsync(
|
|
262
265
|
this.queryContext,
|
|
@@ -284,7 +287,7 @@ export default class EntityLoader<
|
|
|
284
287
|
}
|
|
285
288
|
|
|
286
289
|
/**
|
|
287
|
-
* Invalidate all caches for an entity's fields. Exposed primarily for internal use by
|
|
290
|
+
* Invalidate all caches for an entity's fields. Exposed primarily for internal use by EntityMutator.
|
|
288
291
|
* @param objectFields - entity data object to be invalidated
|
|
289
292
|
*/
|
|
290
293
|
async invalidateFieldsAsync(objectFields: Readonly<TFields>): Promise<void> {
|
|
@@ -350,7 +353,7 @@ export default class EntityLoader<
|
|
|
350
353
|
fieldValues: readonly TFields[N][]
|
|
351
354
|
): void {
|
|
352
355
|
const fieldDefinition = this.entityConfiguration.schema.get(fieldName);
|
|
353
|
-
invariant(fieldDefinition, `must have field definition for field = ${fieldName}`);
|
|
356
|
+
invariant(fieldDefinition, `must have field definition for field = ${String(fieldName)}`);
|
|
354
357
|
for (const fieldValue of fieldValues) {
|
|
355
358
|
const isInputValid = fieldDefinition.validateInputValue(fieldValue);
|
|
356
359
|
if (!isInputValid) {
|
|
@@ -20,6 +20,7 @@ export type EntityValidatorMutationInfo<
|
|
|
20
20
|
| {
|
|
21
21
|
type: EntityMutationType.UPDATE;
|
|
22
22
|
previousValue: TEntity;
|
|
23
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -50,6 +51,7 @@ export type EntityTriggerMutationInfo<
|
|
|
50
51
|
| {
|
|
51
52
|
type: EntityMutationType.UPDATE;
|
|
52
53
|
previousValue: TEntity;
|
|
54
|
+
cascadingDeleteCause: EntityCascadingDeletionInfo | null;
|
|
53
55
|
}
|
|
54
56
|
| {
|
|
55
57
|
type: EntityMutationType.DELETE;
|
|
@@ -85,7 +85,7 @@ export abstract class EntityMutationTrigger<
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
* A non-transactional trigger is like a
|
|
88
|
+
* A non-transactional trigger is like a EntityMutationTrigger but used for afterCommit triggers
|
|
89
89
|
* since they explicitly occur outside of the transaction.
|
|
90
90
|
*/
|
|
91
91
|
export abstract class EntityNonTransactionalMutationTrigger<
|