-
-
Notifications
You must be signed in to change notification settings - Fork 598
LocalDatastore fixes for React-Native #753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f25edda
f592d16
ef60559
f3a01e8
16e270b
54a5e5f
5ce846d
ce0cbb9
9c7dc51
5f3fc5a
868bb26
b953906
21e9335
5745166
d0a56ad
4f2d1c0
8df047a
6c96156
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,10 +13,28 @@ import CoreManager from './CoreManager'; | |
|
||
import type ParseObject from './ParseObject'; | ||
import ParseQuery from './ParseQuery'; | ||
import { DEFAULT_PIN, PIN_PREFIX, OBJECT_PREFIX } from './LocalDatastoreUtils'; | ||
|
||
const DEFAULT_PIN = '_default'; | ||
const PIN_PREFIX = 'parsePin_'; | ||
|
||
/** | ||
* Provides a local datastore which can be used to store and retrieve <code>Parse.Object</code>. <br /> | ||
* To enable this functionality, call <code>Parse.enableLocalDatastore()</code>. | ||
* | ||
* Pin object to add to local datastore | ||
* | ||
* <pre>await object.pin();</pre> | ||
* <pre>await object.pinWithName('pinName');</pre> | ||
* | ||
* Query pinned objects | ||
* | ||
* <pre>query.fromLocalDatastore();</pre> | ||
* <pre>query.fromPin();</pre> | ||
* <pre>query.fromPinWithName();</pre> | ||
* | ||
* <pre>const localObjects = await query.find();</pre> | ||
* | ||
* @class Parse.LocalDatastore | ||
* @static | ||
*/ | ||
const LocalDatastore = { | ||
fromPinWithName(name: string): Promise { | ||
const controller = CoreManager.getLocalDatastoreController(); | ||
|
@@ -38,44 +56,62 @@ 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(); | ||
}, | ||
|
||
// 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<ParseObject>): Promise { | ||
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 toPinPromises = []; | ||
const objectKeys = []; | ||
dplewis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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); | ||
toPinPromises.push(this.pinWithName(objectKey, [children[objectKey]])); | ||
} | ||
} | ||
const pinned = await this.fromPinWithName(pinName) || []; | ||
const objectIds = Object.keys(objects); | ||
const toPin = [...new Set([...pinned, ...objectIds])]; | ||
await this.pinWithName(pinName, toPin); | ||
const fromPinPromise = this.fromPinWithName(pinName); | ||
const [pinned] = await Promise.all([fromPinPromise, toPinPromises]); | ||
const toPin = [...new Set([...(pinned || []), ...objectKeys])]; | ||
return this.pinWithName(pinName, toPin); | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. take 2 at using reduce here, this time with your code. Untested as I'm not setup to test. Just for fun,
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried that but for some reason to get the toPins I would have to call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd have to be able to step through this with a debugger to see. One thing I'm curious about is why I'm assuming that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getChildren returns key -> object and not key -> [object] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having another pair of eyes debugging this is really helpful |
||
|
||
// 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<ParseObject>) { | ||
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, ...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)) { | ||
|
@@ -87,9 +123,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 | ||
|
@@ -130,20 +167,22 @@ const LocalDatastore = { | |
const localDatastore = await this._getAllContents(); | ||
const allObjects = []; | ||
for (const key in localDatastore) { | ||
if (key !== DEFAULT_PIN && !key.startsWith(PIN_PREFIX)) { | ||
allObjects.push(localDatastore[key]); | ||
if (key.startsWith(OBJECT_PREFIX)) { | ||
allObjects.push(localDatastore[key][0]); | ||
} | ||
} | ||
if (!name) { | ||
return Promise.resolve(allObjects); | ||
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 Promise.resolve([]); | ||
return []; | ||
} | ||
const objects = pinned.map(async (objectKey) => await this.fromPinWithName(objectKey)); | ||
return Promise.all(objects); | ||
const promises = pinned.map((objectKey) => this.fromPinWithName(objectKey)); | ||
let objects = await Promise.all(promises); | ||
objects = [].concat(...objects); | ||
return objects.filter(object => object != null); | ||
dplewis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
|
||
// Replaces object pointers with pinned pointers | ||
|
@@ -154,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 = {}; | ||
|
@@ -172,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; | ||
|
@@ -187,15 +226,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; | ||
} | ||
const objectKey = this.getKeyForObject(object); | ||
const pinned = await this.fromPinWithName(objectKey); | ||
if (pinned) { | ||
await this.pinWithName(objectKey, object._toFullJSON()); | ||
if (!pinned || pinned.length === 0) { | ||
return; | ||
} | ||
return this.pinWithName(objectKey, [object._toFullJSON()]); | ||
}, | ||
|
||
// Called when object is destroyed | ||
|
@@ -211,7 +251,9 @@ const LocalDatastore = { | |
if (!pin) { | ||
return; | ||
} | ||
await this.unPinWithName(objectKey); | ||
const promises = [ | ||
this.unPinWithName(objectKey) | ||
]; | ||
delete localDatastore[objectKey]; | ||
|
||
for (const key in localDatastore) { | ||
|
@@ -220,31 +262,34 @@ 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)); | ||
dplewis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
async _updateLocalIdForObject(localId, object: ParseObject) { | ||
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); | ||
if (!unsaved) { | ||
if (!unsaved || unsaved.length === 0) { | ||
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) { | ||
|
@@ -253,11 +298,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); | ||
}, | ||
|
||
/** | ||
|
@@ -266,7 +312,8 @@ const LocalDatastore = { | |
* <pre> | ||
* await Parse.LocalDatastore.updateFromServer(); | ||
* </pre> | ||
* | ||
* @method updateFromServer | ||
* @name Parse.LocalDatastore.updateFromServer | ||
* @static | ||
*/ | ||
async updateFromServer() { | ||
|
@@ -276,7 +323,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 +333,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 +361,14 @@ 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.error('Error syncing LocalDatastore: ', error); | ||
dplewis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.isSyncing = false; | ||
} | ||
}, | ||
|
||
getKeyForObject(object: any) { | ||
const objectId = object.objectId || object._getId(); | ||
return `${object.className}_${objectId}`; | ||
return `${OBJECT_PREFIX}${object.className}_${objectId}`; | ||
}, | ||
|
||
getPinName(pinName: ?string) { | ||
|
@@ -333,14 +380,12 @@ 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; | ||
} | ||
}; | ||
|
||
LocalDatastore.DEFAULT_PIN = DEFAULT_PIN; | ||
LocalDatastore.PIN_PREFIX = PIN_PREFIX; | ||
LocalDatastore.isEnabled = false; | ||
LocalDatastore.isSyncing = false; | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.