Skip to content

Commit bbe7a5b

Browse files
Junaed/fssdk 10936 async storage dynamic import (#972)
* [FSSDK-10936] async storage dynamic import
1 parent 16e638a commit bbe7a5b

10 files changed

+236
-135
lines changed

__mocks__/@react-native-async-storage/async-storage-event-processor.ts

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export default class AsyncStorage {
3636
return new Promise(resolve => {
3737
setTimeout(() => {
3838
items[key] && delete items[key]
39+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
3940
// @ts-ignore
4041
resolve()
4142
}, 1)

__mocks__/@react-native-async-storage/async-storage.ts

+32-39
Original file line numberDiff line numberDiff line change
@@ -14,50 +14,43 @@
1414
* limitations under the License.
1515
*/
1616

17-
let items: {[key: string]: string} = {}
1817
export default class AsyncStorage {
19-
static getItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
20-
return new Promise((resolve, reject) => {
21-
switch (key) {
22-
case 'keyThatExists':
23-
resolve('{ "name": "Awesome Object" }')
24-
break
25-
case 'keyThatDoesNotExist':
26-
resolve(null)
27-
break
28-
case 'keyWithInvalidJsonObject':
29-
resolve('bad json }')
30-
break
31-
default:
32-
setTimeout(() => resolve(items[key] || null), 1)
33-
}
34-
})
35-
}
18+
private static items: Record<string, string> = {};
3619

37-
static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise<void> {
38-
return new Promise((resolve) => {
39-
setTimeout(() => {
40-
items[key] = value
41-
resolve()
42-
}, 1)
43-
})
20+
static getItem(
21+
key: string,
22+
callback?: (error?: Error, result?: string | null) => void
23+
): Promise<string | null> {
24+
const value = AsyncStorage.items[key] || null;
25+
callback?.(undefined, value);
26+
return Promise.resolve(value);
4427
}
45-
46-
static removeItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
47-
return new Promise(resolve => {
48-
setTimeout(() => {
49-
items[key] && delete items[key]
50-
// @ts-ignore
51-
resolve()
52-
}, 1)
53-
})
28+
29+
static setItem(
30+
key: string,
31+
value: string,
32+
callback?: (error?: Error) => void
33+
): Promise<void> {
34+
AsyncStorage.items[key] = value;
35+
callback?.(undefined);
36+
return Promise.resolve();
5437
}
55-
56-
static dumpItems(): {[key: string]: string} {
57-
return items
38+
39+
static removeItem(
40+
key: string,
41+
callback?: (error?: Error, result?: string | null) => void
42+
): Promise<string | null> {
43+
const value = AsyncStorage.items[key] || null;
44+
if (key in AsyncStorage.items) {
45+
delete AsyncStorage.items[key];
46+
}
47+
callback?.(undefined, value);
48+
return Promise.resolve(value);
5849
}
5950

60-
static clearStore(): void {
61-
items = {}
51+
static clearStore(): Promise<void> {
52+
AsyncStorage.items = {};
53+
return Promise.resolve();
6254
}
55+
6356
}

lib/event_processor/event_processor_factory.react_native.spec.ts

+54-13
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ vi.mock('./forwarding_event_processor', () => {
2525
return { getForwardingEventProcessor };
2626
});
2727

28-
vi.mock('./event_processor_factory', async (importOriginal) => {
28+
vi.mock('./event_processor_factory', async importOriginal => {
2929
const getBatchEventProcessor = vi.fn().mockImplementation(() => {
3030
return {};
3131
});
@@ -46,13 +46,14 @@ vi.mock('@react-native-community/netinfo', () => {
4646
});
4747

4848
let isNetInfoAvailable = false;
49+
let isAsyncStorageAvailable = true;
4950

5051
await vi.hoisted(async () => {
5152
await mockRequireNetInfo();
5253
});
5354

5455
async function mockRequireNetInfo() {
55-
const {Module} = await import('module');
56+
const { Module } = await import('module');
5657
const M: any = Module;
5758

5859
M._load_original = M._load;
@@ -61,14 +62,19 @@ async function mockRequireNetInfo() {
6162
if (isNetInfoAvailable) return {};
6263
throw new Error('Module not found: @react-native-community/netinfo');
6364
}
65+
if (uri === '@react-native-async-storage/async-storage') {
66+
if (isAsyncStorageAvailable) return {};
67+
throw new Error('Module not found: @react-native-async-storage/async-storage');
68+
}
69+
6470
return M._load_original(uri, parent);
6571
};
6672
}
6773

6874
import { createForwardingEventProcessor, createBatchEventProcessor } from './event_processor_factory.react_native';
6975
import { getForwardingEventProcessor } from './forwarding_event_processor';
7076
import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser';
71-
import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory';
77+
import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL, getPrefixEventStore } from './event_processor_factory';
7278
import { getBatchEventProcessor } from './event_processor_factory';
7379
import { AsyncCache, AsyncPrefixCache, SyncCache, SyncPrefixCache } from '../utils/cache/cache';
7480
import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native';
@@ -96,7 +102,7 @@ describe('createForwardingEventProcessor', () => {
96102

97103
it('uses the browser default event dispatcher if none is provided', () => {
98104
const processor = createForwardingEventProcessor();
99-
105+
100106
expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true);
101107
expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, defaultEventDispatcher);
102108
});
@@ -146,14 +152,50 @@ describe('createBatchEventProcessor', () => {
146152
expect(transformSet('value')).toBe('value');
147153
});
148154

155+
it('should throw error if @react-native-async-storage/async-storage is not available', async () => {
156+
isAsyncStorageAvailable = false;
157+
const { AsyncStorageCache } = await vi.importActual<
158+
typeof import('../utils/cache/async_storage_cache.react_native')
159+
>('../utils/cache/async_storage_cache.react_native');
160+
161+
MockAsyncStorageCache.mockImplementationOnce(() => {
162+
return new AsyncStorageCache();
163+
});
164+
165+
expect(() => createBatchEventProcessor({})).toThrowError(
166+
'Module not found: @react-native-async-storage/async-storage'
167+
);
168+
169+
isAsyncStorageAvailable = true;
170+
});
171+
172+
it('should not throw error if eventStore is provided and @react-native-async-storage/async-storage is not available', async () => {
173+
isAsyncStorageAvailable = false;
174+
const eventStore = {
175+
operation: 'sync',
176+
} as SyncCache<string>;
177+
178+
const { AsyncStorageCache } = await vi.importActual<
179+
typeof import('../utils/cache/async_storage_cache.react_native')
180+
>('../utils/cache/async_storage_cache.react_native');
181+
182+
MockAsyncStorageCache.mockImplementationOnce(() => {
183+
return new AsyncStorageCache();
184+
});
185+
186+
expect(() => createBatchEventProcessor({ eventStore })).not.toThrow();
187+
188+
isAsyncStorageAvailable = true;
189+
});
190+
149191
it('wraps the provided eventStore in a SyncPrefixCache if a SyncCache is provided as eventStore', () => {
150192
const eventStore = {
151193
operation: 'sync',
152194
} as SyncCache<string>;
153195

154196
const processor = createBatchEventProcessor({ eventStore });
155197
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
156-
198+
157199
expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockSyncPrefixCache.mock.results[0].value);
158200
const [cache, prefix, transformGet, transformSet] = MockSyncPrefixCache.mock.calls[0];
159201

@@ -172,7 +214,7 @@ describe('createBatchEventProcessor', () => {
172214

173215
const processor = createBatchEventProcessor({ eventStore });
174216
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
175-
217+
176218
expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockAsyncPrefixCache.mock.results[0].value);
177219
const [cache, prefix, transformGet, transformSet] = MockAsyncPrefixCache.mock.calls[0];
178220

@@ -184,7 +226,6 @@ describe('createBatchEventProcessor', () => {
184226
expect(transformSet({ value: 1 })).toBe('{"value":1}');
185227
});
186228

187-
188229
it('uses the provided eventDispatcher', () => {
189230
const eventDispatcher = {
190231
dispatchEvent: vi.fn(),
@@ -196,7 +237,7 @@ describe('createBatchEventProcessor', () => {
196237
});
197238

198239
it('uses the default browser event dispatcher if none is provided', () => {
199-
const processor = createBatchEventProcessor({ });
240+
const processor = createBatchEventProcessor({});
200241
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
201242
expect(mockGetBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(defaultEventDispatcher);
202243
});
@@ -210,7 +251,7 @@ describe('createBatchEventProcessor', () => {
210251
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
211252
expect(mockGetBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(closingEventDispatcher);
212253

213-
const processor2 = createBatchEventProcessor({ });
254+
const processor2 = createBatchEventProcessor({});
214255
expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true);
215256
expect(mockGetBatchEventProcessor.mock.calls[1][0].closingEventDispatcher).toBe(undefined);
216257
});
@@ -220,7 +261,7 @@ describe('createBatchEventProcessor', () => {
220261
expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
221262
expect(mockGetBatchEventProcessor.mock.calls[0][0].flushInterval).toBe(2000);
222263

223-
const processor2 = createBatchEventProcessor({ });
264+
const processor2 = createBatchEventProcessor({});
224265
expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true);
225266
expect(mockGetBatchEventProcessor.mock.calls[1][0].flushInterval).toBe(undefined);
226267
});
@@ -230,19 +271,19 @@ describe('createBatchEventProcessor', () => {
230271
expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
231272
expect(mockGetBatchEventProcessor.mock.calls[0][0].batchSize).toBe(20);
232273

233-
const processor2 = createBatchEventProcessor({ });
274+
const processor2 = createBatchEventProcessor({});
234275
expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true);
235276
expect(mockGetBatchEventProcessor.mock.calls[1][0].batchSize).toBe(undefined);
236277
});
237278

238279
it('uses maxRetries value of 5', () => {
239-
const processor = createBatchEventProcessor({ });
280+
const processor = createBatchEventProcessor({});
240281
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
241282
expect(mockGetBatchEventProcessor.mock.calls[0][0].retryOptions?.maxRetries).toBe(5);
242283
});
243284

244285
it('uses the default failedEventRetryInterval', () => {
245-
const processor = createBatchEventProcessor({ });
286+
const processor = createBatchEventProcessor({});
246287
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
247288
expect(mockGetBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(FAILED_EVENT_RETRY_INTERVAL);
248289
});

lib/plugins/key_value_cache/reactNativeAsyncStorageCache.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,29 @@
1414
* limitations under the License.
1515
*/
1616

17-
import AsyncStorage from '@react-native-async-storage/async-storage';
1817
import PersistentKeyValueCache from './persistentKeyValueCache';
18+
import { getDefaultAsyncStorage } from '../../utils/import.react_native/@react-native-async-storage/async-storage';
1919

2020
export default class ReactNativeAsyncStorageCache implements PersistentKeyValueCache {
21+
private asyncStorage = getDefaultAsyncStorage();
22+
2123
async contains(key: string): Promise<boolean> {
22-
return await AsyncStorage.getItem(key) !== null;
24+
return (await this.asyncStorage.getItem(key)) !== null;
2325
}
2426

2527
async get(key: string): Promise<string | undefined> {
26-
return (await AsyncStorage.getItem(key) || undefined);
28+
return (await this.asyncStorage.getItem(key)) || undefined;
2729
}
2830

2931
async remove(key: string): Promise<boolean> {
3032
if (await this.contains(key)) {
31-
await AsyncStorage.removeItem(key);
33+
await this.asyncStorage.removeItem(key);
3234
return true;
3335
}
3436
return false;
3537
}
3638

3739
set(key: string, val: string): Promise<void> {
38-
return AsyncStorage.setItem(key, val);
40+
return this.asyncStorage.setItem(key, val);
3941
}
4042
}

0 commit comments

Comments
 (0)