@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
|
@@ -341,7 +341,6 @@ describe(EntityMutatorFactory, () => {
|
|
|
341
341
|
describe('forCreate', () => {
|
|
342
342
|
it('creates entities', async () => {
|
|
343
343
|
const viewerContext = mock<ViewerContext>();
|
|
344
|
-
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
345
344
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
346
345
|
|
|
347
346
|
const id1 = uuidv4();
|
|
@@ -365,7 +364,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
365
364
|
},
|
|
366
365
|
]);
|
|
367
366
|
const newEntity = await entityMutatorFactory
|
|
368
|
-
.forCreate(viewerContext, queryContext
|
|
367
|
+
.forCreate(viewerContext, queryContext)
|
|
369
368
|
.setField('stringField', 'huh')
|
|
370
369
|
.enforceCreateAsync();
|
|
371
370
|
expect(newEntity).toBeTruthy();
|
|
@@ -373,7 +372,6 @@ describe(EntityMutatorFactory, () => {
|
|
|
373
372
|
|
|
374
373
|
it('checks privacy', async () => {
|
|
375
374
|
const viewerContext = mock<ViewerContext>();
|
|
376
|
-
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
377
375
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
378
376
|
|
|
379
377
|
const id1 = uuidv4();
|
|
@@ -400,7 +398,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
400
398
|
const spiedPrivacyPolicy = spy(privacyPolicy);
|
|
401
399
|
|
|
402
400
|
await entityMutatorFactory
|
|
403
|
-
.forCreate(viewerContext, queryContext
|
|
401
|
+
.forCreate(viewerContext, queryContext)
|
|
404
402
|
.setField('stringField', 'huh')
|
|
405
403
|
.enforceCreateAsync();
|
|
406
404
|
|
|
@@ -408,7 +406,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
408
406
|
spiedPrivacyPolicy.authorizeCreateAsync(
|
|
409
407
|
viewerContext,
|
|
410
408
|
anyOfClass(EntityTransactionalQueryContext),
|
|
411
|
-
|
|
409
|
+
deepEqual({ cascadingDeleteCause: null }),
|
|
412
410
|
anyOfClass(TestEntity),
|
|
413
411
|
anything()
|
|
414
412
|
)
|
|
@@ -417,7 +415,6 @@ describe(EntityMutatorFactory, () => {
|
|
|
417
415
|
|
|
418
416
|
it('executes triggers', async () => {
|
|
419
417
|
const viewerContext = mock<ViewerContext>();
|
|
420
|
-
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
421
418
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
422
419
|
|
|
423
420
|
const id1 = uuidv4();
|
|
@@ -444,7 +441,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
444
441
|
const triggerSpies = setUpMutationTriggerSpies(mutationTriggers);
|
|
445
442
|
|
|
446
443
|
await entityMutatorFactory
|
|
447
|
-
.forCreate(viewerContext, queryContext
|
|
444
|
+
.forCreate(viewerContext, queryContext)
|
|
448
445
|
.setField('stringField', 'huh')
|
|
449
446
|
.enforceCreateAsync();
|
|
450
447
|
|
|
@@ -465,7 +462,6 @@ describe(EntityMutatorFactory, () => {
|
|
|
465
462
|
|
|
466
463
|
it('executes validators', async () => {
|
|
467
464
|
const viewerContext = mock<ViewerContext>();
|
|
468
|
-
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
469
465
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
470
466
|
|
|
471
467
|
const id1 = uuidv4();
|
|
@@ -492,7 +488,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
492
488
|
const validatorSpies = setUpMutationValidatorSpies(mutationValidators);
|
|
493
489
|
|
|
494
490
|
await entityMutatorFactory
|
|
495
|
-
.forCreate(viewerContext, queryContext
|
|
491
|
+
.forCreate(viewerContext, queryContext)
|
|
496
492
|
.setField('stringField', 'huh')
|
|
497
493
|
.enforceCreateAsync();
|
|
498
494
|
|
|
@@ -534,7 +530,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
534
530
|
);
|
|
535
531
|
|
|
536
532
|
const updatedEntity = await entityMutatorFactory
|
|
537
|
-
.forUpdate(existingEntity, queryContext
|
|
533
|
+
.forUpdate(existingEntity, queryContext)
|
|
538
534
|
.setField('stringField', 'huh2')
|
|
539
535
|
.enforceUpdateAsync();
|
|
540
536
|
|
|
@@ -586,7 +582,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
586
582
|
);
|
|
587
583
|
|
|
588
584
|
await entityMutatorFactory
|
|
589
|
-
.forUpdate(existingEntity, queryContext
|
|
585
|
+
.forUpdate(existingEntity, queryContext)
|
|
590
586
|
.setField('stringField', 'huh2')
|
|
591
587
|
.enforceUpdateAsync();
|
|
592
588
|
|
|
@@ -594,7 +590,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
594
590
|
spiedPrivacyPolicy.authorizeUpdateAsync(
|
|
595
591
|
viewerContext,
|
|
596
592
|
anyOfClass(EntityTransactionalQueryContext),
|
|
597
|
-
|
|
593
|
+
deepEqual({ cascadingDeleteCause: null }),
|
|
598
594
|
anyOfClass(TestEntity),
|
|
599
595
|
anything()
|
|
600
596
|
)
|
|
@@ -637,7 +633,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
637
633
|
);
|
|
638
634
|
|
|
639
635
|
await entityMutatorFactory
|
|
640
|
-
.forUpdate(existingEntity, queryContext
|
|
636
|
+
.forUpdate(existingEntity, queryContext)
|
|
641
637
|
.setField('stringField', 'huh2')
|
|
642
638
|
.enforceUpdateAsync();
|
|
643
639
|
|
|
@@ -652,7 +648,11 @@ describe(EntityMutatorFactory, () => {
|
|
|
652
648
|
beforeDelete: false,
|
|
653
649
|
afterDelete: false,
|
|
654
650
|
},
|
|
655
|
-
{
|
|
651
|
+
{
|
|
652
|
+
type: EntityMutationType.UPDATE,
|
|
653
|
+
previousValue: existingEntity,
|
|
654
|
+
cascadingDeleteCause: null,
|
|
655
|
+
}
|
|
656
656
|
);
|
|
657
657
|
});
|
|
658
658
|
it('executes validators', async () => {
|
|
@@ -691,13 +691,14 @@ describe(EntityMutatorFactory, () => {
|
|
|
691
691
|
);
|
|
692
692
|
|
|
693
693
|
await entityMutatorFactory
|
|
694
|
-
.forUpdate(existingEntity, queryContext
|
|
694
|
+
.forUpdate(existingEntity, queryContext)
|
|
695
695
|
.setField('stringField', 'huh2')
|
|
696
696
|
.enforceUpdateAsync();
|
|
697
697
|
|
|
698
698
|
verifyValidatorCounts(viewerContext, validatorSpies, 1, {
|
|
699
699
|
type: EntityMutationType.UPDATE,
|
|
700
700
|
previousValue: existingEntity,
|
|
701
|
+
cascadingDeleteCause: null,
|
|
701
702
|
});
|
|
702
703
|
});
|
|
703
704
|
});
|
|
@@ -727,9 +728,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
727
728
|
);
|
|
728
729
|
expect(existingEntity).toBeTruthy();
|
|
729
730
|
|
|
730
|
-
await entityMutatorFactory
|
|
731
|
-
.forDelete(existingEntity, queryContext, privacyPolicyEvaluationContext)
|
|
732
|
-
.enforceDeleteAsync();
|
|
731
|
+
await entityMutatorFactory.forDelete(existingEntity, queryContext).enforceDeleteAsync();
|
|
733
732
|
|
|
734
733
|
await expect(
|
|
735
734
|
enforceAsyncResult(
|
|
@@ -766,9 +765,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
766
765
|
.loadByIDAsync(id1)
|
|
767
766
|
);
|
|
768
767
|
|
|
769
|
-
await entityMutatorFactory
|
|
770
|
-
.forDelete(existingEntity, queryContext, privacyPolicyEvaluationContext)
|
|
771
|
-
.enforceDeleteAsync();
|
|
768
|
+
await entityMutatorFactory.forDelete(existingEntity, queryContext).enforceDeleteAsync();
|
|
772
769
|
|
|
773
770
|
verify(
|
|
774
771
|
spiedPrivacyPolicy.authorizeDeleteAsync(
|
|
@@ -807,9 +804,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
807
804
|
.loadByIDAsync(id1)
|
|
808
805
|
);
|
|
809
806
|
|
|
810
|
-
await entityMutatorFactory
|
|
811
|
-
.forDelete(existingEntity, queryContext, privacyPolicyEvaluationContext)
|
|
812
|
-
.enforceDeleteAsync();
|
|
807
|
+
await entityMutatorFactory.forDelete(existingEntity, queryContext).enforceDeleteAsync();
|
|
813
808
|
|
|
814
809
|
verifyTriggerCounts(
|
|
815
810
|
viewerContext,
|
|
@@ -852,9 +847,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
852
847
|
.loadByIDAsync(id1)
|
|
853
848
|
);
|
|
854
849
|
|
|
855
|
-
await entityMutatorFactory
|
|
856
|
-
.forDelete(existingEntity, queryContext, privacyPolicyEvaluationContext)
|
|
857
|
-
.enforceDeleteAsync();
|
|
850
|
+
await entityMutatorFactory.forDelete(existingEntity, queryContext).enforceDeleteAsync();
|
|
858
851
|
|
|
859
852
|
verifyValidatorCounts(viewerContext, validatorSpies, 0, {
|
|
860
853
|
type: EntityMutationType.DELETE as any,
|
|
@@ -888,7 +881,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
888
881
|
|
|
889
882
|
await enforceAsyncResult(
|
|
890
883
|
entityMutatorFactory
|
|
891
|
-
.forCreate(viewerContext, queryContext
|
|
884
|
+
.forCreate(viewerContext, queryContext)
|
|
892
885
|
.setField('stringField', 'huh')
|
|
893
886
|
.createAsync()
|
|
894
887
|
);
|
|
@@ -903,7 +896,6 @@ describe(EntityMutatorFactory, () => {
|
|
|
903
896
|
|
|
904
897
|
it('throws error when field not valid', async () => {
|
|
905
898
|
const viewerContext = mock<ViewerContext>();
|
|
906
|
-
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
907
899
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
908
900
|
const id1 = uuidv4();
|
|
909
901
|
const { entityMutatorFactory } = createEntityMutatorFactory([
|
|
@@ -919,19 +911,19 @@ describe(EntityMutatorFactory, () => {
|
|
|
919
911
|
|
|
920
912
|
await expect(
|
|
921
913
|
entityMutatorFactory
|
|
922
|
-
.forCreate(viewerContext, queryContext
|
|
914
|
+
.forCreate(viewerContext, queryContext)
|
|
923
915
|
.setField('stringField', 10 as any)
|
|
924
916
|
.createAsync()
|
|
925
917
|
).rejects.toThrowError('Entity field not valid: TestEntity (stringField = 10)');
|
|
926
918
|
|
|
927
919
|
const createdEntity = await entityMutatorFactory
|
|
928
|
-
.forCreate(viewerContext, queryContext
|
|
920
|
+
.forCreate(viewerContext, queryContext)
|
|
929
921
|
.setField('stringField', 'hello')
|
|
930
922
|
.enforceCreateAsync();
|
|
931
923
|
|
|
932
924
|
await expect(
|
|
933
925
|
entityMutatorFactory
|
|
934
|
-
.forUpdate(createdEntity, queryContext
|
|
926
|
+
.forUpdate(createdEntity, queryContext)
|
|
935
927
|
.setField('stringField', 10 as any)
|
|
936
928
|
.updateAsync()
|
|
937
929
|
).rejects.toThrowError('Entity field not valid: TestEntity (stringField = 10)');
|
|
@@ -939,7 +931,6 @@ describe(EntityMutatorFactory, () => {
|
|
|
939
931
|
|
|
940
932
|
it('returns error result when not authorized to create', async () => {
|
|
941
933
|
const viewerContext = instance(mock(ViewerContext));
|
|
942
|
-
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
943
934
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
944
935
|
const privacyPolicyMock = mock(SimpleTestEntityPrivacyPolicy);
|
|
945
936
|
const databaseAdapter = instance(mock<EntityDatabaseAdapter<SimpleTestFields>>());
|
|
@@ -999,7 +990,7 @@ describe(EntityMutatorFactory, () => {
|
|
|
999
990
|
);
|
|
1000
991
|
|
|
1001
992
|
const entityCreateResult = await entityMutatorFactory
|
|
1002
|
-
.forCreate(viewerContext, queryContext
|
|
993
|
+
.forCreate(viewerContext, queryContext)
|
|
1003
994
|
.createAsync();
|
|
1004
995
|
expect(entityCreateResult.ok).toBe(false);
|
|
1005
996
|
expect(entityCreateResult.reason).toEqual(rejectionError);
|
|
@@ -1011,14 +1002,14 @@ describe(EntityMutatorFactory, () => {
|
|
|
1011
1002
|
});
|
|
1012
1003
|
|
|
1013
1004
|
const entityUpdateResult = await entityMutatorFactory
|
|
1014
|
-
.forUpdate(fakeEntity, queryContext
|
|
1005
|
+
.forUpdate(fakeEntity, queryContext)
|
|
1015
1006
|
.updateAsync();
|
|
1016
1007
|
expect(entityUpdateResult.ok).toBe(false);
|
|
1017
1008
|
expect(entityUpdateResult.reason).toEqual(rejectionError);
|
|
1018
1009
|
expect(entityUpdateResult.value).toBe(undefined);
|
|
1019
1010
|
|
|
1020
1011
|
const entityDeleteResult = await entityMutatorFactory
|
|
1021
|
-
.forDelete(fakeEntity, queryContext
|
|
1012
|
+
.forDelete(fakeEntity, queryContext)
|
|
1022
1013
|
.deleteAsync();
|
|
1023
1014
|
expect(entityDeleteResult.ok).toBe(false);
|
|
1024
1015
|
expect(entityDeleteResult.reason).toEqual(rejectionError);
|
|
@@ -1027,7 +1018,6 @@ describe(EntityMutatorFactory, () => {
|
|
|
1027
1018
|
|
|
1028
1019
|
it('throws error when db adapter throws', async () => {
|
|
1029
1020
|
const viewerContext = instance(mock(ViewerContext));
|
|
1030
|
-
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
1031
1021
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
1032
1022
|
const privacyPolicy = instance(mock(SimpleTestEntityPrivacyPolicy));
|
|
1033
1023
|
const databaseAdapterMock = mock<EntityDatabaseAdapter<SimpleTestFields>>();
|
|
@@ -1083,48 +1073,37 @@ describe(EntityMutatorFactory, () => {
|
|
|
1083
1073
|
});
|
|
1084
1074
|
|
|
1085
1075
|
await expect(
|
|
1086
|
-
entityMutatorFactory
|
|
1087
|
-
.forCreate(viewerContext, queryContext, privacyPolicyEvaluationContext)
|
|
1088
|
-
.createAsync()
|
|
1076
|
+
entityMutatorFactory.forCreate(viewerContext, queryContext).createAsync()
|
|
1089
1077
|
).rejects.toEqual(rejectionError);
|
|
1090
1078
|
await expect(
|
|
1091
|
-
entityMutatorFactory
|
|
1092
|
-
.forUpdate(fakeEntity, queryContext, privacyPolicyEvaluationContext)
|
|
1093
|
-
.updateAsync()
|
|
1079
|
+
entityMutatorFactory.forUpdate(fakeEntity, queryContext).updateAsync()
|
|
1094
1080
|
).rejects.toEqual(rejectionError);
|
|
1095
1081
|
await expect(
|
|
1096
|
-
entityMutatorFactory
|
|
1097
|
-
.forDelete(fakeEntity, queryContext, privacyPolicyEvaluationContext)
|
|
1098
|
-
.deleteAsync()
|
|
1082
|
+
entityMutatorFactory.forDelete(fakeEntity, queryContext).deleteAsync()
|
|
1099
1083
|
).rejects.toEqual(rejectionError);
|
|
1100
1084
|
});
|
|
1101
1085
|
|
|
1102
1086
|
it('records metrics appropriately', async () => {
|
|
1103
1087
|
const viewerContext = mock<ViewerContext>();
|
|
1104
|
-
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
1105
1088
|
const queryContext = StubQueryContextProvider.getQueryContext();
|
|
1106
1089
|
const { entityMutatorFactory, metricsAdapter } = createEntityMutatorFactory([]);
|
|
1107
1090
|
const spiedMetricsAdapter = spy(metricsAdapter);
|
|
1108
1091
|
|
|
1109
1092
|
const newEntity = await enforceAsyncResult(
|
|
1110
1093
|
entityMutatorFactory
|
|
1111
|
-
.forCreate(viewerContext, queryContext
|
|
1094
|
+
.forCreate(viewerContext, queryContext)
|
|
1112
1095
|
.setField('stringField', 'huh')
|
|
1113
1096
|
.createAsync()
|
|
1114
1097
|
);
|
|
1115
1098
|
|
|
1116
1099
|
await enforceAsyncResult(
|
|
1117
1100
|
entityMutatorFactory
|
|
1118
|
-
.forUpdate(newEntity, queryContext
|
|
1101
|
+
.forUpdate(newEntity, queryContext)
|
|
1119
1102
|
.setField('stringField', 'wat')
|
|
1120
1103
|
.updateAsync()
|
|
1121
1104
|
);
|
|
1122
1105
|
|
|
1123
|
-
await enforceAsyncResult(
|
|
1124
|
-
entityMutatorFactory
|
|
1125
|
-
.forDelete(newEntity, queryContext, privacyPolicyEvaluationContext)
|
|
1126
|
-
.deleteAsync()
|
|
1127
|
-
);
|
|
1106
|
+
await enforceAsyncResult(entityMutatorFactory.forDelete(newEntity, queryContext).deleteAsync());
|
|
1128
1107
|
|
|
1129
1108
|
verify(
|
|
1130
1109
|
spiedMetricsAdapter.logMutatorMutationEvent(
|
|
@@ -213,7 +213,7 @@ describe('EntityEdgeDeletionBehavior.SET_NULL', () => {
|
|
|
213
213
|
describe('EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE', () => {
|
|
214
214
|
it('invalidates the cache', async () => {
|
|
215
215
|
const { CategoryEntity } = makeEntityClass(
|
|
216
|
-
EntityEdgeDeletionBehavior.
|
|
216
|
+
EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE_ONLY
|
|
217
217
|
);
|
|
218
218
|
|
|
219
219
|
const companionProvider = createUnitTestEntityCompanionProvider();
|
|
@@ -283,7 +283,7 @@ describe('EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE', () => {
|
|
|
283
283
|
|
|
284
284
|
it('handles cycles', async () => {
|
|
285
285
|
const { CategoryEntity } = makeEntityClass(
|
|
286
|
-
EntityEdgeDeletionBehavior.
|
|
286
|
+
EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE_ONLY
|
|
287
287
|
);
|
|
288
288
|
|
|
289
289
|
const companionProvider = createUnitTestEntityCompanionProvider();
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { mock, instance, verify } from 'ts-mockito';
|
|
2
2
|
|
|
3
3
|
import EntityMutatorFactory from '../EntityMutatorFactory';
|
|
4
|
-
import { EntityPrivacyPolicyEvaluationContext } from '../EntityPrivacyPolicy';
|
|
5
4
|
import { EntityQueryContext } from '../EntityQueryContext';
|
|
6
5
|
import ViewerContext from '../ViewerContext';
|
|
7
6
|
import ViewerScopedEntityMutatorFactory from '../ViewerScopedEntityMutatorFactory';
|
|
@@ -10,7 +9,6 @@ import TestEntity, { TestFields, TestEntityPrivacyPolicy } from '../testfixtures
|
|
|
10
9
|
describe(ViewerScopedEntityMutatorFactory, () => {
|
|
11
10
|
it('correctly scopes viewer to entity mutations', async () => {
|
|
12
11
|
const viewerContext = instance(mock(ViewerContext));
|
|
13
|
-
const privacyPolicyEvaluationContext = instance(mock<EntityPrivacyPolicyEvaluationContext>());
|
|
14
12
|
const queryContext = instance(mock(EntityQueryContext));
|
|
15
13
|
const baseMutatorFactory =
|
|
16
14
|
mock<
|
|
@@ -27,10 +25,8 @@ describe(ViewerScopedEntityMutatorFactory, () => {
|
|
|
27
25
|
keyof TestFields
|
|
28
26
|
>(baseMutatorFactoryInstance, viewerContext);
|
|
29
27
|
|
|
30
|
-
viewerScopedEntityLoader.forCreate(queryContext
|
|
28
|
+
viewerScopedEntityLoader.forCreate(queryContext);
|
|
31
29
|
|
|
32
|
-
verify(
|
|
33
|
-
baseMutatorFactory.forCreate(viewerContext, queryContext, privacyPolicyEvaluationContext)
|
|
34
|
-
).once();
|
|
30
|
+
verify(baseMutatorFactory.forCreate(viewerContext, queryContext)).once();
|
|
35
31
|
});
|
|
36
32
|
});
|
|
@@ -34,6 +34,6 @@ export default class EntityInvalidFieldValueError<
|
|
|
34
34
|
fieldName: N,
|
|
35
35
|
fieldValue?: TFields[N]
|
|
36
36
|
) {
|
|
37
|
-
super(`Entity field not valid: ${entityClass.name} (${fieldName} = ${fieldValue})`);
|
|
37
|
+
super(`Entity field not valid: ${entityClass.name} (${String(fieldName)} = ${fieldValue})`);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -34,6 +34,6 @@ export default class EntityNotFoundError<
|
|
|
34
34
|
fieldName: N,
|
|
35
35
|
fieldValue: TFields[N]
|
|
36
36
|
) {
|
|
37
|
-
super(`Entity not found: ${entityClass.name} (${fieldName} = ${fieldValue})`);
|
|
37
|
+
super(`Entity not found: ${entityClass.name} (${String(fieldName)} = ${fieldValue})`);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -3,6 +3,7 @@ import DataLoader from 'dataloader';
|
|
|
3
3
|
import EntityDatabaseAdapter, {
|
|
4
4
|
FieldEqualityCondition,
|
|
5
5
|
QuerySelectionModifiers,
|
|
6
|
+
QuerySelectionModifiersWithOrderByRaw,
|
|
6
7
|
} from '../EntityDatabaseAdapter';
|
|
7
8
|
import { EntityQueryContext } from '../EntityQueryContext';
|
|
8
9
|
import EntityQueryContextProvider from '../EntityQueryContextProvider';
|
|
@@ -11,15 +12,18 @@ import {
|
|
|
11
12
|
timeAndLogLoadEventAsync,
|
|
12
13
|
timeAndLogLoadMapEventAsync,
|
|
13
14
|
} from '../metrics/EntityMetricsUtils';
|
|
14
|
-
import IEntityMetricsAdapter, {
|
|
15
|
+
import IEntityMetricsAdapter, {
|
|
16
|
+
EntityMetricsLoadType,
|
|
17
|
+
IncrementLoadCountEventType,
|
|
18
|
+
} from '../metrics/IEntityMetricsAdapter';
|
|
15
19
|
import { computeIfAbsent, zipToMap } from '../utils/collections/maps';
|
|
16
20
|
import ReadThroughEntityCache from './ReadThroughEntityCache';
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
* A data manager is responsible for orchestrating multiple sources of entity
|
|
20
|
-
* data including local caches,
|
|
24
|
+
* data including local caches, EntityCacheAdapter, and EntityDatabaseAdapter.
|
|
21
25
|
*
|
|
22
|
-
* It is also responsible for invalidating all sources of data when mutated using
|
|
26
|
+
* It is also responsible for invalidating all sources of data when mutated using EntityMutator.
|
|
23
27
|
*/
|
|
24
28
|
export default class EntityDataManager<TFields> {
|
|
25
29
|
private readonly fieldDataLoaders: Map<
|
|
@@ -57,7 +61,8 @@ export default class EntityDataManager<TFields> {
|
|
|
57
61
|
fieldName: N,
|
|
58
62
|
fieldValues: readonly NonNullable<TFields[N]>[]
|
|
59
63
|
): Promise<ReadonlyMap<NonNullable<TFields[N]>, readonly Readonly<TFields>[]>> {
|
|
60
|
-
this.metricsAdapter.
|
|
64
|
+
this.metricsAdapter.incrementDataManagerLoadCount({
|
|
65
|
+
type: IncrementLoadCountEventType.CACHE,
|
|
61
66
|
fieldValueCount: fieldValues.length,
|
|
62
67
|
entityClassName: this.entityClassName,
|
|
63
68
|
});
|
|
@@ -65,7 +70,8 @@ export default class EntityDataManager<TFields> {
|
|
|
65
70
|
fieldName,
|
|
66
71
|
fieldValues,
|
|
67
72
|
async (fetcherValues) => {
|
|
68
|
-
this.metricsAdapter.
|
|
73
|
+
this.metricsAdapter.incrementDataManagerLoadCount({
|
|
74
|
+
type: IncrementLoadCountEventType.DATABASE,
|
|
69
75
|
fieldValueCount: fieldValues.length,
|
|
70
76
|
entityClassName: this.entityClassName,
|
|
71
77
|
});
|
|
@@ -108,7 +114,9 @@ export default class EntityDataManager<TFields> {
|
|
|
108
114
|
);
|
|
109
115
|
if (nullOrUndefinedValueIndex >= 0) {
|
|
110
116
|
throw new Error(
|
|
111
|
-
`Invalid load: ${this.entityClassName} (${fieldName} = ${
|
|
117
|
+
`Invalid load: ${this.entityClassName} (${String(fieldName)} = ${
|
|
118
|
+
fieldValues[nullOrUndefinedValueIndex]
|
|
119
|
+
})`
|
|
112
120
|
);
|
|
113
121
|
}
|
|
114
122
|
|
|
@@ -117,7 +125,8 @@ export default class EntityDataManager<TFields> {
|
|
|
117
125
|
return await this.databaseAdapter.fetchManyWhereAsync(queryContext, fieldName, fieldValues);
|
|
118
126
|
}
|
|
119
127
|
|
|
120
|
-
this.metricsAdapter.
|
|
128
|
+
this.metricsAdapter.incrementDataManagerLoadCount({
|
|
129
|
+
type: IncrementLoadCountEventType.DATALOADER,
|
|
121
130
|
fieldValueCount: fieldValues.length,
|
|
122
131
|
entityClassName: this.entityClassName,
|
|
123
132
|
});
|
|
@@ -165,14 +174,14 @@ export default class EntityDataManager<TFields> {
|
|
|
165
174
|
* @param queryContext - query context in which to perform the load
|
|
166
175
|
* @param rawWhereClause - parameterized SQL WHERE clause with positional binding placeholders or named binding placeholders
|
|
167
176
|
* @param bindings - array of positional bindings or object of named bindings
|
|
168
|
-
* @param querySelectionModifiers - limit, offset, and
|
|
177
|
+
* @param querySelectionModifiers - limit, offset, orderBy, and orderByRaw for the query
|
|
169
178
|
* @returns array of objects matching the query
|
|
170
179
|
*/
|
|
171
180
|
async loadManyByRawWhereClauseAsync(
|
|
172
181
|
queryContext: EntityQueryContext,
|
|
173
182
|
rawWhereClause: string,
|
|
174
183
|
bindings: any[] | object,
|
|
175
|
-
querySelectionModifiers:
|
|
184
|
+
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>
|
|
176
185
|
): Promise<readonly Readonly<TFields>[]> {
|
|
177
186
|
return await timeAndLogLoadEventAsync(
|
|
178
187
|
this.metricsAdapter,
|
|
@@ -24,7 +24,7 @@ export const getDatabaseFieldForEntityField = <TFields>(
|
|
|
24
24
|
entityField: keyof TFields
|
|
25
25
|
): string => {
|
|
26
26
|
const databaseField = entityConfiguration.entityToDBFieldsKeyMapping.get(entityField);
|
|
27
|
-
invariant(databaseField, `database field mapping missing for ${entityField}`);
|
|
27
|
+
invariant(databaseField, `database field mapping missing for ${String(entityField)}`);
|
|
28
28
|
return databaseField!;
|
|
29
29
|
};
|
|
30
30
|
|
|
@@ -23,8 +23,8 @@ export type CacheLoadResult<TFields> =
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* A read-through entity cache is responsible for coordinating
|
|
27
|
-
*
|
|
26
|
+
* A read-through entity cache is responsible for coordinating EntityDatabaseAdapter and
|
|
27
|
+
* EntityCacheAdapter within the EntityDataManager.
|
|
28
28
|
*/
|
|
29
29
|
export default class ReadThroughEntityCache<TFields> {
|
|
30
30
|
constructor(
|
|
@@ -102,7 +102,9 @@ export default class ReadThroughEntityCache<TFields> {
|
|
|
102
102
|
// TODO(wschurman): emit or throw here since console may not be available
|
|
103
103
|
// eslint-disable-next-line no-console
|
|
104
104
|
console.warn(
|
|
105
|
-
`unique key ${fieldName} in ${
|
|
105
|
+
`unique key ${String(fieldName)} in ${
|
|
106
|
+
this.entityConfiguration.tableName
|
|
107
|
+
} returned multiple rows for ${fieldValue}`
|
|
106
108
|
);
|
|
107
109
|
continue;
|
|
108
110
|
}
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
when,
|
|
5
5
|
anything,
|
|
6
6
|
verify,
|
|
7
|
-
anyNumber,
|
|
8
7
|
objectContaining,
|
|
9
8
|
spy,
|
|
10
9
|
anyString,
|
|
@@ -13,7 +12,10 @@ import {
|
|
|
13
12
|
} from 'ts-mockito';
|
|
14
13
|
|
|
15
14
|
import EntityDatabaseAdapter from '../../EntityDatabaseAdapter';
|
|
16
|
-
import IEntityMetricsAdapter, {
|
|
15
|
+
import IEntityMetricsAdapter, {
|
|
16
|
+
EntityMetricsLoadType,
|
|
17
|
+
IncrementLoadCountEventType,
|
|
18
|
+
} from '../../metrics/IEntityMetricsAdapter';
|
|
17
19
|
import NoOpEntityMetricsAdapter from '../../metrics/NoOpEntityMetricsAdapter';
|
|
18
20
|
import TestEntity, { testEntityConfiguration, TestFields } from '../../testfixtures/TestEntity';
|
|
19
21
|
import {
|
|
@@ -519,24 +521,27 @@ describe(EntityDataManager, () => {
|
|
|
519
521
|
).once();
|
|
520
522
|
|
|
521
523
|
verify(
|
|
522
|
-
metricsAdapterMock.
|
|
524
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
523
525
|
deepEqual({
|
|
526
|
+
type: IncrementLoadCountEventType.DATALOADER,
|
|
524
527
|
fieldValueCount: 1,
|
|
525
528
|
entityClassName: TestEntity.name,
|
|
526
529
|
})
|
|
527
530
|
)
|
|
528
531
|
).once();
|
|
529
532
|
verify(
|
|
530
|
-
metricsAdapterMock.
|
|
533
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
531
534
|
deepEqual({
|
|
535
|
+
type: IncrementLoadCountEventType.CACHE,
|
|
532
536
|
fieldValueCount: 1,
|
|
533
537
|
entityClassName: TestEntity.name,
|
|
534
538
|
})
|
|
535
539
|
)
|
|
536
540
|
).once();
|
|
537
541
|
verify(
|
|
538
|
-
metricsAdapterMock.
|
|
542
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
539
543
|
deepEqual({
|
|
544
|
+
type: IncrementLoadCountEventType.DATABASE,
|
|
540
545
|
fieldValueCount: 1,
|
|
541
546
|
entityClassName: TestEntity.name,
|
|
542
547
|
})
|
|
@@ -565,9 +570,7 @@ describe(EntityDataManager, () => {
|
|
|
565
570
|
)
|
|
566
571
|
).once();
|
|
567
572
|
|
|
568
|
-
verify(metricsAdapterMock.
|
|
569
|
-
verify(metricsAdapterMock.incrementDataManagerCacheLoadCount(anyNumber())).never();
|
|
570
|
-
verify(metricsAdapterMock.incrementDataManagerDatabaseLoadCount(anyNumber())).never();
|
|
573
|
+
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).never();
|
|
571
574
|
});
|
|
572
575
|
|
|
573
576
|
it('throws when a load-by value is null or undefined', async () => {
|