From 2eef25ca97babef225b8feb2c79a7683a5b96dcf Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Thu, 12 Mar 2020 12:25:46 -0700 Subject: [PATCH 01/10] Added an interface for key val cache and added an implementation for react native async storage --- packages/datafile-manager/package-lock.json | 5 ++ packages/datafile-manager/package.json | 3 +- .../src/persistentKeyValueCache.ts | 51 ++++++++++++++++++ .../src/reactNativeAsyncStorageCache.ts | 52 +++++++++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 packages/datafile-manager/src/persistentKeyValueCache.ts create mode 100644 packages/datafile-manager/src/reactNativeAsyncStorageCache.ts diff --git a/packages/datafile-manager/package-lock.json b/packages/datafile-manager/package-lock.json index eb5c01b1f..1332f0e70 100644 --- a/packages/datafile-manager/package-lock.json +++ b/packages/datafile-manager/package-lock.json @@ -400,6 +400,11 @@ "uuid": "^3.3.2" } }, + "@react-native-community/async-storage": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.8.1.tgz", + "integrity": "sha512-MA1fTp4SB7OOtDmNAwds6jIpiwwty1NIoFboWjEWkoyWW35zIuxlhHxD4joSy21aWEzUVwvv6JJ2hSsP/HTb7A==" + }, "@sinonjs/commons": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz", diff --git a/packages/datafile-manager/package.json b/packages/datafile-manager/package.json index 5b4ec99cd..c74d46328 100644 --- a/packages/datafile-manager/package.json +++ b/packages/datafile-manager/package.json @@ -36,7 +36,8 @@ }, "dependencies": { "@optimizely/js-sdk-logging": "^0.1.0", - "@optimizely/js-sdk-utils": "^0.1.0" + "@optimizely/js-sdk-utils": "^0.1.0", + "@react-native-community/async-storage": "^1.8.1" }, "scripts": { "test": "jest", diff --git a/packages/datafile-manager/src/persistentKeyValueCache.ts b/packages/datafile-manager/src/persistentKeyValueCache.ts new file mode 100644 index 000000000..838ccde22 --- /dev/null +++ b/packages/datafile-manager/src/persistentKeyValueCache.ts @@ -0,0 +1,51 @@ +/** + * Copyright 2020, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * An Interface to implement a persistent key value cache which supports strings as keys + * and string or JSON Object as value. + */ +export default interface PersistentKeyValueCache { + + /** + * Returns value stored against a key or null if not found. + * @param key + * @returns Promise which resolves with a + * 1. String if the value found is a string. + * 2. Object if value found was stored as a JSON Object. + * 3. null if the key does not exist in the cache. + */ + get(key: string): Promise + + /** + * Stores value in the persistent cache against a key + * @param key + * @param val + */ + set(key: string, val: string | Object): void + + /** + * Checks if a key exists in the cache + * @param key + */ + contains(key: string): Promise + + /** + * Removed the key value pair from cache. + * @param key + */ + remove(key: string): void +} diff --git a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts new file mode 100644 index 000000000..0f20cb208 --- /dev/null +++ b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts @@ -0,0 +1,52 @@ +/** + * Copyright 2020, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AsyncStorage from '@react-native-community/async-storage'; +import PersistentKeyValueCache from './persistentKeyValueCache'; + +export default class ReactNativeAsyncStorageCache implements PersistentKeyValueCache { + // If value is a valid JSON string, it converts it to object before returning + get(key: string): Promise { + return new Promise((resolve) => { + AsyncStorage.getItem(key).then((val: String | null) => { + let finalVal = val; + if (val) { + try { + finalVal = JSON.parse(val.valueOf()); + } catch (ex) { + // couldnt parse, its a string + } + } + resolve(finalVal); + }); + }); + } + + // If value is an Object, it stringifies it before storing. + set(key: string, val: string | Object): void { + AsyncStorage.setItem(key, typeof val === 'object' ? JSON.stringify(val) : val); + } + + contains(key: string): Promise { + return new Promise(resolve => + AsyncStorage.getItem(key).then((val: String | null) => resolve(val !== null)) + ) + } + + remove(key: string): void { + AsyncStorage.removeItem(key); + } +} From 9cfda129aa71b0e631c5b46600caa7c3259b58c2 Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Thu, 12 Mar 2020 13:27:49 -0700 Subject: [PATCH 02/10] Added promise rejection handling --- .../src/persistentKeyValueCache.ts | 4 +-- .../src/reactNativeAsyncStorageCache.ts | 25 +++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/datafile-manager/src/persistentKeyValueCache.ts b/packages/datafile-manager/src/persistentKeyValueCache.ts index 838ccde22..9fae6771e 100644 --- a/packages/datafile-manager/src/persistentKeyValueCache.ts +++ b/packages/datafile-manager/src/persistentKeyValueCache.ts @@ -35,7 +35,7 @@ export default interface PersistentKeyValueCache { * @param key * @param val */ - set(key: string, val: string | Object): void + set(key: string, val: string | Object): Promise /** * Checks if a key exists in the cache @@ -47,5 +47,5 @@ export default interface PersistentKeyValueCache { * Removed the key value pair from cache. * @param key */ - remove(key: string): void + remove(key: string): Promise } diff --git a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts index 0f20cb208..8e565a2ef 100644 --- a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts +++ b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts @@ -20,7 +20,7 @@ import PersistentKeyValueCache from './persistentKeyValueCache'; export default class ReactNativeAsyncStorageCache implements PersistentKeyValueCache { // If value is a valid JSON string, it converts it to object before returning get(key: string): Promise { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { AsyncStorage.getItem(key).then((val: String | null) => { let finalVal = val; if (val) { @@ -31,22 +31,31 @@ export default class ReactNativeAsyncStorageCache implements PersistentKeyValueC } } resolve(finalVal); - }); + }) + .catch(reject); }); } // If value is an Object, it stringifies it before storing. - set(key: string, val: string | Object): void { - AsyncStorage.setItem(key, typeof val === 'object' ? JSON.stringify(val) : val); + set(key: string, val: string | Object): Promise { + return new Promise((resolve, reject) => { + try { + AsyncStorage.setItem(key, typeof val === 'object' ? JSON.stringify(val) : val) + .then(resolve).catch(reject); + } catch (e) { + // JSON.stringify might through exception on some inputs. + reject(e); + } + }); } contains(key: string): Promise { - return new Promise(resolve => - AsyncStorage.getItem(key).then((val: String | null) => resolve(val !== null)) + return new Promise((resolve, reject) => + AsyncStorage.getItem(key).then((val: String | null) => resolve(val !== null)).catch(reject) ) } - remove(key: string): void { - AsyncStorage.removeItem(key); + remove(key: string): Promise { + return AsyncStorage.removeItem(key); } } From 7aca2e20425490adc0d0c9826c2decac98a821e4 Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Tue, 17 Mar 2020 23:34:50 -0700 Subject: [PATCH 03/10] Made some simplifications --- packages/datafile-manager/package-lock.json | 5 --- packages/datafile-manager/package.json | 4 ++- .../src/persistentKeyValueCache.ts | 4 +-- .../src/reactNativeAsyncStorageCache.ts | 35 ++++++++----------- 4 files changed, 20 insertions(+), 28 deletions(-) diff --git a/packages/datafile-manager/package-lock.json b/packages/datafile-manager/package-lock.json index 1332f0e70..eb5c01b1f 100644 --- a/packages/datafile-manager/package-lock.json +++ b/packages/datafile-manager/package-lock.json @@ -400,11 +400,6 @@ "uuid": "^3.3.2" } }, - "@react-native-community/async-storage": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.8.1.tgz", - "integrity": "sha512-MA1fTp4SB7OOtDmNAwds6jIpiwwty1NIoFboWjEWkoyWW35zIuxlhHxD4joSy21aWEzUVwvv6JJ2hSsP/HTb7A==" - }, "@sinonjs/commons": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz", diff --git a/packages/datafile-manager/package.json b/packages/datafile-manager/package.json index c74d46328..623df4a08 100644 --- a/packages/datafile-manager/package.json +++ b/packages/datafile-manager/package.json @@ -36,7 +36,9 @@ }, "dependencies": { "@optimizely/js-sdk-logging": "^0.1.0", - "@optimizely/js-sdk-utils": "^0.1.0", + "@optimizely/js-sdk-utils": "^0.1.0" + }, + "peerDependencies": { "@react-native-community/async-storage": "^1.8.1" }, "scripts": { diff --git a/packages/datafile-manager/src/persistentKeyValueCache.ts b/packages/datafile-manager/src/persistentKeyValueCache.ts index 9fae6771e..82fce20eb 100644 --- a/packages/datafile-manager/src/persistentKeyValueCache.ts +++ b/packages/datafile-manager/src/persistentKeyValueCache.ts @@ -28,14 +28,14 @@ export default interface PersistentKeyValueCache { * 2. Object if value found was stored as a JSON Object. * 3. null if the key does not exist in the cache. */ - get(key: string): Promise + get(key: string): Promise /** * Stores value in the persistent cache against a key * @param key * @param val */ - set(key: string, val: string | Object): Promise + set(key: string, val: Object): Promise /** * Checks if a key exists in the cache diff --git a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts index 8e565a2ef..242d3fdc5 100644 --- a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts +++ b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts @@ -14,39 +14,34 @@ * limitations under the License. */ +import { getLogger } from '@optimizely/js-sdk-logging'; import AsyncStorage from '@react-native-community/async-storage'; + import PersistentKeyValueCache from './persistentKeyValueCache'; +const logger = getLogger('DatafileManager') + export default class ReactNativeAsyncStorageCache implements PersistentKeyValueCache { - // If value is a valid JSON string, it converts it to object before returning - get(key: string): Promise { + + get(key: string): Promise { return new Promise((resolve, reject) => { AsyncStorage.getItem(key).then((val: String | null) => { - let finalVal = val; if (val) { try { - finalVal = JSON.parse(val.valueOf()); + resolve(JSON.parse(val.valueOf())); } catch (ex) { - // couldnt parse, its a string - } + logger.error('Error Parsing Object from cache'); + reject(ex); + } + } else { + resolve(null); } - resolve(finalVal); - }) - .catch(reject); + }).catch(reject); }); } - // If value is an Object, it stringifies it before storing. - set(key: string, val: string | Object): Promise { - return new Promise((resolve, reject) => { - try { - AsyncStorage.setItem(key, typeof val === 'object' ? JSON.stringify(val) : val) - .then(resolve).catch(reject); - } catch (e) { - // JSON.stringify might through exception on some inputs. - reject(e); - } - }); + set(key: string, val: Object): Promise { + return AsyncStorage.setItem(key, typeof val === 'object' ? JSON.stringify(val) : val); } contains(key: string): Promise { From 8d36c5b0dae5373caccfdea20af17438d94bccc9 Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Tue, 17 Mar 2020 23:36:56 -0700 Subject: [PATCH 04/10] made changes to some comments --- packages/datafile-manager/src/persistentKeyValueCache.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/datafile-manager/src/persistentKeyValueCache.ts b/packages/datafile-manager/src/persistentKeyValueCache.ts index 82fce20eb..698b53003 100644 --- a/packages/datafile-manager/src/persistentKeyValueCache.ts +++ b/packages/datafile-manager/src/persistentKeyValueCache.ts @@ -16,7 +16,7 @@ /** * An Interface to implement a persistent key value cache which supports strings as keys - * and string or JSON Object as value. + * and JSON Object as value. */ export default interface PersistentKeyValueCache { @@ -24,14 +24,13 @@ export default interface PersistentKeyValueCache { * Returns value stored against a key or null if not found. * @param key * @returns Promise which resolves with a - * 1. String if the value found is a string. - * 2. Object if value found was stored as a JSON Object. - * 3. null if the key does not exist in the cache. + * 1. Object if value found was stored as a JSON Object. + * 2. null if the key does not exist in the cache. */ get(key: string): Promise /** - * Stores value in the persistent cache against a key + * Stores Object in the persistent cache against a key * @param key * @param val */ From 1b97abea13face05864cdd07703f5ee31ed9151e Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Wed, 18 Mar 2020 14:52:41 -0700 Subject: [PATCH 05/10] Added unit tests --- .../@react-native-community/async-storage.ts | 37 +++++++++ .../reactNativeAsyncStorageCache.spec.ts | 79 +++++++++++++++++++ .../src/reactNativeAsyncStorageCache.ts | 33 +++++--- 3 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts create mode 100644 packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts diff --git a/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts b/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts new file mode 100644 index 000000000..0da73eb04 --- /dev/null +++ b/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts @@ -0,0 +1,37 @@ +/** + * Copyright 2020, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default class AsyncStorage { + static getItem(key: string, callback?: (error?: Error, result?: string) => void): Promise { + return new Promise((resolve, reject) => { + switch (key) { + case 'keyThatExists': + resolve('{ "name": "Awesome Object" }') + break; + case 'keyThatDoesNotExist': + resolve(null) + break; + case 'keyWithInvalidJsonObject': + resolve('asdfa }') + break; + } + }) + } + + static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise { + return new Promise(resolve => resolve()) + } +} diff --git a/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts b/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts new file mode 100644 index 000000000..1540e5615 --- /dev/null +++ b/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts @@ -0,0 +1,79 @@ +/** + * Copyright 2020, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import ReactNativeAsyncStorageCache from '../src/reactNativeAsyncStorageCache' + +describe('reactNativeAsyncStorageCache', () => { + let cacheInstance: ReactNativeAsyncStorageCache + + beforeEach(() => { + cacheInstance = new ReactNativeAsyncStorageCache() + }) + + describe('get', function() { + it('should return correct object when item is found in cache', function(done) { + cacheInstance.get('keyThatExists') + .then(v => { + expect(v).toEqual({ name: "Awesome Object" }) + done() + }) + }) + + it('should return null if item is not found in cache', function(done) { + cacheInstance.get('keyThatDoesNotExist') + .then(v => { + expect(v).toBeNull() + done() + }) + }) + + it('should reject promise error if string has an incorrect JSON format', function(done) { + cacheInstance.get('keyWithInvalidJsonObject') + .catch(e => { + done() + }) + }) + }) + + describe('set', function() { + it('should resolve promise if item was successfully set in the cache', function(done) { + const testObj = { name: "Awesome Object" } + cacheInstance.set('testKey', testObj).then(() => done()) + }) + + it('should reject promise if item was not set in the cache because of json stringifying error', function(done) { + const testObj: any = { name: "Awesome Object" } + testObj.myOwnReference = testObj + cacheInstance.set('testKey', testObj).catch(() => done()) + }) + }) + + describe('contains', function() { + it('should return true if object with key exists', function(done) { + cacheInstance.contains('keyThatExists').then(v => { + expect(v).toBeTruthy() + done() + }) + }) + + it('should return false if object with key does not exist', function(done) { + cacheInstance.contains('keyThatDoesNotExist').then(v => { + expect(v).toBeFalsy() + done() + }) + }) + }) +}) diff --git a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts index 242d3fdc5..45ca2feb6 100644 --- a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts +++ b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { getLogger } from '@optimizely/js-sdk-logging'; -import AsyncStorage from '@react-native-community/async-storage'; +import { getLogger } from '@optimizely/js-sdk-logging' +import AsyncStorage from '@react-native-community/async-storage' -import PersistentKeyValueCache from './persistentKeyValueCache'; +import PersistentKeyValueCache from './persistentKeyValueCache' const logger = getLogger('DatafileManager') @@ -25,32 +25,39 @@ export default class ReactNativeAsyncStorageCache implements PersistentKeyValueC get(key: string): Promise { return new Promise((resolve, reject) => { - AsyncStorage.getItem(key).then((val: String | null) => { + AsyncStorage.getItem(key).then((val: string | null) => { if (val) { try { - resolve(JSON.parse(val.valueOf())); + resolve(JSON.parse(val)) } catch (ex) { - logger.error('Error Parsing Object from cache'); - reject(ex); + logger.error('Error Parsing Object from cache - %s', ex) + reject(ex) } } else { - resolve(null); + resolve(null) } - }).catch(reject); - }); + }).catch(reject) + }) } set(key: string, val: Object): Promise { - return AsyncStorage.setItem(key, typeof val === 'object' ? JSON.stringify(val) : val); + return new Promise((resolve, reject) => { + try { + AsyncStorage.setItem(key, JSON.stringify(val)).then(resolve).catch(reject) + } catch (e) { + logger.error('Error stringifying Object to Json - %s', e) + reject(e) + } + }) } contains(key: string): Promise { return new Promise((resolve, reject) => - AsyncStorage.getItem(key).then((val: String | null) => resolve(val !== null)).catch(reject) + AsyncStorage.getItem(key).then((val: string | null) => resolve(val !== null)).catch(reject) ) } remove(key: string): Promise { - return AsyncStorage.removeItem(key); + return AsyncStorage.removeItem(key) } } From 310f1817cef9b2db0ef0f9f6ce03f26e76afa47c Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Wed, 18 Mar 2020 15:04:24 -0700 Subject: [PATCH 06/10] changed a value --- .../__mocks__/@react-native-community/async-storage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts b/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts index 0da73eb04..e0eec2cff 100644 --- a/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts +++ b/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts @@ -25,7 +25,7 @@ export default class AsyncStorage { resolve(null) break; case 'keyWithInvalidJsonObject': - resolve('asdfa }') + resolve('bad json }') break; } }) From a4520230335018de3514983957f0b0eb6812b34d Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Thu, 19 Mar 2020 15:46:58 -0700 Subject: [PATCH 07/10] Incorporated review changes --- .../reactNativeAsyncStorageCache.spec.ts | 50 +++++++------------ .../src/reactNativeAsyncStorageCache.ts | 43 ++++++---------- 2 files changed, 33 insertions(+), 60 deletions(-) diff --git a/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts b/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts index 1540e5615..a60add920 100644 --- a/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts +++ b/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts @@ -23,57 +23,41 @@ describe('reactNativeAsyncStorageCache', () => { cacheInstance = new ReactNativeAsyncStorageCache() }) - describe('get', function() { - it('should return correct object when item is found in cache', function(done) { - cacheInstance.get('keyThatExists') - .then(v => { - expect(v).toEqual({ name: "Awesome Object" }) - done() - }) + describe('get', function() { + it('should return correct object when item is found in cache', function() { + return cacheInstance.get('keyThatExists') + .then(v => expect(v).toEqual({ name: "Awesome Object" })) }) - it('should return null if item is not found in cache', function(done) { - cacheInstance.get('keyThatDoesNotExist') - .then(v => { - expect(v).toBeNull() - done() - }) + it('should return null if item is not found in cache', function() { + return cacheInstance.get('keyThatDoesNotExist').then(v => expect(v).toBeNull()) }) - it('should reject promise error if string has an incorrect JSON format', function(done) { - cacheInstance.get('keyWithInvalidJsonObject') - .catch(e => { - done() - }) + it('should reject promise error if string has an incorrect JSON format', function() { + return cacheInstance.get('keyWithInvalidJsonObject').catch(() => {}) }) }) describe('set', function() { - it('should resolve promise if item was successfully set in the cache', function(done) { + it('should resolve promise if item was successfully set in the cache', function() { const testObj = { name: "Awesome Object" } - cacheInstance.set('testKey', testObj).then(() => done()) + return cacheInstance.set('testKey', testObj) }) - it('should reject promise if item was not set in the cache because of json stringifying error', function(done) { + it('should reject promise if item was not set in the cache because of json stringifying error', function() { const testObj: any = { name: "Awesome Object" } testObj.myOwnReference = testObj - cacheInstance.set('testKey', testObj).catch(() => done()) + return cacheInstance.set('testKey', testObj).catch(() => {}) }) }) - describe('contains', function() { - it('should return true if object with key exists', function(done) { - cacheInstance.contains('keyThatExists').then(v => { - expect(v).toBeTruthy() - done() - }) + describe('contains', function() { + it('should return true if object with key exists', function() { + return cacheInstance.contains('keyThatExists').then(v => expect(v).toBeTruthy()) }) - it('should return false if object with key does not exist', function(done) { - cacheInstance.contains('keyThatDoesNotExist').then(v => { - expect(v).toBeFalsy() - done() - }) + it('should return false if object with key does not exist', function() { + return cacheInstance.contains('keyThatDoesNotExist').then(v => expect(v).toBeFalsy()) }) }) }) diff --git a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts index 45ca2feb6..4a43e8a2e 100644 --- a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts +++ b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts @@ -24,37 +24,26 @@ const logger = getLogger('DatafileManager') export default class ReactNativeAsyncStorageCache implements PersistentKeyValueCache { get(key: string): Promise { - return new Promise((resolve, reject) => { - AsyncStorage.getItem(key).then((val: string | null) => { - if (val) { - try { - resolve(JSON.parse(val)) - } catch (ex) { - logger.error('Error Parsing Object from cache - %s', ex) - reject(ex) - } - } else { - resolve(null) - } - }).catch(reject) - }) + try { + return AsyncStorage.getItem(key) + .then((val: string | null) => val ? JSON.parse(val) : null) + } catch (ex) { + logger.error('Error Parsing Object from cache - %s', ex) + return Promise.resolve(ex); + } } - - set(key: string, val: Object): Promise { - return new Promise((resolve, reject) => { - try { - AsyncStorage.setItem(key, JSON.stringify(val)).then(resolve).catch(reject) - } catch (e) { - logger.error('Error stringifying Object to Json - %s', e) - reject(e) - } - }) + + set(key: string, val: Object): Promise { + try { + return AsyncStorage.setItem(key, JSON.stringify(val)) + } catch (e) { + logger.error('Error stringifying Object to Json - %s', e) + return Promise.reject(e) + } } contains(key: string): Promise { - return new Promise((resolve, reject) => - AsyncStorage.getItem(key).then((val: string | null) => resolve(val !== null)).catch(reject) - ) + return AsyncStorage.getItem(key).then((val: string | null) => (val !== null)) } remove(key: string): Promise { From 8a8687ff01150691708c4b62b23586edb5a104ab Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Thu, 19 Mar 2020 17:02:00 -0700 Subject: [PATCH 08/10] Incorporate some more feedback --- .../@react-native-community/async-storage.ts | 10 ++++---- .../src/persistentKeyValueCache.ts | 23 ++++++++++++++----- .../src/reactNativeAsyncStorageCache.ts | 4 ++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts b/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts index e0eec2cff..32f721144 100644 --- a/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts +++ b/packages/datafile-manager/__mocks__/@react-native-community/async-storage.ts @@ -18,20 +18,20 @@ export default class AsyncStorage { static getItem(key: string, callback?: (error?: Error, result?: string) => void): Promise { return new Promise((resolve, reject) => { switch (key) { - case 'keyThatExists': + case 'keyThatExists': resolve('{ "name": "Awesome Object" }') - break; + break case 'keyThatDoesNotExist': resolve(null) - break; + break case 'keyWithInvalidJsonObject': resolve('bad json }') - break; + break } }) } static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise { - return new Promise(resolve => resolve()) + return Promise.resolve() } } diff --git a/packages/datafile-manager/src/persistentKeyValueCache.ts b/packages/datafile-manager/src/persistentKeyValueCache.ts index 698b53003..92c390cf9 100644 --- a/packages/datafile-manager/src/persistentKeyValueCache.ts +++ b/packages/datafile-manager/src/persistentKeyValueCache.ts @@ -23,28 +23,39 @@ export default interface PersistentKeyValueCache { /** * Returns value stored against a key or null if not found. * @param key - * @returns Promise which resolves with a + * @returns + * Resolves promise with * 1. Object if value found was stored as a JSON Object. * 2. null if the key does not exist in the cache. + * Rejects the promise in case of an error */ - get(key: string): Promise + get(key: string): Promise /** * Stores Object in the persistent cache against a key * @param key * @param val + * @returns + * Resolves promise without a value if successful + * Rejects the promise in case of an error */ - set(key: string, val: Object): Promise + set(key: string, val: any): Promise /** * Checks if a key exists in the cache - * @param key + * @param key + * Resolves promise with + * 1. true if the key exists + * 2. false if the key does not exist + * Rejects the promise in case of an error */ contains(key: string): Promise /** - * Removed the key value pair from cache. - * @param key + * Removes the key value pair from cache. + * @param key + * Resolves promise without a value if successful + * Rejects the promise in case of an error */ remove(key: string): Promise } diff --git a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts index 4a43e8a2e..61a69ccd5 100644 --- a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts +++ b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts @@ -23,7 +23,7 @@ const logger = getLogger('DatafileManager') export default class ReactNativeAsyncStorageCache implements PersistentKeyValueCache { - get(key: string): Promise { + get(key: string): Promise { try { return AsyncStorage.getItem(key) .then((val: string | null) => val ? JSON.parse(val) : null) @@ -33,7 +33,7 @@ export default class ReactNativeAsyncStorageCache implements PersistentKeyValueC } } - set(key: string, val: Object): Promise { + set(key: string, val: any): Promise { try { return AsyncStorage.setItem(key, JSON.stringify(val)) } catch (e) { From d91afffc4eb5c5990c4d7d827f42d86dabfb1e7a Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Thu, 19 Mar 2020 20:16:18 -0700 Subject: [PATCH 09/10] Change async storage peer dependency version to the minimum that works --- .travis.yml | 5 +++-- packages/datafile-manager/package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d9e97f4cf..7ed28d782 100644 --- a/.travis.yml +++ b/.travis.yml @@ -82,10 +82,11 @@ jobs: - &packagetest stage: 'Test sub packages' node_js: '12' - before_install: cd packages/datafile-manager + before_install: cd packages/utils - <<: *packagetest before_install: cd packages/event-processor - <<: *packagetest before_install: cd packages/logging - <<: *packagetest - before_install: cd packages/utils + before_script: npm install "@react-native-community/async-storage" + before_install: cd packages/datafile-manager diff --git a/packages/datafile-manager/package.json b/packages/datafile-manager/package.json index 623df4a08..43b27f7da 100644 --- a/packages/datafile-manager/package.json +++ b/packages/datafile-manager/package.json @@ -39,7 +39,7 @@ "@optimizely/js-sdk-utils": "^0.1.0" }, "peerDependencies": { - "@react-native-community/async-storage": "^1.8.1" + "@react-native-community/async-storage": "^1.2.0" }, "scripts": { "test": "jest", From 0ad00f63154db38fb48c8e22a9b83e41430fa59d Mon Sep 17 00:00:00 2001 From: Zeeshan Ashraf Date: Fri, 20 Mar 2020 11:31:25 -0700 Subject: [PATCH 10/10] Incorporated more review feedback. --- .../reactNativeAsyncStorageCache.spec.ts | 6 +++-- .../src/reactNativeAsyncStorageCache.ts | 26 +++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts b/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts index a60add920..02f4eff0b 100644 --- a/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts +++ b/packages/datafile-manager/__test__/reactNativeAsyncStorageCache.spec.ts @@ -34,7 +34,8 @@ describe('reactNativeAsyncStorageCache', () => { }) it('should reject promise error if string has an incorrect JSON format', function() { - return cacheInstance.get('keyWithInvalidJsonObject').catch(() => {}) + return cacheInstance.get('keyWithInvalidJsonObject') + .catch(() => 'exception caught').then(v => { expect(v).toEqual('exception caught') }) }) }) @@ -47,7 +48,8 @@ describe('reactNativeAsyncStorageCache', () => { it('should reject promise if item was not set in the cache because of json stringifying error', function() { const testObj: any = { name: "Awesome Object" } testObj.myOwnReference = testObj - return cacheInstance.set('testKey', testObj).catch(() => {}) + return cacheInstance.set('testKey', testObj) + .catch(() => 'exception caught').then(v => expect(v).toEqual('exception caught')) }) }) diff --git a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts index 61a69ccd5..64c7a70a6 100644 --- a/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts +++ b/packages/datafile-manager/src/reactNativeAsyncStorageCache.ts @@ -22,23 +22,27 @@ import PersistentKeyValueCache from './persistentKeyValueCache' const logger = getLogger('DatafileManager') export default class ReactNativeAsyncStorageCache implements PersistentKeyValueCache { - get(key: string): Promise { - try { - return AsyncStorage.getItem(key) - .then((val: string | null) => val ? JSON.parse(val) : null) - } catch (ex) { - logger.error('Error Parsing Object from cache - %s', ex) - return Promise.resolve(ex); - } + return AsyncStorage.getItem(key) + .then((val: string | null) => { + if (!val) { + return null + } + try { + return JSON.parse(val); + } catch (ex) { + logger.error('Error Parsing Object from cache - %s', ex) + throw ex + } + }) } set(key: string, val: any): Promise { try { return AsyncStorage.setItem(key, JSON.stringify(val)) - } catch (e) { - logger.error('Error stringifying Object to Json - %s', e) - return Promise.reject(e) + } catch (ex) { + logger.error('Error stringifying Object to Json - %s', ex) + return Promise.reject(ex) } }