@expo/entity 0.19.0 → 0.23.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 (53) hide show
  1. package/build/EntityCacheAdapter.d.ts +2 -9
  2. package/build/EntityCacheAdapter.js.map +1 -1
  3. package/build/EntityMutationInfo.d.ts +26 -0
  4. package/build/EntityMutationInfo.js +10 -0
  5. package/build/EntityMutationInfo.js.map +1 -0
  6. package/build/EntityMutationTriggerConfiguration.d.ts +3 -3
  7. package/build/EntityMutationValidator.d.ts +2 -2
  8. package/build/EntityMutationValidator.js.map +1 -1
  9. package/build/EntityMutator.d.ts +4 -15
  10. package/build/EntityMutator.js +45 -41
  11. package/build/EntityMutator.js.map +1 -1
  12. package/build/EntityQueryContext.d.ts +24 -0
  13. package/build/EntityQueryContext.js +43 -0
  14. package/build/EntityQueryContext.js.map +1 -1
  15. package/build/EntityQueryContextProvider.js +1 -0
  16. package/build/EntityQueryContextProvider.js.map +1 -1
  17. package/build/__tests__/EntityEdges-test.js +99 -3
  18. package/build/__tests__/EntityEdges-test.js.map +1 -1
  19. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.d.ts +1 -0
  20. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js +81 -0
  21. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js.map +1 -0
  22. package/build/__tests__/EntityMutator-test.js +9 -7
  23. package/build/__tests__/EntityMutator-test.js.map +1 -1
  24. package/build/__tests__/EntityQueryContext-test.d.ts +1 -0
  25. package/build/__tests__/EntityQueryContext-test.js +56 -0
  26. package/build/__tests__/EntityQueryContext-test.js.map +1 -0
  27. package/build/index.d.ts +1 -0
  28. package/build/index.js +1 -0
  29. package/build/index.js.map +1 -1
  30. package/build/internal/ReadThroughEntityCache.d.ts +2 -3
  31. package/build/internal/ReadThroughEntityCache.js +2 -6
  32. package/build/internal/ReadThroughEntityCache.js.map +1 -1
  33. package/build/internal/__tests__/ReadThroughEntityCache-test.js +0 -32
  34. package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
  35. package/build/utils/testing/StubCacheAdapter.d.ts +6 -9
  36. package/build/utils/testing/StubCacheAdapter.js +0 -6
  37. package/build/utils/testing/StubCacheAdapter.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/EntityCacheAdapter.ts +2 -10
  40. package/src/EntityMutationInfo.ts +47 -0
  41. package/src/EntityMutationTriggerConfiguration.ts +3 -3
  42. package/src/EntityMutationValidator.ts +8 -2
  43. package/src/EntityMutator.ts +81 -68
  44. package/src/EntityQueryContext.ts +54 -0
  45. package/src/EntityQueryContextProvider.ts +1 -0
  46. package/src/__tests__/EntityEdges-test.ts +163 -9
  47. package/src/__tests__/EntityMutator-MutationCacheConsistency-test.ts +105 -0
  48. package/src/__tests__/EntityMutator-test.ts +24 -6
  49. package/src/__tests__/EntityQueryContext-test.ts +82 -0
  50. package/src/index.ts +1 -0
  51. package/src/internal/ReadThroughEntityCache.ts +6 -28
  52. package/src/internal/__tests__/ReadThroughEntityCache-test.ts +0 -44
  53. package/src/utils/testing/StubCacheAdapter.ts +11 -17
@@ -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
+ });
@@ -14,12 +14,16 @@ import { v4 as uuidv4 } from 'uuid';
14
14
 
15
15
  import EntityDatabaseAdapter from '../EntityDatabaseAdapter';
16
16
  import EntityLoaderFactory from '../EntityLoaderFactory';
17
+ import {
18
+ EntityMutationType,
19
+ EntityTriggerMutationInfo,
20
+ EntityValidatorMutationInfo,
21
+ } from '../EntityMutationInfo';
17
22
  import EntityMutationTriggerConfiguration, {
18
23
  EntityMutationTrigger,
19
24
  EntityNonTransactionalMutationTrigger,
20
25
  } from '../EntityMutationTriggerConfiguration';
