Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Junaed/fssdk 10936 async storage dynamic import #972

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default class AsyncStorage {
return new Promise(resolve => {
setTimeout(() => {
items[key] && delete items[key]
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
resolve()
}, 1)
Expand Down
71 changes: 32 additions & 39 deletions __mocks__/@react-native-async-storage/async-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,43 @@
* limitations under the License.
*/

let items: {[key: string]: string} = {}
export default class AsyncStorage {
static getItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
return new Promise((resolve, reject) => {
switch (key) {
case 'keyThatExists':
resolve('{ "name": "Awesome Object" }')
break
case 'keyThatDoesNotExist':
resolve(null)
break
case 'keyWithInvalidJsonObject':
resolve('bad json }')
break
default:
setTimeout(() => resolve(items[key] || null), 1)
}
})
}
private static items: Record<string, string> = {};

static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
items[key] = value
resolve()
}, 1)
})
static getItem(
key: string,
callback?: (error?: Error, result?: string | null) => void
): Promise<string | null> {
const value = AsyncStorage.items[key] || null;
callback?.(undefined, value);
return Promise.resolve(value);
}

static removeItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
return new Promise(resolve => {
setTimeout(() => {
items[key] && delete items[key]
// @ts-ignore
resolve()
}, 1)
})
static setItem(
key: string,
value: string,
callback?: (error?: Error) => void
): Promise<void> {
AsyncStorage.items[key] = value;
callback?.(undefined);
return Promise.resolve();
}

static dumpItems(): {[key: string]: string} {
return items

static removeItem(
key: string,
callback?: (error?: Error, result?: string | null) => void
): Promise<string | null> {
const value = AsyncStorage.items[key] || null;
if (key in AsyncStorage.items) {
delete AsyncStorage.items[key];
}
callback?.(undefined, value);
return Promise.resolve(value);
}

static clearStore(): void {
items = {}
static clearStore(): Promise<void> {
AsyncStorage.items = {};
return Promise.resolve();
}

}
67 changes: 54 additions & 13 deletions lib/event_processor/event_processor_factory.react_native.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ vi.mock('./forwarding_event_processor', () => {
return { getForwardingEventProcessor };
});

vi.mock('./event_processor_factory', async (importOriginal) => {
vi.mock('./event_processor_factory', async importOriginal => {
const getBatchEventProcessor = vi.fn().mockImplementation(() => {
return {};
});
Expand All @@ -46,13 +46,14 @@ vi.mock('@react-native-community/netinfo', () => {
});

let isNetInfoAvailable = false;
let isAsyncStorageAvailable = true;

await vi.hoisted(async () => {
await mockRequireNetInfo();
});

async function mockRequireNetInfo() {
const {Module} = await import('module');
const { Module } = await import('module');
const M: any = Module;

M._load_original = M._load;
Expand All @@ -61,14 +62,19 @@ async function mockRequireNetInfo() {
if (isNetInfoAvailable) return {};
throw new Error('Module not found: @react-native-community/netinfo');
}
if (uri === '@react-native-async-storage/async-storage') {
if (isAsyncStorageAvailable) return {};
throw new Error('Module not found: @react-native-async-storage/async-storage');
}

return M._load_original(uri, parent);
};
}

import { createForwardingEventProcessor, createBatchEventProcessor } from './event_processor_factory.react_native';
import { getForwardingEventProcessor } from './forwarding_event_processor';
import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser';
import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory';
import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL, getPrefixEventStore } from './event_processor_factory';
import { getBatchEventProcessor } from './event_processor_factory';
import { AsyncCache, AsyncPrefixCache, SyncCache, SyncPrefixCache } from '../utils/cache/cache';
import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native';
Expand Down Expand Up @@ -96,7 +102,7 @@ describe('createForwardingEventProcessor', () => {

it('uses the browser default event dispatcher if none is provided', () => {
const processor = createForwardingEventProcessor();

expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true);
expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, defaultEventDispatcher);
});
Expand Down Expand Up @@ -146,14 +152,50 @@ describe('createBatchEventProcessor', () => {
expect(transformSet('value')).toBe('value');
});

it('should throw error if @react-native-async-storage/async-storage is not available', async () => {
isAsyncStorageAvailable = false;
const { AsyncStorageCache } = await vi.importActual<
typeof import('../utils/cache/async_storage_cache.react_native')
>('../utils/cache/async_storage_cache.react_native');

MockAsyncStorageCache.mockImplementationOnce(() => {
return new AsyncStorageCache();
});

expect(() => createBatchEventProcessor({})).toThrowError(
'Module not found: @react-native-async-storage/async-storage'
);

isAsyncStorageAvailable = true;
});

it('should not throw error if eventStore is provided and @react-native-async-storage/async-storage is not available', async () => {
isAsyncStorageAvailable = false;
const eventStore = {
operation: 'sync',
} as SyncCache<string>;

const { AsyncStorageCache } = await vi.importActual<
typeof import('../utils/cache/async_storage_cache.react_native')
>('../utils/cache/async_storage_cache.react_native');

MockAsyncStorageCache.mockImplementationOnce(() => {
return new AsyncStorageCache();
});

expect(() => createBatchEventProcessor({ eventStore })).not.toThrow();

isAsyncStorageAvailable = true;
});

it('wraps the provided eventStore in a SyncPrefixCache if a SyncCache is provided as eventStore', () => {
const eventStore = {
operation: 'sync',
} as SyncCache<string>;

const processor = createBatchEventProcessor({ eventStore });
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);

expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockSyncPrefixCache.mock.results[0].value);
const [cache, prefix, transformGet, transformSet] = MockSyncPrefixCache.mock.calls[0];

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

const processor = createBatchEventProcessor({ eventStore });
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);

expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockAsyncPrefixCache.mock.results[0].value);
const [cache, prefix, transformGet, transformSet] = MockAsyncPrefixCache.mock.calls[0];

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


it('uses the provided eventDispatcher', () => {
const eventDispatcher = {
dispatchEvent: vi.fn(),
Expand All @@ -196,7 +237,7 @@ describe('createBatchEventProcessor', () => {
});

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

const processor2 = createBatchEventProcessor({ });
const processor2 = createBatchEventProcessor({});
expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true);
expect(mockGetBatchEventProcessor.mock.calls[1][0].closingEventDispatcher).toBe(undefined);
});
Expand All @@ -220,7 +261,7 @@ describe('createBatchEventProcessor', () => {
expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
expect(mockGetBatchEventProcessor.mock.calls[0][0].flushInterval).toBe(2000);

const processor2 = createBatchEventProcessor({ });
const processor2 = createBatchEventProcessor({});
expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true);
expect(mockGetBatchEventProcessor.mock.calls[1][0].flushInterval).toBe(undefined);
});
Expand All @@ -230,19 +271,19 @@ describe('createBatchEventProcessor', () => {
expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
expect(mockGetBatchEventProcessor.mock.calls[0][0].batchSize).toBe(20);

const processor2 = createBatchEventProcessor({ });
const processor2 = createBatchEventProcessor({});
expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true);
expect(mockGetBatchEventProcessor.mock.calls[1][0].batchSize).toBe(undefined);
});

