@expo/entity 0.25.2 → 0.26.1

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.
Files changed (117) hide show
  1. package/build/ComposedEntityCacheAdapter.d.ts +4 -2
  2. package/build/ComposedEntityCacheAdapter.js +39 -3
  3. package/build/ComposedEntityCacheAdapter.js.map +1 -1
  4. package/build/ComposedSecondaryEntityCache.d.ts +3 -2
  5. package/build/ComposedSecondaryEntityCache.js +3 -2
  6. package/build/ComposedSecondaryEntityCache.js.map +1 -1
  7. package/build/Entity.js +4 -4
  8. package/build/Entity.js.map +1 -1
  9. package/build/EntityAssociationLoader.d.ts +5 -0
  10. package/build/EntityAssociationLoader.js +5 -0
  11. package/build/EntityAssociationLoader.js.map +1 -1
  12. package/build/EntityFieldDefinition.d.ts +16 -8
  13. package/build/EntityFieldDefinition.js +12 -5
  14. package/build/EntityFieldDefinition.js.map +1 -1
  15. package/build/EntityLoader.js +2 -2
  16. package/build/EntityLoader.js.map +1 -1
  17. package/build/EntityMutationInfo.d.ts +2 -0
  18. package/build/EntityMutator.d.ts +6 -8
  19. package/build/EntityMutator.js +58 -46
  20. package/build/EntityMutator.js.map +1 -1
  21. package/build/EntityMutatorFactory.d.ts +4 -4
  22. package/build/EntityMutatorFactory.js +6 -6
  23. package/build/EntityMutatorFactory.js.map +1 -1
  24. package/build/EntityPrivacyPolicy.d.ts +13 -0
  25. package/build/EntityPrivacyPolicy.js +13 -0
  26. package/build/EntityPrivacyPolicy.js.map +1 -1
  27. package/build/EntityQueryContext.d.ts +11 -0
  28. package/build/EntityQueryContext.js +11 -0
  29. package/build/EntityQueryContext.js.map +1 -1
  30. package/build/ViewerScopedEntityMutatorFactory.d.ts +4 -4
  31. package/build/ViewerScopedEntityMutatorFactory.js +6 -6
  32. package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
  33. package/build/__tests__/ComposedCacheAdapter-test.js +37 -4
  34. package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -1
  35. package/build/__tests__/EntityCommonUseCases-test.js +5 -1
  36. package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
  37. package/build/__tests__/EntityCompanion-test.js +5 -1
  38. package/build/__tests__/EntityCompanion-test.js.map +1 -1
  39. package/build/__tests__/EntityCompanionProvider-test.js +5 -1
  40. package/build/__tests__/EntityCompanionProvider-test.js.map +1 -1
  41. package/build/__tests__/EntityEdges-test.js +199 -33
  42. package/build/__tests__/EntityEdges-test.js.map +1 -1
  43. package/build/__tests__/EntityLoader-test.js +5 -1
  44. package/build/__tests__/EntityLoader-test.js.map +1 -1
  45. package/build/__tests__/EntityMutator-test.js +38 -53
  46. package/build/__tests__/EntityMutator-test.js.map +1 -1
  47. package/build/__tests__/EntityPrivacyPolicy-test.js +5 -1
  48. package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
  49. package/build/__tests__/EntitySelfReferentialEdges-test.js +2 -2
  50. package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
  51. package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js +5 -1
  52. package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js.map +1 -1
  53. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +2 -3
  54. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
  55. package/build/errors/EntityCacheAdapterError.js +5 -1
  56. package/build/errors/EntityCacheAdapterError.js.map +1 -1
  57. package/build/errors/EntityDatabaseAdapterError.js +5 -1
  58. package/build/errors/EntityDatabaseAdapterError.js.map +1 -1
  59. package/build/errors/EntityInvalidFieldValueError.js +6 -2
  60. package/build/errors/EntityInvalidFieldValueError.js.map +1 -1
  61. package/build/errors/EntityNotAuthorizedError.js +5 -1
  62. package/build/errors/EntityNotAuthorizedError.js.map +1 -1
  63. package/build/errors/EntityNotFoundError.js +6 -2
  64. package/build/errors/EntityNotFoundError.js.map +1 -1
  65. package/build/index.js +5 -1
  66. package/build/index.js.map +1 -1
  67. package/build/internal/EntityDataManager.js +7 -4
  68. package/build/internal/EntityDataManager.js.map +1 -1
  69. package/build/internal/EntityFieldTransformationUtils.js +1 -1
  70. package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
  71. package/build/internal/ReadThroughEntityCache.js +1 -1
  72. package/build/internal/ReadThroughEntityCache.js.map +1 -1
  73. package/build/internal/__tests__/EntityDataManager-test.js +12 -7
  74. package/build/internal/__tests__/EntityDataManager-test.js.map +1 -1
  75. package/build/internal/__tests__/ReadThroughEntityCache-test.js +5 -1
  76. package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
  77. package/build/metrics/IEntityMetricsAdapter.d.ts +62 -17
  78. package/build/metrics/IEntityMetricsAdapter.js +17 -1
  79. package/build/metrics/IEntityMetricsAdapter.js.map +1 -1
  80. package/build/metrics/NoOpEntityMetricsAdapter.d.ts +1 -3
  81. package/build/metrics/NoOpEntityMetricsAdapter.js +1 -3
  82. package/build/metrics/NoOpEntityMetricsAdapter.js.map +1 -1
  83. package/build/rules/AlwaysAllowPrivacyPolicyRule.js +5 -1
  84. package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
  85. package/build/rules/AlwaysDenyPrivacyPolicyRule.js +5 -1
  86. package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
  87. package/build/rules/AlwaysSkipPrivacyPolicyRule.js +5 -1
  88. package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
  89. package/build/utils/testing/StubDatabaseAdapter.js +6 -2
  90. package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
  91. package/package.json +1 -1
  92. package/src/ComposedEntityCacheAdapter.ts +44 -3
  93. package/src/ComposedSecondaryEntityCache.ts +3 -2
  94. package/src/Entity.ts +4 -4
  95. package/src/EntityAssociationLoader.ts +5 -0
  96. package/src/EntityFieldDefinition.ts +15 -6
  97. package/src/EntityLoader.ts +4 -2
  98. package/src/EntityMutationInfo.ts +2 -0
  99. package/src/EntityMutator.ts +98 -67
  100. package/src/EntityMutatorFactory.ts +4 -10
  101. package/src/EntityPrivacyPolicy.ts +15 -0
  102. package/src/EntityQueryContext.ts +11 -0
  103. package/src/ViewerScopedEntityMutatorFactory.ts +7 -22
  104. package/src/__tests__/ComposedCacheAdapter-test.ts +43 -4
  105. package/src/__tests__/EntityEdges-test.ts +287 -32
  106. package/src/__tests__/EntityMutator-test.ts +33 -54
  107. package/src/__tests__/EntitySelfReferentialEdges-test.ts +2 -2
  108. package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +2 -6
  109. package/src/errors/EntityInvalidFieldValueError.ts +1 -1
  110. package/src/errors/EntityNotFoundError.ts +1 -1
  111. package/src/internal/EntityDataManager.ts +13 -5
  112. package/src/internal/EntityFieldTransformationUtils.ts +1 -1
  113. package/src/internal/ReadThroughEntityCache.ts +3 -1
  114. package/src/internal/__tests__/EntityDataManager-test.ts +11 -8
  115. package/src/metrics/IEntityMetricsAdapter.ts +72 -19
  116. package/src/metrics/NoOpEntityMetricsAdapter.ts +1 -5
  117. package/src/utils/testing/StubDatabaseAdapter.ts +4 -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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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