21
26
  import EntityMutationValidator from '../EntityMutationValidator';
22
- import { EntityMutationInfo, EntityMutationType } from '../EntityMutator';
23
27
  import EntityMutatorFactory from '../EntityMutatorFactory';
24
28
  import { EntityTransactionalQueryContext, EntityQueryContext } from '../EntityQueryContext';
25
29
  import ViewerContext from '../ViewerContext';
@@ -53,7 +57,7 @@ class TestMutationTrigger extends EntityMutationTrigger<
53
57
  _viewerContext: ViewerContext,
54
58
  _queryContext: EntityQueryContext,
55
59
  _entity: TestEntity,
56
- _mutationInfo: EntityMutationInfo<
60
+ _mutationInfo: EntityTriggerMutationInfo<
57
61
  TestFields,
58
62
  string,
59
63
  ViewerContext,
@@ -95,7 +99,13 @@ const verifyValidatorCounts = (
95
99
  keyof TestFields
96
100
  >[],
97
101
  expectedCalls: number,
98
- mutationInfo: EntityMutationInfo<TestFields, string, ViewerContext, TestEntity, keyof TestFields>
102
+ mutationInfo: EntityValidatorMutationInfo<
103
+ TestFields,
104
+ string,
105
+ ViewerContext,
106
+ TestEntity,
107
+ keyof TestFields
108
+ >
99
109
  ): void => {
100
110
  for (const validator of mutationValidatorSpies) {
101
111
  verify(
@@ -164,7 +174,13 @@ const verifyTriggerCounts = (
164
174
  >,
165
175
  boolean
166
176
  >,
167
- mutationInfo: EntityMutationInfo<TestFields, string, ViewerContext, TestEntity, keyof TestFields>
177
+ mutationInfo: EntityTriggerMutationInfo<
178
+ TestFields,
179
+ string,
180
+ ViewerContext,
181
+ TestEntity,
182
+ keyof TestFields
183
+ >
168
184
  ): void => {
169
185
  Object.keys(executed).forEach((s) => {
170
186
  if ((executed as any)[s]) {
@@ -767,7 +783,7 @@ describe(EntityMutatorFactory, () => {
767
783
  beforeDelete: true,
768
784
  afterDelete: true,
769
785
  },
770
- { type: EntityMutationType.DELETE }
786
+ { type: EntityMutationType.DELETE, cascadingDeleteCause: null }
771
787
  );
772
788
  });
773
789
 
@@ -796,7 +812,9 @@ describe(EntityMutatorFactory, () => {
796
812
 
797
813
  await entityMutatorFactory.forDelete(existingEntity, queryContext).enforceDeleteAsync();
798
814
 
799
- verifyValidatorCounts(viewerContext, validatorSpies, 0, { type: EntityMutationType.DELETE });
815
+ verifyValidatorCounts(viewerContext, validatorSpies, 0, {
816
+ type: EntityMutationType.DELETE as any,
817
+ });
800
818
  });
801
819
  });
802
820
 
@@ -0,0 +1,82 @@
1
+ import invariant from 'invariant';
2
+
3
+ import { EntityQueryContext } from '../EntityQueryContext';
4
+ import ViewerContext from '../ViewerContext';
5
+ import { createUnitTestEntityCompanionProvider } from '../utils/testing/createUnitTestEntityCompanionProvider';
6
+
7
+ describe(EntityQueryContext, () => {
8
+ describe('callbacks', () => {
9
+ it('calls all callbacks, and calls invalidation first', async () => {
10
+ const companionProvider = createUnitTestEntityCompanionProvider();
11
+ const viewerContext = new ViewerContext(companionProvider);
12
+
13
+ const preCommitFirstCallback = jest.fn(async (): Promise<void> => {});
14
+ const preCommitSecondCallback = jest.fn(async (): Promise<void> => {});
15
+ const postCommitInvalidationCallback = jest.fn(async (): Promise<void> => {
16
+ invariant(
17
+ preCommitFirstCallback.mock.calls.length === 1,
18
+ 'preCommit should be called before postCommitInvalidation'
19
+ );
20
+ invariant(
21
+ preCommitSecondCallback.mock.calls.length === 1,
22
+ 'preCommit should be called before postCommitInvalidation'
23
+ );
24
+ });
25
+ const postCommitCallback = jest.fn(async (): Promise<void> => {
26
+ invariant(
27
+ preCommitFirstCallback.mock.calls.length === 1,
28
+ 'preCommit should be called before postCommit'
29
+ );
30
+ invariant(
31
+ preCommitSecondCallback.mock.calls.length === 1,
32
+ 'preCommit should be called before postCommit'
33
+ );
34
+ invariant(
35
+ postCommitInvalidationCallback.mock.calls.length === 1,
36
+ 'postCommitInvalidation should be called before postCommit'
37
+ );
38
+ });
39
+
40
+ await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
41
+ 'postgres',
42
+ async (queryContext) => {
43
+ queryContext.appendPostCommitCallback(postCommitCallback);
44
+ queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
45
+ queryContext.appendPreCommitCallback(preCommitSecondCallback, 2);
46
+ queryContext.appendPreCommitCallback(preCommitFirstCallback, 1);
47
+ }
48
+ );
49
+
50
+ expect(preCommitFirstCallback).toHaveBeenCalledTimes(1);
51
+ expect(preCommitSecondCallback).toHaveBeenCalledTimes(1);
52
+ expect(postCommitCallback).toHaveBeenCalledTimes(1);
53
+ expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(1);
54
+ });
55
+
56
+ it('prevents transaction from finishing when precommit throws (post commit callbacks are not called)', async () => {
57
+ const companionProvider = createUnitTestEntityCompanionProvider();
58
+ const viewerContext = new ViewerContext(companionProvider);
59
+
60
+ const preCommitCallback = jest.fn(async (): Promise<void> => {
61
+ throw new Error('wat');
62
+ });
63
+ const postCommitInvalidationCallback = jest.fn(async (): Promise<void> => {});
64
+ const postCommitCallback = jest.fn(async (): Promise<void> => {});
65
+
66
+ await expect(
67
+ viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
68
+ 'postgres',
69
+ async (queryContext) => {
70
+ queryContext.appendPostCommitCallback(postCommitCallback);
71
+ queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
72
+ queryContext.appendPreCommitCallback(preCommitCallback, 0);
73
+ }
74
+ )
75
+ ).rejects.toThrowError('wat');
76
+
77
+ expect(preCommitCallback).toHaveBeenCalledTimes(1);
78
+ expect(postCommitCallback).toHaveBeenCalledTimes(0);
79
+ expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(0);
80
+ });
81
+ });
82
+ });
package/src/index.ts CHANGED
@@ -33,6 +33,7 @@ export { default as EntitySecondaryCacheLoader } from './EntitySecondaryCacheLoa
33
33
  export * from './EntitySecondaryCacheLoader';
34
34
  export * from './EntityMutator';
35
35
  export { default as EntityMutationValidator } from './EntityMutationValidator';
36
+ export * from './EntityMutationInfo';
36
37
  export * from './EntityMutationTriggerConfiguration';
37
38
  export { default as EntityMutationTriggerConfiguration } from './EntityMutationTriggerConfiguration';
38
39
  export { default as EntityMutatorFactory } from './EntityMutatorFactory';
@@ -3,11 +3,6 @@ import invariant from 'invariant';
3
3
  import EntityCacheAdapter from '../EntityCacheAdapter';
4
4
  import EntityConfiguration from '../EntityConfiguration';
5
5
  import { filterMap } from '../utils/collections/maps';
6
- import {
7
- FieldTransformerMap,
8
- transformCacheObjectToFields,
9
- transformFieldsToCacheObject,
10
- } from './EntityFieldTransformationUtils';
11
6
 
12
7
  export enum CacheStatus {
13
8
  HIT,
@@ -15,10 +10,10 @@ export enum CacheStatus {
15
10
  NEGATIVE,
16
11
  }
17
12
 
18
- export type CacheLoadResult =
13
+ export type CacheLoadResult<TFields> =
19
14
  | {
20
15
  status: CacheStatus.HIT;
21
- item: Readonly<object>;
16
+ item: Readonly<TFields>;
22
17
  }
23
18
  | {
24
19
  status: CacheStatus.MISS;
@@ -32,14 +27,10 @@ export type CacheLoadResult =
32
27
  * {@link EntityCacheAdapter} within the {@link EntityDataManager}.
33
28
  */
34
29
  export default class ReadThroughEntityCache<TFields> {
35
- private readonly fieldTransformerMap: FieldTransformerMap;
36
-
37
30
  constructor(
38
31
  private readonly entityConfiguration: EntityConfiguration<TFields>,
39
32
  private readonly entityCacheAdapter: EntityCacheAdapter<TFields>
40
- ) {
41
- this.fieldTransformerMap = entityCacheAdapter.getFieldTransformerMap();
42
- }
33
+ ) {}
43
34
 
44
35
  private isFieldCacheable<N extends keyof TFields>(fieldName: N): boolean {
45
36
  return this.entityConfiguration.cacheableKeys.has(fieldName);
@@ -91,13 +82,7 @@ export default class ReadThroughEntityCache<TFields> {
91
82
  const results: Map<NonNullable<TFields[N]>, readonly Readonly<TFields>[]> = new Map();
92
83
  cacheLoadResults.forEach((cacheLoadResult, fieldValue) => {
93
84
  if (cacheLoadResult.status === CacheStatus.HIT) {
94
- results.set(fieldValue, [
95
- transformCacheObjectToFields(
96
- this.entityConfiguration,
97
- this.fieldTransformerMap,
98
- cacheLoadResult.item
99
- ),
100
- ]);
85
+ results.set(fieldValue, [cacheLoadResult.item]);
101
86
  }
102
87
  });
103
88
 
@@ -110,7 +95,7 @@ export default class ReadThroughEntityCache<TFields> {
110
95
  return !objectsFromFulfillerForFv || objectsFromFulfillerForFv.length === 0;
111
96
  });
112
97
 
113
- const objectsToCache: Map<NonNullable<TFields[N]>, object> = new Map();
98
+ const objectsToCache: Map<NonNullable<TFields[N]>, Readonly<TFields>> = new Map();
114
99
  for (const [fieldValue, objects] of dbFetchResults.entries()) {
115
100
  if (objects.length > 1) {
116
101
  // multiple objects received for what was supposed to be a unique query, don't add to return map nor cache
@@ -123,14 +108,7 @@ export default class ReadThroughEntityCache<TFields> {
123
108
  }
124
109
  const uniqueObject = objects[0];
125
110
  if (uniqueObject) {
126
- objectsToCache.set(
127
- fieldValue,
128
- transformFieldsToCacheObject(
129
- this.entityConfiguration,
130
- this.fieldTransformerMap,
131
- uniqueObject
132
- )
133
- );
111
+ objectsToCache.set(fieldValue, uniqueObject);
134
112
  results.set(fieldValue, [uniqueObject]);
135
113
  }
136
114
  }
@@ -40,7 +40,6 @@ describe(ReadThroughEntityCache, () => {
40
40
  describe('readManyThroughAsync', () => {
41
41
  it('fetches from DB upon cache miss and caches the result', async () => {
42
42
  const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
43
- when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
44
43
  const cacheAdapter = instance(cacheAdapterMock);
45
44
  const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
46
45
  const fetcher = createIdFetcher(['wat', 'who']);
@@ -77,7 +76,6 @@ describe(ReadThroughEntityCache, () => {
77
76
 
78
77
  it('does not fetch from the DB or cache results when all cache fetches are hits', async () => {
79
78
  const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
80
- when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
81
79
  const cacheAdapter = instance(cacheAdapterMock);
82
80
  const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
83
81
  const fetcher = createIdFetcher(['wat', 'who']);
@@ -114,7 +112,6 @@ describe(ReadThroughEntityCache, () => {
114
112
 
115
113
  it('negatively caches db misses', async () => {
116
114
  const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
117
- when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
118
115
  const cacheAdapter = instance(cacheAdapterMock);
119
116
  const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
120
117
 
@@ -135,7 +132,6 @@ describe(ReadThroughEntityCache, () => {
135
132
 
136
133
  it('does not return or fetch negatively cached results from DB', async () => {
137
134
  const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
138
- when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
139
135
  const cacheAdapter = instance(cacheAdapterMock);
140
136
  const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
141
137
  const fetcher = createIdFetcher([]);
@@ -153,7 +149,6 @@ describe(ReadThroughEntityCache, () => {
153
149
 
154
150
  it('does a mix and match of hit, miss, and negative', async () => {
155
151
  const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
156
- when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
157
152
  const cacheAdapter = instance(cacheAdapterMock);
158
153
  const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
159
154
  const fetcher = createIdFetcher(['wat', 'who', 'why']);
@@ -189,7 +184,6 @@ describe(ReadThroughEntityCache, () => {
189
184
 
190
185
  it('does not call into cache for field that is not cacheable', async () => {
191
186
  const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
192
- when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(new Map());
193
187
  const cacheAdapter = instance(cacheAdapterMock);
194
188
  const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(false), cacheAdapter);
195
189
  const fetcher = createIdFetcher(['wat']);
@@ -197,44 +191,6 @@ describe(ReadThroughEntityCache, () => {
197
191
  verify(cacheAdapterMock.loadManyAsync('id', anything())).never();
198
192
  expect(result).toEqual(new Map([['wat', [{ id: 'wat' }]]]));
199
193
  });
200
-
201
- it('transforms fields for cache storage', async () => {
202
- const cacheAdapterMock = mock<EntityCacheAdapter<BlahFields>>();
203
- when(cacheAdapterMock.getFieldTransformerMap()).thenReturn(
204
- new Map([
205
- [
206
- UUIDField.name,
207
- {
208
- read: (val) => val.split('-')[0],
209
- write: (val) => `${val}-in-cache`,
210
- },
211
- ],
212
- ])
213
- );
214
- const cacheAdapter = instance(cacheAdapterMock);
215
- const entityCache = new ReadThroughEntityCache(makeEntityConfiguration(true), cacheAdapter);
216
- const fetcher = createIdFetcher(['wat', 'who']);
217
-
218
- when(cacheAdapterMock.loadManyAsync('id', deepEqual(['wat', 'who']))).thenResolve(
219
- new Map([
220
- ['wat', { status: CacheStatus.MISS }],
221
- ['who', { status: CacheStatus.HIT, item: { id: 'who-in-cache' } }],
222
- ])
223
- );
224
-
225
- const result = await entityCache.readManyThroughAsync('id', ['wat', 'who'], fetcher);
226
-
227
- verify(cacheAdapterMock.loadManyAsync('id', deepEqual(['wat', 'who']))).once();
228
- verify(
229
- cacheAdapterMock.cacheManyAsync('id', deepEqual(new Map([['wat', { id: 'wat-in-cache' }]])))
230
- ).once();
231
- expect(result).toEqual(
232
- new Map([
233
- ['wat', [{ id: 'wat' }]],
234
- ['who', [{ id: 'who' }]],
235
- ])
236
- );
237
- });
238
194
  });
239
195
 
240
196
  describe('invalidateManyAsync', () => {
@@ -3,7 +3,6 @@ import invariant from 'invariant';
3
3
  import EntityCacheAdapter from '../../EntityCacheAdapter';
4
4
  import EntityConfiguration from '../../EntityConfiguration';
5
5
  import IEntityCacheAdapterProvider from '../../IEntityCacheAdapterProvider';
6
- import { FieldTransformerMap } from '../../internal/EntityFieldTransformationUtils';
7
6
  import { CacheStatus, CacheLoadResult } from '../../internal/ReadThroughEntityCache';
8
7
 
9
8
  export class NoCacheStubCacheAdapterProvider implements IEntityCacheAdapterProvider {
@@ -15,15 +14,11 @@ export class NoCacheStubCacheAdapterProvider implements IEntityCacheAdapterProvi
15
14
  }
16
15
 
17
16
  export class NoCacheStubCacheAdapter<TFields> extends EntityCacheAdapter<TFields> {
18
- public getFieldTransformerMap(): FieldTransformerMap {
19
- return new Map();
20
- }
21
-
22
17
  public async loadManyAsync<N extends keyof TFields>(
23
18
  _fieldName: N,
24
19
  fieldValues: readonly NonNullable<TFields[N]>[]
25
- ): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult>> {
26
- return fieldValues.reduce((acc: Map<NonNullable<TFields[N]>, CacheLoadResult>, v) => {
20
+ ): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult<TFields>>> {
21
+ return fieldValues.reduce((acc: Map<NonNullable<TFields[N]>, CacheLoadResult<TFields>>, v) => {
27
22
  acc.set(v, {
28
23
  status: CacheStatus.MISS,
29
24
  });
@@ -33,7 +28,7 @@ export class NoCacheStubCacheAdapter<TFields> extends EntityCacheAdapter<TFields
33
28
 
34
29
  public async cacheManyAsync<N extends keyof TFields>(
35
30
  _fieldName: N,
36
- _objectMap: ReadonlyMap<NonNullable<TFields[N]>, object>
31
+ _objectMap: ReadonlyMap<NonNullable<TFields[N]>, Readonly<TFields>>
37
32
  ): Promise<void> {}
38
33
 
39
34
  public async cacheDBMissesAsync<N extends keyof TFields>(
@@ -53,27 +48,26 @@ export class InMemoryFullCacheStubCacheAdapterProvider implements IEntityCacheAd
53
48
  getCacheAdapter<TFields>(
54
49
  entityConfiguration: EntityConfiguration<TFields>
55
50
  ): EntityCacheAdapter<TFields> {
56
- return new InMemoryFullCacheStubCacheAdapter(entityConfiguration, this.cache);
51
+ return new InMemoryFullCacheStubCacheAdapter(
52
+ entityConfiguration,
53
+ this.cache as Map<string, Readonly<TFields>>
54
+ );
57
55
  }
58
56
  }
59
57
 
60
58
  export class InMemoryFullCacheStubCacheAdapter<TFields> extends EntityCacheAdapter<TFields> {
61
59
  constructor(
62
60
  entityConfiguration: EntityConfiguration<TFields>,
63
- readonly cache: Map<string, Readonly<object>>
61
+ readonly cache: Map<string, Readonly<TFields>>
64
62
  ) {
65
63
  super(entityConfiguration);
66
64
  }
67
65
 
68
- public getFieldTransformerMap(): FieldTransformerMap {
69
- return new Map();
70
- }
71
-
72
66
  public async loadManyAsync<N extends keyof TFields>(
73
67
  fieldName: N,
74
68
  fieldValues: readonly NonNullable<TFields[N]>[]
75
- ): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult>> {
76
- const results = new Map<NonNullable<TFields[N]>, CacheLoadResult>();
69
+ ): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult<TFields>>> {
70
+ const results = new Map<NonNullable<TFields[N]>, CacheLoadResult<TFields>>();
77
71
  fieldValues.forEach((fieldValue) => {
78
72
  const cacheKey = this.createCacheKey(fieldName, fieldValue);
79
73
  if (!this.cache.has(cacheKey)) {
@@ -94,7 +88,7 @@ export class InMemoryFullCacheStubCacheAdapter<TFields> extends EntityCacheAdapt
94
88
 
95
89
  public async cacheManyAsync<N extends keyof TFields>(
96
90
  fieldName: N,
97
- objectMap: ReadonlyMap<NonNullable<TFields[N]>, object>
91
+ objectMap: ReadonlyMap<NonNullable<TFields[N]>, Readonly<TFields>>
98
92
  ): Promise<void> {
99
93
  objectMap.forEach((obj, fieldValue) => {
100
94
  const cacheKey = this.createCacheKey(fieldName, fieldValue);