Skip to content

Commit cab645d

Browse files
Tree-shakeable FirestoreClient
1 parent 829de71 commit cab645d

File tree

1 file changed

+178
-90
lines changed

1 file changed

+178
-90
lines changed

packages/firestore/src/core/firestore_client.ts

Lines changed: 178 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import {
4747
OfflineComponentProvider,
4848
OnlineComponentProvider
4949
} from './component_provider';
50-
import { Unsubscribe } from '../api/observer';
50+
import { PartialObserver, Unsubscribe } from '../api/observer';
5151
import { AsyncObserver } from '../util/async_observer';
5252

5353
const LOG_TAG = 'FirestoreClient';
@@ -209,10 +209,11 @@ export class FirestoreClient {
209209
/** Enables the network connection and requeues all pending operations. */
210210
enableNetwork(): Promise<void> {
211211
this.verifyNotTerminated();
212-
return this.asyncQueue.enqueue(() => {
213-
this.persistence.setNetworkEnabled(true);
214-
return this.remoteStore.enableNetwork();
215-
});
212+
return enqueueEnableNetwork(
213+
this.asyncQueue,
214+
this.remoteStore,
215+
this.persistence
216+
);
216217
}
217218

218219
/**
@@ -352,10 +353,11 @@ export class FirestoreClient {
352353
/** Disables the network connection. Pending operations will not complete. */
353354
disableNetwork(): Promise<void> {
354355
this.verifyNotTerminated();
355-
return this.asyncQueue.enqueue(() => {
356-
this.persistence.setNetworkEnabled(false);
357-
return this.remoteStore.disableNetwork();
358-
});
356+
return enqueueDisableNetwork(
357+
this.asyncQueue,
358+
this.remoteStore,
359+
this.persistence
360+
);
359361
}
360362

361363
terminate(): Promise<void> {
@@ -383,12 +385,7 @@ export class FirestoreClient {
383385
*/
384386
waitForPendingWrites(): Promise<void> {
385387
this.verifyNotTerminated();
386-
387-
const deferred = new Deferred<void>();
388-
this.asyncQueue.enqueueAndForget(() => {
389-
return this.syncEngine.registerPendingWritesCallback(deferred);
390-
});
391-
return deferred.promise;
388+
return enqueueWaitForPendingWrites(this.asyncQueue, this.syncEngine);
392389
}
393390

394391
listen(
@@ -397,83 +394,28 @@ export class FirestoreClient {
397394
observer: AsyncObserver<ViewSnapshot>
398395
): Unsubscribe {
399396
this.verifyNotTerminated();
400-
const listener = new QueryListener(query, observer, options);
401-
this.asyncQueue.enqueueAndForget(() => this.eventMgr.listen(listener));
402-
return () => {
403-
observer.mute();
404-
this.asyncQueue.enqueueAndForget(() => this.eventMgr.unlisten(listener));
405-
};
397+
return enqueueListen(
398+
this.asyncQueue,
399+
this.eventMgr,
400+
query,
401+
options,
402+
observer
403+
);
406404
}
407405

408-
async getDocumentFromLocalCache(
409-
docKey: DocumentKey
410-
): Promise<Document | null> {
406+
getDocumentFromLocalCache(docKey: DocumentKey): Promise<Document | null> {
411407
this.verifyNotTerminated();
412-
const deferred = new Deferred<Document | null>();
413-
await this.asyncQueue.enqueue(async () => {
414-
try {
415-
const maybeDoc = await this.localStore.readDocument(docKey);
416-
if (maybeDoc instanceof Document) {
417-
deferred.resolve(maybeDoc);
418-
} else if (maybeDoc instanceof NoDocument) {
419-
deferred.resolve(null);
420-
} else {
421-
deferred.reject(
422-
new FirestoreError(
423-
Code.UNAVAILABLE,
424-
'Failed to get document from cache. (However, this document may ' +
425-
"exist on the server. Run again without setting 'source' in " +
426-
'the GetOptions to attempt to retrieve the document from the ' +
427-
'server.)'
428-
)
429-
);
430-
}
431-
} catch (e) {
432-
const firestoreError = wrapInUserErrorIfRecoverable(
433-
e,
434-
`Failed to get document '${docKey} from cache`
435-
);
436-
deferred.reject(firestoreError);
437-
}
438-
});
439-
440-
return deferred.promise;
408+
return enqueueReadDocument(this.asyncQueue, this.localStore, docKey);
441409
}
442410

443-
async getDocumentsFromLocalCache(query: Query): Promise<ViewSnapshot> {
411+
getDocumentsFromLocalCache(query: Query): Promise<ViewSnapshot> {
444412
this.verifyNotTerminated();
445-
const deferred = new Deferred<ViewSnapshot>();
446-
await this.asyncQueue.enqueue(async () => {
447-
try {
448-
const queryResult = await this.localStore.executeQuery(
449-
query,
450-
/* usePreviousResults= */ true
451-
);
452-
const view = new View(query, queryResult.remoteKeys);
453-
const viewDocChanges = view.computeDocChanges(queryResult.documents);
454-
const viewChange = view.applyChanges(
455-
viewDocChanges,
456-
/* updateLimboDocuments= */ false
457-
);
458-
deferred.resolve(viewChange.snapshot!);
459-
} catch (e) {
460-
const firestoreError = wrapInUserErrorIfRecoverable(
461-
e,
462-
`Failed to execute query '${query} against cache`
463-
);
464-
deferred.reject(firestoreError);
465-
}
466-
});
467-
return deferred.promise;
413+
return enqueueExecuteQuery(this.asyncQueue, this.localStore, query);
468414
}
469415

470416
write(mutations: Mutation[]): Promise<void> {
471417
this.verifyNotTerminated();
472-
const deferred = new Deferred<void>();
473-
this.asyncQueue.enqueueAndForget(() =>
474-
this.syncEngine.write(mutations, deferred)
475-
);
476-
return deferred.promise;
418+
return enqueueWrite(this.asyncQueue, this.syncEngine, mutations);
477419
}
478420

479421
databaseId(): DatabaseId {
@@ -482,15 +424,11 @@ export class FirestoreClient {
482424

483425
addSnapshotsInSyncListener(observer: AsyncObserver<void>): Unsubscribe {
484426
this.verifyNotTerminated();
485-
this.asyncQueue.enqueueAndForget(async () =>
486-
this.eventMgr.addSnapshotsInSyncListener(observer)
427+
return enqueueSnapshotsInSyncListen(
428+
this.asyncQueue,
429+
this.eventMgr,
430+
observer
487431
);
488-
return () => {
489-
observer.mute();
490-
this.asyncQueue.enqueueAndForget(async () =>
491-
this.eventMgr.removeSnapshotsInSyncListener(observer)
492-
);
493-
};
494432
}
495433

496434
get clientTerminated(): boolean {
@@ -512,3 +450,153 @@ export class FirestoreClient {
512450
return deferred.promise;
513451
}
514452
}
453+
454+
export function enqueueWrite(
455+
asyncQueue: AsyncQueue,
456+
syncEngine: SyncEngine,
457+
mutations: Mutation[]
458+
): Promise<void> {
459+
const deferred = new Deferred<void>();
460+
asyncQueue.enqueueAndForget(() => syncEngine.write(mutations, deferred));
461+
return deferred.promise;
462+
}
463+
464+
/** Enables the network connection and requeues all pending operations. */
465+
export function enqueueEnableNetwork(
466+
asyncQueue: AsyncQueue,
467+
remoteStore: RemoteStore,
468+
persistence: Persistence
469+
): Promise<void> {
470+
return asyncQueue.enqueue(() => {
471+
persistence.setNetworkEnabled(true);
472+
return remoteStore.enableNetwork();
473+
});
474+
}
475+
476+
/** Disables the network connection. Pending operations will not complete. */
477+
export function enqueueDisableNetwork(
478+
asyncQueue: AsyncQueue,
479+
remoteStore: RemoteStore,
480+
persistence: Persistence
481+
): Promise<void> {
482+
return asyncQueue.enqueue(() => {
483+
persistence.setNetworkEnabled(false);
484+
return remoteStore.disableNetwork();
485+
});
486+
}
487+
488+
/**
489+
* Returns a Promise that resolves when all writes that were pending at the time this
490+
* method was called received server acknowledgement. An acknowledgement can be either acceptance
491+
* or rejection.
492+
*/
493+
export function enqueueWaitForPendingWrites(
494+
asyncQueue: AsyncQueue,
495+
syncEngine: SyncEngine
496+
): Promise<void> {
497+
const deferred = new Deferred<void>();
498+
asyncQueue.enqueueAndForget(() => {
499+
return syncEngine.registerPendingWritesCallback(deferred);
500+
});
501+
return deferred.promise;
502+
}
503+
504+
export function enqueueListen(
505+
asyncQueue: AsyncQueue,
506+
eventManger: EventManager,
507+
query: Query,
508+
options: ListenOptions,
509+
observer: PartialObserver<ViewSnapshot>
510+
): Unsubscribe {
511+
const asyncObserver = new AsyncObserver(observer);
512+
const listener = new QueryListener(query, asyncObserver, options);
513+
asyncQueue.enqueueAndForget(() => eventManger.listen(listener));
514+
return () => {
515+
asyncObserver.mute();
516+
asyncQueue.enqueueAndForget(() => eventManger.unlisten(listener));
517+
};
518+
}
519+
520+
/** Registers the listener for onSnapshotsInSync() */
521+
export function enqueueSnapshotsInSyncListen(
522+
asyncQueue: AsyncQueue,
523+
eventManager: EventManager,
524+
observer: PartialObserver<void>
525+
): Unsubscribe {
526+
const asyncObserver = new AsyncObserver(observer);
527+
asyncQueue.enqueueAndForget(async () =>
528+
eventManager.addSnapshotsInSyncListener(asyncObserver)
529+
);
530+
return () => {
531+
asyncObserver.mute();
532+
asyncQueue.enqueueAndForget(async () =>
533+
eventManager.removeSnapshotsInSyncListener(asyncObserver)
534+
);
535+
};
536+
}
537+
538+
export async function enqueueReadDocument(
539+
asyncQueue: AsyncQueue,
540+
localStore: LocalStore,
541+
docKey: DocumentKey
542+
): Promise<Document | null> {
543+
const deferred = new Deferred<Document | null>();
544+
await asyncQueue.enqueue(async () => {
545+
try {
546+
const maybeDoc = await localStore.readDocument(docKey);
547+
if (maybeDoc instanceof Document) {
548+
deferred.resolve(maybeDoc);
549+
} else if (maybeDoc instanceof NoDocument) {
550+
deferred.resolve(null);
551+
} else {
552+
deferred.reject(
553+
new FirestoreError(
554+
Code.UNAVAILABLE,
555+
'Failed to get document from cache. (However, this document may ' +
556+
"exist on the server. Run again without setting 'source' in " +
557+
'the GetOptions to attempt to retrieve the document from the ' +
558+
'server.)'
559+
)
560+
);
561+
}
562+
} catch (e) {
563+
const firestoreError = wrapInUserErrorIfRecoverable(
564+
e,
565+
`Failed to get document '${docKey} from cache`
566+
);
567+
deferred.reject(firestoreError);
568+
}
569+
});
570+
571+
return deferred.promise;
572+
}
573+
574+
export async function enqueueExecuteQuery(
575+
asyncQueue: AsyncQueue,
576+
localStore: LocalStore,
577+
query: Query
578+
): Promise<ViewSnapshot> {
579+
const deferred = new Deferred<ViewSnapshot>();
580+
await asyncQueue.enqueue(async () => {
581+
try {
582+
const queryResult = await localStore.executeQuery(
583+
query,
584+
/* usePreviousResults= */ true
585+
);
586+
const view = new View(query, queryResult.remoteKeys);
587+
const viewDocChanges = view.computeDocChanges(queryResult.documents);
588+
const viewChange = view.applyChanges(
589+
viewDocChanges,
590+
/* updateLimboDocuments= */ false
591+
);
592+
deferred.resolve(viewChange.snapshot!);
593+
} catch (e) {
594+
const firestoreError = wrapInUserErrorIfRecoverable(
595+
e,
596+
`Failed to execute query '${query} against cache`
597+
);
598+
deferred.reject(firestoreError);
599+
}
600+
});
601+
return deferred.promise;
602+
}

0 commit comments

Comments
 (0)