@expo/entity 0.17.0 → 0.21.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.
Files changed (215) hide show
  1. package/build/EnforcingEntityLoader.js +2 -2
  2. package/build/EnforcingEntityLoader.js.map +1 -1
  3. package/build/Entity.js +8 -2
  4. package/build/Entity.js.map +1 -1
  5. package/build/EntityAssociationLoader.js +3 -3
  6. package/build/EntityAssociationLoader.js.map +1 -1
  7. package/build/EntityCompanion.d.ts +5 -0
  8. package/build/EntityCompanion.js +8 -1
  9. package/build/EntityCompanion.js.map +1 -1
  10. package/build/EntityCompanionProvider.d.ts +1 -1
  11. package/build/EntityCompanionProvider.js +5 -5
  12. package/build/EntityCompanionProvider.js.map +1 -1
  13. package/build/EntityConfiguration.d.ts +1 -1
  14. package/build/EntityConfiguration.js +3 -3
  15. package/build/EntityConfiguration.js.map +1 -1
  16. package/build/EntityDatabaseAdapter.d.ts +4 -4
  17. package/build/EntityDatabaseAdapter.js +13 -13
  18. package/build/EntityDatabaseAdapter.js.map +1 -1
  19. package/build/EntityFieldDefinition.d.ts +77 -0
  20. package/build/EntityFieldDefinition.js +53 -0
  21. package/build/EntityFieldDefinition.js.map +1 -0
  22. package/build/EntityFields.d.ts +1 -77
  23. package/build/EntityFields.js +13 -61
  24. package/build/EntityFields.js.map +1 -1
  25. package/build/EntityLoader.d.ts +3 -1
  26. package/build/EntityLoader.js +19 -15
  27. package/build/EntityLoader.js.map +1 -1
  28. package/build/EntityLoaderFactory.d.ts +3 -1
  29. package/build/EntityLoaderFactory.js +3 -2
  30. package/build/EntityLoaderFactory.js.map +1 -1
  31. package/build/EntityMutationInfo.d.ts +26 -0
  32. package/build/EntityMutationInfo.js +10 -0
  33. package/build/EntityMutationInfo.js.map +1 -0
  34. package/build/EntityMutationTriggerConfiguration.d.ts +4 -4
  35. package/build/EntityMutationValidator.d.ts +3 -3
  36. package/build/EntityMutationValidator.js.map +1 -1
  37. package/build/EntityMutator.d.ts +5 -16
  38. package/build/EntityMutator.js +62 -58
  39. package/build/EntityMutator.js.map +1 -1
  40. package/build/EntityPrivacyPolicy.d.ts +5 -4
  41. package/build/EntityPrivacyPolicy.js +60 -12
  42. package/build/EntityPrivacyPolicy.js.map +1 -1
  43. package/build/EntityQueryContext.d.ts +24 -0
  44. package/build/EntityQueryContext.js +43 -0
  45. package/build/EntityQueryContext.js.map +1 -1
  46. package/build/EntityQueryContextProvider.js +1 -0
  47. package/build/EntityQueryContextProvider.js.map +1 -1
  48. package/build/EntitySecondaryCacheLoader.js +2 -2
  49. package/build/EntitySecondaryCacheLoader.js.map +1 -1
  50. package/build/ReadonlyEntity.js +3 -4
  51. package/build/ReadonlyEntity.js.map +1 -1
  52. package/build/ViewerScopedEntityCompanion.d.ts +5 -0
  53. package/build/ViewerScopedEntityCompanion.js +6 -0
  54. package/build/ViewerScopedEntityCompanion.js.map +1 -1
  55. package/build/__tests__/EnforcingEntityLoader-test.js +82 -82
  56. package/build/__tests__/EnforcingEntityLoader-test.js.map +1 -1
  57. package/build/__tests__/Entity-test.js +6 -6
  58. package/build/__tests__/Entity-test.js.map +1 -1
  59. package/build/__tests__/EntityAssociationLoader-test.js +40 -40
  60. package/build/__tests__/EntityAssociationLoader-test.js.map +1 -1
  61. package/build/__tests__/EntityCommonUseCases-test.js +11 -11
  62. package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
  63. package/build/__tests__/EntityCompanion-test.js +3 -3
  64. package/build/__tests__/EntityCompanion-test.js.map +1 -1
  65. package/build/__tests__/EntityCompanionProvider-test.js +1 -1
  66. package/build/__tests__/EntityCompanionProvider-test.js.map +1 -1
  67. package/build/__tests__/EntityDatabaseAdapter-test.js +12 -12
  68. package/build/__tests__/EntityDatabaseAdapter-test.js.map +1 -1
  69. package/build/__tests__/EntityEdges-test.js +103 -6
  70. package/build/__tests__/EntityEdges-test.js.map +1 -1
  71. package/build/__tests__/EntityFields-test.js +18 -27
  72. package/build/__tests__/EntityFields-test.js.map +1 -1
  73. package/build/__tests__/EntityLoader-constructor-test.d.ts +22 -0
  74. package/build/__tests__/EntityLoader-constructor-test.js +111 -0
  75. package/build/__tests__/EntityLoader-constructor-test.js.map +1 -0
  76. package/build/__tests__/EntityLoader-test.js +72 -64
  77. package/build/__tests__/EntityLoader-test.js.map +1 -1
  78. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.d.ts +1 -0
  79. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js +81 -0
  80. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js.map +1 -0
  81. package/build/__tests__/EntityMutator-test.js +116 -114
  82. package/build/__tests__/EntityMutator-test.js.map +1 -1
  83. package/build/__tests__/EntityPrivacyPolicy-test.js +143 -67
  84. package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
  85. package/build/__tests__/EntityQueryContext-test.d.ts +1 -0
  86. package/build/__tests__/EntityQueryContext-test.js +56 -0
  87. package/build/__tests__/EntityQueryContext-test.js.map +1 -0
  88. package/build/__tests__/EntitySecondaryCacheLoader-test.js +15 -15
  89. package/build/__tests__/EntitySecondaryCacheLoader-test.js.map +1 -1
  90. package/build/__tests__/EntitySelfReferentialEdges-test.js +13 -12
  91. package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
  92. package/build/__tests__/ReadonlyEntity-test.js +11 -11
  93. package/build/__tests__/ReadonlyEntity-test.js.map +1 -1
  94. package/build/__tests__/ViewerContext-test.js +2 -2
  95. package/build/__tests__/ViewerContext-test.js.map +1 -1
  96. package/build/__tests__/ViewerScopedEntityCompanion-test.js +2 -2
  97. package/build/__tests__/ViewerScopedEntityCompanion-test.js.map +1 -1
  98. package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js +2 -2
  99. package/build/__tests__/ViewerScopedEntityCompanionProvider-test.js.map +1 -1
  100. package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js +5 -5
  101. package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js.map +1 -1
  102. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js +5 -5
  103. package/build/__tests__/ViewerScopedEntityMutatorFactory-test.js.map +1 -1
  104. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js +5 -5
  105. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js.map +1 -1
  106. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js +2 -2
  107. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js.map +1 -1
  108. package/build/__tests__/entityUtils-test.js +21 -21
  109. package/build/__tests__/entityUtils-test.js.map +1 -1
  110. package/build/index.d.ts +3 -0
  111. package/build/index.js +5 -1
  112. package/build/index.js.map +1 -1
  113. package/build/internal/EntityDataManager.js +8 -7
  114. package/build/internal/EntityDataManager.js.map +1 -1
  115. package/build/internal/EntityFieldTransformationUtils.js +2 -2
  116. package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
  117. package/build/internal/ReadThroughEntityCache.js +4 -4
  118. package/build/internal/ReadThroughEntityCache.js.map +1 -1
  119. package/build/internal/__tests__/EntityDataManager-test.js +17 -17
  120. package/build/internal/__tests__/EntityDataManager-test.js.map +1 -1
  121. package/build/internal/__tests__/EntityFieldTransformationUtils-test.js +8 -8
  122. package/build/internal/__tests__/EntityFieldTransformationUtils-test.js.map +1 -1
  123. package/build/internal/__tests__/ReadThroughEntityCache-test.js +48 -48
  124. package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
  125. package/build/metrics/EntityMetricsUtils.js +1 -1
  126. package/build/metrics/EntityMetricsUtils.js.map +1 -1
  127. package/build/metrics/IEntityMetricsAdapter.d.ts +16 -0
  128. package/build/metrics/IEntityMetricsAdapter.js +6 -1
  129. package/build/metrics/IEntityMetricsAdapter.js.map +1 -1
  130. package/build/metrics/NoOpEntityMetricsAdapter.d.ts +2 -1
  131. package/build/metrics/NoOpEntityMetricsAdapter.js +1 -0
  132. package/build/metrics/NoOpEntityMetricsAdapter.js.map +1 -1
  133. package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js +4 -4
  134. package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js.map +1 -1
  135. package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js +4 -4
  136. package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js.map +1 -1
  137. package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js +4 -4
  138. package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js.map +1 -1
  139. package/build/testfixtures/DateIDTestEntity.js.map +1 -1
  140. package/build/testfixtures/SimpleTestEntity.js.map +1 -1
  141. package/build/testfixtures/TestEntity.d.ts +5 -5
  142. package/build/testfixtures/TestEntity.js +2 -2
  143. package/build/testfixtures/TestEntity.js.map +1 -1
  144. package/build/testfixtures/TestEntity2.d.ts +5 -5
  145. package/build/testfixtures/TestEntity2.js.map +1 -1
  146. package/build/testfixtures/TestEntityNumberKey.js.map +1 -1
  147. package/build/utils/collections/__tests__/maps-test.js +13 -13
  148. package/build/utils/collections/__tests__/maps-test.js.map +1 -1
  149. package/build/utils/collections/maps.js +1 -1
  150. package/build/utils/collections/maps.js.map +1 -1
  151. package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +6 -6
  152. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js +1 -1
  153. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
  154. package/build/utils/testing/StubCacheAdapter.js +1 -1
  155. package/build/utils/testing/StubCacheAdapter.js.map +1 -1
  156. package/build/utils/testing/StubDatabaseAdapter.js +6 -6
  157. package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
  158. package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js +9 -9
  159. package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js.map +1 -1
  160. package/build/utils/testing/describeFieldTestCase.d.ts +2 -0
  161. package/build/utils/testing/describeFieldTestCase.js +18 -0
  162. package/build/utils/testing/describeFieldTestCase.js.map +1 -0
  163. package/package.json +2 -1
  164. package/src/Entity.ts +10 -2
  165. package/src/EntityAssociationLoader.ts +1 -1
  166. package/src/EntityCompanion.ts +10 -2
  167. package/src/EntityCompanionProvider.ts +5 -9
  168. package/src/EntityConfiguration.ts +1 -1
  169. package/src/EntityDatabaseAdapter.ts +10 -8
  170. package/src/EntityFieldDefinition.ts +124 -0
  171. package/src/EntityFields.ts +2 -125
  172. package/src/EntityLoader.ts +12 -4
  173. package/src/EntityLoaderFactory.ts +5 -2
  174. package/src/EntityMutationInfo.ts +47 -0
  175. package/src/EntityMutationTriggerConfiguration.ts +5 -5
  176. package/src/EntityMutationValidator.ts +10 -4
  177. package/src/EntityMutator.ts +98 -76
  178. package/src/EntityPrivacyPolicy.ts +77 -19
  179. package/src/EntityQueryContext.ts +54 -0
  180. package/src/EntityQueryContextProvider.ts +1 -0
  181. package/src/ReadonlyEntity.ts +3 -2
  182. package/src/ViewerScopedEntityCompanion.ts +8 -0
  183. package/src/__tests__/Entity-test.ts +8 -8
  184. package/src/__tests__/EntityCommonUseCases-test.ts +4 -4
  185. package/src/__tests__/EntityEdges-test.ts +169 -14
  186. package/src/__tests__/EntityFields-test.ts +2 -21
  187. package/src/__tests__/EntityLoader-constructor-test.ts +177 -0
  188. package/src/__tests__/EntityLoader-test.ts +39 -11
  189. package/src/__tests__/EntityMutator-MutationCacheConsistency-test.ts +105 -0
  190. package/src/__tests__/EntityMutator-test.ts +140 -133
  191. package/src/__tests__/EntityPrivacyPolicy-test.ts +215 -78
  192. package/src/__tests__/EntityQueryContext-test.ts +82 -0
  193. package/src/__tests__/EntitySecondaryCacheLoader-test.ts +7 -9
  194. package/src/__tests__/EntitySelfReferentialEdges-test.ts +6 -5
  195. package/src/__tests__/ViewerContext-test.ts +7 -6
  196. package/src/__tests__/ViewerScopedEntityCompanion-test.ts +11 -10
  197. package/src/__tests__/ViewerScopedEntityMutatorFactory-test.ts +4 -3
  198. package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +6 -6
  199. package/src/__tests__/cases/TwoEntitySameTableOverlappingRows-test.ts +4 -4
  200. package/src/index.ts +3 -0
  201. package/src/internal/EntityDataManager.ts +2 -1
  202. package/src/internal/__tests__/EntityDataManager-test.ts +2 -2
  203. package/src/internal/__tests__/ReadThroughEntityCache-test.ts +15 -13
  204. package/src/metrics/EntityMetricsUtils.ts +56 -50
  205. package/src/metrics/IEntityMetricsAdapter.ts +23 -0
  206. package/src/metrics/NoOpEntityMetricsAdapter.ts +2 -0
  207. package/src/testfixtures/DateIDTestEntity.ts +4 -4
  208. package/src/testfixtures/SimpleTestEntity.ts +4 -4
  209. package/src/testfixtures/TestEntity.ts +4 -4
  210. package/src/testfixtures/TestEntity2.ts +4 -4
  211. package/src/testfixtures/TestEntityNumberKey.ts +4 -4
  212. package/src/utils/testing/StubDatabaseAdapter.ts +2 -2
  213. package/src/utils/testing/__tests__/StubDatabaseAdapter-test.ts +1 -1
  214. package/src/utils/testing/describeFieldTestCase.ts +21 -0
  215. package/CHANGELOG.md +0 -252
