From f25edda9d3362f4000a8fd68f69f9cc484d8d586 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Mon, 18 Mar 2019 20:09:45 -0500 Subject: [PATCH 01/14] cleaner tests and docs --- src/LocalDatastore.js | 46 ++++- src/__tests__/LocalDatastore-test.js | 245 +++++++++++++-------------- 2 files changed, 158 insertions(+), 133 deletions(-) diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 3c6e3a153..30ee7f17b 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -16,7 +16,30 @@ import ParseQuery from './ParseQuery'; const DEFAULT_PIN = '_default'; const PIN_PREFIX = 'parsePin_'; +const OBJECT_PREFIX = 'Parse_LDS_'; +/** + * Provides a local datastore which can be used to store and retrieve Parse.Object.
+ * To enable this functionality, call Parse.enableLocalDatastore(). + * + * To store objects you can pin them. + * + *
await object.pin();
+ *
await object.pinWithName('pinName');
+ * + * You can query your objects by changing the source of your query. + * + *
query.fromLocalDatastore();
+ *
query.fromPin();
+ *
query.fromPinWithName();
+ * + * Once your source is changed, you can query your objects from LocalDatastore + * + *
const localObjects = await query.find();
+ * + * @class Parse.LocalDatastore + * @static + */ const LocalDatastore = { fromPinWithName(name: string): Promise { const controller = CoreManager.getLocalDatastoreController(); @@ -130,7 +153,7 @@ const LocalDatastore = { const localDatastore = await this._getAllContents(); const allObjects = []; for (const key in localDatastore) { - if (key !== DEFAULT_PIN && !key.startsWith(PIN_PREFIX)) { + if (key.startsWith(OBJECT_PREFIX)) { allObjects.push(localDatastore[key]); } } @@ -236,7 +259,7 @@ const LocalDatastore = { if (!this.isEnabled) { return; } - const localKey = `${object.className}_${localId}`; + const localKey = `${OBJECT_PREFIX}${object.className}_${localId}`; const objectKey = this.getKeyForObject(object); const unsaved = await this.fromPinWithName(localKey); @@ -247,6 +270,7 @@ const LocalDatastore = { await this.pinWithName(objectKey, unsaved); const localDatastore = await this._getAllContents(); + console for (const key in localDatastore) { if (key === DEFAULT_PIN || key.startsWith(PIN_PREFIX)) { let pinned = localDatastore[key] || []; @@ -266,7 +290,8 @@ const LocalDatastore = { *
    * await Parse.LocalDatastore.updateFromServer();
    * 
- * + * @method updateFromServer + * @name Parse.LocalDatastore.updateFromServer * @static */ async updateFromServer() { @@ -276,7 +301,7 @@ const LocalDatastore = { const localDatastore = await this._getAllContents(); const keys = []; for (const key in localDatastore) { - if (key !== DEFAULT_PIN && !key.startsWith(PIN_PREFIX)) { + if (key.startsWith(OBJECT_PREFIX)) { keys.push(key); } } @@ -286,7 +311,8 @@ const LocalDatastore = { this.isSyncing = true; const pointersHash = {}; for (const key of keys) { - const [className, objectId] = key.split('_'); + // Ignore the OBJECT_PREFIX + const [ , , className, objectId] = key.split('_'); if (!(className in pointersHash)) { pointersHash[className] = new Set(); } @@ -313,15 +339,18 @@ const LocalDatastore = { await Promise.all(pinPromises); this.isSyncing = false; } catch(error) { - console.log('Error syncing LocalDatastore'); // eslint-disable-line - console.log(error); // eslint-disable-line + console.log('Error syncing LocalDatastore: ', error); // eslint-disable-line this.isSyncing = false; } }, + isLocalDatastoreKey(key: string) { + return !!(key && (key === DEFAULT_PIN || key.startsWith(PIN_PREFIX) || key.startsWith(OBJECT_PREFIX))); + }, + getKeyForObject(object: any) { const objectId = object.objectId || object._getId(); - return `${object.className}_${objectId}`; + return `${OBJECT_PREFIX}${object.className}_${objectId}`; }, getPinName(pinName: ?string) { @@ -341,6 +370,7 @@ const LocalDatastore = { LocalDatastore.DEFAULT_PIN = DEFAULT_PIN; LocalDatastore.PIN_PREFIX = PIN_PREFIX; +LocalDatastore.OBJECT_PREFIX = OBJECT_PREFIX; LocalDatastore.isEnabled = false; LocalDatastore.isSyncing = false; diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index debdbf912..c9e13da04 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -105,6 +105,22 @@ const mockLocalStorage = require('./test_helpers/mockLocalStorage'); global.localStorage = mockLocalStorage; +const item1 = new ParseObject('Item'); +const item2 = new ParseObject('Item'); +const item3 = new ParseObject('Item'); + +item1.id = '1'; +item2.id = '2'; +item3.id = '3'; + +const KEY1 = LocalDatastore.getKeyForObject(item1); +const KEY2 = LocalDatastore.getKeyForObject(item2); +const KEY3 = LocalDatastore.getKeyForObject(item2); + +const DEFAULT_PIN = LocalDatastore.DEFAULT_PIN; +const PIN_PREFIX = LocalDatastore.PIN_PREFIX; +const OBJECT_PREFIX = LocalDatastore.OBJECT_PREFIX; + describe('LocalDatastore', () => { beforeEach(() => { CoreManager.setLocalDatastoreController(mockLocalStorageController); @@ -148,7 +164,7 @@ describe('LocalDatastore', () => { it('_handlePinWithName default pin', async () => { const object = new ParseObject('Item'); - await LocalDatastore._handlePinWithName(LocalDatastore.DEFAULT_PIN, object); + await LocalDatastore._handlePinWithName(DEFAULT_PIN, object); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); @@ -171,76 +187,71 @@ describe('LocalDatastore', () => { }); it('_handleUnPinWithName default pin', async () => { - const object = new ParseObject('Item'); const LDS = { - [LocalDatastore.DEFAULT_PIN]: [`Item_${object._getId()}`, '1234'], + [DEFAULT_PIN]: [KEY1, KEY2], }; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._handleUnPinWithName(LocalDatastore.DEFAULT_PIN, object); + await LocalDatastore._handleUnPinWithName(DEFAULT_PIN, item1); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(LocalDatastore.DEFAULT_PIN, ['1234']); + expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(DEFAULT_PIN, [KEY2]); }); it('_handleUnPinWithName specific pin', async () => { - const object = new ParseObject('Item'); const LDS = { - parsePin_test_pin: [`Item_${object._getId()}`, '1234'], + parsePin_test_pin: [KEY1, KEY2], }; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._handleUnPinWithName('test_pin', object); + await LocalDatastore._handleUnPinWithName('test_pin', item1); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(LocalDatastore.PIN_PREFIX + 'test_pin', ['1234']); + expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(PIN_PREFIX + 'test_pin', [KEY2]); }); it('_handleUnPinWithName default pin remove pinName', async () => { const object = new ParseObject('Item'); const LDS = { - [LocalDatastore.DEFAULT_PIN]: [], + [DEFAULT_PIN]: [], }; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._handleUnPinWithName(LocalDatastore.DEFAULT_PIN, object); + await LocalDatastore._handleUnPinWithName(DEFAULT_PIN, object); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledTimes(2); - expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(LocalDatastore.DEFAULT_PIN); + expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(DEFAULT_PIN); }); it('_handleUnPinWithName specific pin remove pinName', async () => { const object = new ParseObject('Item'); const LDS = { - [LocalDatastore.PIN_PREFIX + 'test_pin']: [], + [PIN_PREFIX + 'test_pin']: [], }; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); await LocalDatastore._handleUnPinWithName('test_pin', object); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledTimes(2); - expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(LocalDatastore.PIN_PREFIX + 'test_pin'); + expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(PIN_PREFIX + 'test_pin'); }); it('_handleUnPinWithName remove if exist', async () => { - const obj1 = new ParseObject('Item'); - const obj2 = new ParseObject('Item'); - const obj3 = new ParseObject('Item'); - const objects = [`Item_${obj1.id}`, `Item_${obj2.id}`, `Item_${obj3.id}`]; + const objects = [KEY1, KEY2, KEY3]; const LDS = { - [LocalDatastore.PIN_PREFIX + 'test_pin']: objects, - [LocalDatastore.PIN_PREFIX + 'test_pin_2']: objects, - [`Item_${obj1.id}`]: obj1._toFullJSON(), - [`Item_${obj2.id}`]: obj2._toFullJSON(), + [PIN_PREFIX + 'test_pin']: objects, + [PIN_PREFIX + 'test_pin_2']: objects, + [KEY1]: item1._toFullJSON(), + [KEY2]: item2._toFullJSON(), } mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._handleUnPinWithName('test_pin', obj1); + await LocalDatastore._handleUnPinWithName('test_pin', item1); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(LocalDatastore.PIN_PREFIX + 'test_pin', [`Item_${obj2.id}`, `Item_${obj3.id}`]); + expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(PIN_PREFIX + 'test_pin', [KEY2, KEY3]); }); it('_updateObjectIfPinned not pinned', async () => { @@ -251,25 +262,22 @@ describe('LocalDatastore', () => { }); it('_updateObjectIfPinned if pinned', async () => { - const object = new ParseObject('Item'); mockLocalStorageController .fromPinWithName - .mockImplementationOnce(() => [object]); + .mockImplementationOnce(() => [item1]); LocalDatastore.isEnabled = true; - await LocalDatastore._updateObjectIfPinned(object); + await LocalDatastore._updateObjectIfPinned(item1); expect(mockLocalStorageController.fromPinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.fromPinWithName.mock.results[0].value).toEqual([object]); + expect(mockLocalStorageController.fromPinWithName.mock.results[0].value).toEqual([item1]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(`Item_${object.id}`, object._toFullJSON()); + expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(KEY1, item1._toFullJSON()); }); it('_updateLocalIdForObject not pinned', async () => { - const object = new ParseObject('Item'); - object.id = '1234' - await LocalDatastore._updateLocalIdForObject('local0', object); + await LocalDatastore._updateLocalIdForObject('local0', item1); expect(mockLocalStorageController.fromPinWithName.mock.results[0].value).toEqual(undefined); }); @@ -277,9 +285,9 @@ describe('LocalDatastore', () => { const object = new ParseObject('Item'); const json = object._toFullJSON(); const localId = 'local' + object.id; - const localKey = `Item_${localId}`; + const localKey = `${OBJECT_PREFIX}Item_${localId}`; const LDS = { - [LocalDatastore.DEFAULT_PIN]: [localKey], + [DEFAULT_PIN]: [localKey], [localKey]: json, }; mockLocalStorageController @@ -298,9 +306,9 @@ describe('LocalDatastore', () => { const object = new ParseObject('Item'); const json = object._toFullJSON(); const localId = 'local' + object.id; - const localKey = `Item_${localId}`; + const localKey = `${OBJECT_PREFIX}Item_${localId}`; const LDS = { - [LocalDatastore.PIN_PREFIX + 'test_pin']: [localKey], + [PIN_PREFIX + 'test_pin']: [localKey], [localKey]: json, }; mockLocalStorageController @@ -320,7 +328,7 @@ describe('LocalDatastore', () => { const json = object._toFullJSON(); const localId = 'local' + object.id; const LDS = { - [LocalDatastore.DEFAULT_PIN]: [object.id], + [DEFAULT_PIN]: [object.id], [object.id]: json, }; mockLocalStorageController @@ -337,20 +345,19 @@ describe('LocalDatastore', () => { expect(mockLocalStorageController.fromPinWithName.mock.results[0].value).toEqual(json); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(`Item_${localId}`); + expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(`${OBJECT_PREFIX}Item_${localId}`); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(`Item_${object.id}`, json); + expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(`${OBJECT_PREFIX}Item_${object.id}`, json); expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); }); it('_serializeObjectsFromPinName no name returns all objects', async () => { - const object = new ParseObject('Item'); - const json = object._toFullJSON(); + const json = item1._toFullJSON(); const LDS = { - [LocalDatastore.DEFAULT_PIN]: [object.id], - [object.id]: json, + [DEFAULT_PIN]: [KEY1], + [KEY1]: json, }; mockLocalStorageController @@ -367,7 +374,7 @@ describe('LocalDatastore', () => { const object = new ParseObject('Item'); const json = object._toFullJSON(); const LDS = { - [LocalDatastore.DEFAULT_PIN]: [object.id, 'local10', 'local11'], + [DEFAULT_PIN]: [object.id, 'local10', 'local11'], [object.id]: json, randomName: [object.id], }; @@ -376,7 +383,7 @@ describe('LocalDatastore', () => { .getAllContents .mockImplementationOnce(() => LDS); - const results = await LocalDatastore._serializeObjectsFromPinName(LocalDatastore.DEFAULT_PIN); + const results = await LocalDatastore._serializeObjectsFromPinName(DEFAULT_PIN); expect(results).toEqual([]); expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); @@ -409,7 +416,6 @@ describe('LocalDatastore', () => { expect(results).toEqual([obj1._toFullJSON(), obj2._toFullJSON(), obj3._toFullJSON()]); expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.fromPinWithName).toHaveBeenCalledTimes(4); }); @@ -419,7 +425,7 @@ describe('LocalDatastore', () => { const json = object._toFullJSON(); const objectKey = `Item_1234`; const LDS = { - [LocalDatastore.DEFAULT_PIN]: [objectKey], + [DEFAULT_PIN]: [objectKey], [objectKey]: json, }; @@ -446,50 +452,49 @@ describe('LocalDatastore', () => { }); it('_serializeObject with children', async () => { - const object = new ParseObject('Item'); - object.id = 1234; + const parent = new ParseObject('Item'); + parent.id = 1234; const child = new ParseObject('Item'); child.id = 5678; - object.set('child', child); - const newData = child._toFullJSON(); - newData.field = 'Serialize Me'; + parent.set('child', child); + const childJSON = child._toFullJSON(); + childJSON.field = 'Serialize Me'; + + const parentKey = LocalDatastore.getKeyForObject(parent); + const childKey = LocalDatastore.getKeyForObject(child); const LDS = { - [LocalDatastore.DEFAULT_PIN]: [`Item_${object.id}`, `Item_${child.id}`], - [`Item_${object.id}`]: object._toFullJSON(), - [`Item_${child.id}`]: newData, + [DEFAULT_PIN]: [parentKey, childKey], + [parentKey]: parent._toFullJSON(), + [childKey]: childJSON, }; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - const expectedResults = object._toFullJSON(); - expectedResults.child = newData; - const result = await LocalDatastore._serializeObject(`Item_${object.id}`); + const expectedResults = parent._toFullJSON(); + expectedResults.child = childJSON; + const result = await LocalDatastore._serializeObject(parentKey); expect(result).toEqual(expectedResults); }); it('_destroyObjectIfPinned no objects found in pinName', async () => { - const object = new ParseObject('Item'); let LDS = {}; LocalDatastore.isEnabled = true; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._destroyObjectIfPinned(object); + await LocalDatastore._destroyObjectIfPinned(item1); expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledTimes(0); jest.clearAllMocks(); - const obj1 = new ParseObject('Item'); - const obj2 = new ParseObject('Item'); - LDS = { - [`Item_${obj1.id}`]: obj1._toFullJSON(), - [`Item_${obj2.id}`]: obj2._toFullJSON(), - [LocalDatastore.DEFAULT_PIN]: [], + [KEY1]: item1._toFullJSON(), + [KEY2]: item2._toFullJSON(), + [DEFAULT_PIN]: [], }; mockLocalStorageController @@ -497,25 +502,20 @@ describe('LocalDatastore', () => { .mockImplementationOnce(() => LDS); LocalDatastore.isEnabled = true; - await LocalDatastore._destroyObjectIfPinned(obj1); + await LocalDatastore._destroyObjectIfPinned(item1); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(`Item_${obj1.id}`); - + expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(KEY1); expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(0); }); it('_destroyObjectIfPinned no objects found in pinName remove pinName', async () => { - const obj1 = new ParseObject('Item'); - const obj2 = new ParseObject('Item'); - const LDS = { - [`Item_${obj1.id}`]: obj1._toFullJSON(), - [`Item_${obj2.id}`]: obj2._toFullJSON(), - [LocalDatastore.PIN_PREFIX + 'Custom_Pin']: [`Item_${obj2.id}`], - [LocalDatastore.DEFAULT_PIN]: [`Item_${obj2.id}`], + [KEY1]: item1._toFullJSON(), + [KEY2]: item2._toFullJSON(), + [PIN_PREFIX + 'Custom_Pin']: [KEY2], + [DEFAULT_PIN]: [KEY2], }; mockLocalStorageController @@ -523,24 +523,19 @@ describe('LocalDatastore', () => { .mockImplementationOnce(() => LDS); LocalDatastore.isEnabled = true; - await LocalDatastore._destroyObjectIfPinned(obj2); + await LocalDatastore._destroyObjectIfPinned(item2); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledTimes(3); - expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(`Item_${obj2.id}`); - + expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(KEY2); expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(0); }); it('_destroyObjectIfPinned', async () => { - const obj1 = new ParseObject('Item'); - const obj2 = new ParseObject('Item'); - const LDS = { - [`Item_${obj1.id}`]: obj1._toFullJSON(), - [`Item_${obj2.id}`]: obj2._toFullJSON(), - [LocalDatastore.DEFAULT_PIN]: [`Item_${obj1.id}`, `Item_${obj2.id}`], + [KEY1]: item1._toFullJSON(), + [KEY2]: item2._toFullJSON(), + [DEFAULT_PIN]: [KEY1, KEY2], }; mockLocalStorageController @@ -548,41 +543,37 @@ describe('LocalDatastore', () => { .mockImplementationOnce(() => LDS); LocalDatastore.isEnabled = true; - await LocalDatastore._destroyObjectIfPinned(obj1); + await LocalDatastore._destroyObjectIfPinned(item1); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(`Item_${obj1.id}`); - + expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(KEY1); expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(LocalDatastore.DEFAULT_PIN, [`Item_${obj2.id}`]); + expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(DEFAULT_PIN, [KEY2]); }); it('_traverse', () => { // Skip if no objectId - let object = {} + const json = item1._toFullJSON(); let encountered = {}; - LocalDatastore._traverse(object, encountered); + LocalDatastore._traverse({}, encountered); expect(encountered).toEqual({}); // Set Encountered - object = { objectId: 1234, className: 'Item' }; encountered = {}; - LocalDatastore._traverse(object, encountered); - expect(encountered).toEqual({ 'Item_1234': object }); + LocalDatastore._traverse(json, encountered); + expect(encountered).toEqual({ [KEY1]: item1._toFullJSON() }); // Skip if already encountered - object = { objectId: 1234, className: 'Item' }; - encountered = { 'Item_1234': object }; - LocalDatastore._traverse(object, encountered); - expect(encountered).toEqual({ 'Item_1234': object }); + encountered = { [KEY1]: item1._toFullJSON() }; + LocalDatastore._traverse(json, encountered); + expect(encountered).toEqual({ [KEY1]: json }); // Test if null field exist still encounter - object = { objectId: 1234, className: 'Item', field: null }; + const object = { objectId: 1234, className: 'Item', field: null }; encountered = {}; LocalDatastore._traverse(object, encountered); - expect(encountered).toEqual({ 'Item_1234': object }); + expect(encountered).toEqual({ [`${OBJECT_PREFIX}Item_1234`]: object }); }); it('do not sync if disabled', async () => { @@ -623,19 +614,18 @@ describe('LocalDatastore', () => { it('updateFromServer on one object', async () => { LocalDatastore.isEnabled = true; LocalDatastore.isSyncing = false; - const object = new ParseObject('Item'); const LDS = { - [`Item_${object.id}`]: object._toFullJSON(), - [`${LocalDatastore.PIN_PREFIX}_testPinName`]: [`Item_${object.id}`], - [LocalDatastore.DEFAULT_PIN]: [`Item_${object.id}`], + [KEY1]: item1._toFullJSON(), + [`${PIN_PREFIX}_testPinName`]: [KEY1], + [DEFAULT_PIN]: [KEY1], }; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - object.set('updatedField', 'foo'); - mockQueryFind.mockImplementationOnce(() => Promise.resolve([object])); + item1.set('updatedField', 'foo'); + mockQueryFind.mockImplementationOnce(() => Promise.resolve([item1])); await LocalDatastore.updateFromServer(); @@ -653,19 +643,18 @@ describe('LocalDatastore', () => { LocalDatastore.isSyncing = false; const object = new ParseObject('Item'); const LDS = { - [`Item_${object.id}`]: object._toFullJSON(), - [`${LocalDatastore.PIN_PREFIX}_testPinName`]: [`Item_${object.id}`], - [LocalDatastore.DEFAULT_PIN]: [`Item_${object.id}`], + [KEY1]: item1._toFullJSON(), + [`${PIN_PREFIX}_testPinName`]: [KEY1], + [DEFAULT_PIN]: [KEY1], }; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - object.set('updatedField', 'foo'); mockQueryFind.mockImplementationOnce(() => { expect(LocalDatastore.isSyncing).toBe(true); - return Promise.reject('Unable to connect to the Parse API') + return Promise.reject('Unable to connect to the Parse API'); }); jest.spyOn(console, 'log'); @@ -678,22 +667,20 @@ describe('LocalDatastore', () => { expect(mockQueryInstance.equalTo.mock.calls.length).toBe(1); expect(mockQueryFind).toHaveBeenCalledTimes(1); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(0); - expect(console.log).toHaveBeenCalledTimes(2); + expect(console.log).toHaveBeenCalledTimes(1); expect(LocalDatastore.isSyncing).toBe(false); }); it('updateFromServer on mixed object', async () => { LocalDatastore.isEnabled = true; LocalDatastore.isSyncing = false; - const obj1 = new ParseObject('Item'); - const obj2 = new ParseObject('Item'); - const obj3 = new ParseObject('TestObject'); + const testObject = new ParseObject('TestObject'); const LDS = { - [`Item_${obj1.id}`]: obj1._toFullJSON(), - [`Item_${obj2.id}`]: obj2._toFullJSON(), - [`TestObject_${obj3.id}`]: obj3._toFullJSON(), - [`${LocalDatastore.PIN_PREFIX}_testPinName`]: [`Item_${obj1.id}`], - [LocalDatastore.DEFAULT_PIN]: [`Item_${obj1.id}`], + [KEY1]: item1._toFullJSON(), + [KEY2]: item2._toFullJSON(), + [LocalDatastore.getKeyForObject(testObject)]: testObject._toFullJSON(), + [`${PIN_PREFIX}_testPinName`]: [KEY1], + [DEFAULT_PIN]: [KEY1], }; mockLocalStorageController @@ -701,8 +688,8 @@ describe('LocalDatastore', () => { .mockImplementationOnce(() => LDS); mockQueryFind - .mockImplementationOnce(() => Promise.resolve([obj1, obj2])) - .mockImplementationOnce(() => Promise.resolve([obj3])); + .mockImplementationOnce(() => Promise.resolve([item1, item2])) + .mockImplementationOnce(() => Promise.resolve([testObject])); await LocalDatastore.updateFromServer(); @@ -717,6 +704,14 @@ describe('LocalDatastore', () => { expect(mockQueryFind).toHaveBeenCalledTimes(2); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(3); }); + + it('isLocalDatastoreKey', () => { + expect(LocalDatastore.isLocalDatastoreKey(null)).toBe(false); + expect(LocalDatastore.isLocalDatastoreKey('')).toBe(false); + expect(LocalDatastore.isLocalDatastoreKey(DEFAULT_PIN)).toBe(true); + expect(LocalDatastore.isLocalDatastoreKey(PIN_PREFIX)).toBe(true); + expect(LocalDatastore.isLocalDatastoreKey(OBJECT_PREFIX)).toBe(true); + }); }); describe('BrowserDatastoreController', async () => { From f592d16b0aa91669845ba81ce71ec305b8c29521 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Tue, 19 Mar 2019 11:26:48 -0500 Subject: [PATCH 02/14] batch requests --- integration/test/ParseLocalDatastoreTest.js | 375 ++++++++++--------- integration/test/mockRNStorage.js | 10 + src/LocalDatastore.js | 6 + src/LocalDatastoreController.react-native.js | 45 ++- src/StorageController.react-native.js | 24 ++ src/__tests__/LocalDatastore-test.js | 1 - 6 files changed, 280 insertions(+), 181 deletions(-) diff --git a/integration/test/ParseLocalDatastoreTest.js b/integration/test/ParseLocalDatastoreTest.js index 9b01139b5..0e7874905 100644 --- a/integration/test/ParseLocalDatastoreTest.js +++ b/integration/test/ParseLocalDatastoreTest.js @@ -12,14 +12,39 @@ const mockRNStorage = require('./mockRNStorage'); const DEFAULT_PIN = Parse.LocalDatastore.DEFAULT_PIN; const PIN_PREFIX = Parse.LocalDatastore.PIN_PREFIX; +function LDS_KEY(object) { + return Parse.LocalDatastore.getKeyForObject(object); +} + function runTest(controller) { describe(`Parse Object Pinning (${controller.name})`, () => { - beforeEach(() => { + beforeEach(async () => { const StorageController = require(controller.file); Parse.CoreManager.setAsyncStorage(mockRNStorage); Parse.CoreManager.setLocalDatastoreController(StorageController); Parse.enableLocalDatastore(); - Parse.LocalDatastore._clear(); + await Parse.LocalDatastore._clear(); + }); + + it(`${controller.name} can clear localDatastore`, async () => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + const obj3 = new TestObject(); + const objects = [obj1, obj2, obj3]; + await Parse.Object.pinAll(objects); + await Parse.Object.pinAllWithName('test_pin', objects); + await Parse.Object.saveAll(objects); + + await Parse.LocalDatastore.pinWithName('DO_NOT_CLEAR', {}); + + let storage = await Parse.LocalDatastore._getRawStorage(); + assert.equal(Object.keys(storage).length, 6); + + await Parse.LocalDatastore._clear(); + + storage = await Parse.LocalDatastore._getRawStorage(); + assert.equal(Object.keys(storage).length, 1); + assert.equal(storage['DO_NOT_CLEAR'], '{}'); }); it(`${controller.name} can pin (unsaved)`, async () => { @@ -28,14 +53,14 @@ function runTest(controller) { // Since object not saved check localId let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${object.className}_${object._localId}`]); - assert.deepEqual(localDatastore[`${object.className}_${object._localId}`], object._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(object)]); + assert.deepEqual(localDatastore[LDS_KEY(object)], object._toFullJSON()); await object.save(); // Check if localDatastore updated localId to objectId localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${object.className}_${object.id}`]); - assert.deepEqual(localDatastore[`${object.className}_${object.id}`], object._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(object)]); + assert.deepEqual(localDatastore[LDS_KEY(object)], object._toFullJSON()); }); it(`${controller.name} cannot pin unsaved pointer`, async () => { @@ -55,10 +80,10 @@ function runTest(controller) { await object.save(); await object.pin(); const localDatastore = await Parse.LocalDatastore._getAllContents(); - const cachedObject = localDatastore[`${object.className}_${object.id}`]; + const cachedObject = localDatastore[LDS_KEY(object)]; assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${object.className}_${object.id}`]); - assert.deepEqual(localDatastore[`${object.className}_${object.id}`], object._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(object)]); + assert.deepEqual(localDatastore[LDS_KEY(object)], object._toFullJSON()); assert.equal(cachedObject.objectId, object.id); assert.equal(cachedObject.field, 'test'); }); @@ -73,10 +98,10 @@ function runTest(controller) { await object.save(); const localDatastore = await Parse.LocalDatastore._getAllContents(); - const cachedObject = localDatastore[`${object.className}_${object.id}`]; + const cachedObject = localDatastore[LDS_KEY(object)]; assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${object.className}_${object.id}`]); - assert.deepEqual(localDatastore[`${object.className}_${object.id}`], object._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(object)]); + assert.deepEqual(localDatastore[LDS_KEY(object)], object._toFullJSON()); assert.equal(cachedObject.objectId, object.id); assert.equal(cachedObject.field, 'new info'); }); @@ -109,12 +134,12 @@ function runTest(controller) { await parent.pin(); const localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${parent.className}_${parent.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${child.className}_${child.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${grandchild.className}_${grandchild.id}`), true); - assert.deepEqual(localDatastore[`${parent.className}_${parent.id}`], parent._toFullJSON()); - assert.deepEqual(localDatastore[`${child.className}_${child.id}`], child._toFullJSON()); - assert.deepEqual(localDatastore[`${grandchild.className}_${grandchild.id}`], grandchild._toFullJSON()); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(parent)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(child)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(grandchild)), true); + assert.deepEqual(localDatastore[LDS_KEY(parent)], parent._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(child)], child._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(grandchild)], grandchild._toFullJSON()); }); it(`${controller.name} can pinAll (unsaved)`, async () => { @@ -127,19 +152,19 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1.id}`, `${obj2.className}_${obj2.id}`, `${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2.id}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can pinAll (saved)`, async () => { @@ -153,10 +178,10 @@ function runTest(controller) { await Parse.Object.pinAll(objects); const localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1.id}`, `${obj2.className}_${obj2.id}`, `${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2.id}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can pinAllWithName (unsaved)`, async () => { @@ -169,19 +194,19 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [`${obj1.className}_${obj1.id}`, `${obj2.className}_${obj2.id}`, `${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2.id}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can pinAllWithName (saved)`, async () => { @@ -194,10 +219,10 @@ function runTest(controller) { await Parse.Object.pinAllWithName('test_pin', objects); const localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [`${obj1.className}_${obj1.id}`, `${obj2.className}_${obj2.id}`, `${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2.id}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPin on destroy`, async () => { @@ -212,20 +237,20 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 4); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj1.className}_${obj1.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj2.className}_${obj2.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj1.className}_${obj1.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj2.className}_${obj2.id}`), true); - assert(localDatastore[`${obj1.className}_${obj1.id}`]); - assert(localDatastore[`${obj2.className}_${obj2.id}`]); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj1)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj2)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj1)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj2)), true); + assert(localDatastore[LDS_KEY(obj1)]); + assert(localDatastore[LDS_KEY(obj2)]); await obj1.destroy(); localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 3); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj2.className}_${obj2.id}`]); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [`${obj2.className}_${obj2.id}`]); - assert(localDatastore[`${obj2.className}_${obj2.id}`]); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj2)]); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [LDS_KEY(obj2)]); + assert(localDatastore[LDS_KEY(obj2)]); }); it(`${controller.name} can unPin on destroyAll`, async () => { @@ -239,23 +264,23 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 5); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj1.className}_${obj1.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj2.className}_${obj2.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj3.className}_${obj3.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj1.className}_${obj1.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj2.className}_${obj2.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj3.className}_${obj3.id}`), true); - assert(localDatastore[`${obj1.className}_${obj1.id}`]); - assert(localDatastore[`${obj2.className}_${obj2.id}`]); - assert(localDatastore[`${obj3.className}_${obj3.id}`]); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj1)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj2)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj3)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj1)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj2)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj3)), true); + assert(localDatastore[LDS_KEY(obj1)]); + assert(localDatastore[LDS_KEY(obj2)]); + assert(localDatastore[LDS_KEY(obj3)]); await Parse.Object.destroyAll([obj1, obj3]); localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 3); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj2.className}_${obj2.id}`]); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [`${obj2.className}_${obj2.id}`]); - assert(localDatastore[`${obj2.className}_${obj2.id}`]); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj2)]); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [LDS_KEY(obj2)]); + assert(localDatastore[LDS_KEY(obj2)]); }); it(`${controller.name} can unPin with pinAll (unsaved)`, async () => { @@ -268,26 +293,26 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await obj2.unPin(); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1.id}`, `${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPin with pinAll (saved)`, async () => { @@ -300,10 +325,10 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); @@ -311,9 +336,9 @@ function runTest(controller) { localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1.id}`, `${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPin / unPinAll without pin (unsaved)`, async () => { @@ -360,24 +385,24 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 4); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.unPinAll([obj1, obj2]); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPinAll (saved)`, async () => { @@ -390,18 +415,18 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); await Parse.Object.unPinAll([obj1, obj2]); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPinAllObjects (unsaved)`, async () => { @@ -414,26 +439,26 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 4); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.unPinAllObjects(); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2.id}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPinAllObjects (saved)`, async () => { @@ -446,19 +471,19 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[DEFAULT_PIN], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); Parse.Object.unPinAllObjects(); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2.id}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPinAllWithName (unsaved)`, async () => { @@ -471,24 +496,24 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 4); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.unPinAllWithName('test_unpin', [obj1, obj2]); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [`${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [`${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPinAllWithName (saved)`, async () => { @@ -501,18 +526,18 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); await Parse.Object.unPinAllWithName('test_unpin', [obj1, obj2]); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [`${obj3.className}_${obj3.id}`]); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPinAllObjectsWithName (unsaved)`, async () => { @@ -525,26 +550,26 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 4); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.unPinAllObjectsWithName('test_unpin'); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2.id}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPinAllObjectsWithName (saved)`, async () => { @@ -557,19 +582,19 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); - assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [`${obj1.className}_${obj1._localId}`, `${obj2.className}_${obj2._localId}`, `${obj3.className}_${obj3._localId}`]); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1._localId}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2._localId}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3._localId}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); await Parse.Object.saveAll(objects); await Parse.Object.unPinAllObjectsWithName('test_unpin'); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[`${obj1.className}_${obj1.id}`], obj1._toFullJSON()); - assert.deepEqual(localDatastore[`${obj2.className}_${obj2.id}`], obj2._toFullJSON()); - assert.deepEqual(localDatastore[`${obj3.className}_${obj3.id}`], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); }); it(`${controller.name} can unPin and save reference`, async () => { @@ -583,20 +608,20 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 5); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj1.className}_${obj1.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj2.className}_${obj2.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj3.className}_${obj3.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj1.className}_${obj1.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj2.className}_${obj2.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj3.className}_${obj3.id}`), true); - assert(localDatastore[`${obj1.className}_${obj1.id}`]); - assert(localDatastore[`${obj2.className}_${obj2.id}`]); - assert(localDatastore[`${obj3.className}_${obj3.id}`]); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj1)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj2)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj3)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj1)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj2)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj3)), true); + assert(localDatastore[LDS_KEY(obj1)]); + assert(localDatastore[LDS_KEY(obj2)]); + assert(localDatastore[LDS_KEY(obj3)]); await obj1.unPin(); localDatastore = await Parse.LocalDatastore._getAllContents(); - assert(localDatastore[`${obj1.className}_${obj1.id}`]); + assert(localDatastore[LDS_KEY(obj1)]); }); it(`${controller.name} can unPin and save reference with children`, async () => { @@ -612,24 +637,24 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 6); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj1.className}_${obj1.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj2.className}_${obj2.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${obj3.className}_${obj3.id}`), true); - assert.equal(localDatastore[DEFAULT_PIN].includes(`${child.className}_${child.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj1.className}_${obj1.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj2.className}_${obj2.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${obj3.className}_${obj3.id}`), true); - assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(`${child.className}_${child.id}`), true); - assert(localDatastore[`${obj1.className}_${obj1.id}`]); - assert(localDatastore[`${obj2.className}_${obj2.id}`]); - assert(localDatastore[`${obj3.className}_${obj3.id}`]); - assert(localDatastore[`${child.className}_${child.id}`]); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj1)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj2)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(obj3)), true); + assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(child)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj1)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj2)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(obj3)), true); + assert.equal(localDatastore[PIN_PREFIX + 'test_pin'].includes(LDS_KEY(child)), true); + assert(localDatastore[LDS_KEY(obj1)]); + assert(localDatastore[LDS_KEY(obj2)]); + assert(localDatastore[LDS_KEY(obj3)]); + assert(localDatastore[LDS_KEY(child)]); await obj1.unPin(); localDatastore = await Parse.LocalDatastore._getAllContents(); - assert(localDatastore[`${obj1.className}_${obj1.id}`]); - assert(localDatastore[`${child.className}_${child.id}`]); + assert(localDatastore[LDS_KEY(obj1)]); + assert(localDatastore[LDS_KEY(child)]); }); it(`${controller.name} cannot fetchFromLocalDatastore (unsaved)`, async () => { @@ -756,7 +781,7 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); - assert.equal(localDatastore[`Item_${item.id}`].foo, 'bar'); + assert.equal(localDatastore[LDS_KEY(item)].foo, 'bar'); const itemAgain = new Item(); itemAgain.id = item.id; @@ -766,7 +791,7 @@ function runTest(controller) { assert.equal(itemAgain.get('foo'), 'changed'); assert.equal(fetchedItem.get('foo'), 'changed'); - assert.equal(localDatastore[`Item_${item.id}`].foo, 'changed'); + assert.equal(localDatastore[LDS_KEY(item)].foo, 'changed'); }); it('fetchAll updates LocalDatastore', async () => { @@ -783,8 +808,8 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); - assert.equal(localDatastore[`Item_${item1.id}`].foo, 'bar'); - assert.equal(localDatastore[`Item_${item2.id}`].foo, 'baz'); + assert.equal(localDatastore[LDS_KEY(item1)].foo, 'bar'); + assert.equal(localDatastore[LDS_KEY(item2)].foo, 'baz'); const item1Again = new Item(); item1Again.id = item1.id; @@ -799,8 +824,8 @@ function runTest(controller) { assert.equal(item2Again.get('foo'), 'changed'); assert.equal(fetchedItems[0].get('foo'), 'changed'); assert.equal(fetchedItems[1].get('foo'), 'changed'); - assert.equal(localDatastore[`Item_${fetchedItems[0].id}`].foo, 'changed'); - assert.equal(localDatastore[`Item_${fetchedItems[1].id}`].foo, 'changed'); + assert.equal(localDatastore[LDS_KEY(fetchedItems[0])].foo, 'changed'); + assert.equal(localDatastore[LDS_KEY(fetchedItems[1])].foo, 'changed'); }); it(`${controller.name} can update Local Datastore from network`, async () => { @@ -822,7 +847,7 @@ function runTest(controller) { await Parse.LocalDatastore.updateFromServer(); const updatedLDS = await Parse.LocalDatastore._getAllContents(); - const childJSON = updatedLDS[`${child.className}_${child.id}`]; + const childJSON = updatedLDS[LDS_KEY(child)]; assert.equal(childJSON.foo, 'changed'); }); }); @@ -2434,8 +2459,8 @@ describe('Parse LocalDatastore', () => { }); const controllers = [ - { name: 'Default', file: '../../lib/node/LocalDatastoreController.default' }, - { name: 'Browser', file: '../../lib/node/LocalDatastoreController.browser' }, + // { name: 'Default', file: '../../lib/node/LocalDatastoreController.default' }, + // { name: 'Browser', file: '../../lib/node/LocalDatastoreController.browser' }, { name: 'React-Native', file: '../../lib/node/LocalDatastoreController.react-native' }, ]; diff --git a/integration/test/mockRNStorage.js b/integration/test/mockRNStorage.js index 4e09cfac8..9318d2451 100644 --- a/integration/test/mockRNStorage.js +++ b/integration/test/mockRNStorage.js @@ -27,6 +27,16 @@ const mockRNStorage = { cb(undefined, Object.keys(mockStorage)); }, + multiGet(keys, cb) { + const objects = keys.map((key) => [key, mockStorage[key]]); + cb(undefined, objects); + }, + + multiRemove(keys, cb) { + keys.map((key) => delete mockStorage[key]); + cb(undefined); + }, + clear() { mockStorage = {}; }, diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 30ee7f17b..a13df4cf3 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -61,6 +61,12 @@ const LocalDatastore = { return controller.getAllContents(); }, + // Use for testing + _getRawStorage(): Promise { + const controller = CoreManager.getLocalDatastoreController(); + return controller.getRawStorage(); + }, + _clear(): Promise { const controller = CoreManager.getLocalDatastoreController(); return controller.clear(); diff --git a/src/LocalDatastoreController.react-native.js b/src/LocalDatastoreController.react-native.js index 62c6f1dfc..11ac52471 100644 --- a/src/LocalDatastoreController.react-native.js +++ b/src/LocalDatastoreController.react-native.js @@ -10,6 +10,7 @@ */ const RNStorage = require('./StorageController.react-native'); +const LocalDatastore = require('./LocalDatastore'); const LocalDatastoreController = { async fromPinWithName(name: string): Promise { @@ -38,18 +39,52 @@ const LocalDatastoreController = { }, async getAllContents(): Promise { - const LDS = {}; const keys = await RNStorage.getAllKeys(); + const batch = []; for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; - const value = await RNStorage.getItemAsync(key); - LDS[key] = JSON.parse(value); + if (LocalDatastore.isLocalDatastoreKey(key)) { + batch.push(key); + } } + const LDS = {}; + const results = await RNStorage.multiGet(batch); + results.map((pair) => { + const [key, value] = pair; + try { + LDS[key] = JSON.parse(value); + } catch (error) { + LDS[key] = null; + } + }); return Promise.resolve(LDS); }, - clear(): Promise { - return RNStorage.clear(); + async getRawStorage(): Promise { + const keys = await RNStorage.getAllKeys(); + const storage = {}; + const results = await RNStorage.multiGet(keys); + results.map((pair) => { + const [key, value] = pair; + storage[key] = value; + }); + return Promise.resolve(storage); + }, + + async clear(): void { + try { + const keys = await RNStorage.getAllKeys(); + const batch = []; + for (let i = 0; i < keys.length; i += 1) { + const key = keys[i]; + if (LocalDatastore.isLocalDatastoreKey(key)) { + batch.push(key); + } + } + await RNStorage.multiRemove(batch); + } catch (error) { + console.log(error); // eslint-disable-line + } } }; diff --git a/src/StorageController.react-native.js b/src/StorageController.react-native.js index ad51fc773..9a8968ca7 100644 --- a/src/StorageController.react-native.js +++ b/src/StorageController.react-native.js @@ -66,6 +66,30 @@ const StorageController = { }); }, + multiGet(keys: Array): Promise { + return new Promise((resolve, reject) => { + this.getAsyncStorage().multiGet(keys, function(err, result) { + if (err) { + reject(err); + } else { + resolve(result); + } + }); + }); + }, + + multiRemove(keys: Array): Promise { + return new Promise((resolve, reject) => { + this.getAsyncStorage().multiRemove(keys, function(err) { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + }, + clear() { return this.getAsyncStorage().clear(); } diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index c9e13da04..43ed59180 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -641,7 +641,6 @@ describe('LocalDatastore', () => { it('updateFromServer handle error', async () => { LocalDatastore.isEnabled = true; LocalDatastore.isSyncing = false; - const object = new ParseObject('Item'); const LDS = { [KEY1]: item1._toFullJSON(), [`${PIN_PREFIX}_testPinName`]: [KEY1], From ef605596836b888f94e59f3e3494e4e1a739da41 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Tue, 19 Mar 2019 12:42:51 -0500 Subject: [PATCH 03/14] getRawStorage --- integration/test/ParseLocalDatastoreTest.js | 24 ++++++++++++-- src/LocalDatastore.js | 1 + src/LocalDatastoreController.browser.js | 29 +++++++++++++++-- src/LocalDatastoreController.default.js | 36 +++++++++++++++------ 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/integration/test/ParseLocalDatastoreTest.js b/integration/test/ParseLocalDatastoreTest.js index 0e7874905..77af44f1d 100644 --- a/integration/test/ParseLocalDatastoreTest.js +++ b/integration/test/ParseLocalDatastoreTest.js @@ -45,6 +45,26 @@ function runTest(controller) { storage = await Parse.LocalDatastore._getRawStorage(); assert.equal(Object.keys(storage).length, 1); assert.equal(storage['DO_NOT_CLEAR'], '{}'); + await Parse.LocalDatastore.unPinWithName('DO_NOT_CLEAR'); + }); + + it(`${controller.name} can getAllContents localDatastore`, async () => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + const obj3 = new TestObject(); + const objects = [obj1, obj2, obj3]; + await Parse.Object.pinAll(objects); + await Parse.Object.pinAllWithName('test_pin', objects); + await Parse.Object.saveAll(objects); + + await Parse.LocalDatastore.pinWithName('DO_NOT_FETCH', {}); + + const storage = await Parse.LocalDatastore._getRawStorage(); + assert.equal(Object.keys(storage).length, 6); + + const LDS = await Parse.LocalDatastore._getAllContents(); + assert.equal(Object.keys(LDS).length, 5); + assert.equal(LDS['DO_NOT_FETCH'], null); }); it(`${controller.name} can pin (unsaved)`, async () => { @@ -2459,8 +2479,8 @@ describe('Parse LocalDatastore', () => { }); const controllers = [ - // { name: 'Default', file: '../../lib/node/LocalDatastoreController.default' }, - // { name: 'Browser', file: '../../lib/node/LocalDatastoreController.browser' }, + { name: 'Default', file: '../../lib/node/LocalDatastoreController.default' }, + { name: 'Browser', file: '../../lib/node/LocalDatastoreController.browser' }, { name: 'React-Native', file: '../../lib/node/LocalDatastoreController.react-native' }, ]; diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index a13df4cf3..328a8ba36 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -387,6 +387,7 @@ if (process.env.PARSE_BUILD === 'react-native') { } else if (process.env.PARSE_BUILD === 'browser') { CoreManager.setLocalDatastoreController(require('./LocalDatastoreController.browser')); } else { + console.log(require('./LocalDatastoreController.default')); CoreManager.setLocalDatastoreController(require('./LocalDatastoreController.default')); } CoreManager.setLocalDatastore(LocalDatastore); diff --git a/src/LocalDatastoreController.browser.js b/src/LocalDatastoreController.browser.js index 3212fdceb..52b93939e 100644 --- a/src/LocalDatastoreController.browser.js +++ b/src/LocalDatastoreController.browser.js @@ -10,6 +10,7 @@ */ /* global localStorage */ +const LocalDatastore = require('./LocalDatastore'); const LocalDatastoreController = { fromPinWithName(name: string): Promise { @@ -40,14 +41,36 @@ const LocalDatastoreController = { const LDS = {}; for (let i = 0; i < localStorage.length; i += 1) { const key = localStorage.key(i); - const value = localStorage.getItem(key); - LDS[key] = JSON.parse(value); + if (LocalDatastore.isLocalDatastoreKey(key)) { + const value = localStorage.getItem(key); + LDS[key] = JSON.parse(value); + } } return Promise.resolve(LDS); }, + getRawStorage(): Promise { + const storage = {}; + for (let i = 0; i < localStorage.length; i += 1) { + const key = localStorage.key(i); + const value = localStorage.getItem(key); + storage[key] = value; + } + return Promise.resolve(storage); + }, + clear(): Promise { - return Promise.resolve(localStorage.clear()); + const toRemove = []; + for (let i = 0; i < localStorage.length; i += 1) { + const key = localStorage.key(i); + if (LocalDatastore.isLocalDatastoreKey(key)) { + toRemove.push(key); + } + } + for (const key of toRemove) { + localStorage.removeItem(key); + } + return Promise.resolve(); } }; diff --git a/src/LocalDatastoreController.default.js b/src/LocalDatastoreController.default.js index c788247e4..64b6a4153 100644 --- a/src/LocalDatastoreController.default.js +++ b/src/LocalDatastoreController.default.js @@ -8,34 +8,50 @@ * * @flow */ +const LocalDatastore = require('./LocalDatastore'); const memMap = {}; const LocalDatastoreController = { - fromPinWithName(name: string): ?any { - if (memMap.hasOwnProperty(name)) { - return memMap[name]; + fromPinWithName(name: string): Promise { + if (!memMap.hasOwnProperty(name)) { + return Promise.resolve(null); } - return null; + const objects = JSON.parse(memMap[name]); + return Promise.resolve(objects); }, pinWithName(name: string, value: any) { - memMap[name] = value; + const values = JSON.stringify(value); + memMap[name] = values; + return Promise.resolve(); }, - unPinWithName(name: string) { + unPinWithName(name: string): Promise { delete memMap[name]; + return Promise.resolve(); }, - getAllContents() { - return memMap; + getAllContents(): Promise { + const LDS = {}; + for (const key in memMap) { + if (memMap.hasOwnProperty(key) && LocalDatastore.isLocalDatastoreKey(key)) { + LDS[key] = JSON.parse(memMap[key]); + } + } + return Promise.resolve(LDS); + }, + + getRawStorage(): Promise { + return Promise.resolve(memMap); }, - clear() { + clear(): Promise { for (const key in memMap) { - if (memMap.hasOwnProperty(key)) { + if (memMap.hasOwnProperty(key) && LocalDatastore.isLocalDatastoreKey(key)) { delete memMap[key]; } } + return Promise.resolve(); } }; From f3a01e81bb186bb59407626ba30e6d059127eb98 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Tue, 19 Mar 2019 23:35:24 -0500 Subject: [PATCH 04/14] improve coverage --- integration/test/ParseLocalDatastoreTest.js | 11 +- src/LocalDatastore.js | 22 +--- src/LocalDatastoreController.browser.js | 6 +- src/LocalDatastoreController.default.js | 6 +- src/LocalDatastoreController.react-native.js | 6 +- src/LocalDatastoreUtils.js | 24 ++++ src/ParseObject.js | 103 +++++++++------ src/ParseQuery.js | 20 ++- src/__tests__/LocalDatastore-test.js | 128 ++++++++++++------- src/__tests__/ParseObject-test.js | 81 +++++++++--- src/__tests__/ParseQuery-test.js | 5 +- src/__tests__/test_helpers/mockRNStorage.js | 10 ++ 12 files changed, 280 insertions(+), 142 deletions(-) create mode 100644 src/LocalDatastoreUtils.js diff --git a/integration/test/ParseLocalDatastoreTest.js b/integration/test/ParseLocalDatastoreTest.js index 77af44f1d..6e5a3b251 100644 --- a/integration/test/ParseLocalDatastoreTest.js +++ b/integration/test/ParseLocalDatastoreTest.js @@ -8,9 +8,10 @@ const Item = Parse.Object.extend('Item'); global.localStorage = require('./mockLocalStorage'); const mockRNStorage = require('./mockRNStorage'); +const LocalDatastoreUtils = require('../../lib/node/LocalDatastoreUtils'); -const DEFAULT_PIN = Parse.LocalDatastore.DEFAULT_PIN; -const PIN_PREFIX = Parse.LocalDatastore.PIN_PREFIX; +const DEFAULT_PIN = LocalDatastoreUtils.DEFAULT_PIN; +const PIN_PREFIX = LocalDatastoreUtils.PIN_PREFIX; function LDS_KEY(object) { return Parse.LocalDatastore.getKeyForObject(object); @@ -926,10 +927,14 @@ function runTest(controller) { const obj1 = new TestObject({ field: 1 }); const obj2 = new TestObject({ field: 2 }); const obj3 = new TestObject({ field: 3 }); + const obj4 = new TestObject({ field: 4 }); const objects = [obj1, obj2, obj3]; - await Parse.Object.saveAll(objects); + await Parse.Object.saveAll(objects); await Parse.Object.pinAll(objects); + await obj4.save(); + await obj4.pinWithName('DO_NOT_QUERY'); + const query = new Parse.Query(TestObject); query.fromPin(); const results = await query.find(); diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 328a8ba36..5911c48bc 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -13,10 +13,7 @@ import CoreManager from './CoreManager'; import type ParseObject from './ParseObject'; import ParseQuery from './ParseQuery'; - -const DEFAULT_PIN = '_default'; -const PIN_PREFIX = 'parsePin_'; -const OBJECT_PREFIX = 'Parse_LDS_'; +import { DEFAULT_PIN, PIN_PREFIX, OBJECT_PREFIX } from './LocalDatastoreUtils'; /** * Provides a local datastore which can be used to store and retrieve Parse.Object.
@@ -216,15 +213,16 @@ const LocalDatastore = { // Called when an object is save / fetched // Update object pin value - async _updateObjectIfPinned(object: ParseObject) { + async _updateObjectIfPinned(object: ParseObject): Promise { if (!this.isEnabled) { - return; + return Promise.resolve(); } const objectKey = this.getKeyForObject(object); const pinned = await this.fromPinWithName(objectKey); - if (pinned) { - await this.pinWithName(objectKey, object._toFullJSON()); + if (!pinned) { + return Promise.resolve(); } + return this.pinWithName(objectKey, object._toFullJSON()); }, // Called when object is destroyed @@ -350,10 +348,6 @@ const LocalDatastore = { } }, - isLocalDatastoreKey(key: string) { - return !!(key && (key === DEFAULT_PIN || key.startsWith(PIN_PREFIX) || key.startsWith(OBJECT_PREFIX))); - }, - getKeyForObject(object: any) { const objectId = object.objectId || object._getId(); return `${OBJECT_PREFIX}${object.className}_${objectId}`; @@ -374,9 +368,6 @@ const LocalDatastore = { } }; -LocalDatastore.DEFAULT_PIN = DEFAULT_PIN; -LocalDatastore.PIN_PREFIX = PIN_PREFIX; -LocalDatastore.OBJECT_PREFIX = OBJECT_PREFIX; LocalDatastore.isEnabled = false; LocalDatastore.isSyncing = false; @@ -387,7 +378,6 @@ if (process.env.PARSE_BUILD === 'react-native') { } else if (process.env.PARSE_BUILD === 'browser') { CoreManager.setLocalDatastoreController(require('./LocalDatastoreController.browser')); } else { - console.log(require('./LocalDatastoreController.default')); CoreManager.setLocalDatastoreController(require('./LocalDatastoreController.default')); } CoreManager.setLocalDatastore(LocalDatastore); diff --git a/src/LocalDatastoreController.browser.js b/src/LocalDatastoreController.browser.js index 52b93939e..9b662c292 100644 --- a/src/LocalDatastoreController.browser.js +++ b/src/LocalDatastoreController.browser.js @@ -10,7 +10,7 @@ */ /* global localStorage */ -const LocalDatastore = require('./LocalDatastore'); +import { isLocalDatastoreKey } from './LocalDatastoreUtils'; const LocalDatastoreController = { fromPinWithName(name: string): Promise { @@ -41,7 +41,7 @@ const LocalDatastoreController = { const LDS = {}; for (let i = 0; i < localStorage.length; i += 1) { const key = localStorage.key(i); - if (LocalDatastore.isLocalDatastoreKey(key)) { + if (isLocalDatastoreKey(key)) { const value = localStorage.getItem(key); LDS[key] = JSON.parse(value); } @@ -63,7 +63,7 @@ const LocalDatastoreController = { const toRemove = []; for (let i = 0; i < localStorage.length; i += 1) { const key = localStorage.key(i); - if (LocalDatastore.isLocalDatastoreKey(key)) { + if (isLocalDatastoreKey(key)) { toRemove.push(key); } } diff --git a/src/LocalDatastoreController.default.js b/src/LocalDatastoreController.default.js index 64b6a4153..429ee8423 100644 --- a/src/LocalDatastoreController.default.js +++ b/src/LocalDatastoreController.default.js @@ -8,7 +8,7 @@ * * @flow */ -const LocalDatastore = require('./LocalDatastore'); +import { isLocalDatastoreKey } from './LocalDatastoreUtils'; const memMap = {}; const LocalDatastoreController = { @@ -34,7 +34,7 @@ const LocalDatastoreController = { getAllContents(): Promise { const LDS = {}; for (const key in memMap) { - if (memMap.hasOwnProperty(key) && LocalDatastore.isLocalDatastoreKey(key)) { + if (memMap.hasOwnProperty(key) && isLocalDatastoreKey(key)) { LDS[key] = JSON.parse(memMap[key]); } } @@ -47,7 +47,7 @@ const LocalDatastoreController = { clear(): Promise { for (const key in memMap) { - if (memMap.hasOwnProperty(key) && LocalDatastore.isLocalDatastoreKey(key)) { + if (memMap.hasOwnProperty(key) && isLocalDatastoreKey(key)) { delete memMap[key]; } } diff --git a/src/LocalDatastoreController.react-native.js b/src/LocalDatastoreController.react-native.js index 11ac52471..fc0296eda 100644 --- a/src/LocalDatastoreController.react-native.js +++ b/src/LocalDatastoreController.react-native.js @@ -10,7 +10,7 @@ */ const RNStorage = require('./StorageController.react-native'); -const LocalDatastore = require('./LocalDatastore'); +import { isLocalDatastoreKey } from './LocalDatastoreUtils'; const LocalDatastoreController = { async fromPinWithName(name: string): Promise { @@ -43,7 +43,7 @@ const LocalDatastoreController = { const batch = []; for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; - if (LocalDatastore.isLocalDatastoreKey(key)) { + if (isLocalDatastoreKey(key)) { batch.push(key); } } @@ -77,7 +77,7 @@ const LocalDatastoreController = { const batch = []; for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; - if (LocalDatastore.isLocalDatastoreKey(key)) { + if (isLocalDatastoreKey(key)) { batch.push(key); } } diff --git a/src/LocalDatastoreUtils.js b/src/LocalDatastoreUtils.js new file mode 100644 index 000000000..e35055d82 --- /dev/null +++ b/src/LocalDatastoreUtils.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ +const DEFAULT_PIN = '_default'; +const PIN_PREFIX = 'parsePin_'; +const OBJECT_PREFIX = 'Parse_LDS_'; + +function isLocalDatastoreKey(key: string): boolean { + return !!(key && (key === DEFAULT_PIN || key.startsWith(PIN_PREFIX) || key.startsWith(OBJECT_PREFIX))); +} + +export { + DEFAULT_PIN, + PIN_PREFIX, + OBJECT_PREFIX, + isLocalDatastoreKey, +}; diff --git a/src/ParseObject.js b/src/ParseObject.js index 678f6457c..cd004606d 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -19,6 +19,7 @@ import parseDate from './parseDate'; import ParseError from './ParseError'; import ParseFile from './ParseFile'; import { when, continueWhile } from './promiseUtils'; +import { DEFAULT_PIN, PIN_PREFIX } from './LocalDatastoreUtils'; import { opFromJSON, @@ -1212,10 +1213,11 @@ class ParseObject { * * To retrieve object: * query.fromLocalDatastore() or query.fromPin() + * + * @return {Promise} A promise that is fulfilled when the pin completes. */ pin(): Promise { - const localDatastore = CoreManager.getLocalDatastore(); - return ParseObject.pinAllWithName(localDatastore.DEFAULT_PIN, [this]); + return ParseObject.pinAllWithName(DEFAULT_PIN, [this]); } /** @@ -1225,10 +1227,11 @@ class ParseObject { *
    * await object.unPin();
    * 
+ * + * @return {Promise} A promise that is fulfilled when the unPin completes. */ unPin(): Promise { - const localDatastore = CoreManager.getLocalDatastore(); - return ParseObject.unPinAllWithName(localDatastore.DEFAULT_PIN, [this]); + return ParseObject.unPinAllWithName(DEFAULT_PIN, [this]); } /** @@ -1237,15 +1240,18 @@ class ParseObject { *
    * const isPinned = await object.isPinned();
    * 
+ * + * @return {Promise} A boolean promise that is fulfilled if object is pinned. */ async isPinned(): Promise { const localDatastore = CoreManager.getLocalDatastore(); - if (localDatastore.checkIfEnabled()) { - const objectKey = localDatastore.getKeyForObject(this); - const pin = await localDatastore.fromPinWithName(objectKey); - if (pin) { - return Promise.resolve(true); - } + if (!localDatastore.isEnabled) { + return Promise.reject('Parse.enableLocalDatastore() must be called first'); + } + const objectKey = localDatastore.getKeyForObject(this); + const pin = await localDatastore.fromPinWithName(objectKey); + if (pin) { + return Promise.resolve(true); } return Promise.resolve(false); } @@ -1264,6 +1270,7 @@ class ParseObject { * query.fromLocalDatastore() or query.fromPinWithName(name) * * @param {String} name Name of Pin. + * @return {Promise} A promise that is fulfilled when the pin completes. */ pinWithName(name: string): Promise { return ParseObject.pinAllWithName(name, [this]); @@ -1277,6 +1284,7 @@ class ParseObject { * * * @param {String} name Name of Pin. + * @return {Promise} A promise that is fulfilled when the unPin completes. */ unPinWithName(name: string): Promise { return ParseObject.unPinAllWithName(name, [this]); @@ -1291,20 +1299,23 @@ class ParseObject { * * You can create an unfetched pointer with Parse.Object.createWithoutData() * and then call fetchFromLocalDatastore() on it. + * + * @return {Promise} A promise that is fulfilled when the fetch completes. */ async fetchFromLocalDatastore(): Promise { const localDatastore = CoreManager.getLocalDatastore(); - if (localDatastore.checkIfEnabled()) { - const objectKey = localDatastore.getKeyForObject(this); - const pinned = await localDatastore._serializeObject(objectKey); - if (!pinned) { - throw new Error('Cannot fetch an unsaved ParseObject'); - } - const result = ParseObject.fromJSON(pinned); - this._finishFetch(result.toJSON()); - - return Promise.resolve(this); + if (!localDatastore.isEnabled) { + return Promise.reject('Parse.enableLocalDatastore() must be called first'); } + const objectKey = localDatastore.getKeyForObject(this); + const pinned = await localDatastore._serializeObject(objectKey); + if (!pinned) { + throw new Error('Cannot fetch an unsaved ParseObject'); + } + const result = ParseObject.fromJSON(pinned); + this._finishFetch(result.toJSON()); + + return Promise.resolve(this); } /** Static methods **/ @@ -1797,13 +1808,15 @@ class ParseObject { * query.fromLocalDatastore() or query.fromPin() * * @param {Array} objects A list of Parse.Object. + * @return {Promise} A promise that is fulfilled when the pin completes. * @static */ static pinAll(objects: Array): Promise { const localDatastore = CoreManager.getLocalDatastore(); - if (localDatastore.checkIfEnabled()) { - return ParseObject.pinAllWithName(localDatastore.DEFAULT_PIN, objects); + if (!localDatastore.isEnabled) { + return Promise.reject('Parse.enableLocalDatastore() must be called first'); } + return ParseObject.pinAllWithName(DEFAULT_PIN, objects); } /** @@ -1821,15 +1834,19 @@ class ParseObject { * * @param {String} name Name of Pin. * @param {Array} objects A list of Parse.Object. + * @return {Promise} A promise that is fulfilled when the pin completes. * @static */ - static async pinAllWithName(name: string, objects: Array): Promise { + static pinAllWithName(name: string, objects: Array): Promise { const localDatastore = CoreManager.getLocalDatastore(); - if (localDatastore.checkIfEnabled()) { - for (const object of objects) { - await localDatastore._handlePinWithName(name, object); - } + if (!localDatastore.isEnabled) { + return Promise.reject('Parse.enableLocalDatastore() must be called first'); + } + let promise = Promise.resolve(); + for (const object of objects) { + promise = promise.then(() => localDatastore._handlePinWithName(name, object)); } + return promise; } /** @@ -1841,13 +1858,15 @@ class ParseObject { * * * @param {Array} objects A list of Parse.Object. + * @return {Promise} A promise that is fulfilled when the unPin completes. * @static */ static unPinAll(objects: Array): Promise { const localDatastore = CoreManager.getLocalDatastore(); - if (localDatastore.checkIfEnabled()) { - return ParseObject.unPinAllWithName(localDatastore.DEFAULT_PIN, objects); + if (!localDatastore.isEnabled) { + return Promise.reject('Parse.enableLocalDatastore() must be called first'); } + return ParseObject.unPinAllWithName(DEFAULT_PIN, objects); } /** @@ -1859,15 +1878,19 @@ class ParseObject { * * @param {String} name Name of Pin. * @param {Array} objects A list of Parse.Object. + * @return {Promise} A promise that is fulfilled when the unPin completes. * @static */ - static async unPinAllWithName(name: string, objects: Array): Promise { + static unPinAllWithName(name: string, objects: Array): Promise { const localDatastore = CoreManager.getLocalDatastore(); - if (localDatastore.checkIfEnabled()) { - for (const object of objects) { - await localDatastore._handleUnPinWithName(name, object); - } + if (!localDatastore.isEnabled) { + return Promise.reject('Parse.enableLocalDatastore() must be called first'); + } + let promise = Promise.resolve(); + for (const object of objects) { + promise = promise.then(() => localDatastore._handleUnPinWithName(name, object)); } + return promise; } /** @@ -1877,13 +1900,15 @@ class ParseObject { * await Parse.Object.unPinAllObjects(); * * + * @return {Promise} A promise that is fulfilled when the unPin completes. * @static */ static unPinAllObjects(): Promise { const localDatastore = CoreManager.getLocalDatastore(); - if (localDatastore.checkIfEnabled()) { - return localDatastore.unPinWithName(localDatastore.DEFAULT_PIN); + if (!localDatastore.isEnabled) { + return Promise.reject('Parse.enableLocalDatastore() must be called first'); } + return localDatastore.unPinWithName(DEFAULT_PIN); } /** @@ -1895,13 +1920,15 @@ class ParseObject { * * * @param {String} name Name of Pin. + * @return {Promise} A promise that is fulfilled when the unPin completes. * @static */ static unPinAllObjectsWithName(name: string): Promise { const localDatastore = CoreManager.getLocalDatastore(); - if (localDatastore.checkIfEnabled()) { - return localDatastore.unPinWithName(localDatastore.PIN_PREFIX + name); + if (!localDatastore.isEnabled) { + return Promise.reject('Parse.enableLocalDatastore() must be called first'); } + return localDatastore.unPinWithName(PIN_PREFIX + name); } } diff --git a/src/ParseQuery.js b/src/ParseQuery.js index e0ea9cc66..6c8adc5b7 100644 --- a/src/ParseQuery.js +++ b/src/ParseQuery.js @@ -16,6 +16,7 @@ import ParseError from './ParseError'; import ParseGeoPoint from './ParseGeoPoint'; import ParseObject from './ParseObject'; import OfflineQuery from './OfflineQuery'; +import { DEFAULT_PIN } from './LocalDatastoreUtils'; import type { RequestOptions, FullOptions } from './RESTController'; @@ -1542,28 +1543,35 @@ class ParseQuery { /** * Changes the source of this query to all pinned objects. + * + * @return {Parse.Query} Returns the query, so you can chain this call. */ - fromLocalDatastore() { - this.fromPinWithName(null); + fromLocalDatastore(): ParseQuery { + return this.fromPinWithName(null); } /** * Changes the source of this query to the default group of pinned objects. + * + * @return {Parse.Query} Returns the query, so you can chain this call. */ - fromPin() { - const localDatastore = CoreManager.getLocalDatastore(); - this.fromPinWithName(localDatastore.DEFAULT_PIN); + fromPin(): ParseQuery { + return this.fromPinWithName(DEFAULT_PIN); } /** * Changes the source of this query to a specific group of pinned objects. + * + * @param {String} name The name of query source. + * @return {Parse.Query} Returns the query, so you can chain this call. */ - fromPinWithName(name: string) { + fromPinWithName(name: string): ParseQuery { const localDatastore = CoreManager.getLocalDatastore(); if (localDatastore.checkIfEnabled()) { this._queriesLocalDatastore = true; this._localDatastorePinName = name; } + return this; } } diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index 43ed59180..603dbd367 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -8,6 +8,7 @@ */ jest.autoMockOff(); +jest.unmock('../LocalDatastoreUtils'); const encode = require('../encode').default; @@ -117,9 +118,7 @@ const KEY1 = LocalDatastore.getKeyForObject(item1); const KEY2 = LocalDatastore.getKeyForObject(item2); const KEY3 = LocalDatastore.getKeyForObject(item2); -const DEFAULT_PIN = LocalDatastore.DEFAULT_PIN; -const PIN_PREFIX = LocalDatastore.PIN_PREFIX; -const OBJECT_PREFIX = LocalDatastore.OBJECT_PREFIX; +import { DEFAULT_PIN, PIN_PREFIX, OBJECT_PREFIX, isLocalDatastoreKey } from '../LocalDatastoreUtils'; describe('LocalDatastore', () => { beforeEach(() => { @@ -705,11 +704,11 @@ describe('LocalDatastore', () => { }); it('isLocalDatastoreKey', () => { - expect(LocalDatastore.isLocalDatastoreKey(null)).toBe(false); - expect(LocalDatastore.isLocalDatastoreKey('')).toBe(false); - expect(LocalDatastore.isLocalDatastoreKey(DEFAULT_PIN)).toBe(true); - expect(LocalDatastore.isLocalDatastoreKey(PIN_PREFIX)).toBe(true); - expect(LocalDatastore.isLocalDatastoreKey(OBJECT_PREFIX)).toBe(true); + expect(isLocalDatastoreKey(null)).toBe(false); + expect(isLocalDatastoreKey('')).toBe(false); + expect(isLocalDatastoreKey(DEFAULT_PIN)).toBe(true); + expect(isLocalDatastoreKey(PIN_PREFIX)).toBe(true); + expect(isLocalDatastoreKey(OBJECT_PREFIX)).toBe(true); }); }); @@ -727,16 +726,18 @@ describe('BrowserDatastoreController', async () => { }); it('can store and retrieve values', async () => { - expect(await BrowserDatastoreController.fromPinWithName('myKey')).toEqual(null); - await BrowserDatastoreController.pinWithName('myKey', [{ name: 'test' }]); - expect(await BrowserDatastoreController.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); + expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual(null); + await BrowserDatastoreController.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + expect(await BrowserDatastoreController.getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); }); it('can remove values', async () => { - await BrowserDatastoreController.pinWithName('myKey', [{ name: 'test' }]); - expect(await BrowserDatastoreController.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); - await BrowserDatastoreController.unPinWithName('myKey'); - expect(await BrowserDatastoreController.fromPinWithName('myKey')).toEqual(null); + await BrowserDatastoreController.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + await BrowserDatastoreController.unPinWithName(KEY1); + expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual(null); + expect(await BrowserDatastoreController.getAllContents()).toEqual({}); }); }); @@ -754,17 +755,17 @@ describe('DefaultDatastoreController', () => { }); it('can store and retrieve values', async () => { - expect(await DefaultDatastoreController.fromPinWithName('myKey')).toEqual(null); - await DefaultDatastoreController.pinWithName('myKey', [{ name: 'test' }]); - expect(await DefaultDatastoreController.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); - expect(await DefaultDatastoreController.getAllContents()).toEqual({ myKey: [ { name: 'test' } ] }); + expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual(null); + await DefaultDatastoreController.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + expect(await DefaultDatastoreController.getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); }); it('can remove values', async () => { - await DefaultDatastoreController.pinWithName('myKey', [{ name: 'test' }]); - expect(await DefaultDatastoreController.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); - await DefaultDatastoreController.unPinWithName('myKey'); - expect(await DefaultDatastoreController.fromPinWithName('myKey')).toEqual(null); + await DefaultDatastoreController.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + await DefaultDatastoreController.unPinWithName(KEY1); + expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual(null); expect(await DefaultDatastoreController.getAllContents()).toEqual({}); }); }); @@ -776,18 +777,20 @@ describe('LocalDatastore (BrowserDatastoreController)', () => { }); it('can store and retrieve values', async () => { - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual(null); - await LocalDatastore.pinWithName('myKey', [{ name: 'test' }]); - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); - expect(await LocalDatastore._getAllContents()).toEqual({ myKey: [ { name: 'test' } ] }); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); + await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + expect(await LocalDatastore._getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); + expect(await LocalDatastore._getRawStorage()).toEqual({ [KEY1]: JSON.stringify([item1._toFullJSON()]) }); }); it('can remove values', async () => { - await LocalDatastore.pinWithName('myKey', [{ name: 'test' }]); - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); - await LocalDatastore.unPinWithName('myKey'); - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual(null); + await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + await LocalDatastore.unPinWithName(KEY1); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); expect(await LocalDatastore._getAllContents()).toEqual({}); + expect(await LocalDatastore._getRawStorage()).toEqual({}); }); it('can handle store error', async () => { @@ -814,18 +817,20 @@ describe('LocalDatastore (DefaultDatastoreController)', () => { }); it('can store and retrieve values', async () => { - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual(null); - await LocalDatastore.pinWithName('myKey', [{ name: 'test' }]); - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); - expect(await LocalDatastore._getAllContents()).toEqual({ myKey: [ { name: 'test' } ] }); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); + await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + expect(await LocalDatastore._getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); + expect(await LocalDatastore._getRawStorage()).toEqual({ [KEY1]: JSON.stringify([item1._toFullJSON()]) }); }); it('can remove values', async () => { - await LocalDatastore.pinWithName('myKey', [{ name: 'test' }]); - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); - await LocalDatastore.unPinWithName('myKey'); - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual(null); + await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + await LocalDatastore.unPinWithName(KEY1); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); expect(await LocalDatastore._getAllContents()).toEqual({}); + expect(await LocalDatastore._getRawStorage()).toEqual({}); }); }); @@ -837,18 +842,20 @@ describe('LocalDatastore (RNDatastoreController)', () => { }); it('can store and retrieve values', async () => { - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual(null); - await LocalDatastore.pinWithName('myKey', [{ name: 'test' }]); - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); - expect(await LocalDatastore._getAllContents()).toEqual({ myKey: [{ name: 'test' }] }); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); + await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + expect(await LocalDatastore._getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); + expect(await LocalDatastore._getRawStorage()).toEqual({ [KEY1]: JSON.stringify([item1._toFullJSON()]) }); }); it('can remove values', async () => { - await LocalDatastore.pinWithName('myKey', [{ name: 'test' }]); - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual([{ name: 'test' }]); - await LocalDatastore.unPinWithName('myKey'); - expect(await LocalDatastore.fromPinWithName('myKey')).toEqual(null); + await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); + await LocalDatastore.unPinWithName(KEY1); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); expect(await LocalDatastore._getAllContents()).toEqual({}); + expect(await LocalDatastore._getRawStorage()).toEqual({}); }); it('can handle store error', async () => { @@ -864,4 +871,31 @@ describe('LocalDatastore (RNDatastoreController)', () => { expect(e.message).toBe('error thrown'); } }); + + it('can handle getAllContents undefined', async () => { + await RNDatastoreController.pinWithName(KEY1, undefined); + const contents = await RNDatastoreController.getAllContents(); + expect(contents[KEY1]).toEqual(null); + }); + + it('can handle getAllContents non-LDS object', async () => { + await RNDatastoreController.pinWithName(KEY1, item1._toFullJSON()); + await RNDatastoreController.pinWithName('DO_NOT_FETCH', undefined); + const contents = await RNDatastoreController.getAllContents(); + expect(contents[KEY1]).toEqual(item1._toFullJSON()); + expect(contents['DO_NOT_FETCH']).toBeUndefined(); + }); + + it('can handle clear error', async () => { + const mockStorageError = { + multiRemove(keys, cb) { + cb('error thrown'); + }, + getAllKeys(cb) { + cb(undefined, [KEY1, 'DO_NOT_CLEAR']); + } + }; + CoreManager.setAsyncStorage(mockStorageError); + await LocalDatastore._clear(); + }); }); diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index 891d988fd..5c5254f8f 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -2713,28 +2713,67 @@ describe('ParseObject pin', () => { it('cannot pin when localDatastore disabled', async () => { mockLocalDatastore.isEnabled = false; - const spy = jest.spyOn( - console, - 'log' - ); const name = 'test_pin'; const obj = new ParseObject('Item'); - await obj.pin(); - await obj.unPin(); - await obj.isPinned(); - await obj.pinWithName(name); - await obj.unPinWithName(name); - await obj.fetchFromLocalDatastore(); - - await ParseObject.pinAll([obj]); - await ParseObject.unPinAll([obj]); - await ParseObject.pinAllWithName(name, [obj]); - await ParseObject.unPinAllWithName(name, [obj]); - await ParseObject.unPinAllObjects(); - await ParseObject.unPinAllObjectsWithName(name); - - expect(spy).toHaveBeenCalledTimes(12); - expect(spy).toHaveBeenCalledWith('Parse.enableLocalDatastore() must be called first'); - spy.mockRestore(); + try { + await obj.pin(); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await obj.unPin(); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await obj.isPinned(); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await obj.pinWithName(); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await obj.unPinWithName(); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await obj.fetchFromLocalDatastore(); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await ParseObject.pinAll([obj]); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await ParseObject.unPinAll([obj]); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await ParseObject.pinAllWithName(name, [obj]); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await ParseObject.unPinAllWithName(name, [obj]); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await ParseObject.unPinAllObjects(); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } + try { + await ParseObject.unPinAllObjectsWithName(name); + } catch (error) { + expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + } }); }); diff --git a/src/__tests__/ParseQuery-test.js b/src/__tests__/ParseQuery-test.js index 347bd8755..30dc4abcc 100644 --- a/src/__tests__/ParseQuery-test.js +++ b/src/__tests__/ParseQuery-test.js @@ -44,12 +44,13 @@ const mockLocalDatastore = { jest.setMock('../LocalDatastore', mockLocalDatastore); let CoreManager = require('../CoreManager'); -const LocalDatastore = require('../LocalDatastore'); const ParseError = require('../ParseError').default; const ParseGeoPoint = require('../ParseGeoPoint').default; let ParseObject = require('../ParseObject'); let ParseQuery = require('../ParseQuery').default; +import { DEFAULT_PIN } from '../LocalDatastoreUtils'; + describe('ParseQuery', () => { it('can be constructed from a class name', () => { const q = new ParseQuery('Item'); @@ -2190,7 +2191,7 @@ describe('ParseQuery LocalDatastore', () => { expect(q._localDatastorePinName).toBe(null); q.fromPin(); expect(q._queriesLocalDatastore).toBe(true); - expect(q._localDatastorePinName).toBe(LocalDatastore.DEFAULT_PIN); + expect(q._localDatastorePinName).toBe(DEFAULT_PIN); }); it('can query from pin with name', () => { diff --git a/src/__tests__/test_helpers/mockRNStorage.js b/src/__tests__/test_helpers/mockRNStorage.js index 4e09cfac8..9318d2451 100644 --- a/src/__tests__/test_helpers/mockRNStorage.js +++ b/src/__tests__/test_helpers/mockRNStorage.js @@ -27,6 +27,16 @@ const mockRNStorage = { cb(undefined, Object.keys(mockStorage)); }, + multiGet(keys, cb) { + const objects = keys.map((key) => [key, mockStorage[key]]); + cb(undefined, objects); + }, + + multiRemove(keys, cb) { + keys.map((key) => delete mockStorage[key]); + cb(undefined); + }, + clear() { mockStorage = {}; }, From 16e270b1dccdb25184b76df76db549b6d98d2044 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Tue, 19 Mar 2019 23:43:10 -0500 Subject: [PATCH 05/14] nits --- src/LocalDatastore.js | 1 - src/LocalDatastoreController.default.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 5911c48bc..280384b44 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -274,7 +274,6 @@ const LocalDatastore = { await this.pinWithName(objectKey, unsaved); const localDatastore = await this._getAllContents(); - console for (const key in localDatastore) { if (key === DEFAULT_PIN || key.startsWith(PIN_PREFIX)) { let pinned = localDatastore[key] || []; diff --git a/src/LocalDatastoreController.default.js b/src/LocalDatastoreController.default.js index 429ee8423..a389ebaec 100644 --- a/src/LocalDatastoreController.default.js +++ b/src/LocalDatastoreController.default.js @@ -20,7 +20,7 @@ const LocalDatastoreController = { return Promise.resolve(objects); }, - pinWithName(name: string, value: any) { + pinWithName(name: string, value: any): Promise { const values = JSON.stringify(value); memMap[name] = values; return Promise.resolve(); From 5ce846db1dc481bcccb844afc7963fbf30ac06f5 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 20 Mar 2019 14:43:11 -0500 Subject: [PATCH 06/14] nits --- .eslintrc.json | 3 ++- src/LocalDatastore.js | 10 +++----- src/LocalDatastoreController.default.js | 23 ++++++++--------- src/LocalDatastoreController.react-native.js | 26 +++++++++++--------- src/ParseObject.js | 20 ++++++--------- src/StorageController.react-native.js | 2 +- src/__tests__/LocalDatastore-test.js | 20 ++++++++++++--- src/__tests__/ParseObject-test.js | 4 +-- 8 files changed, 59 insertions(+), 49 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 5ee807d06..b45eb6759 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -23,6 +23,7 @@ "prefer-const": "error", "space-infix-ops": "error", "no-useless-escape": "off", - "no-var": "error" + "no-var": "error", + "no-console": 0 } } diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 280384b44..50fde49c6 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -19,19 +19,17 @@ import { DEFAULT_PIN, PIN_PREFIX, OBJECT_PREFIX } from './LocalDatastoreUtils'; * Provides a local datastore which can be used to store and retrieve Parse.Object.
* To enable this functionality, call Parse.enableLocalDatastore(). * - * To store objects you can pin them. + * Pin object to add to local datastore * *
await object.pin();
*
await object.pinWithName('pinName');
* - * You can query your objects by changing the source of your query. + * Query objects by changing query source * *
query.fromLocalDatastore();
*
query.fromPin();
*
query.fromPinWithName();
* - * Once your source is changed, you can query your objects from LocalDatastore - * *
const localObjects = await query.find();
* * @class Parse.LocalDatastore @@ -342,7 +340,7 @@ const LocalDatastore = { await Promise.all(pinPromises); this.isSyncing = false; } catch(error) { - console.log('Error syncing LocalDatastore: ', error); // eslint-disable-line + console.error('Error syncing LocalDatastore: ', error); this.isSyncing = false; } }, @@ -361,7 +359,7 @@ const LocalDatastore = { checkIfEnabled() { if (!this.isEnabled) { - console.log('Parse.enableLocalDatastore() must be called first'); // eslint-disable-line no-console + console.error('Parse.enableLocalDatastore() must be called first'); } return this.isEnabled; } diff --git a/src/LocalDatastoreController.default.js b/src/LocalDatastoreController.default.js index a389ebaec..35724be62 100644 --- a/src/LocalDatastoreController.default.js +++ b/src/LocalDatastoreController.default.js @@ -12,46 +12,43 @@ import { isLocalDatastoreKey } from './LocalDatastoreUtils'; const memMap = {}; const LocalDatastoreController = { - fromPinWithName(name: string): Promise { + fromPinWithName(name: string) { if (!memMap.hasOwnProperty(name)) { - return Promise.resolve(null); + return null; } const objects = JSON.parse(memMap[name]); - return Promise.resolve(objects); + return objects; }, - pinWithName(name: string, value: any): Promise { + pinWithName(name: string, value: any) { const values = JSON.stringify(value); memMap[name] = values; - return Promise.resolve(); }, - unPinWithName(name: string): Promise { + unPinWithName(name: string) { delete memMap[name]; - return Promise.resolve(); }, - getAllContents(): Promise { + getAllContents() { const LDS = {}; for (const key in memMap) { if (memMap.hasOwnProperty(key) && isLocalDatastoreKey(key)) { LDS[key] = JSON.parse(memMap[key]); } } - return Promise.resolve(LDS); + return LDS; }, - getRawStorage(): Promise { - return Promise.resolve(memMap); + getRawStorage() { + return memMap; }, - clear(): Promise { + clear() { for (const key in memMap) { if (memMap.hasOwnProperty(key) && isLocalDatastoreKey(key)) { delete memMap[key]; } } - return Promise.resolve(); } }; diff --git a/src/LocalDatastoreController.react-native.js b/src/LocalDatastoreController.react-native.js index fc0296eda..70e3b1bad 100644 --- a/src/LocalDatastoreController.react-native.js +++ b/src/LocalDatastoreController.react-native.js @@ -16,10 +16,10 @@ const LocalDatastoreController = { async fromPinWithName(name: string): Promise { const values = await RNStorage.getItemAsync(name); if (!values) { - return Promise.resolve(null); + return null; } const objects = JSON.parse(values); - return Promise.resolve(objects); + return objects; }, async pinWithName(name: string, value: any): Promise { @@ -28,14 +28,12 @@ const LocalDatastoreController = { await RNStorage.setItemAsync(name, values); } catch (e) { // Quota exceeded, possibly due to Safari Private Browsing mode - console.log(e.message); // eslint-disable-line no-console + console.error(e.message); } - return Promise.resolve(); }, - async unPinWithName(name: string): Promise { - await RNStorage.removeItemAsync(name); - return Promise.resolve(); + unPinWithName(name: string): Promise { + return RNStorage.removeItemAsync(name); }, async getAllContents(): Promise { @@ -48,7 +46,13 @@ const LocalDatastoreController = { } } const LDS = {}; - const results = await RNStorage.multiGet(batch); + let results = []; + try { + results = await RNStorage.multiGet(batch); + } catch (error) { + console.error('Error getAllContents: ', error); + return {}; + } results.map((pair) => { const [key, value] = pair; try { @@ -57,7 +61,7 @@ const LocalDatastoreController = { LDS[key] = null; } }); - return Promise.resolve(LDS); + return LDS; }, async getRawStorage(): Promise { @@ -68,7 +72,7 @@ const LocalDatastoreController = { const [key, value] = pair; storage[key] = value; }); - return Promise.resolve(storage); + return storage; }, async clear(): void { @@ -83,7 +87,7 @@ const LocalDatastoreController = { } await RNStorage.multiRemove(batch); } catch (error) { - console.log(error); // eslint-disable-line + console.error('Error clearing local datastore', error); } } }; diff --git a/src/ParseObject.js b/src/ParseObject.js index cd004606d..fb2157c88 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1251,9 +1251,9 @@ class ParseObject { const objectKey = localDatastore.getKeyForObject(this); const pin = await localDatastore.fromPinWithName(objectKey); if (pin) { - return Promise.resolve(true); + return true; } - return Promise.resolve(false); + return false; } /** @@ -1305,7 +1305,7 @@ class ParseObject { async fetchFromLocalDatastore(): Promise { const localDatastore = CoreManager.getLocalDatastore(); if (!localDatastore.isEnabled) { - return Promise.reject('Parse.enableLocalDatastore() must be called first'); + throw new Error('Parse.enableLocalDatastore() must be called first'); } const objectKey = localDatastore.getKeyForObject(this); const pinned = await localDatastore._serializeObject(objectKey); @@ -1315,7 +1315,7 @@ class ParseObject { const result = ParseObject.fromJSON(pinned); this._finishFetch(result.toJSON()); - return Promise.resolve(this); + return this; } /** Static methods **/ @@ -1837,16 +1837,14 @@ class ParseObject { * @return {Promise} A promise that is fulfilled when the pin completes. * @static */ - static pinAllWithName(name: string, objects: Array): Promise { + static async pinAllWithName(name: string, objects: Array): Promise { const localDatastore = CoreManager.getLocalDatastore(); if (!localDatastore.isEnabled) { return Promise.reject('Parse.enableLocalDatastore() must be called first'); } - let promise = Promise.resolve(); for (const object of objects) { - promise = promise.then(() => localDatastore._handlePinWithName(name, object)); + await localDatastore._handlePinWithName(name, object); } - return promise; } /** @@ -1881,16 +1879,14 @@ class ParseObject { * @return {Promise} A promise that is fulfilled when the unPin completes. * @static */ - static unPinAllWithName(name: string, objects: Array): Promise { + static async unPinAllWithName(name: string, objects: Array): Promise { const localDatastore = CoreManager.getLocalDatastore(); if (!localDatastore.isEnabled) { return Promise.reject('Parse.enableLocalDatastore() must be called first'); } - let promise = Promise.resolve(); for (const object of objects) { - promise = promise.then(() => localDatastore._handleUnPinWithName(name, object)); + await localDatastore._handleUnPinWithName(name, object); } - return promise; } /** diff --git a/src/StorageController.react-native.js b/src/StorageController.react-native.js index 9a8968ca7..1c69d4132 100644 --- a/src/StorageController.react-native.js +++ b/src/StorageController.react-native.js @@ -84,7 +84,7 @@ const StorageController = { if (err) { reject(err); } else { - resolve(); + resolve(keys); } }); }); diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index 603dbd367..41efb20a3 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -134,7 +134,7 @@ describe('LocalDatastore', () => { }); it('isDisabled', () => { - const spy = jest.spyOn(console, 'log'); + const spy = jest.spyOn(console, 'error'); LocalDatastore.isEnabled = false; const isEnabled = LocalDatastore.checkIfEnabled(); expect(isEnabled).toBe(false); @@ -655,7 +655,7 @@ describe('LocalDatastore', () => { return Promise.reject('Unable to connect to the Parse API'); }); - jest.spyOn(console, 'log'); + jest.spyOn(console, 'error'); await LocalDatastore.updateFromServer(); expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); @@ -665,7 +665,7 @@ describe('LocalDatastore', () => { expect(mockQueryInstance.equalTo.mock.calls.length).toBe(1); expect(mockQueryFind).toHaveBeenCalledTimes(1); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(0); - expect(console.log).toHaveBeenCalledTimes(1); + expect(console.error).toHaveBeenCalledTimes(1); expect(LocalDatastore.isSyncing).toBe(false); }); @@ -898,4 +898,18 @@ describe('LocalDatastore (RNDatastoreController)', () => { CoreManager.setAsyncStorage(mockStorageError); await LocalDatastore._clear(); }); + + it('can handle multiget error', async () => { + const mockStorageError = { + multiGet(keys, cb) { + cb('error thrown'); + }, + getAllKeys(cb) { + cb(undefined, [KEY1, 'DO_NOT_CLEAR']); + } + }; + CoreManager.setAsyncStorage(mockStorageError); + const LDS = await LocalDatastore._getAllContents(); + expect(LDS).toEqual({}); + }); }); diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index 5c5254f8f..a8739c9f2 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -124,7 +124,7 @@ const mockLocalDatastore = { getKeyForObject: jest.fn(), checkIfEnabled: jest.fn(() => { if (!mockLocalDatastore.isEnabled) { - console.log('Parse.enableLocalDatastore() must be called first'); // eslint-disable-line no-console + console.error('Parse.enableLocalDatastore() must be called first'); } return mockLocalDatastore.isEnabled; }), @@ -2743,7 +2743,7 @@ describe('ParseObject pin', () => { try { await obj.fetchFromLocalDatastore(); } catch (error) { - expect(error).toBe('Parse.enableLocalDatastore() must be called first'); + expect(error.message).toBe('Parse.enableLocalDatastore() must be called first'); } try { await ParseObject.pinAll([obj]); From ce0cbb932d1bbce69d2d76accee8bd6b62b031e8 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 20 Mar 2019 16:05:57 -0500 Subject: [PATCH 07/14] _handlePinWithName -> _handlePinAllWithName --- src/LocalDatastore.js | 23 ++++++++++++++------- src/ParseObject.js | 4 +--- src/__tests__/LocalDatastore-test.js | 8 ++++---- src/__tests__/ParseObject-test.js | 30 +++++++++++++--------------- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 50fde49c6..9201097a6 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -69,16 +69,25 @@ const LocalDatastore = { // Pin the object and children recursively // Saves the object and children key to Pin Name - async _handlePinWithName(name: string, object: ParseObject): Promise { + async _handlePinAllWithName(name: string, objects: Array): Promise { + if (!objects || objects.length === 0) { + return; + } const pinName = this.getPinName(name); - const objects = this._getChildren(object); - objects[this.getKeyForObject(object)] = object._toFullJSON(); - for (const objectKey in objects) { - await this.pinWithName(objectKey, objects[objectKey]); + const promises = []; + const objectKeys = []; + for (const parent of objects) { + const children = this._getChildren(parent); + const parentKey = this.getKeyForObject(parent); + children[parentKey] = parent._toFullJSON(); + for (const objectKey in children) { + objectKeys.push(objectKey); + promises.push(this.pinWithName(objectKey, children[objectKey])); + } } + await Promise.all(promises); const pinned = await this.fromPinWithName(pinName) || []; - const objectIds = Object.keys(objects); - const toPin = [...new Set([...pinned, ...objectIds])]; + const toPin = [...new Set([...pinned, ...objectKeys])]; await this.pinWithName(pinName, toPin); }, diff --git a/src/ParseObject.js b/src/ParseObject.js index fb2157c88..ca1bf91f0 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1842,9 +1842,7 @@ class ParseObject { if (!localDatastore.isEnabled) { return Promise.reject('Parse.enableLocalDatastore() must be called first'); } - for (const object of objects) { - await localDatastore._handlePinWithName(name, object); - } + await localDatastore._handlePinAllWithName(name, objects); } /** diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index 41efb20a3..80859febf 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -157,13 +157,13 @@ describe('LocalDatastore', () => { it('_handlePinWithName no children', async () => { const object = new ParseObject('Item'); - await LocalDatastore._handlePinWithName('test_pin', object); + await LocalDatastore._handlePinAllWithName('test_pin', [object]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); it('_handlePinWithName default pin', async () => { const object = new ParseObject('Item'); - await LocalDatastore._handlePinWithName(DEFAULT_PIN, object); + await LocalDatastore._handlePinAllWithName(DEFAULT_PIN, [object]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); @@ -171,7 +171,7 @@ describe('LocalDatastore', () => { const parent = new ParseObject('Item'); const unsaved = { className: 'Item', __type: 'Object' }; parent.set('child', unsaved); - await LocalDatastore._handlePinWithName('test_pin', parent); + await LocalDatastore._handlePinAllWithName('test_pin', [parent]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); @@ -181,7 +181,7 @@ describe('LocalDatastore', () => { const grandchild = new ParseObject('Item'); child.set('grandchild', grandchild); parent.set('child', child); - await LocalDatastore._handlePinWithName('test_pin', parent); + await LocalDatastore._handlePinAllWithName('test_pin', [parent]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(4); }); diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index a8739c9f2..17df6a10c 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -103,14 +103,14 @@ mockQuery.prototype.find = function() { }; jest.setMock('../ParseQuery', mockQuery); +import { DEFAULT_PIN, PIN_PREFIX } from '../LocalDatastoreUtils'; + const mockLocalDatastore = { - DEFAULT_PIN: '_default', - PIN_PREFIX: 'parsePin_', isEnabled: false, fromPinWithName: jest.fn(), pinWithName: jest.fn(), unPinWithName: jest.fn(), - _handlePinWithName: jest.fn(), + _handlePinAllWithName: jest.fn(), _handleUnPinWithName: jest.fn(), _getAllContent: jest.fn(), _serializeObjectsFromPinName: jest.fn(), @@ -141,7 +141,6 @@ const ParseOp = require('../ParseOp'); const RESTController = require('../RESTController'); const SingleInstanceStateController = require('../SingleInstanceStateController'); const unsavedChildren = require('../unsavedChildren').default; -const LocalDatastore = require('../LocalDatastore'); const mockXHR = require('./test_helpers/mockXHR'); @@ -2615,22 +2614,22 @@ describe('ParseObject pin', () => { it('can pin to default', async () => { const object = new ParseObject('Item'); await object.pin(); - expect(mockLocalDatastore._handlePinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalDatastore._handlePinWithName).toHaveBeenCalledWith(LocalDatastore.DEFAULT_PIN, object); + expect(mockLocalDatastore._handlePinAllWithName).toHaveBeenCalledTimes(1); + expect(mockLocalDatastore._handlePinAllWithName).toHaveBeenCalledWith(DEFAULT_PIN, [object]); }); it('can unPin to default', async () => { const object = new ParseObject('Item'); await object.unPin(); expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledWith(LocalDatastore.DEFAULT_PIN, object); + expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledWith(DEFAULT_PIN, object); }); it('can pin to specific pin', async () => { const object = new ParseObject('Item'); await object.pinWithName('test_pin'); - expect(mockLocalDatastore._handlePinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalDatastore._handlePinWithName).toHaveBeenCalledWith('test_pin', object); + expect(mockLocalDatastore._handlePinAllWithName).toHaveBeenCalledTimes(1); + expect(mockLocalDatastore._handlePinAllWithName).toHaveBeenCalledWith('test_pin', [object]); }); it('can unPin to specific', async () => { @@ -2685,9 +2684,8 @@ describe('ParseObject pin', () => { const obj1 = new ParseObject('Item'); const obj2 = new ParseObject('Item'); await ParseObject.pinAll([obj1, obj2]); - expect(mockLocalDatastore._handlePinWithName).toHaveBeenCalledTimes(2); - expect(mockLocalDatastore._handlePinWithName.mock.calls[0]).toEqual([LocalDatastore.DEFAULT_PIN, obj1]); - expect(mockLocalDatastore._handlePinWithName.mock.calls[1]).toEqual([LocalDatastore.DEFAULT_PIN, obj2]); + expect(mockLocalDatastore._handlePinAllWithName).toHaveBeenCalledTimes(1); + expect(mockLocalDatastore._handlePinAllWithName.mock.calls[0]).toEqual([DEFAULT_PIN, [obj1, obj2]]); }); it('can unPinAll', async () => { @@ -2695,20 +2693,20 @@ describe('ParseObject pin', () => { const obj2 = new ParseObject('Item'); await ParseObject.unPinAll([obj1, obj2]); expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledTimes(2); - expect(mockLocalDatastore._handleUnPinWithName.mock.calls[0]).toEqual([LocalDatastore.DEFAULT_PIN, obj1]); - expect(mockLocalDatastore._handleUnPinWithName.mock.calls[1]).toEqual([LocalDatastore.DEFAULT_PIN, obj2]); + expect(mockLocalDatastore._handleUnPinWithName.mock.calls[0]).toEqual([DEFAULT_PIN, obj1]); + expect(mockLocalDatastore._handleUnPinWithName.mock.calls[1]).toEqual([DEFAULT_PIN, obj2]); }); it('can unPinAllObjects', async () => { await ParseObject.unPinAllObjects(); expect(mockLocalDatastore.unPinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalDatastore.unPinWithName.mock.calls[0]).toEqual([LocalDatastore.DEFAULT_PIN]); + expect(mockLocalDatastore.unPinWithName.mock.calls[0]).toEqual([DEFAULT_PIN]); }); it('can unPinAllObjectsWithName', async () => { await ParseObject.unPinAllObjectsWithName('123'); expect(mockLocalDatastore.unPinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalDatastore.unPinWithName.mock.calls[0]).toEqual([LocalDatastore.PIN_PREFIX + '123']); + expect(mockLocalDatastore.unPinWithName.mock.calls[0]).toEqual([PIN_PREFIX + '123']); }); it('cannot pin when localDatastore disabled', async () => { From 9c7dc51e10e7bc3de9a4f05c7440fb75f781e2f3 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 20 Mar 2019 16:50:09 -0500 Subject: [PATCH 08/14] _handleUnPinWithName -> _handleUnPinAllWithName --- src/LocalDatastore.js | 29 +++++++++++++++++++--------- src/ParseObject.js | 10 ++++------ src/__tests__/LocalDatastore-test.js | 20 +++++++++---------- src/__tests__/ParseObject-test.js | 15 +++++++------- 4 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 9201097a6..30e5b147b 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -93,22 +93,32 @@ const LocalDatastore = { // Removes object and children keys from pin name // Keeps the object and children pinned - async _handleUnPinWithName(name: string, object: ParseObject) { + async _handleUnPinAllWithName(name: string, objects: Array) { + if (!objects || objects.length === 0) { + return; + } const localDatastore = await this._getAllContents(); const pinName = this.getPinName(name); - const objects = this._getChildren(object); - const objectIds = Object.keys(objects); - objectIds.push(this.getKeyForObject(object)); + const promises = []; + let objectKeys = []; + for (const parent of objects) { + const children = this._getChildren(parent); + const parentKey = this.getKeyForObject(parent); + objectKeys.push(parentKey); + objectKeys.push(...Object.keys(children)); + } + objectKeys = [...new Set(objectKeys)]; + let pinned = localDatastore[pinName] || []; - pinned = pinned.filter(item => !objectIds.includes(item)); + pinned = pinned.filter(item => !objectKeys.includes(item)); if (pinned.length == 0) { - await this.unPinWithName(pinName); + promises.push(this.unPinWithName(pinName)); delete localDatastore[pinName]; } else { - await this.pinWithName(pinName, pinned); + promises.push(this.pinWithName(pinName, pinned)); localDatastore[pinName] = pinned; } - for (const objectKey of objectIds) { + for (const objectKey of objectKeys) { let hasReference = false; for (const key in localDatastore) { if (key === DEFAULT_PIN || key.startsWith(PIN_PREFIX)) { @@ -120,9 +130,10 @@ const LocalDatastore = { } } if (!hasReference) { - await this.unPinWithName(objectKey); + promises.push(this.unPinWithName(objectKey)); } } + return Promise.all(promises); }, // Retrieve all pointer fields from object recursively diff --git a/src/ParseObject.js b/src/ParseObject.js index ca1bf91f0..7b1fb6b66 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1837,12 +1837,12 @@ class ParseObject { * @return {Promise} A promise that is fulfilled when the pin completes. * @static */ - static async pinAllWithName(name: string, objects: Array): Promise { + static pinAllWithName(name: string, objects: Array): Promise { const localDatastore = CoreManager.getLocalDatastore(); if (!localDatastore.isEnabled) { return Promise.reject('Parse.enableLocalDatastore() must be called first'); } - await localDatastore._handlePinAllWithName(name, objects); + return localDatastore._handlePinAllWithName(name, objects); } /** @@ -1877,14 +1877,12 @@ class ParseObject { * @return {Promise} A promise that is fulfilled when the unPin completes. * @static */ - static async unPinAllWithName(name: string, objects: Array): Promise { + static unPinAllWithName(name: string, objects: Array): Promise { const localDatastore = CoreManager.getLocalDatastore(); if (!localDatastore.isEnabled) { return Promise.reject('Parse.enableLocalDatastore() must be called first'); } - for (const object of objects) { - await localDatastore._handleUnPinWithName(name, object); - } + return localDatastore._handleUnPinAllWithName(name, objects); } /** diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index 80859febf..549a59ba8 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -185,31 +185,31 @@ describe('LocalDatastore', () => { expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(4); }); - it('_handleUnPinWithName default pin', async () => { + it('_handleUnPinAllWithName default pin', async () => { const LDS = { [DEFAULT_PIN]: [KEY1, KEY2], }; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._handleUnPinWithName(DEFAULT_PIN, item1); + await LocalDatastore._handleUnPinAllWithName(DEFAULT_PIN, [item1]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(DEFAULT_PIN, [KEY2]); }); - it('_handleUnPinWithName specific pin', async () => { + it('_handleUnPinAllWithName specific pin', async () => { const LDS = { parsePin_test_pin: [KEY1, KEY2], }; mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._handleUnPinWithName('test_pin', item1); + await LocalDatastore._handleUnPinAllWithName('test_pin', [item1]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(PIN_PREFIX + 'test_pin', [KEY2]); }); - it('_handleUnPinWithName default pin remove pinName', async () => { + it('_handleUnPinAllWithName default pin remove pinName', async () => { const object = new ParseObject('Item'); const LDS = { [DEFAULT_PIN]: [], @@ -217,12 +217,12 @@ describe('LocalDatastore', () => { mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._handleUnPinWithName(DEFAULT_PIN, object); + await LocalDatastore._handleUnPinAllWithName(DEFAULT_PIN, [object]); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledTimes(2); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(DEFAULT_PIN); }); - it('_handleUnPinWithName specific pin remove pinName', async () => { + it('_handleUnPinAllWithName specific pin remove pinName', async () => { const object = new ParseObject('Item'); const LDS = { [PIN_PREFIX + 'test_pin']: [], @@ -230,12 +230,12 @@ describe('LocalDatastore', () => { mockLocalStorageController .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._handleUnPinWithName('test_pin', object); + await LocalDatastore._handleUnPinAllWithName('test_pin', [object]); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledTimes(2); expect(mockLocalStorageController.unPinWithName).toHaveBeenCalledWith(PIN_PREFIX + 'test_pin'); }); - it('_handleUnPinWithName remove if exist', async () => { + it('_handleUnPinAllWithName remove if exist', async () => { const objects = [KEY1, KEY2, KEY3]; const LDS = { [PIN_PREFIX + 'test_pin']: objects, @@ -247,7 +247,7 @@ describe('LocalDatastore', () => { .getAllContents .mockImplementationOnce(() => LDS); - await LocalDatastore._handleUnPinWithName('test_pin', item1); + await LocalDatastore._handleUnPinAllWithName('test_pin', [item1]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(PIN_PREFIX + 'test_pin', [KEY2, KEY3]); diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index 17df6a10c..4ed580440 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -111,7 +111,7 @@ const mockLocalDatastore = { pinWithName: jest.fn(), unPinWithName: jest.fn(), _handlePinAllWithName: jest.fn(), - _handleUnPinWithName: jest.fn(), + _handleUnPinAllWithName: jest.fn(), _getAllContent: jest.fn(), _serializeObjectsFromPinName: jest.fn(), _serializeObject: jest.fn(), @@ -2621,8 +2621,8 @@ describe('ParseObject pin', () => { it('can unPin to default', async () => { const object = new ParseObject('Item'); await object.unPin(); - expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledWith(DEFAULT_PIN, object); + expect(mockLocalDatastore._handleUnPinAllWithName).toHaveBeenCalledTimes(1); + expect(mockLocalDatastore._handleUnPinAllWithName).toHaveBeenCalledWith(DEFAULT_PIN, [object]); }); it('can pin to specific pin', async () => { @@ -2635,8 +2635,8 @@ describe('ParseObject pin', () => { it('can unPin to specific', async () => { const object = new ParseObject('Item'); await object.unPinWithName('test_pin'); - expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledWith('test_pin', object); + expect(mockLocalDatastore._handleUnPinAllWithName).toHaveBeenCalledTimes(1); + expect(mockLocalDatastore._handleUnPinAllWithName).toHaveBeenCalledWith('test_pin', [object]); }); it('can check if pinned', async () => { @@ -2692,9 +2692,8 @@ describe('ParseObject pin', () => { const obj1 = new ParseObject('Item'); const obj2 = new ParseObject('Item'); await ParseObject.unPinAll([obj1, obj2]); - expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledTimes(2); - expect(mockLocalDatastore._handleUnPinWithName.mock.calls[0]).toEqual([DEFAULT_PIN, obj1]); - expect(mockLocalDatastore._handleUnPinWithName.mock.calls[1]).toEqual([DEFAULT_PIN, obj2]); + expect(mockLocalDatastore._handleUnPinAllWithName).toHaveBeenCalledTimes(1); + expect(mockLocalDatastore._handleUnPinAllWithName.mock.calls[0]).toEqual([DEFAULT_PIN, [obj1, obj2]]); }); it('can unPinAllObjects', async () => { From 5f3fc5a9e8f4c4996659c95cd8a610131bfed6d9 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 20 Mar 2019 17:21:09 -0500 Subject: [PATCH 09/14] docs fix --- src/LocalDatastore.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 30e5b147b..2e1a3d303 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -24,7 +24,7 @@ import { DEFAULT_PIN, PIN_PREFIX, OBJECT_PREFIX } from './LocalDatastoreUtils'; *
await object.pin();
*
await object.pinWithName('pinName');
* - * Query objects by changing query source + * Query pinned objects * *
query.fromLocalDatastore();
*
query.fromPin();
@@ -88,7 +88,7 @@ const LocalDatastore = { await Promise.all(promises); const pinned = await this.fromPinWithName(pinName) || []; const toPin = [...new Set([...pinned, ...objectKeys])]; - await this.pinWithName(pinName, toPin); + return this.pinWithName(pinName, toPin); }, // Removes object and children keys from pin name @@ -104,8 +104,7 @@ const LocalDatastore = { for (const parent of objects) { const children = this._getChildren(parent); const parentKey = this.getKeyForObject(parent); - objectKeys.push(parentKey); - objectKeys.push(...Object.keys(children)); + objectKeys.push(parentKey, ...Object.keys(children)); } objectKeys = [...new Set(objectKeys)]; @@ -179,12 +178,12 @@ const LocalDatastore = { } } if (!name) { - return Promise.resolve(allObjects); + return allObjects; } const pinName = await this.getPinName(name); const pinned = await this.fromPinWithName(pinName); if (!Array.isArray(pinned)) { - return Promise.resolve([]); + return []; } const objects = pinned.map(async (objectKey) => await this.fromPinWithName(objectKey)); return Promise.all(objects); @@ -233,12 +232,12 @@ const LocalDatastore = { // Update object pin value async _updateObjectIfPinned(object: ParseObject): Promise { if (!this.isEnabled) { - return Promise.resolve(); + return; } const objectKey = this.getKeyForObject(object); const pinned = await this.fromPinWithName(objectKey); if (!pinned) { - return Promise.resolve(); + return; } return this.pinWithName(objectKey, object._toFullJSON()); }, From 868bb26658b58e213f57d32b07e779ada7256f6b Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 20 Mar 2019 19:10:39 -0500 Subject: [PATCH 10/14] optimizations --- src/.flowconfig | 7 ++-- src/LocalDatastore.js | 43 +++++++++++++------------ src/LocalDatastoreController.browser.js | 24 +++++++------- src/ParseObject.js | 4 +-- src/__tests__/LocalDatastore-test.js | 5 ++- 5 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/.flowconfig b/src/.flowconfig index 3c28636fc..2d84ec651 100644 --- a/src/.flowconfig +++ b/src/.flowconfig @@ -1,11 +1,10 @@ [ignore] -.*/node_modules/.* +.*/node_modules/ +.*/lib/ [include] -../package.json [libs] -interfaces/ [options] -unsafe.enable_getters_and_setters=true +suppress_comment= \\(.\\|\n\\)*\\@flow-disable-next \ No newline at end of file diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 2e1a3d303..90343c3f6 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -70,11 +70,8 @@ const LocalDatastore = { // Pin the object and children recursively // Saves the object and children key to Pin Name async _handlePinAllWithName(name: string, objects: Array): Promise { - if (!objects || objects.length === 0) { - return; - } const pinName = this.getPinName(name); - const promises = []; + const toPinPromises = []; const objectKeys = []; for (const parent of objects) { const children = this._getChildren(parent); @@ -82,21 +79,18 @@ const LocalDatastore = { children[parentKey] = parent._toFullJSON(); for (const objectKey in children) { objectKeys.push(objectKey); - promises.push(this.pinWithName(objectKey, children[objectKey])); + toPinPromises.push(this.pinWithName(objectKey, children[objectKey])); } } - await Promise.all(promises); - const pinned = await this.fromPinWithName(pinName) || []; - const toPin = [...new Set([...pinned, ...objectKeys])]; + const fromPinPromise = this.fromPinWithName(pinName); + const [pinned] = await Promise.all([fromPinPromise, toPinPromises]); + const toPin = [...new Set([...(pinned || []), ...objectKeys])]; return this.pinWithName(pinName, toPin); }, // Removes object and children keys from pin name // Keeps the object and children pinned async _handleUnPinAllWithName(name: string, objects: Array) { - if (!objects || objects.length === 0) { - return; - } const localDatastore = await this._getAllContents(); const pinName = this.getPinName(name); const promises = []; @@ -180,13 +174,14 @@ const LocalDatastore = { if (!name) { return allObjects; } - const pinName = await this.getPinName(name); - const pinned = await this.fromPinWithName(pinName); + const pinName = this.getPinName(name); + const pinned = localDatastore[pinName]; if (!Array.isArray(pinned)) { return []; } - const objects = pinned.map(async (objectKey) => await this.fromPinWithName(objectKey)); - return Promise.all(objects); + const promises = pinned.map((objectKey) => this.fromPinWithName(objectKey)); + const objects = await Promise.all(promises); + return objects.filter(object => object != null); }, // Replaces object pointers with pinned pointers @@ -255,7 +250,9 @@ const LocalDatastore = { if (!pin) { return; } - await this.unPinWithName(objectKey); + const promises = [ + this.unPinWithName(objectKey) + ]; delete localDatastore[objectKey]; for (const key in localDatastore) { @@ -264,15 +261,16 @@ const LocalDatastore = { if (pinned.includes(objectKey)) { pinned = pinned.filter(item => item !== objectKey); if (pinned.length == 0) { - await this.unPinWithName(key); + promises.push(this.unPinWithName(key)); delete localDatastore[key]; } else { - await this.pinWithName(key, pinned); + promises.push(this.pinWithName(key, pinned)); localDatastore[key] = pinned; } } } } + return Promise.all(promises); }, // Update pin and references of the unsaved object @@ -287,8 +285,10 @@ const LocalDatastore = { if (!unsaved) { return; } - await this.unPinWithName(localKey); - await this.pinWithName(objectKey, unsaved); + const promises = [ + this.unPinWithName(localKey), + this.pinWithName(objectKey, unsaved), + ]; const localDatastore = await this._getAllContents(); for (const key in localDatastore) { @@ -297,11 +297,12 @@ const LocalDatastore = { if (pinned.includes(localKey)) { pinned = pinned.filter(item => item !== localKey); pinned.push(objectKey); - await this.pinWithName(key, pinned); + promises.push(this.pinWithName(key, pinned)); localDatastore[key] = pinned; } } } + return Promise.all(promises); }, /** diff --git a/src/LocalDatastoreController.browser.js b/src/LocalDatastoreController.browser.js index 9b662c292..12ca2b682 100644 --- a/src/LocalDatastoreController.browser.js +++ b/src/LocalDatastoreController.browser.js @@ -13,16 +13,16 @@ import { isLocalDatastoreKey } from './LocalDatastoreUtils'; const LocalDatastoreController = { - fromPinWithName(name: string): Promise { + fromPinWithName(name: string): Object | Array { const values = localStorage.getItem(name); if (!values) { - return Promise.resolve(null); + return null; } const objects = JSON.parse(values); - return Promise.resolve(objects); + return objects; }, - pinWithName(name: string, value: any): Promise { + pinWithName(name: string, value: any) { try { const values = JSON.stringify(value); localStorage.setItem(name, values); @@ -30,14 +30,13 @@ const LocalDatastoreController = { // Quota exceeded, possibly due to Safari Private Browsing mode console.log(e.message); // eslint-disable-line no-console } - return Promise.resolve(); }, - unPinWithName(name: string): Promise { - return Promise.resolve(localStorage.removeItem(name)); + unPinWithName(name: string) { + localStorage.removeItem(name); }, - getAllContents(): Promise { + getAllContents(): Object { const LDS = {}; for (let i = 0; i < localStorage.length; i += 1) { const key = localStorage.key(i); @@ -46,20 +45,20 @@ const LocalDatastoreController = { LDS[key] = JSON.parse(value); } } - return Promise.resolve(LDS); + return LDS; }, - getRawStorage(): Promise { + getRawStorage(): Object { const storage = {}; for (let i = 0; i < localStorage.length; i += 1) { const key = localStorage.key(i); const value = localStorage.getItem(key); storage[key] = value; } - return Promise.resolve(storage); + return storage; }, - clear(): Promise { + clear(): void { const toRemove = []; for (let i = 0; i < localStorage.length; i += 1) { const key = localStorage.key(i); @@ -70,7 +69,6 @@ const LocalDatastoreController = { for (const key of toRemove) { localStorage.removeItem(key); } - return Promise.resolve(); } }; diff --git a/src/ParseObject.js b/src/ParseObject.js index 7b1fb6b66..9e8d1126a 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1241,9 +1241,9 @@ class ParseObject { * const isPinned = await object.isPinned(); * * - * @return {Promise} A boolean promise that is fulfilled if object is pinned. + * @return {Promise} A boolean promise that is fulfilled if object is pinned. */ - async isPinned(): Promise { + async isPinned(): Promise { const localDatastore = CoreManager.getLocalDatastore(); if (!localDatastore.isEnabled) { return Promise.reject('Parse.enableLocalDatastore() must be called first'); diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index 549a59ba8..4ac655146 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -397,12 +397,11 @@ describe('LocalDatastore', () => { [obj1.id]: obj1._toFullJSON(), [obj2.id]: obj2._toFullJSON(), [obj3.id]: obj3._toFullJSON(), - testPin: [obj1.id, obj2.id, obj3.id], + [PIN_PREFIX + 'testPin']: [obj1.id, obj2.id, obj3.id], }; mockLocalStorageController .fromPinWithName - .mockImplementationOnce(() => LDS.testPin) .mockImplementationOnce(() => LDS[obj1.id]) .mockImplementationOnce(() => LDS[obj2.id]) .mockImplementationOnce(() => LDS[obj3.id]); @@ -415,7 +414,7 @@ describe('LocalDatastore', () => { expect(results).toEqual([obj1._toFullJSON(), obj2._toFullJSON(), obj3._toFullJSON()]); expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.fromPinWithName).toHaveBeenCalledTimes(4); + expect(mockLocalStorageController.fromPinWithName).toHaveBeenCalledTimes(3); }); it('_serializeObject no children', async () => { From 21e93353e64c2cb8bd7ddd32ae026f7e13c4b9f6 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 21 Mar 2019 12:10:32 -0500 Subject: [PATCH 11/14] remove unused async --- src/__tests__/LocalDatastore-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index 4ac655146..fabadd8c9 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -711,7 +711,7 @@ describe('LocalDatastore', () => { }); }); -describe('BrowserDatastoreController', async () => { +describe('BrowserDatastoreController', () => { beforeEach(async () => { await BrowserDatastoreController.clear(); }); From d0a56adceab4e1b6872431a30b3806dc616014ad Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 21 Mar 2019 18:08:47 -0500 Subject: [PATCH 12/14] consistency with storing array values in LDS --- integration/test/ParseLocalDatastoreTest.js | 186 +++++++++---------- src/.flowconfig | 2 +- src/LocalDatastore.js | 21 ++- src/LocalDatastoreController.browser.js | 6 +- src/LocalDatastoreController.default.js | 4 +- src/LocalDatastoreController.react-native.js | 24 +-- src/ParseObject.js | 5 +- src/StorageController.react-native.js | 2 +- src/__tests__/LocalDatastore-test.js | 78 ++++---- src/__tests__/ParseObject-test.js | 4 +- 10 files changed, 163 insertions(+), 169 deletions(-) diff --git a/integration/test/ParseLocalDatastoreTest.js b/integration/test/ParseLocalDatastoreTest.js index 6e5a3b251..cbf9c2428 100644 --- a/integration/test/ParseLocalDatastoreTest.js +++ b/integration/test/ParseLocalDatastoreTest.js @@ -75,13 +75,13 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(object)]); - assert.deepEqual(localDatastore[LDS_KEY(object)], object._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(object)], [object._toFullJSON()]); await object.save(); // Check if localDatastore updated localId to objectId localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(object)]); - assert.deepEqual(localDatastore[LDS_KEY(object)], object._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(object)], [object._toFullJSON()]); }); it(`${controller.name} cannot pin unsaved pointer`, async () => { @@ -101,10 +101,10 @@ function runTest(controller) { await object.save(); await object.pin(); const localDatastore = await Parse.LocalDatastore._getAllContents(); - const cachedObject = localDatastore[LDS_KEY(object)]; + const cachedObject = localDatastore[LDS_KEY(object)][0]; assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(object)]); - assert.deepEqual(localDatastore[LDS_KEY(object)], object._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(object)], [object._toFullJSON()]); assert.equal(cachedObject.objectId, object.id); assert.equal(cachedObject.field, 'test'); }); @@ -119,10 +119,10 @@ function runTest(controller) { await object.save(); const localDatastore = await Parse.LocalDatastore._getAllContents(); - const cachedObject = localDatastore[LDS_KEY(object)]; + const cachedObject = localDatastore[LDS_KEY(object)][0]; assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(object)]); - assert.deepEqual(localDatastore[LDS_KEY(object)], object._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(object)], [object._toFullJSON()]); assert.equal(cachedObject.objectId, object.id); assert.equal(cachedObject.field, 'new info'); }); @@ -158,9 +158,9 @@ function runTest(controller) { assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(parent)), true); assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(child)), true); assert.equal(localDatastore[DEFAULT_PIN].includes(LDS_KEY(grandchild)), true); - assert.deepEqual(localDatastore[LDS_KEY(parent)], parent._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(child)], child._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(grandchild)], grandchild._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(parent)], [parent._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(child)], [child._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(grandchild)], [grandchild._toFullJSON()]); }); it(`${controller.name} can pinAll (unsaved)`, async () => { @@ -174,18 +174,18 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can pinAll (saved)`, async () => { @@ -200,9 +200,9 @@ function runTest(controller) { const localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can pinAllWithName (unsaved)`, async () => { @@ -216,18 +216,18 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can pinAllWithName (saved)`, async () => { @@ -241,9 +241,9 @@ function runTest(controller) { const localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_pin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPin on destroy`, async () => { @@ -315,25 +315,25 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await obj2.unPin(); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPin with pinAll (saved)`, async () => { @@ -347,9 +347,9 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); @@ -358,8 +358,8 @@ function runTest(controller) { localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPin / unPinAll without pin (unsaved)`, async () => { @@ -407,23 +407,23 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 4); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.unPinAll([obj1, obj2]); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPinAll (saved)`, async () => { @@ -437,9 +437,9 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); @@ -447,7 +447,7 @@ function runTest(controller) { localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPinAllObjects (unsaved)`, async () => { @@ -461,25 +461,25 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 4); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.unPinAllObjects(); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPinAllObjects (saved)`, async () => { @@ -493,18 +493,18 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[DEFAULT_PIN], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); Parse.Object.unPinAllObjects(); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPinAllWithName (unsaved)`, async () => { @@ -518,23 +518,23 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 4); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.unPinAllWithName('test_unpin', [obj1, obj2]); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPinAllWithName (saved)`, async () => { @@ -548,9 +548,9 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); @@ -558,7 +558,7 @@ function runTest(controller) { localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 2); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPinAllObjectsWithName (unsaved)`, async () => { @@ -572,25 +572,25 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert(Object.keys(localDatastore).length === 4); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.unPinAllObjectsWithName('test_unpin'); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPinAllObjectsWithName (saved)`, async () => { @@ -604,18 +604,18 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 4); assert.deepEqual(localDatastore[PIN_PREFIX + 'test_unpin'], [LDS_KEY(obj1), LDS_KEY(obj2), LDS_KEY(obj3)]); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); await Parse.Object.saveAll(objects); await Parse.Object.unPinAllObjectsWithName('test_unpin'); localDatastore = await Parse.LocalDatastore._getAllContents(); assert.equal(Object.keys(localDatastore).length, 3); - assert.deepEqual(localDatastore[LDS_KEY(obj1)], obj1._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj2)], obj2._toFullJSON()); - assert.deepEqual(localDatastore[LDS_KEY(obj3)], obj3._toFullJSON()); + assert.deepEqual(localDatastore[LDS_KEY(obj1)], [obj1._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj2)], [obj2._toFullJSON()]); + assert.deepEqual(localDatastore[LDS_KEY(obj3)], [obj3._toFullJSON()]); }); it(`${controller.name} can unPin and save reference`, async () => { @@ -802,7 +802,7 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); - assert.equal(localDatastore[LDS_KEY(item)].foo, 'bar'); + assert.equal(localDatastore[LDS_KEY(item)][0].foo, 'bar'); const itemAgain = new Item(); itemAgain.id = item.id; @@ -812,7 +812,7 @@ function runTest(controller) { assert.equal(itemAgain.get('foo'), 'changed'); assert.equal(fetchedItem.get('foo'), 'changed'); - assert.equal(localDatastore[LDS_KEY(item)].foo, 'changed'); + assert.equal(localDatastore[LDS_KEY(item)][0].foo, 'changed'); }); it('fetchAll updates LocalDatastore', async () => { @@ -829,8 +829,8 @@ function runTest(controller) { let localDatastore = await Parse.LocalDatastore._getAllContents(); - assert.equal(localDatastore[LDS_KEY(item1)].foo, 'bar'); - assert.equal(localDatastore[LDS_KEY(item2)].foo, 'baz'); + assert.equal(localDatastore[LDS_KEY(item1)][0].foo, 'bar'); + assert.equal(localDatastore[LDS_KEY(item2)][0].foo, 'baz'); const item1Again = new Item(); item1Again.id = item1.id; @@ -845,8 +845,8 @@ function runTest(controller) { assert.equal(item2Again.get('foo'), 'changed'); assert.equal(fetchedItems[0].get('foo'), 'changed'); assert.equal(fetchedItems[1].get('foo'), 'changed'); - assert.equal(localDatastore[LDS_KEY(fetchedItems[0])].foo, 'changed'); - assert.equal(localDatastore[LDS_KEY(fetchedItems[1])].foo, 'changed'); + assert.equal(localDatastore[LDS_KEY(fetchedItems[0])][0].foo, 'changed'); + assert.equal(localDatastore[LDS_KEY(fetchedItems[1])][0].foo, 'changed'); }); it(`${controller.name} can update Local Datastore from network`, async () => { diff --git a/src/.flowconfig b/src/.flowconfig index 2d84ec651..e36f40442 100644 --- a/src/.flowconfig +++ b/src/.flowconfig @@ -7,4 +7,4 @@ [libs] [options] -suppress_comment= \\(.\\|\n\\)*\\@flow-disable-next \ No newline at end of file +suppress_comment= \\(.\\|\n\\)*\\@flow-disable-next diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index 90343c3f6..d09ca434f 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -79,7 +79,7 @@ const LocalDatastore = { children[parentKey] = parent._toFullJSON(); for (const objectKey in children) { objectKeys.push(objectKey); - toPinPromises.push(this.pinWithName(objectKey, children[objectKey])); + toPinPromises.push(this.pinWithName(objectKey, [children[objectKey]])); } } const fromPinPromise = this.fromPinWithName(pinName); @@ -168,7 +168,7 @@ const LocalDatastore = { const allObjects = []; for (const key in localDatastore) { if (key.startsWith(OBJECT_PREFIX)) { - allObjects.push(localDatastore[key]); + allObjects.push(localDatastore[key][0]); } } if (!name) { @@ -180,7 +180,8 @@ const LocalDatastore = { return []; } const promises = pinned.map((objectKey) => this.fromPinWithName(objectKey)); - const objects = await Promise.all(promises); + let objects = await Promise.all(promises); + objects = [].concat(...objects); return objects.filter(object => object != null); }, @@ -192,10 +193,10 @@ const LocalDatastore = { if (!LDS) { LDS = await this._getAllContents(); } - const root = LDS[objectKey]; - if (!root) { + if (!LDS[objectKey] || LDS[objectKey].length === 0) { return null; } + const root = LDS[objectKey][0]; const queue = []; const meta = {}; @@ -210,8 +211,8 @@ const LocalDatastore = { const value = subTreeRoot[field]; if (value.__type && value.__type === 'Object') { const key = this.getKeyForObject(value); - const pointer = LDS[key]; - if (pointer) { + if (LDS[key] && LDS[key].length > 0) { + const pointer = LDS[key][0]; uniqueId++; meta[uniqueId] = pointer; subTreeRoot[field] = pointer; @@ -231,10 +232,10 @@ const LocalDatastore = { } const objectKey = this.getKeyForObject(object); const pinned = await this.fromPinWithName(objectKey); - if (!pinned) { + if (pinned && pinned.length === 0) { return; } - return this.pinWithName(objectKey, object._toFullJSON()); + return this.pinWithName(objectKey, [object._toFullJSON()]); }, // Called when object is destroyed @@ -282,7 +283,7 @@ const LocalDatastore = { const objectKey = this.getKeyForObject(object); const unsaved = await this.fromPinWithName(localKey); - if (!unsaved) { + if (unsaved && unsaved.length === 0) { return; } const promises = [ diff --git a/src/LocalDatastoreController.browser.js b/src/LocalDatastoreController.browser.js index 12ca2b682..60a90dd72 100644 --- a/src/LocalDatastoreController.browser.js +++ b/src/LocalDatastoreController.browser.js @@ -13,10 +13,10 @@ import { isLocalDatastoreKey } from './LocalDatastoreUtils'; const LocalDatastoreController = { - fromPinWithName(name: string): Object | Array { + fromPinWithName(name: string): Array { const values = localStorage.getItem(name); if (!values) { - return null; + return []; } const objects = JSON.parse(values); return objects; @@ -28,7 +28,7 @@ const LocalDatastoreController = { localStorage.setItem(name, values); } catch (e) { // Quota exceeded, possibly due to Safari Private Browsing mode - console.log(e.message); // eslint-disable-line no-console + console.log(e.message); } }, diff --git a/src/LocalDatastoreController.default.js b/src/LocalDatastoreController.default.js index 35724be62..524472169 100644 --- a/src/LocalDatastoreController.default.js +++ b/src/LocalDatastoreController.default.js @@ -12,9 +12,9 @@ import { isLocalDatastoreKey } from './LocalDatastoreUtils'; const memMap = {}; const LocalDatastoreController = { - fromPinWithName(name: string) { + fromPinWithName(name: string): Array { if (!memMap.hasOwnProperty(name)) { - return null; + return []; } const objects = JSON.parse(memMap[name]); return objects; diff --git a/src/LocalDatastoreController.react-native.js b/src/LocalDatastoreController.react-native.js index 70e3b1bad..848d4b130 100644 --- a/src/LocalDatastoreController.react-native.js +++ b/src/LocalDatastoreController.react-native.js @@ -13,10 +13,10 @@ const RNStorage = require('./StorageController.react-native'); import { isLocalDatastoreKey } from './LocalDatastoreUtils'; const LocalDatastoreController = { - async fromPinWithName(name: string): Promise { + async fromPinWithName(name: string): Promise> { const values = await RNStorage.getItemAsync(name); if (!values) { - return null; + return []; } const objects = JSON.parse(values); return objects; @@ -75,20 +75,16 @@ const LocalDatastoreController = { return storage; }, - async clear(): void { - try { - const keys = await RNStorage.getAllKeys(); - const batch = []; - for (let i = 0; i < keys.length; i += 1) { - const key = keys[i]; - if (isLocalDatastoreKey(key)) { - batch.push(key); - } + async clear(): Promise { + const keys = await RNStorage.getAllKeys(); + const batch = []; + for (let i = 0; i < keys.length; i += 1) { + const key = keys[i]; + if (isLocalDatastoreKey(key)) { + batch.push(key); } - await RNStorage.multiRemove(batch); - } catch (error) { - console.error('Error clearing local datastore', error); } + return RNStorage.multiRemove(batch).catch(error => console.error('Error clearing local datastore: ', error)); } }; diff --git a/src/ParseObject.js b/src/ParseObject.js index 9e8d1126a..90c7fb780 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1250,10 +1250,7 @@ class ParseObject { } const objectKey = localDatastore.getKeyForObject(this); const pin = await localDatastore.fromPinWithName(objectKey); - if (pin) { - return true; - } - return false; + return pin.length > 0; } /** diff --git a/src/StorageController.react-native.js b/src/StorageController.react-native.js index 1c69d4132..dbb3f1b4e 100644 --- a/src/StorageController.react-native.js +++ b/src/StorageController.react-native.js @@ -66,7 +66,7 @@ const StorageController = { }); }, - multiGet(keys: Array): Promise { + multiGet(keys: Array): Promise>> { return new Promise((resolve, reject) => { this.getAsyncStorage().multiGet(keys, function(err, result) { if (err) { diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index fabadd8c9..a80c3717e 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -155,19 +155,19 @@ describe('LocalDatastore', () => { expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); }); - it('_handlePinWithName no children', async () => { + it('_handleUnPinAllWithName no children', async () => { const object = new ParseObject('Item'); await LocalDatastore._handlePinAllWithName('test_pin', [object]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); - it('_handlePinWithName default pin', async () => { + it('_handleUnPinAllWithName default pin', async () => { const object = new ParseObject('Item'); await LocalDatastore._handlePinAllWithName(DEFAULT_PIN, [object]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); - it('_handlePinWithName unsaved children', async () => { + it('_handleUnPinAllWithName unsaved children', async () => { const parent = new ParseObject('Item'); const unsaved = { className: 'Item', __type: 'Object' }; parent.set('child', unsaved); @@ -175,7 +175,7 @@ describe('LocalDatastore', () => { expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); - it('_handlePinWithName with children', async () => { + it('_handleUnPinAllWithName with children', async () => { const parent = new ParseObject('Item'); const child = new ParseObject('Item'); const grandchild = new ParseObject('Item'); @@ -240,8 +240,8 @@ describe('LocalDatastore', () => { const LDS = { [PIN_PREFIX + 'test_pin']: objects, [PIN_PREFIX + 'test_pin_2']: objects, - [KEY1]: item1._toFullJSON(), - [KEY2]: item2._toFullJSON(), + [KEY1]: [item1._toFullJSON()], + [KEY2]: [item2._toFullJSON()], } mockLocalStorageController .getAllContents @@ -272,7 +272,7 @@ describe('LocalDatastore', () => { expect(mockLocalStorageController.fromPinWithName.mock.results[0].value).toEqual([item1]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(1); - expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(KEY1, item1._toFullJSON()); + expect(mockLocalStorageController.pinWithName).toHaveBeenCalledWith(KEY1, [item1._toFullJSON()]); }); it('_updateLocalIdForObject not pinned', async () => { @@ -287,7 +287,7 @@ describe('LocalDatastore', () => { const localKey = `${OBJECT_PREFIX}Item_${localId}`; const LDS = { [DEFAULT_PIN]: [localKey], - [localKey]: json, + [localKey]: [json], }; mockLocalStorageController .fromPinWithName @@ -308,7 +308,7 @@ describe('LocalDatastore', () => { const localKey = `${OBJECT_PREFIX}Item_${localId}`; const LDS = { [PIN_PREFIX + 'test_pin']: [localKey], - [localKey]: json, + [localKey]: [json], }; mockLocalStorageController .fromPinWithName @@ -328,7 +328,7 @@ describe('LocalDatastore', () => { const localId = 'local' + object.id; const LDS = { [DEFAULT_PIN]: [object.id], - [object.id]: json, + [object.id]: [json], }; mockLocalStorageController .fromPinWithName @@ -356,7 +356,7 @@ describe('LocalDatastore', () => { const json = item1._toFullJSON(); const LDS = { [DEFAULT_PIN]: [KEY1], - [KEY1]: json, + [KEY1]: [json], }; mockLocalStorageController @@ -374,7 +374,7 @@ describe('LocalDatastore', () => { const json = object._toFullJSON(); const LDS = { [DEFAULT_PIN]: [object.id, 'local10', 'local11'], - [object.id]: json, + [object.id]: [json], randomName: [object.id], }; @@ -394,9 +394,9 @@ describe('LocalDatastore', () => { const obj3 = new ParseObject('Item'); const LDS = { - [obj1.id]: obj1._toFullJSON(), - [obj2.id]: obj2._toFullJSON(), - [obj3.id]: obj3._toFullJSON(), + [obj1.id]: [obj1._toFullJSON()], + [obj2.id]: [obj2._toFullJSON()], + [obj3.id]: [obj3._toFullJSON()], [PIN_PREFIX + 'testPin']: [obj1.id, obj2.id, obj3.id], }; @@ -424,7 +424,7 @@ describe('LocalDatastore', () => { const objectKey = `Item_1234`; const LDS = { [DEFAULT_PIN]: [objectKey], - [objectKey]: json, + [objectKey]: [json], }; mockLocalStorageController @@ -462,8 +462,8 @@ describe('LocalDatastore', () => { const childKey = LocalDatastore.getKeyForObject(child); const LDS = { [DEFAULT_PIN]: [parentKey, childKey], - [parentKey]: parent._toFullJSON(), - [childKey]: childJSON, + [parentKey]: [parent._toFullJSON()], + [childKey]: [childJSON], }; mockLocalStorageController @@ -490,8 +490,8 @@ describe('LocalDatastore', () => { jest.clearAllMocks(); LDS = { - [KEY1]: item1._toFullJSON(), - [KEY2]: item2._toFullJSON(), + [KEY1]: [item1._toFullJSON()], + [KEY2]: [item2._toFullJSON()], [DEFAULT_PIN]: [], }; @@ -510,8 +510,8 @@ describe('LocalDatastore', () => { it('_destroyObjectIfPinned no objects found in pinName remove pinName', async () => { const LDS = { - [KEY1]: item1._toFullJSON(), - [KEY2]: item2._toFullJSON(), + [KEY1]: [item1._toFullJSON()], + [KEY2]: [item2._toFullJSON()], [PIN_PREFIX + 'Custom_Pin']: [KEY2], [DEFAULT_PIN]: [KEY2], }; @@ -531,8 +531,8 @@ describe('LocalDatastore', () => { it('_destroyObjectIfPinned', async () => { const LDS = { - [KEY1]: item1._toFullJSON(), - [KEY2]: item2._toFullJSON(), + [KEY1]: [item1._toFullJSON()], + [KEY2]: [item2._toFullJSON()], [DEFAULT_PIN]: [KEY1, KEY2], }; @@ -613,7 +613,7 @@ describe('LocalDatastore', () => { LocalDatastore.isEnabled = true; LocalDatastore.isSyncing = false; const LDS = { - [KEY1]: item1._toFullJSON(), + [KEY1]: [item1._toFullJSON()], [`${PIN_PREFIX}_testPinName`]: [KEY1], [DEFAULT_PIN]: [KEY1], }; @@ -640,7 +640,7 @@ describe('LocalDatastore', () => { LocalDatastore.isEnabled = true; LocalDatastore.isSyncing = false; const LDS = { - [KEY1]: item1._toFullJSON(), + [KEY1]: [item1._toFullJSON()], [`${PIN_PREFIX}_testPinName`]: [KEY1], [DEFAULT_PIN]: [KEY1], }; @@ -673,9 +673,9 @@ describe('LocalDatastore', () => { LocalDatastore.isSyncing = false; const testObject = new ParseObject('TestObject'); const LDS = { - [KEY1]: item1._toFullJSON(), - [KEY2]: item2._toFullJSON(), - [LocalDatastore.getKeyForObject(testObject)]: testObject._toFullJSON(), + [KEY1]: [item1._toFullJSON()], + [KEY2]: [item2._toFullJSON()], + [LocalDatastore.getKeyForObject(testObject)]: [testObject._toFullJSON()], [`${PIN_PREFIX}_testPinName`]: [KEY1], [DEFAULT_PIN]: [KEY1], }; @@ -725,7 +725,7 @@ describe('BrowserDatastoreController', () => { }); it('can store and retrieve values', async () => { - expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual(null); + expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual([]); await BrowserDatastoreController.pinWithName(KEY1, [item1._toFullJSON()]); expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); expect(await BrowserDatastoreController.getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); @@ -735,7 +735,7 @@ describe('BrowserDatastoreController', () => { await BrowserDatastoreController.pinWithName(KEY1, [item1._toFullJSON()]); expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); await BrowserDatastoreController.unPinWithName(KEY1); - expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual(null); + expect(await BrowserDatastoreController.fromPinWithName(KEY1)).toEqual([]); expect(await BrowserDatastoreController.getAllContents()).toEqual({}); }); }); @@ -754,7 +754,7 @@ describe('DefaultDatastoreController', () => { }); it('can store and retrieve values', async () => { - expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual(null); + expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual([]); await DefaultDatastoreController.pinWithName(KEY1, [item1._toFullJSON()]); expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); expect(await DefaultDatastoreController.getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); @@ -764,7 +764,7 @@ describe('DefaultDatastoreController', () => { await DefaultDatastoreController.pinWithName(KEY1, [item1._toFullJSON()]); expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); await DefaultDatastoreController.unPinWithName(KEY1); - expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual(null); + expect(await DefaultDatastoreController.fromPinWithName(KEY1)).toEqual([]); expect(await DefaultDatastoreController.getAllContents()).toEqual({}); }); }); @@ -776,7 +776,7 @@ describe('LocalDatastore (BrowserDatastoreController)', () => { }); it('can store and retrieve values', async () => { - expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([]); await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); expect(await LocalDatastore._getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); @@ -787,7 +787,7 @@ describe('LocalDatastore (BrowserDatastoreController)', () => { await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); await LocalDatastore.unPinWithName(KEY1); - expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([]); expect(await LocalDatastore._getAllContents()).toEqual({}); expect(await LocalDatastore._getRawStorage()).toEqual({}); }); @@ -816,7 +816,7 @@ describe('LocalDatastore (DefaultDatastoreController)', () => { }); it('can store and retrieve values', async () => { - expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([]); await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); expect(await LocalDatastore._getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); @@ -827,7 +827,7 @@ describe('LocalDatastore (DefaultDatastoreController)', () => { await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); await LocalDatastore.unPinWithName(KEY1); - expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([]); expect(await LocalDatastore._getAllContents()).toEqual({}); expect(await LocalDatastore._getRawStorage()).toEqual({}); }); @@ -841,7 +841,7 @@ describe('LocalDatastore (RNDatastoreController)', () => { }); it('can store and retrieve values', async () => { - expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([]); await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); expect(await LocalDatastore._getAllContents()).toEqual({ [KEY1]: [item1._toFullJSON()] }); @@ -852,7 +852,7 @@ describe('LocalDatastore (RNDatastoreController)', () => { await LocalDatastore.pinWithName(KEY1, [item1._toFullJSON()]); expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([item1._toFullJSON()]); await LocalDatastore.unPinWithName(KEY1); - expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual(null); + expect(await LocalDatastore.fromPinWithName(KEY1)).toEqual([]); expect(await LocalDatastore._getAllContents()).toEqual({}); expect(await LocalDatastore._getRawStorage()).toEqual({}); }); diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index 4ed580440..e27a14fdd 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -2645,9 +2645,9 @@ describe('ParseObject pin', () => { mockLocalDatastore .fromPinWithName .mockImplementationOnce(() => { - return { 'Item_1234': object._toFullJSON() } + return [object._toFullJSON()]; }) - .mockImplementationOnce(() => null); + .mockImplementationOnce(() => []); let isPinned = await object.isPinned(); expect(isPinned).toEqual(true); From 4f2d1c00a7155fd051d59e8337316d692b895283 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 21 Mar 2019 18:25:09 -0500 Subject: [PATCH 13/14] fix logic --- src/LocalDatastore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LocalDatastore.js b/src/LocalDatastore.js index d09ca434f..0afe3c6d3 100644 --- a/src/LocalDatastore.js +++ b/src/LocalDatastore.js @@ -232,7 +232,7 @@ const LocalDatastore = { } const objectKey = this.getKeyForObject(object); const pinned = await this.fromPinWithName(objectKey); - if (pinned && pinned.length === 0) { + if (!pinned || pinned.length === 0) { return; } return this.pinWithName(objectKey, [object._toFullJSON()]); @@ -283,7 +283,7 @@ const LocalDatastore = { const objectKey = this.getKeyForObject(object); const unsaved = await this.fromPinWithName(localKey); - if (unsaved && unsaved.length === 0) { + if (!unsaved || unsaved.length === 0) { return; } const promises = [ From 6c9615673b6d0263efc9c164a43e3a134df1a964 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Fri, 22 Mar 2019 19:02:47 -0500 Subject: [PATCH 14/14] quick fix --- src/LocalDatastoreController.browser.js | 7 +++---- src/LocalDatastoreController.react-native.js | 4 ++-- src/__tests__/LocalDatastore-test.js | 8 ++++---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/LocalDatastoreController.browser.js b/src/LocalDatastoreController.browser.js index 60a90dd72..5b7333386 100644 --- a/src/LocalDatastoreController.browser.js +++ b/src/LocalDatastoreController.browser.js @@ -58,7 +58,7 @@ const LocalDatastoreController = { return storage; }, - clear(): void { + clear(): Promise { const toRemove = []; for (let i = 0; i < localStorage.length; i += 1) { const key = localStorage.key(i); @@ -66,9 +66,8 @@ const LocalDatastoreController = { toRemove.push(key); } } - for (const key of toRemove) { - localStorage.removeItem(key); - } + const promises = toRemove.map(localStorage.removeItem); + return Promise.all(promises); } }; diff --git a/src/LocalDatastoreController.react-native.js b/src/LocalDatastoreController.react-native.js index 848d4b130..00ef16ff9 100644 --- a/src/LocalDatastoreController.react-native.js +++ b/src/LocalDatastoreController.react-native.js @@ -53,7 +53,7 @@ const LocalDatastoreController = { console.error('Error getAllContents: ', error); return {}; } - results.map((pair) => { + results.forEach((pair) => { const [key, value] = pair; try { LDS[key] = JSON.parse(value); @@ -64,7 +64,7 @@ const LocalDatastoreController = { return LDS; }, - async getRawStorage(): Promise { + async getRawStorage(): Promise { const keys = await RNStorage.getAllKeys(); const storage = {}; const results = await RNStorage.multiGet(keys); diff --git a/src/__tests__/LocalDatastore-test.js b/src/__tests__/LocalDatastore-test.js index a80c3717e..6c326ef4c 100644 --- a/src/__tests__/LocalDatastore-test.js +++ b/src/__tests__/LocalDatastore-test.js @@ -155,19 +155,19 @@ describe('LocalDatastore', () => { expect(mockLocalStorageController.getAllContents).toHaveBeenCalledTimes(1); }); - it('_handleUnPinAllWithName no children', async () => { + it('_handlePinAllWithName no children', async () => { const object = new ParseObject('Item'); await LocalDatastore._handlePinAllWithName('test_pin', [object]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); - it('_handleUnPinAllWithName default pin', async () => { + it('_handlePinAllWithName default pin', async () => { const object = new ParseObject('Item'); await LocalDatastore._handlePinAllWithName(DEFAULT_PIN, [object]); expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); - it('_handleUnPinAllWithName unsaved children', async () => { + it('_handlePinAllWithName unsaved children', async () => { const parent = new ParseObject('Item'); const unsaved = { className: 'Item', __type: 'Object' }; parent.set('child', unsaved); @@ -175,7 +175,7 @@ describe('LocalDatastore', () => { expect(mockLocalStorageController.pinWithName).toHaveBeenCalledTimes(2); }); - it('_handleUnPinAllWithName with children', async () => { + it('_handlePinAllWithName with children', async () => { const parent = new ParseObject('Item'); const child = new ParseObject('Item'); const grandchild = new ParseObject('Item');