diff --git a/packages/firestore/exp/index.node.ts b/packages/firestore/exp/index.node.ts index c5afa20b854..c27c4756092 100644 --- a/packages/firestore/exp/index.node.ts +++ b/packages/firestore/exp/index.node.ts @@ -44,7 +44,7 @@ export { export { runTransaction, Transaction } from '../lite/src/api/transaction'; -export { getDoc } from './src/api/reference'; +export { getDoc, getDocFromCache, getDocFromServer } from './src/api/reference'; export { FieldValue, diff --git a/packages/firestore/exp/src/api/reference.ts b/packages/firestore/exp/src/api/reference.ts index 4328ca88d76..c102109ed8a 100644 --- a/packages/firestore/exp/src/api/reference.ts +++ b/packages/firestore/exp/src/api/reference.ts @@ -30,6 +30,7 @@ import { } from '../../../src/api/database'; import { ViewSnapshot } from '../../../src/core/view_snapshot'; import { DocumentReference } from '../../../lite/src/api/reference'; +import { Document } from '../../../src/model/document'; export function getDoc( reference: firestore.DocumentReference @@ -37,7 +38,47 @@ export function getDoc( const ref = cast>(reference, DocumentReference); const firestore = cast(ref.firestore, Firestore); return firestore._getFirestoreClient().then(async firestoreClient => { - const viewSnapshot = await getDocViaSnapshotListener(firestoreClient, ref); + const viewSnapshot = await getDocViaSnapshotListener( + firestoreClient, + ref._key + ); + return convertToDocSnapshot(firestore, ref, viewSnapshot); + }); +} + +// TODO(firestorexp): Make sure we don't include Datastore/RemoteStore in builds +// that only include `getDocFromCache`. +export function getDocFromCache( + reference: firestore.DocumentReference +): Promise> { + const ref = cast>(reference, DocumentReference); + const firestore = cast(ref.firestore, Firestore); + return firestore._getFirestoreClient().then(async firestoreClient => { + const doc = await firestoreClient.getDocumentFromLocalCache(ref._key); + return new DocumentSnapshot( + firestore, + ref._key, + doc, + ref._converter, + new SnapshotMetadata( + doc instanceof Document ? doc.hasLocalMutations : false, + /* fromCache= */ true + ) + ); + }); +} + +export function getDocFromServer( + reference: firestore.DocumentReference +): Promise> { + const ref = cast>(reference, DocumentReference); + const firestore = cast(ref.firestore, Firestore); + return firestore._getFirestoreClient().then(async firestoreClient => { + const viewSnapshot = await getDocViaSnapshotListener( + firestoreClient, + ref._key, + { source: 'server' } + ); return convertToDocSnapshot(firestore, ref, viewSnapshot); }); } diff --git a/packages/firestore/exp/test/integration.test.ts b/packages/firestore/exp/test/integration.test.ts index e5eadb81393..39435b35fe2 100644 --- a/packages/firestore/exp/test/integration.test.ts +++ b/packages/firestore/exp/test/integration.test.ts @@ -25,7 +25,11 @@ import { initializeFirestore } from '../src/api/database'; import { withTestDoc } from './helpers'; -import { getDoc } from '../src/api/reference'; +import { + getDoc, + getDocFromCache, + getDocFromServer +} from '../src/api/reference'; use(chaiAsPromised); @@ -61,3 +65,25 @@ describe('getDoc()', () => { }); }); }); + +describe('getDocFromCache()', () => { + it('can get a non-existing document', () => { + return withTestDoc(async docRef => { + await expect(getDocFromCache(docRef)).to.eventually.be.rejectedWith( + /Failed to get document from cache./ + ); + }); + }); +}); + +describe('getDocFromServer()', () => { + it('can get a non-existing document', () => { + return withTestDoc(async docRef => { + const docSnap = await getDocFromServer(docRef); + expect(docSnap.metadata.fromCache).to.be.false; + expect(docSnap.metadata.hasPendingWrites).to.be.false; + expect(docSnap.data()).to.be.undefined; + expect(docSnap.exists()).to.be.false; + }); + }); +}); diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 04aa40daf90..59c0c2b8b28 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -1199,7 +1199,7 @@ export class DocumentReference } return addDocSnapshotListener( this._firestoreClient, - this, + this._key, internalOptions, observer ); @@ -1227,7 +1227,7 @@ export class DocumentReference } else { return getDocViaSnapshotListener( this._firestoreClient, - this, + this._key, options ).then(snapshot => this._convertToDocSnapshot(snapshot)); } @@ -1262,9 +1262,9 @@ export class DocumentReference } /** Registers an internal snapshot listener for `ref`. */ -function addDocSnapshotListener( +function addDocSnapshotListener( firestoreClient: FirestoreClient, - ref: DocumentKeyReference, + key: DocumentKey, options: ListenOptions, observer: PartialObserver ): Unsubscribe { @@ -1284,7 +1284,7 @@ function addDocSnapshotListener( error: errHandler }); const internalListener = firestoreClient.listen( - InternalQuery.atPath(ref._key.path), + InternalQuery.atPath(key.path), asyncObserver, options ); @@ -1299,15 +1299,15 @@ function addDocSnapshotListener( * Retrieves a latency-compensated document from the backend via a * SnapshotListener. */ -export function getDocViaSnapshotListener( +export function getDocViaSnapshotListener( firestoreClient: FirestoreClient, - ref: DocumentKeyReference, + key: DocumentKey, options?: firestore.GetOptions ): Promise { const result = new Deferred(); const unlisten = addDocSnapshotListener( firestoreClient, - ref, + key, { includeMetadataChanges: true, waitForSyncWhenOnline: true @@ -1318,7 +1318,7 @@ export function getDocViaSnapshotListener( // user actions affecting the now stale query. unlisten(); - const exists = snap.docs.has(ref._key); + const exists = snap.docs.has(key); if (!exists && snap.fromCache) { // TODO(dimond): If we're online and the document doesn't // exist then we resolve with a doc.exists set to false. If