@expo/entity 0.31.0 → 0.32.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 +4 -6
- package/build/ComposedEntityCacheAdapter.js +3 -6
- package/build/ComposedEntityCacheAdapter.js.map +1 -1
- package/build/EnforcingEntityLoader.d.ts +6 -1
- package/build/EnforcingEntityLoader.js +8 -0
- package/build/EnforcingEntityLoader.js.map +1 -1
- package/build/Entity.d.ts +20 -10
- package/build/Entity.js +2 -2
- package/build/Entity.js.map +1 -1
- package/build/EntityAssociationLoader.d.ts +9 -9
- package/build/EntityCompanion.d.ts +6 -5
- package/build/EntityCompanion.js +6 -4
- package/build/EntityCompanion.js.map +1 -1
- package/build/EntityCompanionProvider.d.ts +28 -36
- package/build/EntityCompanionProvider.js +4 -19
- package/build/EntityCompanionProvider.js.map +1 -1
- package/build/EntityConfiguration.d.ts +3 -3
- package/build/EntityConfiguration.js +2 -2
- package/build/EntityConfiguration.js.map +1 -1
- package/build/EntityDatabaseAdapter.d.ts +1 -1
- package/build/EntityDatabaseAdapter.js +1 -1
- package/build/EntityDatabaseAdapter.js.map +1 -1
- package/build/EntityFieldDefinition.d.ts +2 -2
- package/build/EntityFieldDefinition.js +1 -1
- package/build/EntityFieldDefinition.js.map +1 -1
- package/build/EntityLoader.d.ts +19 -2
- package/build/EntityLoader.js +41 -5
- package/build/EntityLoader.js.map +1 -1
- package/build/EntityLoaderFactory.d.ts +4 -7
- package/build/EntityLoaderFactory.js +3 -5
- package/build/EntityLoaderFactory.js.map +1 -1
- package/build/EntityMutationInfo.d.ts +3 -3
- package/build/EntityMutationInfo.js +1 -1
- package/build/EntityMutationInfo.js.map +1 -1
- package/build/EntityMutationTriggerConfiguration.d.ts +3 -3
- package/build/EntityMutationValidator.d.ts +1 -1
- package/build/EntityMutator.d.ts +9 -7
- package/build/EntityMutator.js +21 -21
- package/build/EntityMutator.js.map +1 -1
- package/build/EntityMutatorFactory.d.ts +4 -2
- package/build/EntityMutatorFactory.js +5 -4
- package/build/EntityMutatorFactory.js.map +1 -1
- package/build/EntityPrivacyPolicy.d.ts +3 -3
- package/build/EntityPrivacyPolicy.js +2 -2
- package/build/EntityPrivacyPolicy.js.map +1 -1
- package/build/EntityQueryContext.d.ts +13 -5
- package/build/EntityQueryContext.js +11 -4
- package/build/EntityQueryContext.js.map +1 -1
- package/build/EntityQueryContextProvider.d.ts +3 -3
- package/build/EntityQueryContextProvider.js +2 -2
- package/build/EntityQueryContextProvider.js.map +1 -1
- package/build/EntitySecondaryCacheLoader.d.ts +1 -1
- package/build/EntitySecondaryCacheLoader.js +1 -1
- package/build/EntitySecondaryCacheLoader.js.map +1 -1
- package/build/GenericEntityCacheAdapter.d.ts +14 -0
- package/build/GenericEntityCacheAdapter.js +38 -0
- package/build/GenericEntityCacheAdapter.js.map +1 -0
- package/build/{EntityCacheAdapter.d.ts → IEntityCacheAdapter.d.ts} +5 -8
- package/build/IEntityCacheAdapter.js +3 -0
- package/build/IEntityCacheAdapter.js.map +1 -0
- package/build/IEntityCacheAdapterProvider.d.ts +2 -2
- package/build/IEntityGenericCacher.d.ts +31 -2
- package/build/ReadonlyEntity.d.ts +19 -7
- package/build/ReadonlyEntity.js +15 -13
- package/build/ReadonlyEntity.js.map +1 -1
- package/build/ViewerContext.d.ts +3 -3
- package/build/ViewerContext.js +3 -3
- package/build/ViewerContext.js.map +1 -1
- package/build/ViewerScopedEntityCompanion.d.ts +2 -2
- package/build/ViewerScopedEntityCompanion.js.map +1 -1
- package/build/ViewerScopedEntityCompanionProvider.d.ts +3 -3
- package/build/ViewerScopedEntityCompanionProvider.js +3 -3
- package/build/ViewerScopedEntityCompanionProvider.js.map +1 -1
- package/build/ViewerScopedEntityLoaderFactory.d.ts +1 -1
- package/build/ViewerScopedEntityMutatorFactory.d.ts +1 -1
- package/build/__tests__/ComposedCacheAdapter-test.js +4 -8
- package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -1
- package/build/__tests__/EnforcingEntityLoader-test.js +26 -0
- package/build/__tests__/EnforcingEntityLoader-test.js.map +1 -1
- package/build/__tests__/Entity-test.js +42 -20
- package/build/__tests__/Entity-test.js.map +1 -1
- package/build/__tests__/EntityAssociationLoader-test.js +6 -6
- package/build/__tests__/EntityAssociationLoader-test.js.map +1 -1
- package/build/__tests__/EntityCommonUseCases-test.js +20 -22
- package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
- package/build/__tests__/EntityCompanion-test.js +2 -1
- package/build/__tests__/EntityCompanion-test.js.map +1 -1
- package/build/__tests__/EntityCompanionProvider-test.js +15 -40
- package/build/__tests__/EntityCompanionProvider-test.js.map +1 -1
- package/build/__tests__/EntityEdges-test.js +48 -54
- package/build/__tests__/EntityEdges-test.js.map +1 -1
- package/build/__tests__/EntityLoader-constructor-test.d.ts +9 -5
- package/build/__tests__/EntityLoader-constructor-test.js +13 -14
- package/build/__tests__/EntityLoader-constructor-test.js.map +1 -1
- package/build/__tests__/EntityLoader-test.js +80 -10
- package/build/__tests__/EntityLoader-test.js.map +1 -1
- package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js +20 -22
- package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js.map +1 -1
- package/build/__tests__/EntityMutator-test.js +67 -14
- package/build/__tests__/EntityMutator-test.js.map +1 -1
- package/build/__tests__/EntityPrivacyPolicy-test.js +82 -29
- package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
- package/build/__tests__/EntityQueryContext-test.js +12 -0
- package/build/__tests__/EntityQueryContext-test.js.map +1 -1
- package/build/__tests__/EntitySecondaryCacheLoader-test.js +1 -2
- package/build/__tests__/EntitySecondaryCacheLoader-test.js.map +1 -1
- package/build/__tests__/EntitySelfReferentialEdges-test.js +16 -20
- package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
- package/build/__tests__/GenericEntityCacheAdapter-test.d.ts +1 -0
- package/build/__tests__/GenericEntityCacheAdapter-test.js +80 -0
- package/build/__tests__/GenericEntityCacheAdapter-test.js.map +1 -0
- package/build/__tests__/ReadonlyEntity-test.js +79 -13
- package/build/__tests__/ReadonlyEntity-test.js.map +1 -1
- package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js +2 -25
- package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js.map +1 -1
- package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js +20 -23
- package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js.map +1 -1
- package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js +17 -20
- package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js.map +1 -1
- package/build/errors/EntityError.js +2 -2
- package/build/errors/EntityError.js.map +1 -1
- package/build/errors/EntityInvalidFieldValueError.d.ts +2 -2
- package/build/errors/EntityInvalidFieldValueError.js.map +1 -1
- package/build/errors/EntityNotAuthorizedError.d.ts +2 -2
- package/build/errors/EntityNotAuthorizedError.js +1 -1
- package/build/errors/EntityNotAuthorizedError.js.map +1 -1
- package/build/errors/EntityNotFoundError.d.ts +2 -2
- package/build/errors/EntityNotFoundError.js.map +1 -1
- package/build/index.d.ts +2 -1
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/build/internal/EntityDataManager.d.ts +1 -1
- package/build/internal/EntityDataManager.js +1 -1
- package/build/internal/EntityDataManager.js.map +1 -1
- package/build/internal/EntityFieldTransformationUtils.d.ts +1 -1
- package/build/internal/EntityFieldTransformationUtils.js +4 -4
- package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
- package/build/internal/EntityTableDataCoordinator.d.ts +3 -3
- package/build/internal/EntityTableDataCoordinator.js.map +1 -1
- package/build/internal/ReadThroughEntityCache.d.ts +3 -3
- package/build/internal/ReadThroughEntityCache.js +1 -1
- package/build/internal/ReadThroughEntityCache.js.map +1 -1
- package/build/internal/__tests__/EntityDataManager-test.js +1 -1
- package/build/internal/__tests__/EntityDataManager-test.js.map +1 -1
- package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
- package/build/metrics/EntityMetricsUtils.js.map +1 -1
- package/build/metrics/IEntityMetricsAdapter.js +4 -4
- package/build/metrics/IEntityMetricsAdapter.js.map +1 -1
- package/build/rules/AlwaysAllowPrivacyPolicyRule.d.ts +2 -2
- package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysDenyPrivacyPolicyRule.d.ts +2 -2
- package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
- package/build/rules/AlwaysSkipPrivacyPolicyRule.d.ts +2 -2
- 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/rules/PrivacyPolicyRule.js.map +1 -1
- package/build/testfixtures/DateIDTestEntity.d.ts +2 -3
- package/build/testfixtures/DateIDTestEntity.js +7 -9
- package/build/testfixtures/DateIDTestEntity.js.map +1 -1
- package/build/testfixtures/SimpleTestEntity.d.ts +3 -4
- package/build/testfixtures/SimpleTestEntity.js +7 -9
- package/build/testfixtures/SimpleTestEntity.js.map +1 -1
- package/build/testfixtures/TestEntity.d.ts +2 -3
- package/build/testfixtures/TestEntity.js +14 -10
- package/build/testfixtures/TestEntity.js.map +1 -1
- package/build/testfixtures/TestEntity2.d.ts +2 -3
- package/build/testfixtures/TestEntity2.js +7 -9
- package/build/testfixtures/TestEntity2.js.map +1 -1
- package/build/testfixtures/TestEntityNumberKey.d.ts +2 -3
- package/build/testfixtures/TestEntityNumberKey.js +7 -9
- package/build/testfixtures/TestEntityNumberKey.js.map +1 -1
- package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +4 -4
- package/build/utils/testing/StubCacheAdapter.d.ts +6 -5
- package/build/utils/testing/StubCacheAdapter.js +5 -6
- package/build/utils/testing/StubCacheAdapter.js.map +1 -1
- package/build/utils/testing/StubDatabaseAdapterProvider.js.map +1 -1
- package/build/utils/testing/StubQueryContextProvider.d.ts +2 -1
- package/build/utils/testing/StubQueryContextProvider.js +1 -1
- package/build/utils/testing/StubQueryContextProvider.js.map +1 -1
- package/build/utils/testing/createUnitTestEntityCompanionProvider.js +2 -2
- package/build/utils/testing/createUnitTestEntityCompanionProvider.js.map +1 -1
- package/package.json +3 -3
- package/src/ComposedEntityCacheAdapter.ts +4 -11
- package/src/EnforcingEntityLoader.ts +17 -1
- package/src/Entity.ts +23 -12
- package/src/EntityAssociationLoader.ts +12 -12
- package/src/EntityCompanion.ts +13 -32
- package/src/EntityCompanionProvider.ts +41 -80
- package/src/EntityConfiguration.ts +4 -5
- package/src/EntityFieldDefinition.ts +2 -2
- package/src/EntityLoader.ts +45 -2
- package/src/EntityLoaderFactory.ts +7 -9
- package/src/EntityMutationInfo.ts +2 -2
- package/src/EntityMutationTriggerConfiguration.ts +3 -3
- package/src/EntityMutationValidator.ts +1 -1
- package/src/EntityMutator.ts +38 -31
- package/src/EntityMutatorFactory.ts +6 -1
- package/src/EntityPrivacyPolicy.ts +2 -2
- package/src/EntityQueryContext.ts +24 -4
- package/src/EntityQueryContextProvider.ts +7 -5
- package/src/EntitySecondaryCacheLoader.ts +1 -1
- package/src/GenericEntityCacheAdapter.ts +65 -0
- package/src/{EntityCacheAdapter.ts → IEntityCacheAdapter.ts} +5 -8
- package/src/IEntityCacheAdapterProvider.ts +2 -2
- package/src/IEntityGenericCacher.ts +32 -2
- package/src/ReadonlyEntity.ts +32 -32
- package/src/ViewerContext.ts +10 -8
- package/src/ViewerScopedEntityCompanion.ts +2 -2
- package/src/ViewerScopedEntityCompanionProvider.ts +4 -12
- package/src/ViewerScopedEntityLoaderFactory.ts +1 -1
- package/src/ViewerScopedEntityMutatorFactory.ts +1 -1
- package/src/__tests__/ComposedCacheAdapter-test.ts +6 -11
- package/src/__tests__/EnforcingEntityLoader-test.ts +41 -0
- package/src/__tests__/Entity-test.ts +42 -21
- package/src/__tests__/EntityCommonUseCases-test.ts +20 -22
- package/src/__tests__/EntityCompanion-test.ts +6 -9
- package/src/__tests__/EntityCompanionProvider-test.ts +14 -26
- package/src/__tests__/EntityEdges-test.ts +43 -49
- package/src/__tests__/EntityLoader-constructor-test.ts +16 -12
- package/src/__tests__/EntityLoader-test.ts +105 -0
- package/src/__tests__/EntityMutator-MutationCacheConsistency-test.ts +20 -22
- package/src/__tests__/EntityMutator-test.ts +119 -19
- package/src/__tests__/EntityPrivacyPolicy-test.ts +82 -29
- package/src/__tests__/EntityQueryContext-test.ts +23 -1
- package/src/__tests__/EntitySelfReferentialEdges-test.ts +8 -10
- package/src/__tests__/GenericEntityCacheAdapter-test.ts +102 -0
- package/src/__tests__/ReadonlyEntity-test.ts +79 -13
- package/src/__tests__/ViewerScopedEntityCompanionProvider-test.ts +2 -5
- package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +30 -24
- package/src/__tests__/cases/TwoEntitySameTableOverlappingRows-test.ts +14 -18
- package/src/errors/EntityInvalidFieldValueError.ts +2 -2
- package/src/errors/EntityNotAuthorizedError.ts +2 -2
- package/src/errors/EntityNotFoundError.ts +2 -2
- package/src/index.ts +2 -1
- package/src/internal/EntityDataManager.ts +1 -1
- package/src/internal/EntityTableDataCoordinator.ts +4 -4
- package/src/internal/ReadThroughEntityCache.ts +2 -2
- package/src/internal/__tests__/EntityDataManager-test.ts +2 -1
- package/src/internal/__tests__/ReadThroughEntityCache-test.ts +8 -8
- package/src/metrics/EntityMetricsUtils.ts +1 -1
- package/src/rules/AlwaysAllowPrivacyPolicyRule.ts +2 -2
- package/src/rules/AlwaysDenyPrivacyPolicyRule.ts +2 -2
- package/src/rules/AlwaysSkipPrivacyPolicyRule.ts +2 -2
- package/src/rules/PrivacyPolicyRule.ts +1 -1
- package/src/testfixtures/DateIDTestEntity.ts +6 -8
- package/src/testfixtures/SimpleTestEntity.ts +6 -8
- package/src/testfixtures/TestEntity.ts +19 -15
- package/src/testfixtures/TestEntity2.ts +6 -8
- package/src/testfixtures/TestEntityNumberKey.ts +6 -8
- package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +4 -4
- package/src/utils/testing/StubCacheAdapter.ts +9 -11
- package/src/utils/testing/StubDatabaseAdapterProvider.ts +1 -1
- package/src/utils/testing/StubQueryContextProvider.ts +4 -3
- package/src/utils/testing/createUnitTestEntityCompanionProvider.ts +3 -3
- package/build/EntityCacheAdapter.js +0 -13
- package/build/EntityCacheAdapter.js.map +0 -1
package/src/EntityLoader.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Result, asyncResult, result } from '@expo/results';
|
|
2
2
|
import invariant from 'invariant';
|
|
3
|
+
import nullthrows from 'nullthrows';
|
|
3
4
|
|
|
4
5
|
import EnforcingEntityLoader from './EnforcingEntityLoader';
|
|
5
6
|
import { IEntityClass } from './Entity';
|
|
@@ -14,6 +15,7 @@ import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './Ent
|
|
|
14
15
|
import { EntityQueryContext } from './EntityQueryContext';
|
|
15
16
|
import ReadonlyEntity from './ReadonlyEntity';
|
|
16
17
|
import ViewerContext from './ViewerContext';
|
|
18
|
+
import { pick } from './entityUtils';
|
|
17
19
|
import EntityInvalidFieldValueError from './errors/EntityInvalidFieldValueError';
|
|
18
20
|
import EntityNotFoundError from './errors/EntityNotFoundError';
|
|
19
21
|
import EntityDataManager from './internal/EntityDataManager';
|
|
@@ -25,7 +27,7 @@ import { mapMap, mapMapAsync } from './utils/collections/maps';
|
|
|
25
27
|
* cached, and authorized against the entity's EntityPrivacyPolicy.
|
|
26
28
|
*/
|
|
27
29
|
export default class EntityLoader<
|
|
28
|
-
TFields,
|
|
30
|
+
TFields extends object,
|
|
29
31
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
30
32
|
TViewerContext extends ViewerContext,
|
|
31
33
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -51,6 +53,7 @@ export default class EntityLoader<
|
|
|
51
53
|
TPrivacyPolicy,
|
|
52
54
|
TSelectedFields
|
|
53
55
|
>,
|
|
56
|
+
private readonly entitySelectedFields: TSelectedFields[] | undefined,
|
|
54
57
|
private readonly privacyPolicy: TPrivacyPolicy,
|
|
55
58
|
private readonly dataManager: EntityDataManager<TFields>,
|
|
56
59
|
protected readonly metricsAdapter: IEntityMetricsAdapter
|
|
@@ -182,6 +185,32 @@ export default class EntityLoader<
|
|
|
182
185
|
});
|
|
183
186
|
}
|
|
184
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Loads the first entity matching the selection constructed from the conjunction of specified
|
|
190
|
+
* operands, or null if no matching entity exists. Entities loaded using this method are not
|
|
191
|
+
* batched or cached.
|
|
192
|
+
*
|
|
193
|
+
* This is a convenience method for {@link loadManyByFieldEqualityConjunctionAsync}. However, the
|
|
194
|
+
* `orderBy` option must be specified to define what "first" means. If ordering doesn't matter,
|
|
195
|
+
* explicitly pass in an empty array.
|
|
196
|
+
*
|
|
197
|
+
* @param fieldEqualityOperands - list of field equality selection operand specifications
|
|
198
|
+
* @param querySelectionModifiers - orderBy and optional offset for the query
|
|
199
|
+
* @returns the first entity results that matches the query, where result error can be
|
|
200
|
+
* UnauthorizedError
|
|
201
|
+
*/
|
|
202
|
+
async loadFirstByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(
|
|
203
|
+
fieldEqualityOperands: FieldEqualityCondition<TFields, N>[],
|
|
204
|
+
querySelectionModifiers: Omit<QuerySelectionModifiers<TFields>, 'limit'> &
|
|
205
|
+
Required<Pick<QuerySelectionModifiers<TFields>, 'orderBy'>>
|
|
206
|
+
): Promise<Result<TEntity> | null> {
|
|
207
|
+
const results = await this.loadManyByFieldEqualityConjunctionAsync(fieldEqualityOperands, {
|
|
208
|
+
...querySelectionModifiers,
|
|
209
|
+
limit: 1,
|
|
210
|
+
});
|
|
211
|
+
return results[0] ?? null;
|
|
212
|
+
}
|
|
213
|
+
|
|
185
214
|
/**
|
|
186
215
|
* Loads many entities matching the selection constructed from the conjunction of specified operands.
|
|
187
216
|
* Entities loaded using this method are not batched or cached.
|
|
@@ -306,7 +335,7 @@ export default class EntityLoader<
|
|
|
306
335
|
private tryConstructEntities(fieldsObjects: readonly TFields[]): readonly Result<TEntity>[] {
|
|
307
336
|
return fieldsObjects.map((fieldsObject) => {
|
|
308
337
|
try {
|
|
309
|
-
return result(
|
|
338
|
+
return result(this.constructEntity(fieldsObject));
|
|
310
339
|
} catch (e) {
|
|
311
340
|
if (!(e instanceof Error)) {
|
|
312
341
|
throw e;
|
|
@@ -316,6 +345,20 @@ export default class EntityLoader<
|
|
|
316
345
|
});
|
|
317
346
|
}
|
|
318
347
|
|
|
348
|
+
public constructEntity(fieldsObject: TFields): TEntity {
|
|
349
|
+
const idField = this.entityConfiguration.idField;
|
|
350
|
+
const id = nullthrows(fieldsObject[idField], 'must provide ID to create an entity');
|
|
351
|
+
const entitySelectedFields =
|
|
352
|
+
this.entitySelectedFields ?? Array.from(this.entityConfiguration.schema.keys());
|
|
353
|
+
const selectedFields = pick(fieldsObject, entitySelectedFields);
|
|
354
|
+
return new this.entityClass({
|
|
355
|
+
viewerContext: this.viewerContext,
|
|
356
|
+
id: id as TID,
|
|
357
|
+
databaseFields: fieldsObject,
|
|
358
|
+
selectedFields,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
319
362
|
/**
|
|
320
363
|
* Construct and authorize entities from fields map, returning error results for entities that fail
|
|
321
364
|
* to construct or fail to authorize.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import EntityConfiguration from './EntityConfiguration';
|
|
1
|
+
import EntityCompanion from './EntityCompanion';
|
|
3
2
|
import EntityLoader from './EntityLoader';
|
|
4
3
|
import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
|
|
5
4
|
import { EntityQueryContext } from './EntityQueryContext';
|
|
@@ -12,7 +11,7 @@ import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';
|
|
|
12
11
|
* The primary entry point for loading entities.
|
|
13
12
|
*/
|
|
14
13
|
export default class EntityLoaderFactory<
|
|
15
|
-
TFields,
|
|
14
|
+
TFields extends object,
|
|
16
15
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
17
16
|
TViewerContext extends ViewerContext,
|
|
18
17
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -26,8 +25,7 @@ export default class EntityLoaderFactory<
|
|
|
26
25
|
TSelectedFields extends keyof TFields
|
|
27
26
|
> {
|
|
28
27
|
constructor(
|
|
29
|
-
private readonly
|
|
30
|
-
private readonly entityClass: IEntityClass<
|
|
28
|
+
private readonly entityCompanion: EntityCompanion<
|
|
31
29
|
TFields,
|
|
32
30
|
TID,
|
|
33
31
|
TViewerContext,
|
|
@@ -35,7 +33,6 @@ export default class EntityLoaderFactory<
|
|
|
35
33
|
TPrivacyPolicy,
|
|
36
34
|
TSelectedFields
|
|
37
35
|
>,
|
|
38
|
-
private readonly privacyPolicyClass: TPrivacyPolicy,
|
|
39
36
|
private readonly dataManager: EntityDataManager<TFields>,
|
|
40
37
|
protected readonly metricsAdapter: IEntityMetricsAdapter
|
|
41
38
|
) {}
|
|
@@ -54,9 +51,10 @@ export default class EntityLoaderFactory<
|
|
|
54
51
|
viewerContext,
|
|
55
52
|
queryContext,
|
|
56
53
|
privacyPolicyEvaluationContext,
|
|
57
|
-
this.entityConfiguration,
|
|
58
|
-
this.entityClass,
|
|
59
|
-
this.
|
|
54
|
+
this.entityCompanion.entityCompanionDefinition.entityConfiguration,
|
|
55
|
+
this.entityCompanion.entityCompanionDefinition.entityClass,
|
|
56
|
+
this.entityCompanion.entityCompanionDefinition.entitySelectedFields,
|
|
57
|
+
this.entityCompanion.privacyPolicy,
|
|
60
58
|
this.dataManager,
|
|
61
59
|
this.metricsAdapter
|
|
62
60
|
);
|
|
@@ -8,7 +8,7 @@ export enum EntityMutationType {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export type EntityValidatorMutationInfo<
|
|
11
|
-
TFields,
|
|
11
|
+
TFields extends object,
|
|
12
12
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
13
13
|
TViewerContext extends ViewerContext,
|
|
14
14
|
TEntity extends Entity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -39,7 +39,7 @@ export type EntityCascadingDeletionInfo = {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
export type EntityTriggerMutationInfo<
|
|
42
|
-
TFields,
|
|
42
|
+
TFields extends object,
|
|
43
43
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
44
44
|
TViewerContext extends ViewerContext,
|
|
45
45
|
TEntity extends Entity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -7,7 +7,7 @@ import ViewerContext from './ViewerContext';
|
|
|
7
7
|
* Interface to define trigger behavior for entities.
|
|
8
8
|
*/
|
|
9
9
|
export default interface EntityMutationTriggerConfiguration<
|
|
10
|
-
TFields,
|
|
10
|
+
TFields extends object,
|
|
11
11
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
12
12
|
TViewerContext extends ViewerContext,
|
|
13
13
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -70,7 +70,7 @@ export default interface EntityMutationTriggerConfiguration<
|
|
|
70
70
|
* the transaction if a transaction is supplied.
|
|
71
71
|
*/
|
|
72
72
|
export abstract class EntityMutationTrigger<
|
|
73
|
-
TFields,
|
|
73
|
+
TFields extends object,
|
|
74
74
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
75
75
|
TViewerContext extends ViewerContext,
|
|
76
76
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -89,7 +89,7 @@ export abstract class EntityMutationTrigger<
|
|
|
89
89
|
* since they explicitly occur outside of the transaction.
|
|
90
90
|
*/
|
|
91
91
|
export abstract class EntityNonTransactionalMutationTrigger<
|
|
92
|
-
TFields,
|
|
92
|
+
TFields extends object,
|
|
93
93
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
94
94
|
TViewerContext extends ViewerContext,
|
|
95
95
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -8,7 +8,7 @@ import ViewerContext from './ViewerContext';
|
|
|
8
8
|
* same transaction as the mutation itself before creating or updating an entity.
|
|
9
9
|
*/
|
|
10
10
|
export default abstract class EntityMutationValidator<
|
|
11
|
-
TFields,
|
|
11
|
+
TFields extends object,
|
|
12
12
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
13
13
|
TViewerContext extends ViewerContext,
|
|
14
14
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
package/src/EntityMutator.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Result, asyncResult, result, enforceAsyncResult } from '@expo/results';
|
|
|
2
2
|
import invariant from 'invariant';
|
|
3
3
|
|
|
4
4
|
import Entity, { IEntityClass } from './Entity';
|
|
5
|
-
import
|
|
5
|
+
import EntityCompanionProvider from './EntityCompanionProvider';
|
|
6
6
|
import EntityConfiguration from './EntityConfiguration';
|
|
7
7
|
import EntityDatabaseAdapter from './EntityDatabaseAdapter';
|
|
8
8
|
import { EntityEdgeDeletionBehavior } from './EntityFieldDefinition';
|
|
@@ -28,7 +28,7 @@ import IEntityMetricsAdapter, { EntityMetricsMutationType } from './metrics/IEnt
|
|
|
28
28
|
import { mapMapAsync } from './utils/collections/maps';
|
|
29
29
|
|
|
30
30
|
abstract class BaseMutator<
|
|
31
|
-
TFields,
|
|
31
|
+
TFields extends object,
|
|
32
32
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
33
33
|
TViewerContext extends ViewerContext,
|
|
34
34
|
TEntity extends Entity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -42,6 +42,7 @@ abstract class BaseMutator<
|
|
|
42
42
|
TSelectedFields extends keyof TFields
|
|
43
43
|
> {
|
|
44
44
|
constructor(
|
|
45
|
+
protected readonly companionProvider: EntityCompanionProvider,
|
|
45
46
|
protected readonly viewerContext: TViewerContext,
|
|
46
47
|
protected readonly queryContext: EntityQueryContext,
|
|
47
48
|
protected readonly entityConfiguration: EntityConfiguration<TFields>,
|
|
@@ -155,7 +156,7 @@ abstract class BaseMutator<
|
|
|
155
156
|
* Mutator for creating a new entity.
|
|
156
157
|
*/
|
|
157
158
|
export class CreateMutator<
|
|
158
|
-
TFields,
|
|
159
|
+
TFields extends object,
|
|
159
160
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
160
161
|
TViewerContext extends ViewerContext,
|
|
161
162
|
TEntity extends Entity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -211,7 +212,11 @@ export class CreateMutator<
|
|
|
211
212
|
): Promise<Result<TEntity>> {
|
|
212
213
|
this.validateFields(this.fieldsForEntity);
|
|
213
214
|
|
|
214
|
-
const
|
|
215
|
+
const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
|
|
216
|
+
cascadingDeleteCause: null,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const temporaryEntityForPrivacyCheck = entityLoader.constructEntity({
|
|
215
220
|
[this.entityConfiguration.idField]: '00000000-0000-0000-0000-000000000000', // zero UUID
|
|
216
221
|
...this.fieldsForEntity,
|
|
217
222
|
} as unknown as TFields);
|
|
@@ -250,14 +255,11 @@ export class CreateMutator<
|
|
|
250
255
|
|
|
251
256
|
const insertResult = await this.databaseAdapter.insertAsync(queryContext, this.fieldsForEntity);
|
|
252
257
|
|
|
253
|
-
const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
|
|
254
|
-
cascadingDeleteCause: null,
|
|
255
|
-
});
|
|
256
258
|
queryContext.appendPostCommitInvalidationCallback(
|
|
257
259
|
entityLoader.invalidateFieldsAsync.bind(entityLoader, insertResult)
|
|
258
260
|
);
|
|
259
261
|
|
|
260
|
-
const unauthorizedEntityAfterInsert =
|
|
262
|
+
const unauthorizedEntityAfterInsert = entityLoader.constructEntity(insertResult);
|
|
261
263
|
const newEntity = await entityLoader
|
|
262
264
|
.enforcing()
|
|
263
265
|
.loadByIDAsync(unauthorizedEntityAfterInsert.getID());
|
|
@@ -292,7 +294,7 @@ export class CreateMutator<
|
|
|
292
294
|
* Mutator for updating an existing entity.
|
|
293
295
|
*/
|
|
294
296
|
export class UpdateMutator<
|
|
295
|
-
TFields,
|
|
297
|
+
TFields extends object,
|
|
296
298
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
297
299
|
TViewerContext extends ViewerContext,
|
|
298
300
|
TEntity extends Entity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -310,6 +312,7 @@ export class UpdateMutator<
|
|
|
310
312
|
private readonly updatedFields: Partial<TFields> = {};
|
|
311
313
|
|
|
312
314
|
constructor(
|
|
315
|
+
companionProvider: EntityCompanionProvider,
|
|
313
316
|
viewerContext: TViewerContext,
|
|
314
317
|
queryContext: EntityQueryContext,
|
|
315
318
|
entityConfiguration: EntityConfiguration<TFields>,
|
|
@@ -349,6 +352,7 @@ export class UpdateMutator<
|
|
|
349
352
|
originalEntity: TEntity
|
|
350
353
|
) {
|
|
351
354
|
super(
|
|
355
|
+
companionProvider,
|
|
352
356
|
viewerContext,
|
|
353
357
|
queryContext,
|
|
354
358
|
entityConfiguration,
|
|
@@ -419,7 +423,11 @@ export class UpdateMutator<
|
|
|
419
423
|
): Promise<Result<TEntity>> {
|
|
420
424
|
this.validateFields(this.updatedFields);
|
|
421
425
|
|
|
422
|
-
const
|
|
426
|
+
const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
|
|
427
|
+
cascadingDeleteCause,
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
const entityAboutToBeUpdated = entityLoader.constructEntity(this.fieldsForEntity);
|
|
423
431
|
const authorizeUpdateResult = await asyncResult(
|
|
424
432
|
this.privacyPolicy.authorizeUpdateAsync(
|
|
425
433
|
this.viewerContext,
|
|
@@ -462,10 +470,6 @@ export class UpdateMutator<
|
|
|
462
470
|
this.updatedFields
|
|
463
471
|
);
|
|
464
472
|
|
|
465
|
-
const entityLoader = this.entityLoaderFactory.forLoad(this.viewerContext, queryContext, {
|
|
466
|
-
cascadingDeleteCause,
|
|
467
|
-
});
|
|
468
|
-
|
|
469
473
|
queryContext.appendPostCommitInvalidationCallback(
|
|
470
474
|
entityLoader.invalidateFieldsAsync.bind(
|
|
471
475
|
entityLoader,
|
|
@@ -481,7 +485,7 @@ export class UpdateMutator<
|
|
|
481
485
|
const updatedEntity = updateResult
|
|
482
486
|
? await entityLoader
|
|
483
487
|
.enforcing()
|
|
484
|
-
.loadByIDAsync(
|
|
488
|
+
.loadByIDAsync(entityLoader.constructEntity(updateResult).getID())
|
|
485
489
|
: entityAboutToBeUpdated;
|
|
486
490
|
|
|
487
491
|
await this.executeMutationTriggersAsync(
|
|
@@ -518,7 +522,7 @@ export class UpdateMutator<
|
|
|
518
522
|
* Mutator for deleting an existing entity.
|
|
519
523
|
*/
|
|
520
524
|
export class DeleteMutator<
|
|
521
|
-
TFields,
|
|
525
|
+
TFields extends object,
|
|
522
526
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
523
527
|
TViewerContext extends ViewerContext,
|
|
524
528
|
TEntity extends Entity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -532,6 +536,7 @@ export class DeleteMutator<
|
|
|
532
536
|
TSelectedFields extends keyof TFields
|
|
533
537
|
> extends BaseMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
534
538
|
constructor(
|
|
539
|
+
companionProvider: EntityCompanionProvider,
|
|
535
540
|
viewerContext: TViewerContext,
|
|
536
541
|
queryContext: EntityQueryContext,
|
|
537
542
|
entityConfiguration: EntityConfiguration<TFields>,
|
|
@@ -571,6 +576,7 @@ export class DeleteMutator<
|
|
|
571
576
|
private readonly entity: TEntity
|
|
572
577
|
) {
|
|
573
578
|
super(
|
|
579
|
+
companionProvider,
|
|
574
580
|
viewerContext,
|
|
575
581
|
queryContext,
|
|
576
582
|
entityConfiguration,
|
|
@@ -723,31 +729,32 @@ export class DeleteMutator<
|
|
|
723
729
|
}
|
|
724
730
|
processedEntityIdentifiers.add(entity.getUniqueIdentifier());
|
|
725
731
|
|
|
726
|
-
const companionDefinition = (
|
|
727
|
-
entity.constructor as
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
732
|
+
const companionDefinition = this.companionProvider.getCompanionForEntity(
|
|
733
|
+
entity.constructor as IEntityClass<
|
|
734
|
+
TFields,
|
|
735
|
+
TID,
|
|
736
|
+
TViewerContext,
|
|
737
|
+
TEntity,
|
|
738
|
+
TPrivacyPolicy,
|
|
739
|
+
TSelectedFields
|
|
740
|
+
>
|
|
741
|
+
).entityCompanionDefinition;
|
|
736
742
|
const entityConfiguration = companionDefinition.entityConfiguration;
|
|
737
|
-
const inboundEdges = entityConfiguration.
|
|
743
|
+
const inboundEdges = entityConfiguration.inboundEdges;
|
|
738
744
|
await Promise.all(
|
|
739
745
|
inboundEdges.map(async (entityClass) => {
|
|
740
746
|
return await mapMapAsync(
|
|
741
|
-
|
|
747
|
+
this.companionProvider.getCompanionForEntity(entityClass).entityCompanionDefinition
|
|
748
|
+
.entityConfiguration.schema,
|
|
742
749
|
async (fieldDefinition, fieldName) => {
|
|
743
750
|
const association = fieldDefinition.association;
|
|
744
751
|
if (!association) {
|
|
745
752
|
return;
|
|
746
753
|
}
|
|
747
754
|
|
|
748
|
-
const associatedConfiguration =
|
|
749
|
-
.
|
|
750
|
-
|
|
755
|
+
const associatedConfiguration = this.companionProvider.getCompanionForEntity(
|
|
756
|
+
association.associatedEntityClass
|
|
757
|
+
).entityCompanionDefinition.entityConfiguration;
|
|
751
758
|
if (associatedConfiguration !== entityConfiguration) {
|
|
752
759
|
return;
|
|
753
760
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Entity, { IEntityClass } from './Entity';
|
|
2
|
+
import EntityCompanionProvider from './EntityCompanionProvider';
|
|
2
3
|
import EntityConfiguration from './EntityConfiguration';
|
|
3
4
|
import EntityDatabaseAdapter from './EntityDatabaseAdapter';
|
|
4
5
|
import EntityLoaderFactory from './EntityLoaderFactory';
|
|
@@ -14,7 +15,7 @@ import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';
|
|
|
14
15
|
* The primary interface for creating, mutating, and deleting entities.
|
|
15
16
|
*/
|
|
16
17
|
export default class EntityMutatorFactory<
|
|
17
|
-
TFields,
|
|
18
|
+
TFields extends object,
|
|
18
19
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
19
20
|
TViewerContext extends ViewerContext,
|
|
20
21
|
TEntity extends Entity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -28,6 +29,7 @@ export default class EntityMutatorFactory<
|
|
|
28
29
|
TSelectedFields extends keyof TFields = keyof TFields
|
|
29
30
|
> {
|
|
30
31
|
constructor(
|
|
32
|
+
private readonly entityCompanionProvider: EntityCompanionProvider,
|
|
31
33
|
private readonly entityConfiguration: EntityConfiguration<TFields>,
|
|
32
34
|
private readonly entityClass: IEntityClass<
|
|
33
35
|
TFields,
|
|
@@ -75,6 +77,7 @@ export default class EntityMutatorFactory<
|
|
|
75
77
|
queryContext: EntityQueryContext
|
|
76
78
|
): CreateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
77
79
|
return new CreateMutator(
|
|
80
|
+
this.entityCompanionProvider,
|
|
78
81
|
viewerContext,
|
|
79
82
|
queryContext,
|
|
80
83
|
this.entityConfiguration,
|
|
@@ -99,6 +102,7 @@ export default class EntityMutatorFactory<
|
|
|
99
102
|
queryContext: EntityQueryContext
|
|
100
103
|
): UpdateMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
101
104
|
return new UpdateMutator(
|
|
105
|
+
this.entityCompanionProvider,
|
|
102
106
|
existingEntity.getViewerContext(),
|
|
103
107
|
queryContext,
|
|
104
108
|
this.entityConfiguration,
|
|
@@ -123,6 +127,7 @@ export default class EntityMutatorFactory<
|
|
|
123
127
|
queryContext: EntityQueryContext
|
|
124
128
|
): DeleteMutator<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
|
|
125
129
|
return new DeleteMutator(
|
|
130
|
+
this.entityCompanionProvider,
|
|
126
131
|
existingEntity.getViewerContext(),
|
|
127
132
|
queryContext,
|
|
128
133
|
this.entityConfiguration,
|
|
@@ -41,7 +41,7 @@ export enum EntityPrivacyPolicyEvaluationMode {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export type EntityPrivacyPolicyEvaluator<
|
|
44
|
-
TFields,
|
|
44
|
+
TFields extends object,
|
|
45
45
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
46
46
|
TViewerContext extends ViewerContext,
|
|
47
47
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -92,7 +92,7 @@ export enum EntityAuthorizationAction {
|
|
|
92
92
|
* ```
|
|
93
93
|
*/
|
|
94
94
|
export default abstract class EntityPrivacyPolicy<
|
|
95
|
-
TFields,
|
|
95
|
+
TFields extends object,
|
|
96
96
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
97
97
|
TViewerContext extends ViewerContext,
|
|
98
98
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -8,6 +8,16 @@ export type PreCommitCallback = (
|
|
|
8
8
|
...args: any
|
|
9
9
|
) => Promise<any>;
|
|
10
10
|
|
|
11
|
+
export enum TransactionIsolationLevel {
|
|
12
|
+
READ_COMMITTED = 'READ_COMMITTED',
|
|
13
|
+
REPEATABLE_READ = 'REPEATABLE_READ',
|
|
14
|
+
SERIALIZABLE = 'SERIALIZABLE',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type TransactionConfig = {
|
|
18
|
+
isolationLevel?: TransactionIsolationLevel;
|
|
19
|
+
};
|
|
20
|
+
|
|
11
21
|
/**
|
|
12
22
|
* Entity framework representation of transactional and non-transactional database
|
|
13
23
|
* query execution units.
|
|
@@ -25,7 +35,8 @@ export abstract class EntityQueryContext {
|
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
abstract runInTransactionIfNotInTransactionAsync<T>(
|
|
28
|
-
transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T
|
|
38
|
+
transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T>,
|
|
39
|
+
transactionConfig?: TransactionConfig
|
|
29
40
|
): Promise<T>;
|
|
30
41
|
}
|
|
31
42
|
|
|
@@ -48,9 +59,13 @@ export class EntityNonTransactionalQueryContext extends EntityQueryContext {
|
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
async runInTransactionIfNotInTransactionAsync<T>(
|
|
51
|
-
transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T
|
|
62
|
+
transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T>,
|
|
63
|
+
transactionConfig?: TransactionConfig
|
|
52
64
|
): Promise<T> {
|
|
53
|
-
return await this.entityQueryContextProvider.runInTransactionAsync(
|
|
65
|
+
return await this.entityQueryContextProvider.runInTransactionAsync(
|
|
66
|
+
transactionScope,
|
|
67
|
+
transactionConfig
|
|
68
|
+
);
|
|
54
69
|
}
|
|
55
70
|
}
|
|
56
71
|
|
|
@@ -132,8 +147,13 @@ export class EntityTransactionalQueryContext extends EntityQueryContext {
|
|
|
132
147
|
}
|
|
133
148
|
|
|
134
149
|
async runInTransactionIfNotInTransactionAsync<T>(
|
|
135
|
-
transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T
|
|
150
|
+
transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T>,
|
|
151
|
+
transactionConfig?: TransactionConfig
|
|
136
152
|
): Promise<T> {
|
|
153
|
+
assert(
|
|
154
|
+
transactionConfig === undefined,
|
|
155
|
+
'Should not pass transactionConfig to a nested transaction'
|
|
156
|
+
);
|
|
137
157
|
return await transactionScope(this);
|
|
138
158
|
}
|
|
139
159
|
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
EntityTransactionalQueryContext,
|
|
3
3
|
EntityNonTransactionalQueryContext,
|
|
4
4
|
EntityNestedTransactionalQueryContext,
|
|
5
|
+
TransactionConfig,
|
|
5
6
|
} from './EntityQueryContext';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -23,9 +24,9 @@ export default abstract class EntityQueryContextProvider {
|
|
|
23
24
|
/**
|
|
24
25
|
* Vend a transaction runner for use in runInTransactionAsync.
|
|
25
26
|
*/
|
|
26
|
-
protected abstract createTransactionRunner<T>(
|
|
27
|
-
|
|
28
|
-
) => Promise<T>;
|
|
27
|
+
protected abstract createTransactionRunner<T>(
|
|
28
|
+
transactionConfig?: TransactionConfig
|
|
29
|
+
): (transactionScope: (queryInterface: any) => Promise<T>) => Promise<T>;
|
|
29
30
|
|
|
30
31
|
protected abstract createNestedTransactionRunner<T>(
|
|
31
32
|
outerQueryInterface: any
|
|
@@ -36,11 +37,12 @@ export default abstract class EntityQueryContextProvider {
|
|
|
36
37
|
* @param transactionScope - async callback to execute within the transaction
|
|
37
38
|
*/
|
|
38
39
|
async runInTransactionAsync<T>(
|
|
39
|
-
transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T
|
|
40
|
+
transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T>,
|
|
41
|
+
transactionConfig?: TransactionConfig
|
|
40
42
|
): Promise<T> {
|
|
41
43
|
const [returnedValue, queryContext] = await this.createTransactionRunner<
|
|
42
44
|
[T, EntityTransactionalQueryContext]
|
|
43
|
-
>()(async (queryInterface) => {
|
|
45
|
+
>(transactionConfig)(async (queryInterface) => {
|
|
44
46
|
const queryContext = new EntityTransactionalQueryContext(queryInterface, this);
|
|
45
47
|
const result = await transactionScope(queryContext);
|
|
46
48
|
await queryContext.runPreCommitCallbacksAsync();
|
|
@@ -45,7 +45,7 @@ export interface ISecondaryEntityCache<TFields, TLoadParams> {
|
|
|
45
45
|
*/
|
|
46
46
|
export default abstract class EntitySecondaryCacheLoader<
|
|
47
47
|
TLoadParams,
|
|
48
|
-
TFields,
|
|
48
|
+
TFields extends object,
|
|
49
49
|
TID extends NonNullable<TFields[TSelectedFields]>,
|
|
50
50
|
TViewerContext extends ViewerContext,
|
|
51
51
|
TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import invariant from 'invariant';
|
|
2
|
+
|
|
3
|
+
import IEntityCacheAdapter from './IEntityCacheAdapter';
|
|
4
|
+
import IEntityGenericCacher from './IEntityGenericCacher';
|
|
5
|
+
import { CacheLoadResult } from './internal/ReadThroughEntityCache';
|
|
6
|
+
import { mapKeys } from './utils/collections/maps';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A standard IEntityCacheAdapter that coordinates caching through an IEntityGenericCacher.
|
|
10
|
+
*/
|
|
11
|
+
export default class GenericEntityCacheAdapter<TFields> implements IEntityCacheAdapter<TFields> {
|
|
12
|
+
constructor(private readonly genericCacher: IEntityGenericCacher<TFields>) {}
|
|
13
|
+
|
|
14
|
+
public async loadManyAsync<N extends keyof TFields>(
|
|
15
|
+
fieldName: N,
|
|
16
|
+
fieldValues: readonly NonNullable<TFields[N]>[]
|
|
17
|
+
): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult<TFields>>> {
|
|
18
|
+
const redisCacheKeyToFieldValueMapping = new Map(
|
|
19
|
+
fieldValues.map((fieldValue) => [
|
|
20
|
+
this.genericCacher.makeCacheKey(fieldName, fieldValue),
|
|
21
|
+
fieldValue,
|
|
22
|
+
])
|
|
23
|
+
);
|
|
24
|
+
const cacheResults = await this.genericCacher.loadManyAsync(
|
|
25
|
+
Array.from(redisCacheKeyToFieldValueMapping.keys())
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
return mapKeys(cacheResults, (redisCacheKey) => {
|
|
29
|
+
const fieldValue = redisCacheKeyToFieldValueMapping.get(redisCacheKey);
|
|
30
|
+
invariant(
|
|
31
|
+
fieldValue !== undefined,
|
|
32
|
+
'Unspecified cache key %s returned from generic cacher',
|
|
33
|
+
redisCacheKey
|
|
34
|
+
);
|
|
35
|
+
return fieldValue;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async cacheManyAsync<N extends keyof TFields>(
|
|
40
|
+
fieldName: N,
|
|
41
|
+
objectMap: ReadonlyMap<NonNullable<TFields[N]>, Readonly<TFields>>
|
|
42
|
+
): Promise<void> {
|
|
43
|
+
await this.genericCacher.cacheManyAsync(
|
|
44
|
+
mapKeys(objectMap, (fieldValue) => this.genericCacher.makeCacheKey(fieldName, fieldValue))
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public async cacheDBMissesAsync<N extends keyof TFields>(
|
|
49
|
+
fieldName: N,
|
|
50
|
+
fieldValues: readonly NonNullable<TFields[N]>[]
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
await this.genericCacher.cacheDBMissesAsync(
|
|
53
|
+
fieldValues.map((fieldValue) => this.genericCacher.makeCacheKey(fieldName, fieldValue))
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public async invalidateManyAsync<N extends keyof TFields>(
|
|
58
|
+
fieldName: N,
|
|
59
|
+
fieldValues: readonly NonNullable<TFields[N]>[]
|
|
60
|
+
): Promise<void> {
|
|
61
|
+
await this.genericCacher.invalidateManyAsync(
|
|
62
|
+
fieldValues.map((fieldValue) => this.genericCacher.makeCacheKey(fieldName, fieldValue))
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import EntityConfiguration from './EntityConfiguration';
|
|
2
1
|
import { CacheLoadResult } from './internal/ReadThroughEntityCache';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* A cache adapter is an interface by which objects can be
|
|
6
5
|
* cached, fetched from cache, and removed from cache (invalidated).
|
|
7
6
|
*/
|
|
8
|
-
export default
|
|
9
|
-
constructor(protected readonly entityConfiguration: EntityConfiguration<TFields>) {}
|
|
10
|
-
|
|
7
|
+
export default interface IEntityCacheAdapter<TFields> {
|
|
11
8
|
/**
|
|
12
9
|
* Load many objects from cache.
|
|
13
10
|
* @param fieldName - object field being queried
|
|
14
11
|
* @param fieldValues - fieldName field values being queried
|
|
15
12
|
* @returns map from all field values to a CacheLoadResult for each input value
|
|
16
13
|
*/
|
|
17
|
-
|
|
14
|
+
loadManyAsync<N extends keyof TFields>(
|
|
18
15
|
fieldName: N,
|
|
19
16
|
fieldValues: readonly NonNullable<TFields[N]>[]
|
|
20
17
|
): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult<TFields>>>;
|
|
@@ -24,7 +21,7 @@ export default abstract class EntityCacheAdapter<TFields> {
|
|
|
24
21
|
* @param fieldName - object field being queried
|
|
25
22
|
* @param objectMap - map from field value to object to cache
|
|
26
23
|
*/
|
|
27
|
-
|
|
24
|
+
cacheManyAsync<N extends keyof TFields>(
|
|
28
25
|
fieldName: N,
|
|
29
26
|
objectMap: ReadonlyMap<NonNullable<TFields[N]>, Readonly<TFields>>
|
|
30
27
|
): Promise<void>;
|
|
@@ -35,7 +32,7 @@ export default abstract class EntityCacheAdapter<TFields> {
|
|
|
35
32
|
* @param fieldValues - fieldValues for objects reported as CacheStatus.NEGATIVE
|
|
36
33
|
* in the cache and not found in the DB.
|
|
37
34
|
*/
|
|
38
|
-
|
|
35
|
+
cacheDBMissesAsync<N extends keyof TFields>(
|
|
39
36
|
fieldName: N,
|
|
40
37
|
fieldValues: readonly NonNullable<TFields[N]>[]
|
|
41
38
|
): Promise<void>;
|
|
@@ -45,7 +42,7 @@ export default abstract class EntityCacheAdapter<TFields> {
|
|
|
45
42
|
* @param fieldName - object field being queried
|
|
46
43
|
* @param fieldValues - fieldName field values to be invalidated
|
|
47
44
|
*/
|
|
48
|
-
|
|
45
|
+
invalidateManyAsync<N extends keyof TFields>(
|
|
49
46
|
fieldName: N,
|
|
50
47
|
fieldValues: readonly NonNullable<TFields[N]>[]
|
|
51
48
|
): Promise<void>;
|