- privacyPolicyEvaluationContext,
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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
- privacyPolicyEvaluationContext,
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, privacyPolicyEvaluationContext)
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
- { type: EntityMutationType.UPDATE, previousValue: existingEntity }
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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, privacyPolicyEvaluationContext)
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.CASCADE_DELETE_INVALIDATE_CACHE
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.CASCADE_DELETE_INVALIDATE_CACHE
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, privacyPolicyEvaluationContext);
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
  }
@@ -11,7 +11,10 @@ import {
11
11
  timeAndLogLoadEventAsync,
12
12
  timeAndLogLoadMapEventAsync,
13
13
  } from '../metrics/EntityMetricsUtils';
14
- import IEntityMetricsAdapter, { EntityMetricsLoadType } from '../metrics/IEntityMetricsAdapter';
14
+ import IEntityMetricsAdapter, {
15
+ EntityMetricsLoadType,
16
+ IncrementLoadCountEventType,
17
+ } from '../metrics/IEntityMetricsAdapter';
15
18
  import { computeIfAbsent, zipToMap } from '../utils/collections/maps';
16
19
  import ReadThroughEntityCache from './ReadThroughEntityCache';
17
20
 
@@ -57,7 +60,8 @@ export default class EntityDataManager<TFields> {
57
60
  fieldName: N,
58
61
  fieldValues: readonly NonNullable<TFields[N]>[]
59
62
  ): Promise<ReadonlyMap<NonNullable<TFields[N]>, readonly Readonly<TFields>[]>> {
60
- this.metricsAdapter.incrementDataManagerCacheLoadCount({
63
+ this.metricsAdapter.incrementDataManagerLoadCount({
64
+ type: IncrementLoadCountEventType.CACHE,
61
65
  fieldValueCount: fieldValues.length,
62
66
  entityClassName: this.entityClassName,
63
67
  });
@@ -65,7 +69,8 @@ export default class EntityDataManager<TFields> {
65
69
  fieldName,
66
70
  fieldValues,
67
71
  async (fetcherValues) => {
68
- this.metricsAdapter.incrementDataManagerDatabaseLoadCount({
72
+ this.metricsAdapter.incrementDataManagerLoadCount({
73
+ type: IncrementLoadCountEventType.DATABASE,
69
74
  fieldValueCount: fieldValues.length,
70
75
  entityClassName: this.entityClassName,
71
76
  });
@@ -108,7 +113,9 @@ export default class EntityDataManager<TFields> {
108
113
  );
109
114
  if (nullOrUndefinedValueIndex >= 0) {
110
115
  throw new Error(
111
- `Invalid load: ${this.entityClassName} (${fieldName} = ${fieldValues[nullOrUndefinedValueIndex]})`
116
+ `Invalid load: ${this.entityClassName} (${String(fieldName)} = ${
117
+ fieldValues[nullOrUndefinedValueIndex]
118
+ })`
112
119
  );
113
120
  }
114
121
 
@@ -117,7 +124,8 @@ export default class EntityDataManager<TFields> {
117
124
  return await this.databaseAdapter.fetchManyWhereAsync(queryContext, fieldName, fieldValues);
118
125
  }
119
126
 
120
- this.metricsAdapter.incrementDataManagerDataloaderLoadCount({
127
+ this.metricsAdapter.incrementDataManagerLoadCount({
128
+ type: IncrementLoadCountEventType.DATALOADER,
121
129
  fieldValueCount: fieldValues.length,
122
130
  entityClassName: this.entityClassName,
123
131
  });
@@ -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
 
@@ -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 ${this.entityConfiguration.tableName} returned multiple rows for ${fieldValue}`
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, { EntityMetricsLoadType } from '../../metrics/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.incrementDataManagerDataloaderLoadCount(
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.incrementDataManagerCacheLoadCount(
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.incrementDataManagerDatabaseLoadCount(
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.incrementDataManagerDataloaderLoadCount(anyNumber())).never();
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 () => {
@@ -9,10 +9,28 @@ export enum EntityMetricsLoadType {
9
9
  LOAD_MANY_RAW,
10
10
  }
11
11
 
12
+ /**
13
+ * Event about a single call to an {@link EntityLoader} method.
14
+ */
12
15
  export interface EntityMetricsLoadEvent {
16
+ /**
17
+ * {@link EntityMetricsLoadType} for this load.
18
+ */
13
19
  type: EntityMetricsLoadType;
20
+
21
+ /**
22
+ * Class name of the {@link Entity} being loaded.
23
+ */
14
24
  entityClassName: string;
25
+
26
+ /**
27
+ * Total duration of this load, including fetch and construction of entities.
28
+ */
15
29
  duration: number;
30
+
31
+ /**
32
+ * Number of entities returned for this load.
33
+ */
16
34
  count: number;
17
35
  }
18
36
 
@@ -23,13 +41,57 @@ export enum EntityMetricsMutationType {
23
41
  }
24
42
 
25
43
  export interface EntityMetricsMutationEvent {
44
+ /**
45
+ * {@link EntityMetricsMutationType} for this mutation.
46
+ */
26
47
  type: EntityMetricsMutationType;
48
+
49
+ /**
50
+ * Class name of the {@link Entity} being mutated.
51
+ */
27
52
  entityClassName: string;
53
+
54
+ /**
55
+ * Total duration of this mutation.
56
+ */
28
57
  duration: number;
29
58
  }
30
59
 
60
+ export enum IncrementLoadCountEventType {
61
+ /**
62
+ * Type for when a dataloader load is initiated via the standard load methods
63
+ * since all loads go through a dataloader.
64
+ */
65
+ DATALOADER,
66
+
67
+ /**
68
+ * Type for when a cache load is initiated due to a dataloader miss.
69
+ */
70
+ CACHE,
71
+
72
+ /**
73
+ * Type for when a database load is initiated due to a dataloader and cache miss, when an entity query doesn't support caching, or during a transaction.
74
+ */
75
+ DATABASE,
76
+ }
77
+
78
+ /**
79
+ * Event used to record dataloader, cache, and database load counts in {@link EntityDataManager}.
80
+ */
31
81
  export interface IncrementLoadCountEvent {
82
+ /**
83
+ * Type of this event.
84
+ */
85
+ type: IncrementLoadCountEventType;
86
+
87
+ /**
88
+ * Number of field values being loaded for this call.
89
+ */
32
90
  fieldValueCount: number;
91
+
92
+ /**
93
+ * Class name of the {@link Entity} being loaded.
94
+ */
33
95
  entityClassName: string;
34
96
  }
35
97
 
@@ -38,7 +100,13 @@ export enum EntityMetricsAuthorizationResult {
38
100
  ALLOW,
39
101
  }
40
102
 
103
+ /**
104
+ * Event used to record a singe {@link EntityPrivacyPolicy} authorization.
105
+ */
41
106
  export interface EntityMetricsAuthorizationEvent {
107
+ /**
108
+ * Class name of the {@link Entity} being authorized.
109
+ */
42
110
  entityClassName: string;
43
111
  action: EntityAuthorizationAction;
44
112
  evaluationResult: EntityMetricsAuthorizationResult;
@@ -69,25 +137,10 @@ export default interface IEntityMetricsAdapter {
69
137
  logMutatorMutationEvent(mutationEvent: EntityMetricsMutationEvent): void;
70
138
 
71
139
  /**
72
- * Called when a dataloader load is initiated via the standard
73
- * load methods (not equality conjunction or raw).
74
- * @param fieldValueCount - count of field values being loaded for a field
75
- */
76
- incrementDataManagerDataloaderLoadCount(incrementLoadCountEvent: IncrementLoadCountEvent): void;
77
-
78
- /**
79
- * Called when a cache load is initiated via the standard
80
- * load methods (not equality conjunction or raw). Occurs upon a dataloader
81
- * miss.
82
- * @param fieldValueCount - count of field values being loaded for a field
83
- */
84
- incrementDataManagerCacheLoadCount(incrementLoadCountEvent: IncrementLoadCountEvent): void;
85
-
86
- /**
87
- * Called when a database load is initiated via the standard
88
- * load methods (not equality conjunction or raw). Occurs upon a cache
89
- * miss or when fetching an uncacheable field.
140
+ * Called when a dataloader, cache, or database load is initiated via the standard
141
+ * load methods (not equality conjunction or raw). Most commonly used for logging
142
+ * a waterfall to determine dataloader and cache hit rates and ratios.
90
143
  * @param fieldValueCount - count of field values being loaded for a field
91
144
  */
92
- incrementDataManagerDatabaseLoadCount(incrementLoadCountEvent: IncrementLoadCountEvent): void;
145
+ incrementDataManagerLoadCount(incrementLoadCountEvent: IncrementLoadCountEvent): void;
93
146
  }
@@ -9,9 +9,5 @@ export default class NoOpEntityMetricsAdapter implements IEntityMetricsAdapter {
9
9
  logAuthorizationEvent(_authorizationEvent: EntityMetricsAuthorizationEvent): void {}
10
10
  logDataManagerLoadEvent(_loadEvent: EntityMetricsLoadEvent): void {}
11
11
  logMutatorMutationEvent(_mutationEvent: EntityMetricsMutationEvent): void {}
12
- incrementDataManagerDataloaderLoadCount(
13
- _incrementLoadCountEvent: IncrementLoadCountEvent
14
- ): void {}
15
- incrementDataManagerCacheLoadCount(_incrementLoadCountEvent: IncrementLoadCountEvent): void {}
16
- incrementDataManagerDatabaseLoadCount(_incrementLoadCountEvent: IncrementLoadCountEvent): void {}
12
+ incrementDataManagerLoadCount(_incrementLoadCountEvent: IncrementLoadCountEvent): void {}
17
13
  }
@@ -158,7 +158,10 @@ export default class StubDatabaseAdapter<T> extends EntityDatabaseAdapter<T> {
158
158
 
159
159
  private generateRandomID(): any {
160
160
  const idSchemaField = this.entityConfiguration2.schema.get(this.entityConfiguration2.idField);
161
- invariant(idSchemaField, `No schema field found for ${this.entityConfiguration2.idField}`);
161
+ invariant(
162
+ idSchemaField,
163
+ `No schema field found for ${String(this.entityConfiguration2.idField)}`
164
+ );
162
165
  if (idSchemaField instanceof StringField) {
163
166
  return uuidv4();
164
167
  } else if (idSchemaField instanceof IntField) {