@@ -13,10 +13,28 @@ import CoreManager from './CoreManager';
13
13
14
14
import type ParseObject from './ParseObject' ;
15
15
import ParseQuery from './ParseQuery' ;
16
+ import { DEFAULT_PIN , PIN_PREFIX , OBJECT_PREFIX } from './LocalDatastoreUtils' ;
16
17
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
+ */
20
38
const LocalDatastore = {
21
39
fromPinWithName ( name : string ) : Promise {
22
40
const controller = CoreManager . getLocalDatastoreController ( ) ;
@@ -38,44 +56,62 @@ const LocalDatastore = {
38
56
return controller . getAllContents ( ) ;
39
57
} ,
40
58
59
+ // Use for testing
60
+ _getRawStorage ( ) : Promise {
61
+ const controller = CoreManager . getLocalDatastoreController ( ) ;
62
+ return controller . getRawStorage ( ) ;
63
+ } ,
64
+
41
65
_clear ( ) : Promise {
42
66
const controller = CoreManager . getLocalDatastoreController ( ) ;
43
67
return controller . clear ( ) ;
44
68
} ,
45
69
46
70
// Pin the object and children recursively
47
71
// 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 {
49
73
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
+ }
54
84
}
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 ) ;
59
89
} ,
60
90
61
91
// Removes object and children keys from pin name
62
92
// Keeps the object and children pinned
63
- async _handleUnPinWithName ( name : string , object : ParseObject ) {
93
+ async _handleUnPinAllWithName ( name : string , objects : Array < ParseObject > ) {
64
94
const localDatastore = await this . _getAllContents ( ) ;
65
95
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
+
69
105
let pinned = localDatastore [ pinName ] || [ ] ;
70
- pinned = pinned . filter ( item => ! objectIds . includes ( item ) ) ;
106
+ pinned = pinned . filter ( item => ! objectKeys . includes ( item ) ) ;
71
107
if ( pinned . length == 0 ) {
72
- await this . unPinWithName ( pinName ) ;
108
+ promises . push ( this . unPinWithName ( pinName ) ) ;
73
109
delete localDatastore [ pinName ] ;
74
110
} else {
75
- await this . pinWithName ( pinName , pinned ) ;
111
+ promises . push ( this . pinWithName ( pinName , pinned ) ) ;
76
112
localDatastore [ pinName ] = pinned ;
77
113
}
78
- for ( const objectKey of objectIds ) {
114
+ for ( const objectKey of objectKeys ) {
79
115
let hasReference = false ;
80
116
for ( const key in localDatastore ) {
81
117
if ( key === DEFAULT_PIN || key . startsWith ( PIN_PREFIX ) ) {
@@ -87,9 +123,10 @@ const LocalDatastore = {
87
123
}
88
124
}
89
125
if ( ! hasReference ) {
90
- await this . unPinWithName ( objectKey ) ;
126
+ promises . push ( this . unPinWithName ( objectKey ) ) ;
91
127
}
92
128
}
129
+ return Promise . all ( promises ) ;
93
130
} ,
94
131
95
132
// Retrieve all pointer fields from object recursively
@@ -130,20 +167,22 @@ const LocalDatastore = {
130
167
const localDatastore = await this . _getAllContents ( ) ;
131
168
const allObjects = [ ] ;
132
169
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 ] ) ;
135
172
}
136
173
}
137
174
if ( ! name ) {
138
- return Promise . resolve ( allObjects ) ;
175
+ return allObjects ;
139
176
}
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 ] ;
142
179
if ( ! Array . isArray ( pinned ) ) {
143
- return Promise . resolve ( [ ] ) ;
180
+ return [ ] ;
144
181
}
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 ) ;
147
186
} ,
148
187
149
188
// Replaces object pointers with pinned pointers
@@ -154,10 +193,10 @@ const LocalDatastore = {
154
193
if ( ! LDS ) {
155
194
LDS = await this . _getAllContents ( ) ;
156
195
}
157
- const root = LDS [ objectKey ] ;
158
- if ( ! root ) {
196
+ if ( ! LDS [ objectKey ] || LDS [ objectKey ] . length === 0 ) {
159
197
return null ;
160
198
}
199
+ const root = LDS [ objectKey ] [ 0 ] ;
161
200
162
201
const queue = [ ] ;
163
202
const meta = { } ;
@@ -172,8 +211,8 @@ const LocalDatastore = {
172
211
const value = subTreeRoot [ field ] ;
173
212
if ( value . __type && value . __type === 'Object' ) {
174
213
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 ] ;
177
216
uniqueId ++ ;
178
217
meta[ uniqueId ] = pointer ;
179
218
subTreeRoot [ field ] = pointer ;
@@ -187,15 +226,16 @@ const LocalDatastore = {
187
226
188
227
// Called when an object is save / fetched
189
228
// Update object pin value
190
- async _updateObjectIfPinned ( object : ParseObject ) {
229
+ async _updateObjectIfPinned ( object : ParseObject ) : Promise {
191
230
if ( ! this . isEnabled ) {
192
231
return ;
193
232
}
194
233
const objectKey = this . getKeyForObject ( object ) ;
195
234
const pinned = await this . fromPinWithName ( objectKey ) ;
196
- if ( pinned ) {
197
- await this . pinWithName ( objectKey , object . _toFullJSON ( ) ) ;
235
+ if ( ! pinned || pinned . length === 0 ) {
236
+ return ;
198
237
}
238
+ return this . pinWithName ( objectKey , [ object . _toFullJSON ( ) ] ) ;
199
239
} ,
200
240
201
241
// Called when object is destroyed
@@ -211,7 +251,9 @@ const LocalDatastore = {
211
251
if ( ! pin ) {
212
252
return;
213
253
}
214
- await this . unPinWithName ( objectKey ) ;
254
+ const promises = [
255
+ this . unPinWithName ( objectKey )
256
+ ] ;
215
257
delete localDatastore [ objectKey ] ;
216
258
217
259
for ( const key in localDatastore ) {
@@ -220,31 +262,34 @@ const LocalDatastore = {
220
262
if ( pinned . includes ( objectKey ) ) {
221
263
pinned = pinned . filter ( item => item !== objectKey ) ;
222
264
if ( pinned . length == 0 ) {
223
- await this . unPinWithName ( key ) ;
265
+ promises . push ( this . unPinWithName ( key ) ) ;
224
266
delete localDatastore [ key ] ;
225
267
} else {
226
- await this . pinWithName ( key , pinned ) ;
268
+ promises . push ( this . pinWithName ( key , pinned ) ) ;
227
269
localDatastore [ key ] = pinned ;
228
270
}
229
271
}
230
272
}
231
273
}
274
+ return Promise . all ( promises ) ;
232
275
} ,
233
276
234
277
// Update pin and references of the unsaved object
235
278
async _updateLocalIdForObject ( localId , object : ParseObject ) {
236
279
if ( ! this . isEnabled ) {
237
280
return ;
238
281
}
239
- const localKey = `${ object . className } _${ localId } ` ;
282
+ const localKey = `${ OBJECT_PREFIX } ${ object . className } _${ localId } ` ;
240
283
const objectKey = this . getKeyForObject ( object ) ;
241
284
242
285
const unsaved = await this . fromPinWithName ( localKey ) ;
243
- if ( ! unsaved ) {
286
+ if ( ! unsaved || unsaved . length === 0 ) {
244
287
return ;
245
288
}
246
- await this . unPinWithName ( localKey ) ;
247
- await this . pinWithName ( objectKey , unsaved ) ;
289
+ const promises = [
290
+ this . unPinWithName ( localKey ) ,
291
+ this . pinWithName ( objectKey , unsaved ) ,
292
+ ] ;
248
293
249
294
const localDatastore = await this . _getAllContents ( ) ;
250
295
for ( const key in localDatastore ) {
@@ -253,11 +298,12 @@ const LocalDatastore = {
253
298
if ( pinned . includes ( localKey ) ) {
254
299
pinned = pinned . filter ( item => item !== localKey ) ;
255
300
pinned . push ( objectKey ) ;
256
- await this . pinWithName ( key , pinned ) ;
301
+ promises . push ( this . pinWithName ( key , pinned ) ) ;
257
302
localDatastore [ key ] = pinned ;
258
303
}
259
304
}
260
305
}
306
+ return Promise . all ( promises ) ;
261
307
} ,
262
308
263
309
/**
@@ -266,7 +312,8 @@ const LocalDatastore = {
266
312
* <pre>
267
313
* await Parse.LocalDatastore.updateFromServer();
268
314
* </pre>
269
- *
315
+ * @method updateFromServer
316
+ * @name Parse.LocalDatastore.updateFromServer
270
317
* @static
271
318
*/
272
319
async updateFromServer ( ) {
@@ -276,7 +323,7 @@ const LocalDatastore = {
276
323
const localDatastore = await this . _getAllContents ( ) ;
277
324
const keys = [ ] ;
278
325
for ( const key in localDatastore ) {
279
- if ( key !== DEFAULT_PIN && ! key . startsWith ( PIN_PREFIX ) ) {
326
+ if ( key . startsWith ( OBJECT_PREFIX ) ) {
280
327
keys . push ( key ) ;
281
328
}
282
329
}
@@ -286,7 +333,8 @@ const LocalDatastore = {
286
333
this . isSyncing = true ;
287
334
const pointersHash = { } ;
288
335
for ( const key of keys ) {
289
- const [ className , objectId ] = key . split ( '_' ) ;
336
+ // Ignore the OBJECT_PREFIX
337
+ const [ , , className , objectId ] = key . split ( '_' ) ;
290
338
if ( ! ( className in pointersHash ) ) {
291
339
pointersHash [ className ] = new Set ( ) ;
292
340
}
@@ -313,15 +361,14 @@ const LocalDatastore = {
313
361
await Promise . all ( pinPromises ) ;
314
362
this . isSyncing = false ;
315
363
} 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 ) ;
318
365
this . isSyncing = false ;
319
366
}
320
367
} ,
321
368
322
369
getKeyForObject ( object : any ) {
323
370
const objectId = object . objectId || object . _getId ( ) ;
324
- return `${object . className } _$ { objectId } `;
371
+ return `${ OBJECT_PREFIX } ${ object . className } _${ objectId } ` ;
325
372
} ,
326
373
327
374
getPinName ( pinName : ?string ) {
@@ -333,14 +380,12 @@ const LocalDatastore = {
333
380
334
381
checkIfEnabled ( ) {
335
382
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 ') ;
337
384
}
338
385
return this . isEnabled ;
339
386
}
340
387
} ;
341
388
342
- LocalDatastore . DEFAULT_PIN = DEFAULT_PIN ;
343
- LocalDatastore . PIN_PREFIX = PIN_PREFIX ;
344
389
LocalDatastore . isEnabled = false ;
345
390
LocalDatastore . isSyncing = false ;
346
391
0 commit comments