@@ -13,10 +13,28 @@ import CoreManager from './CoreManager';
1313
1414import type ParseObject from './ParseObject' ;
1515import ParseQuery from './ParseQuery' ;
16+ import { DEFAULT_PIN , PIN_PREFIX , OBJECT_PREFIX } from './LocalDatastoreUtils' ;
1617
17- const DEFAULT_PIN = '_default' ;
18- const PIN_PREFIX = 'parsePin_' ;
19-
18+ /**
19+ * Provides a local datastore which can be used to store and retrieve <code>Parse.Object</code>. <br />
20+ * To enable this functionality, call <code>Parse.enableLocalDatastore()</code>.
21+ *
22+ * Pin object to add to local datastore
23+ *
24+ * <pre>await object.pin();</pre>
25+ * <pre>await object.pinWithName('pinName');</pre>
26+ *
27+ * Query pinned objects
28+ *
29+ * <pre>query.fromLocalDatastore();</pre>
30+ * <pre>query.fromPin();</pre>
31+ * <pre>query.fromPinWithName();</pre>
32+ *
33+ * <pre>const localObjects = await query.find();</pre>
34+ *
35+ * @class Parse.LocalDatastore
36+ * @static
37+ */
2038const LocalDatastore = {
2139 fromPinWithName ( name : string ) : Promise {
2240 const controller = CoreManager . getLocalDatastoreController ( ) ;
@@ -38,44 +56,62 @@ const LocalDatastore = {
3856 return controller . getAllContents ( ) ;
3957 } ,
4058
59+ // Use for testing
60+ _getRawStorage ( ) : Promise {
61+ const controller = CoreManager . getLocalDatastoreController ( ) ;
62+ return controller . getRawStorage ( ) ;
63+ } ,
64+
4165 _clear ( ) : Promise {
4266 const controller = CoreManager . getLocalDatastoreController ( ) ;
4367 return controller . clear ( ) ;
4468 } ,
4569
4670 // Pin the object and children recursively
4771 // Saves the object and children key to Pin Name
48- async _handlePinWithName ( name : string , object : ParseObject ) : Promise {
72+ async _handlePinAllWithName ( name : string , objects : Array < ParseObject > ) : Promise {
4973 const pinName = this . getPinName ( name ) ;
50- const objects = this . _getChildren ( object ) ;
51- objects [ this . getKeyForObject ( object ) ] = object . _toFullJSON ( ) ;
52- for ( const objectKey in objects ) {
53- await this . pinWithName ( objectKey , objects [ objectKey ] ) ;
74+ const toPinPromises = [ ] ;
75+ const objectKeys = [ ] ;
76+ for ( const parent of objects ) {
77+ const children = this . _getChildren ( parent ) ;
78+ const parentKey = this . getKeyForObject ( parent ) ;
79+ children [ parentKey ] = parent . _toFullJSON ( ) ;
80+ for ( const objectKey in children ) {
81+ objectKeys . push ( objectKey ) ;
82+ toPinPromises . push ( this . pinWithName ( objectKey , [ children [ objectKey ] ] ) ) ;
83+ }
5484 }
55- const pinned = await this . fromPinWithName ( pinName ) || [ ] ;
56- const objectIds = Object . keys ( objects ) ;
57- const toPin = [ ...new Set ( [ ...pinned , ...objectIds ] ) ] ;
58- await this . pinWithName ( pinName , toPin ) ;
85+ const fromPinPromise = this . fromPinWithName ( pinName ) ;
86+ const [ pinned ] = await Promise . all ( [ fromPinPromise , toPinPromises ] ) ;
87+ const toPin = [ ...new Set ( [ ...( pinned || [ ] ) , ...objectKeys ] ) ] ;
88+ return this . pinWithName ( pinName , toPin ) ;
5989 } ,
6090
6191 // Removes object and children keys from pin name
6292 // Keeps the object and children pinned
63- async _handleUnPinWithName ( name : string , object : ParseObject ) {
93+ async _handleUnPinAllWithName ( name : string , objects : Array < ParseObject > ) {
6494 const localDatastore = await this . _getAllContents ( ) ;
6595 const pinName = this . getPinName ( name ) ;
66- const objects = this . _getChildren ( object ) ;
67- const objectIds = Object . keys ( objects ) ;
68- objectIds . push ( this . getKeyForObject ( object ) ) ;
96+ const promises = [ ] ;
97+ let objectKeys = [ ] ;
98+ for ( const parent of objects ) {
99+ const children = this . _getChildren ( parent ) ;
100+ const parentKey = this . getKeyForObject ( parent ) ;
101+ objectKeys . push ( parentKey , ...Object . keys ( children ) ) ;
102+ }
103+ objectKeys = [ ...new Set ( objectKeys ) ] ;
104+
69105 let pinned = localDatastore [ pinName ] || [ ] ;
70- pinned = pinned . filter ( item => ! objectIds . includes ( item ) ) ;
106+ pinned = pinned . filter ( item => ! objectKeys . includes ( item ) ) ;
71107 if ( pinned . length == 0 ) {
72- await this . unPinWithName ( pinName ) ;
108+ promises . push ( this . unPinWithName ( pinName ) ) ;
73109 delete localDatastore [ pinName ] ;
74110 } else {
75- await this . pinWithName ( pinName , pinned ) ;
111+ promises . push ( this . pinWithName ( pinName , pinned ) ) ;
76112 localDatastore [ pinName ] = pinned ;
77113 }
78- for ( const objectKey of objectIds ) {
114+ for ( const objectKey of objectKeys ) {
79115 let hasReference = false ;
80116 for ( const key in localDatastore ) {
81117 if ( key === DEFAULT_PIN || key . startsWith ( PIN_PREFIX ) ) {
@@ -87,9 +123,10 @@ const LocalDatastore = {
87123 }
88124 }
89125 if ( ! hasReference ) {
90- await this . unPinWithName ( objectKey ) ;
126+ promises . push ( this . unPinWithName ( objectKey ) ) ;
91127 }
92128 }
129+ return Promise . all ( promises ) ;
93130 } ,
94131
95132 // Retrieve all pointer fields from object recursively
@@ -130,20 +167,22 @@ const LocalDatastore = {
130167 const localDatastore = await this . _getAllContents ( ) ;
131168 const allObjects = [ ] ;
132169 for ( const key in localDatastore ) {
133- if ( key !== DEFAULT_PIN && ! key . startsWith ( PIN_PREFIX ) ) {
134- allObjects . push ( localDatastore [ key ] ) ;
170+ if ( key . startsWith ( OBJECT_PREFIX ) ) {
171+ allObjects . push ( localDatastore [ key ] [ 0 ] ) ;
135172 }
136173 }
137174 if ( ! name ) {
138- return Promise . resolve ( allObjects ) ;
175+ return allObjects ;
139176 }
140- const pinName = await this . getPinName ( name ) ;
141- const pinned = await this . fromPinWithName ( pinName ) ;
177+ const pinName = this . getPinName ( name ) ;
178+ const pinned = localDatastore [ pinName ] ;
142179 if ( ! Array . isArray ( pinned ) ) {
143- return Promise . resolve ( [ ] ) ;
180+ return [ ] ;
144181 }
145- const objects = pinned . map ( async ( objectKey ) => await this . fromPinWithName ( objectKey ) ) ;
146- return Promise . all ( objects ) ;
182+ const promises = pinned . map ( ( objectKey ) => this . fromPinWithName ( objectKey ) ) ;
183+ let objects = await Promise . all ( promises ) ;
184+ objects = [ ] . concat ( ...objects ) ;
185+ return objects . filter ( object => object != null ) ;
147186 } ,
148187
149188 // Replaces object pointers with pinned pointers
@@ -154,10 +193,10 @@ const LocalDatastore = {
154193 if ( ! LDS ) {
155194 LDS = await this . _getAllContents ( ) ;
156195 }
157- const root = LDS [ objectKey ] ;
158- if ( ! root ) {
196+ if ( ! LDS [ objectKey ] || LDS [ objectKey ] . length === 0 ) {
159197 return null ;
160198 }
199+ const root = LDS [ objectKey ] [ 0 ] ;
161200
162201 const queue = [ ] ;
163202 const meta = { } ;
@@ -172,8 +211,8 @@ const LocalDatastore = {
172211 const value = subTreeRoot [ field ] ;
173212 if ( value . __type && value . __type === 'Object' ) {
174213 const key = this . getKeyForObject ( value ) ;
175- const pointer = LDS [ key ] ;
176- if ( pointer ) {
214+ if ( LDS [ key ] && LDS [ key ] . length > 0 ) {
215+ const pointer = LDS [ key ] [ 0 ] ;
177216 uniqueId ++ ;
178217 meta[ uniqueId ] = pointer ;
179218 subTreeRoot [ field ] = pointer ;
@@ -187,15 +226,16 @@ const LocalDatastore = {
187226
188227 // Called when an object is save / fetched
189228 // Update object pin value
190- async _updateObjectIfPinned ( object : ParseObject ) {
229+ async _updateObjectIfPinned ( object : ParseObject ) : Promise {
191230 if ( ! this . isEnabled ) {
192231 return ;
193232 }
194233 const objectKey = this . getKeyForObject ( object ) ;
195234 const pinned = await this . fromPinWithName ( objectKey ) ;
196- if ( pinned ) {
197- await this . pinWithName ( objectKey , object . _toFullJSON ( ) ) ;
235+ if ( ! pinned || pinned . length === 0 ) {
236+ return ;
198237 }
238+ return this . pinWithName ( objectKey , [ object . _toFullJSON ( ) ] ) ;
199239 } ,
200240
201241 // Called when object is destroyed
@@ -211,7 +251,9 @@ const LocalDatastore = {
211251 if ( ! pin ) {
212252 return;
213253 }
214- await this . unPinWithName ( objectKey ) ;
254+ const promises = [
255+ this . unPinWithName ( objectKey )
256+ ] ;
215257 delete localDatastore [ objectKey ] ;
216258
217259 for ( const key in localDatastore ) {
@@ -220,31 +262,34 @@ const LocalDatastore = {
220262 if ( pinned . includes ( objectKey ) ) {
221263 pinned = pinned . filter ( item => item !== objectKey ) ;
222264 if ( pinned . length == 0 ) {
223- await this . unPinWithName ( key ) ;
265+ promises . push ( this . unPinWithName ( key ) ) ;
224266 delete localDatastore [ key ] ;
225267 } else {
226- await this . pinWithName ( key , pinned ) ;
268+ promises . push ( this . pinWithName ( key , pinned ) ) ;
227269 localDatastore [ key ] = pinned ;
228270 }
229271 }
230272 }
231273 }
274+ return Promise . all ( promises ) ;
232275 } ,
233276
234277 // Update pin and references of the unsaved object
235278 async _updateLocalIdForObject ( localId , object : ParseObject ) {
236279 if ( ! this . isEnabled ) {
237280 return ;
238281 }
239- const localKey = `${ object . className } _${ localId } ` ;
282+ const localKey = `${ OBJECT_PREFIX } ${ object . className } _${ localId } ` ;
240283 const objectKey = this . getKeyForObject ( object ) ;
241284
242285 const unsaved = await this . fromPinWithName ( localKey ) ;
243- if ( ! unsaved ) {
286+ if ( ! unsaved || unsaved . length === 0 ) {
244287 return ;
245288 }
246- await this . unPinWithName ( localKey ) ;
247- await this . pinWithName ( objectKey , unsaved ) ;
289+ const promises = [
290+ this . unPinWithName ( localKey ) ,
291+ this . pinWithName ( objectKey , unsaved ) ,
292+ ] ;
248293
249294 const localDatastore = await this . _getAllContents ( ) ;
250295 for ( const key in localDatastore ) {
@@ -253,11 +298,12 @@ const LocalDatastore = {
253298 if ( pinned . includes ( localKey ) ) {
254299 pinned = pinned . filter ( item => item !== localKey ) ;
255300 pinned . push ( objectKey ) ;
256- await this . pinWithName ( key , pinned ) ;
301+ promises . push ( this . pinWithName ( key , pinned ) ) ;
257302 localDatastore [ key ] = pinned ;
258303 }
259304 }
260305 }
306+ return Promise . all ( promises ) ;
261307 } ,
262308
263309 /**
@@ -266,7 +312,8 @@ const LocalDatastore = {
266312 * <pre>
267313 * await Parse.LocalDatastore.updateFromServer();
268314 * </pre>
269- *
315+ * @method updateFromServer
316+ * @name Parse.LocalDatastore.updateFromServer
270317 * @static
271318 */
272319 async updateFromServer ( ) {
@@ -276,7 +323,7 @@ const LocalDatastore = {
276323 const localDatastore = await this . _getAllContents ( ) ;
277324 const keys = [ ] ;
278325 for ( const key in localDatastore ) {
279- if ( key !== DEFAULT_PIN && ! key . startsWith ( PIN_PREFIX ) ) {
326+ if ( key . startsWith ( OBJECT_PREFIX ) ) {
280327 keys . push ( key ) ;
281328 }
282329 }
@@ -286,7 +333,8 @@ const LocalDatastore = {
286333 this . isSyncing = true ;
287334 const pointersHash = { } ;
288335 for ( const key of keys ) {
289- const [ className , objectId ] = key . split ( '_' ) ;
336+ // Ignore the OBJECT_PREFIX
337+ const [ , , className , objectId ] = key . split ( '_' ) ;
290338 if ( ! ( className in pointersHash ) ) {
291339 pointersHash [ className ] = new Set ( ) ;
292340 }
@@ -313,15 +361,14 @@ const LocalDatastore = {
313361 await Promise . all ( pinPromises ) ;
314362 this . isSyncing = false ;
315363 } catch ( error ) {
316- console . log ( 'Error syncing LocalDatastore' ) ; // eslint-disable-line
317- console . log ( error ) ; // eslint-disable-line
364+ console . error ( 'Error syncing LocalDatastore: ' , error ) ;
318365 this . isSyncing = false ;
319366 }
320367 } ,
321368
322369 getKeyForObject ( object : any ) {
323370 const objectId = object . objectId || object . _getId ( ) ;
324- return `${object . className } _$ { objectId } `;
371+ return `${ OBJECT_PREFIX } ${ object . className } _${ objectId } ` ;
325372 } ,
326373
327374 getPinName ( pinName : ?string ) {
@@ -333,14 +380,12 @@ const LocalDatastore = {
333380
334381 checkIfEnabled ( ) {
335382 if ( ! this . isEnabled ) {
336- console . log ( 'Parse.enableLocalDatastore() must be called first' ) ; // eslint-disable-line no-console
383+ console . error ( 'Parse . enableLocalDatastore ( ) must be called first ') ;
337384 }
338385 return this . isEnabled ;
339386 }
340387} ;
341388
342- LocalDatastore . DEFAULT_PIN = DEFAULT_PIN ;
343- LocalDatastore . PIN_PREFIX = PIN_PREFIX ;
344389LocalDatastore . isEnabled = false ;
345390LocalDatastore . isSyncing = false ;
346391
0 commit comments