@@ -1,7 +1,7 @@
1
1
  import { v1 as uuidv1, v3 as uuidv3, v4 as uuidv4, v5 as uuidv5 } from 'uuid';
2
2
 
3
+ import { EntityFieldDefinition } from '../EntityFieldDefinition';
3
4
  import {
4
- EntityFieldDefinition,
5
5
  StringField,
6
6
  UUIDField,
7
7
  DateField,
@@ -14,6 +14,7 @@ import {
14
14
  JSONArrayField,
15
15
  MaybeJSONArrayField,
16
16
  } from '../EntityFields';
17
+ import describeFieldTestCase from '../utils/testing/describeFieldTestCase';
17
18
 
18
19
  class TestFieldDefinition extends EntityFieldDefinition<string> {
19
20
  protected validateInputValueInternal(value: string): boolean {
@@ -53,26 +54,6 @@ describe(EntityFieldDefinition, () => {
53
54
  });
54
55
  });
55
56
 
56
- const describeFieldTestCase = <T>(
57
- fieldDefinition: EntityFieldDefinition<T>,
58
- validValues: T[],
59
- invalidValues: any[]
60
- ): void => {
61
- describe(fieldDefinition.constructor.name, () => {
62
- if (validValues.length > 0) {
63
- test.each(validValues)(`${fieldDefinition.constructor.name}.valid %p`, (value) => {
64
- expect(fieldDefinition.validateInputValue(value)).toBe(true);
65
- });
66
- }
67
-
68
- if (invalidValues.length > 0) {
69
- test.each(invalidValues)(`${fieldDefinition.constructor.name}.invalid %p`, (value) => {
70
- expect(fieldDefinition.validateInputValue(value)).toBe(false);
71
- });
72
- }
73
- });
74
- };
75
-
76
57
  describeFieldTestCase(new StringField({ columnName: 'wat' }), ['hello', ''], [1, true, {}, [[]]]);
77
58
  describeFieldTestCase(
78
59
  new UUIDField({ columnName: 'wat' }),
@@ -0,0 +1,177 @@
1
+ import { instance, mock } from 'ts-mockito';
2
+
3
+ import Entity from '../Entity';
4
+ import { EntityCompanionDefinition } from '../EntityCompanionProvider';
5
+ import EntityConfiguration from '../EntityConfiguration';
6
+ import { StringField } from '../EntityFields';
7
+ import EntityLoader from '../EntityLoader';
8
+ import EntityPrivacyPolicy from '../EntityPrivacyPolicy';
9
+ import ViewerContext from '../ViewerContext';
10
+ import EntityDataManager from '../internal/EntityDataManager';
11
+ import ReadThroughEntityCache from '../internal/ReadThroughEntityCache';
12
+ import IEntityMetricsAdapter from '../metrics/IEntityMetricsAdapter';
13
+ import AlwaysAllowPrivacyPolicyRule from '../rules/AlwaysAllowPrivacyPolicyRule';
14
+ import { NoCacheStubCacheAdapterProvider } from '../utils/testing/StubCacheAdapter';
15
+ import StubDatabaseAdapter from '../utils/testing/StubDatabaseAdapter';
16
+ import StubQueryContextProvider from '../utils/testing/StubQueryContextProvider';
17
+
18
+ export type TestFields = {
19
+ id: string;
20
+ };
21
+
22
+ export type TestFieldSelection = keyof TestFields;
23
+
24
+ export const testEntityConfiguration = new EntityConfiguration<TestFields>({
25
+ idField: 'id',
26
+ tableName: 'test_entity_should_not_write_to_db',
27
+ schema: {
28
+ id: new StringField({
29
+ columnName: 'id',
30
+ }),
31
+ },
32
+ databaseAdapterFlavor: 'postgres',
33
+ cacheAdapterFlavor: 'redis',
34
+ });
35
+
36
+ export class TestEntityPrivacyPolicy extends EntityPrivacyPolicy<
37
+ TestFields,
38
+ string,
39
+ ViewerContext,
40
+ TestEntity,
41
+ TestFieldSelection
42
+ > {
43
+ protected override readonly readRules = [
44
+ new AlwaysAllowPrivacyPolicyRule<
45
+ TestFields,
46
+ string,
47
+ ViewerContext,
48
+ TestEntity,
49
+ TestFieldSelection
50
+ >(),
51
+ ];
52
+ protected override readonly createRules = [
53
+ new AlwaysAllowPrivacyPolicyRule<
54
+ TestFields,
55
+ string,
56
+ ViewerContext,
57
+ TestEntity,
58
+ TestFieldSelection
59
+ >(),
60
+ ];
61
+ protected override readonly updateRules = [
62
+ new AlwaysAllowPrivacyPolicyRule<
63
+ TestFields,
64
+ string,
65
+ ViewerContext,
66
+ TestEntity,
67
+ TestFieldSelection
68
+ >(),
69
+ ];
70
+ protected override readonly deleteRules = [
71
+ new AlwaysAllowPrivacyPolicyRule<
72
+ TestFields,
73
+ string,
74
+ ViewerContext,
75
+ TestEntity,
76
+ TestFieldSelection
77
+ >(),
78
+ ];
79
+ }
80
+
81
+ const ID_SENTINEL_THROW_LITERAL = 'throw_literal';
82
+ const ID_SENTINEL_THROW_ERROR = 'throw_error';
83
+
84
+ export default class TestEntity extends Entity<
85
+ TestFields,
86
+ string,
87
+ ViewerContext,
88
+ TestFieldSelection
89
+ > {
90
+ constructor(viewerContext: ViewerContext, rawFields: Readonly<TestFields>) {
91
+ if (rawFields.id === ID_SENTINEL_THROW_LITERAL) {
92
+ // eslint-disable-next-line no-throw-literal,@typescript-eslint/no-throw-literal
93
+ throw 'hello';
94
+ } else if (rawFields.id === ID_SENTINEL_THROW_ERROR) {
95
+ throw new Error('world');
96
+ }
97
+ super(viewerContext, rawFields);
98
+ }
99
+
100
+ static getCompanionDefinition(): EntityCompanionDefinition<
101
+ TestFields,
102
+ string,
103
+ ViewerContext,
104
+ TestEntity,
105
+ TestEntityPrivacyPolicy,
106
+ TestFieldSelection
107
+ > {
108
+ return testEntityCompanion;
109
+ }
110
+ }
111
+
112
+ export const testEntityCompanion = new EntityCompanionDefinition({
113
+ entityClass: TestEntity,
114
+ entityConfiguration: testEntityConfiguration,
115
+ privacyPolicyClass: TestEntityPrivacyPolicy,
116
+ });
117
+
118
+ describe(EntityLoader, () => {
119
+ it('handles thrown errors and literals from constructor', async () => {
120
+ const viewerContext = instance(mock(ViewerContext));
121
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
122
+ const queryContext = StubQueryContextProvider.getQueryContext();
123
+
124
+ const databaseAdapter = new StubDatabaseAdapter<TestFields>(
125
+ testEntityConfiguration,
126
+ StubDatabaseAdapter.convertFieldObjectsToDataStore(
127
+ testEntityConfiguration,
128
+ new Map([
129
+ [
130
+ testEntityConfiguration.tableName,
131
+ [
132
+ {
133
+ id: ID_SENTINEL_THROW_LITERAL,
134
+ },
135
+ {
136
+ id: ID_SENTINEL_THROW_ERROR,
137
+ },
138
+ ],
139
+ ],
140
+ ])
141
+ )
142
+ );
143
+ const privacyPolicy = new TestEntityPrivacyPolicy();
144
+ const cacheAdapterProvider = new NoCacheStubCacheAdapterProvider();
145
+ const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
146
+ const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
147
+ const dataManager = new EntityDataManager(
148
+ databaseAdapter,
149
+ entityCache,
150
+ StubQueryContextProvider,
151
+ metricsAdapter,
152
+ TestEntity.name
153
+ );
154
+ const entityLoader = new EntityLoader(
155
+ viewerContext,
156
+ queryContext,
157
+ testEntityConfiguration,
158
+ TestEntity,
159
+ privacyPolicy,
160
+ dataManager,
161
+ metricsAdapter
162
+ );
163
+
164
+ let capturedThrownThing1: any;
165
+ try {
166
+ await entityLoader.loadByIDAsync(ID_SENTINEL_THROW_LITERAL);
167
+ } catch (e) {
168
+ capturedThrownThing1 = e;
169
+ }
170
+ expect(capturedThrownThing1).not.toBeInstanceOf(Error);
171
+ expect(capturedThrownThing1).toEqual('hello');
172
+
173
+ const result = await entityLoader.loadByIDAsync(ID_SENTINEL_THROW_ERROR);
174
+ expect(result.ok).toBe(false);
175
+ expect(result.enforceError().message).toEqual('world');
176
+ });
177
+ });
@@ -21,6 +21,7 @@ describe(EntityLoader, () => {
21
21
  it('loads entities', async () => {
22
22
  const dateToInsert = new Date();
23
23
  const viewerContext = instance(mock(ViewerContext));
24
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
24
25
  const queryContext = StubQueryContextProvider.getQueryContext();
25
26
 
26
27
  const id1 = uuidv4();
@@ -71,7 +72,8 @@ describe(EntityLoader, () => {
71
72
  testEntityConfiguration,
72
73
  TestEntity,
73
74
  privacyPolicy,
74
- dataManager
75
+ dataManager,
76
+ metricsAdapter
75
77
  );
76
78
  const entity = await enforceAsyncResult(entityLoader.loadByIDAsync(id1));
77
79
  expect(entity.getID()).toEqual(id1);
@@ -111,6 +113,7 @@ describe(EntityLoader, () => {
111
113
  const privacyPolicy = new TestEntityPrivacyPolicy();
112
114
  const spiedPrivacyPolicy = spy(privacyPolicy);
113
115
  const viewerContext = instance(mock(ViewerContext));
116
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
114
117
  const queryContext = StubQueryContextProvider.getQueryContext();
115
118
 
116
119
  const id1 = uuidv4();
@@ -169,7 +172,8 @@ describe(EntityLoader, () => {
169
172
  testEntityConfiguration,
170
173
  TestEntity,
171
174
  privacyPolicy,
172
- dataManager
175
+ dataManager,
176
+ metricsAdapter
173
177
  );
174
178
  const entities = await enforceResultsAsync(
175
179
  entityLoader.loadManyByFieldEqualityConjunctionAsync([
@@ -185,7 +189,12 @@ describe(EntityLoader, () => {
185
189
  );
186
190
  expect(entities).toHaveLength(2);
187
191
  verify(
188
- spiedPrivacyPolicy.authorizeReadAsync(viewerContext, queryContext, anyOfClass(TestEntity))
192
+ spiedPrivacyPolicy.authorizeReadAsync(
193
+ viewerContext,
194
+ queryContext,
195
+ anyOfClass(TestEntity),
196
+ anything()
197
+ )
189
198
  ).twice();
190
199
 
191
200
  await expect(
@@ -200,6 +209,7 @@ describe(EntityLoader, () => {
200
209
  const spiedPrivacyPolicy = spy(privacyPolicy);
201
210
 
202
211
  const viewerContext = instance(mock(ViewerContext));
212
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
203
213
  const queryContext = StubQueryContextProvider.getQueryContext();
204
214
 
205
215
  const id1 = uuidv4();
@@ -240,14 +250,18 @@ describe(EntityLoader, () => {
240
250
  testEntityConfiguration,
241
251
  TestEntity,
242
252
  privacyPolicy,
243
- dataManager
253
+ dataManager,
254
+ metricsAdapter
244
255
  );
245
256
  const entity = await enforceAsyncResult(entityLoader.loadByIDAsync(id1));
246
- verify(spiedPrivacyPolicy.authorizeReadAsync(viewerContext, queryContext, entity)).once();
257
+ verify(
258
+ spiedPrivacyPolicy.authorizeReadAsync(viewerContext, queryContext, entity, anything())
259
+ ).once();
247
260
  });
248
261
 
249
262
  it('invalidates upon invalidate one', async () => {
250
263
  const viewerContext = instance(mock(ViewerContext));
264
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
251
265
  const queryContext = StubQueryContextProvider.getQueryContext();
252
266
  const privacyPolicy = instance(mock(TestEntityPrivacyPolicy));
253
267
  const dataManagerMock = mock<EntityDataManager<TestFields>>();
@@ -260,7 +274,8 @@ describe(EntityLoader, () => {
260
274
  testEntityConfiguration,
261
275
  TestEntity,
262
276
  privacyPolicy,
263
- dataManagerInstance
277
+ dataManagerInstance,
278
+ metricsAdapter
264
279
  );
265
280
  await entityLoader.invalidateFieldsAsync({ customIdField: id1 } as any);
266
281
 
@@ -271,6 +286,7 @@ describe(EntityLoader, () => {
271
286
 
272
287
  it('invalidates upon invalidate by field', async () => {
273
288
  const viewerContext = instance(mock(ViewerContext));
289
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
274
290
  const queryContext = StubQueryContextProvider.getQueryContext();
275
291
  const privacyPolicy = instance(mock(TestEntityPrivacyPolicy));
276
292
  const dataManagerMock = mock<EntityDataManager<TestFields>>();
@@ -283,7 +299,8 @@ describe(EntityLoader, () => {
283
299
  testEntityConfiguration,
284
300
  TestEntity,
285
301
  privacyPolicy,
286
- dataManagerInstance
302
+ dataManagerInstance,
303
+ metricsAdapter
287
304
  );
288
305
  await entityLoader.invalidateFieldsAsync({ customIdField: id1 } as any);
289
306
  verify(
@@ -293,6 +310,7 @@ describe(EntityLoader, () => {
293
310
 
294
311
  it('invalidates upon invalidate by entity', async () => {
295
312
  const viewerContext = instance(mock(ViewerContext));
313
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
296
314
  const queryContext = StubQueryContextProvider.getQueryContext();
297
315
  const privacyPolicy = instance(mock(TestEntityPrivacyPolicy));
298
316
  const dataManagerMock = mock<EntityDataManager<TestFields>>();
@@ -309,7 +327,8 @@ describe(EntityLoader, () => {
309
327
  testEntityConfiguration,
310
328
  TestEntity,
311
329
  privacyPolicy,
312
- dataManagerInstance
330
+ dataManagerInstance,
331
+ metricsAdapter
313
332
  );
314
333
  await entityLoader.invalidateEntityAsync(entityInstance);
315
334
  verify(
@@ -319,6 +338,7 @@ describe(EntityLoader, () => {
319
338
 
320
339
  it('returns error result when not allowed', async () => {
321
340
  const viewerContext = instance(mock(ViewerContext));
341
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
322
342
  const queryContext = StubQueryContextProvider.getQueryContext();
323
343
  const privacyPolicyMock = mock(TestEntityPrivacyPolicy);
324
344
  const dataManagerMock = mock<EntityDataManager<TestFields>>();
@@ -331,7 +351,12 @@ describe(EntityLoader, () => {
331
351
  const rejectionError = new Error();
332
352
 
333
353
  when(
334
- privacyPolicyMock.authorizeReadAsync(viewerContext, queryContext, anyOfClass(TestEntity))
354
+ privacyPolicyMock.authorizeReadAsync(
355
+ viewerContext,
356
+ queryContext,
357
+ anyOfClass(TestEntity),
358
+ anything()
359
+ )
335
360
  ).thenReject(rejectionError);
336
361
 
337
362
  const privacyPolicy = instance(privacyPolicyMock);
@@ -343,7 +368,8 @@ describe(EntityLoader, () => {
343
368
  testEntityConfiguration,
344
369
  TestEntity,
345
370
  privacyPolicy,
346
- dataManagerInstance
371
+ dataManagerInstance,
372
+ metricsAdapter
347
373
  );
348
374
 
349
375
  const entityResult = await entityLoader.loadByIDAsync(id1);
@@ -354,6 +380,7 @@ describe(EntityLoader, () => {
354
380
 
355
381
  it('throws upon database adapter error', async () => {
356
382
  const viewerContext = instance(mock(ViewerContext));
383
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
357
384
  const queryContext = StubQueryContextProvider.getQueryContext();
358
385
  const privacyPolicy = instance(mock(TestEntityPrivacyPolicy));
359
386
  const dataManagerMock = mock<EntityDataManager<TestFields>>();
@@ -372,7 +399,8 @@ describe(EntityLoader, () => {
372
399
  testEntityConfiguration,
373
400
  TestEntity,
374
401
  privacyPolicy,
375
- dataManagerInstance
402
+ dataManagerInstance,
403
+ metricsAdapter
376
404
  );
377
405
 
378
406
  const loadByValue = uuidv4();
@@ -0,0 +1,105 @@
1
+ import Entity from '../Entity';
2
+ import { EntityCompanionDefinition } from '../EntityCompanionProvider';
3
+ import EntityConfiguration from '../EntityConfiguration';
4
+ import { UUIDField } from '../EntityFields';
5
+ import { EntityMutationType, EntityTriggerMutationInfo } from '../EntityMutationInfo';
6
+ import { EntityNonTransactionalMutationTrigger } from '../EntityMutationTriggerConfiguration';
7
+ import EntityPrivacyPolicy from '../EntityPrivacyPolicy';
8
+ import ViewerContext from '../ViewerContext';
9
+ import AlwaysAllowPrivacyPolicyRule from '../rules/AlwaysAllowPrivacyPolicyRule';
10
+ import { createUnitTestEntityCompanionProvider } from '../utils/testing/createUnitTestEntityCompanionProvider';
11
+
12
+ type BlahFields = {
13
+ id: string;
14
+ };
15
+
16
+ class BlahEntityPrivacyPolicy extends EntityPrivacyPolicy<
17
+ BlahFields,
18
+ string,
19
+ ViewerContext,
20
+ BlahEntity
21
+ > {
22
+ protected override readonly createRules = [
23
+ new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
24
+ ];
25
+ protected override readonly readRules = [
26
+ new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
27
+ ];
28
+ protected override readonly updateRules = [
29
+ new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
30
+ ];
31
+ protected override readonly deleteRules = [
32
+ new AlwaysAllowPrivacyPolicyRule<BlahFields, string, ViewerContext, BlahEntity>(),
33
+ ];
34
+ }
35
+
36
+ class BlahEntity extends Entity<BlahFields, string, ViewerContext> {
37
+ static getCompanionDefinition(): EntityCompanionDefinition<
38
+ BlahFields,
39
+ string,
40
+ ViewerContext,
41
+ BlahEntity,
42
+ BlahEntityPrivacyPolicy
43
+ > {
44
+ return blahCompanion;
45
+ }
46
+ }
47
+
48
+ const blahCompanion = new EntityCompanionDefinition({
49
+ entityClass: BlahEntity,
50
+ entityConfiguration: new EntityConfiguration<BlahFields>({
51
+ idField: 'id',
52
+ tableName: 'blah_table',
53
+ schema: {
54
+ id: new UUIDField({
55
+ columnName: 'id',
56
+ cache: true,
57
+ }),
58
+ },
59
+ databaseAdapterFlavor: 'postgres',
60
+ cacheAdapterFlavor: 'redis',
61
+ }),
62
+ privacyPolicyClass: BlahEntityPrivacyPolicy,
63
+ mutationTriggers: () => ({
64
+ afterCommit: [new TestNonTransactionalMutationTrigger()],
65
+ }),
66
+ });
67
+
68
+ class TestNonTransactionalMutationTrigger extends EntityNonTransactionalMutationTrigger<
69
+ BlahFields,
70
+ string,
71
+ ViewerContext,
72
+ BlahEntity
73
+ > {
74
+ async executeAsync(
75
+ viewerContext: ViewerContext,
76
+ entity: BlahEntity,
77
+ mutationInfo: EntityTriggerMutationInfo<BlahFields, string, ViewerContext, BlahEntity>
78
+ ): Promise<void> {
79
+ if (mutationInfo.type === EntityMutationType.DELETE) {
80
+ const entityLoaded = await BlahEntity.loader(viewerContext)
81
+ .enforcing()
82
+ .loadByIDNullableAsync(entity.getID());
83
+ if (entityLoaded) {
84
+ throw new Error(
85
+ 'should not have been able to re-load the entity after delete. this means the cache has not been cleared'
86
+ );
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ describe('EntityMutator', () => {
93
+ test('cache consistency with post-commit callbacks', async () => {
94
+ const companionProvider = createUnitTestEntityCompanionProvider();
95
+ const viewerContext = new ViewerContext(companionProvider);
96
+
97
+ // put it in cache
98
+ const entity = await BlahEntity.creator(viewerContext).enforceCreateAsync();
99
+ const entityLoaded = await BlahEntity.loader(viewerContext)
100
+ .enforcing()
101
+ .loadByIDAsync(entity.getID());
102
+
103
+ await BlahEntity.enforceDeleteAsync(entityLoaded);
104
+ });
105
+ });