it('uses maxRetries value of 5', () => {
const processor = createBatchEventProcessor({ });
const processor = createBatchEventProcessor({});
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
expect(mockGetBatchEventProcessor.mock.calls[0][0].retryOptions?.maxRetries).toBe(5);
});

it('uses the default failedEventRetryInterval', () => {
const processor = createBatchEventProcessor({ });
const processor = createBatchEventProcessor({});
expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true);
expect(mockGetBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(FAILED_EVENT_RETRY_INTERVAL);
});
Expand Down
12 changes: 7 additions & 5 deletions lib/plugins/key_value_cache/reactNativeAsyncStorageCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,29 @@
* limitations under the License.
*/

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

export default class ReactNativeAsyncStorageCache implements PersistentKeyValueCache {
private asyncStorage = getDefaultAsyncStorage();

async contains(key: string): Promise<boolean> {
return await AsyncStorage.getItem(key) !== null;
return (await this.asyncStorage.getItem(key)) !== null;
}

async get(key: string): Promise<string | undefined> {
return (await AsyncStorage.getItem(key) || undefined);
return (await this.asyncStorage.getItem(key)) || undefined;
}

async remove(key: string): Promise<boolean> {
if (await this.contains(key)) {
await AsyncStorage.removeItem(key);
await this.asyncStorage.removeItem(key);
return true;
}
return false;
}

set(key: string, val: string): Promise<void> {
return AsyncStorage.setItem(key, val);
return this.asyncStorage.setItem(key, val);
}
}
Loading
Loading