Skip to content

Commit d2adf4e

Browse files
Compat class for DocumentSnapshot (#4051)
1 parent 8602cda commit d2adf4e

File tree

15 files changed

+284
-296
lines changed

15 files changed

+284
-296
lines changed

.changeset/fast-impalas-tickle.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@firebase/firestore": patch
3+
---
4+
5+
Fixed an issue that caused `DocumentReference`s in `DocumentSnapshot`s to be returned with the custom converter of the original `DocumentReference`.

packages/firestore/exp/src/api/reference.ts

+40-8
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ import {
5757
Unsubscribe
5858
} from '../../../src/api/observer';
5959
import {
60-
getEventManager,
6160
executeQueryFromCache,
6261
executeQueryViaSnapshotListener,
63-
readDocumentFromCache,
64-
readDocumentViaSnapshotListener,
62+
firestoreClientWrite,
63+
getEventManager,
6564
getLocalStore,
66-
firestoreClientWrite
65+
readDocumentFromCache,
66+
readDocumentViaSnapshotListener
6767
} from '../../../src/core/firestore_client';
6868
import {
6969
newQueryForPath,
@@ -80,6 +80,15 @@ import {
8080
} from '../../../src/core/event_manager';
8181
import { FirestoreError } from '../../../src/util/error';
8282
import { Compat } from '../../../src/compat/compat';
83+
import { ByteString } from '../../../src/util/byte_string';
84+
import { Bytes } from '../../../lite/src/api/bytes';
85+
import { AbstractUserDataWriter } from '../../../src/api/user_data_writer';
86+
87+
export {
88+
DocumentReference,
89+
CollectionReference,
90+
Query
91+
} from '../../../lite/src/api/reference';
8392

8493
/**
8594
* An options object that can be passed to {@link onSnapshot()} and {@link
@@ -128,6 +137,21 @@ export function getDoc<T>(
128137
);
129138
}
130139

140+
export class ExpUserDataWriter extends AbstractUserDataWriter {
141+
constructor(protected firestore: FirebaseFirestore) {
142+
super();
143+
}
144+
145+
protected convertBytes(bytes: ByteString): Bytes {
146+
return new Bytes(bytes);
147+
}
148+
149+
protected convertReference(name: string): DocumentReference {
150+
const key = this.convertDocumentKey(name, this.firestore._databaseId);
151+
return new DocumentReference(this.firestore, /* converter= */ null, key);
152+
}
153+
}
154+
131155
/**
132156
* Reads the document referred to by this `DocumentReference` from cache.
133157
* Returns an error if the document is not currently cached.
@@ -140,6 +164,7 @@ export function getDocFromCache<T>(
140164
): Promise<DocumentSnapshot<T>> {
141165
const firestore = cast(reference.firestore, FirebaseFirestore);
142166
const client = ensureFirestoreConfigured(firestore);
167+
const userDataWriter = new ExpUserDataWriter(firestore);
143168

144169
const deferred = new Deferred<Document | null>();
145170
firestore._queue.enqueueAndForget(async () => {
@@ -150,6 +175,7 @@ export function getDocFromCache<T>(
150175
doc =>
151176
new DocumentSnapshot(
152177
firestore,
178+
userDataWriter,
153179
reference._key,
154180
doc,
155181
new SnapshotMetadata(
@@ -203,6 +229,7 @@ export function getDocFromServer<T>(
203229
export function getDocs<T>(query: Query<T>): Promise<QuerySnapshot<T>> {
204230
const firestore = cast(query.firestore, FirebaseFirestore);
205231
const client = ensureFirestoreConfigured(firestore);
232+
const userDataWriter = new ExpUserDataWriter(firestore);
206233

207234
validateHasExplicitOrderByForLimitToLast(query._query);
208235

@@ -218,7 +245,7 @@ export function getDocs<T>(query: Query<T>): Promise<QuerySnapshot<T>> {
218245
);
219246
});
220247
return deferred.promise.then(
221-
snapshot => new QuerySnapshot(firestore, query, snapshot)
248+
snapshot => new QuerySnapshot(firestore, userDataWriter, query, snapshot)
222249
);
223250
}
224251

@@ -233,14 +260,15 @@ export function getDocsFromCache<T>(
233260
): Promise<QuerySnapshot<T>> {
234261
const firestore = cast(query.firestore, FirebaseFirestore);
235262
const client = ensureFirestoreConfigured(firestore);
263+
const userDataWriter = new ExpUserDataWriter(firestore);
236264

237265
const deferred = new Deferred<ViewSnapshot>();
238266
firestore._queue.enqueueAndForget(async () => {
239267
const localStore = await getLocalStore(client);
240268
await executeQueryFromCache(localStore, query._query, deferred);
241269
});
242270
return deferred.promise.then(
243-
snapshot => new QuerySnapshot(firestore, query, snapshot)
271+
snapshot => new QuerySnapshot(firestore, userDataWriter, query, snapshot)
244272
);
245273
}
246274

@@ -255,6 +283,7 @@ export function getDocsFromServer<T>(
255283
): Promise<QuerySnapshot<T>> {
256284
const firestore = cast(query.firestore, FirebaseFirestore);
257285
const client = ensureFirestoreConfigured(firestore);
286+
const userDataWriter = new ExpUserDataWriter(firestore);
258287

259288
const deferred = new Deferred<ViewSnapshot>();
260289
firestore._queue.enqueueAndForget(async () => {
@@ -268,7 +297,7 @@ export function getDocsFromServer<T>(
268297
);
269298
});
270299
return deferred.promise.then(
271-
snapshot => new QuerySnapshot(firestore, query, snapshot)
300+
snapshot => new QuerySnapshot(firestore, userDataWriter, query, snapshot)
272301
);
273302
}
274303

@@ -701,12 +730,13 @@ export function onSnapshot<T>(
701730
} else {
702731
firestore = cast(reference.firestore, FirebaseFirestore);
703732
internalQuery = reference._query;
733+
const userDataWriter = new ExpUserDataWriter(firestore);
704734

705735
observer = {
706736
next: snapshot => {
707737
if (args[currArg]) {
708738
(args[currArg] as NextFn<QuerySnapshot<T>>)(
709-
new QuerySnapshot(firestore, reference, snapshot)
739+
new QuerySnapshot(firestore, userDataWriter, reference, snapshot)
710740
);
711741
}
712742
},
@@ -836,8 +866,10 @@ function convertToDocSnapshot<T>(
836866
);
837867
const doc = snapshot.docs.get(ref._key);
838868

869+
const userDataWriter = new ExpUserDataWriter(firestore);
839870
return new DocumentSnapshot(
840871
firestore,
872+
userDataWriter,
841873
ref._key,
842874
doc,
843875
new SnapshotMetadata(snapshot.hasPendingWrites, snapshot.fromCache),

packages/firestore/exp/src/api/snapshot.ts

+16-25
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,14 @@
1717

1818
import { DocumentKey } from '../../../src/model/document_key';
1919
import { Document } from '../../../src/model/document';
20-
import {
21-
ServerTimestampBehavior,
22-
UserDataWriter
23-
} from '../../../src/api/user_data_writer';
20+
import { AbstractUserDataWriter } from '../../../src/api/user_data_writer';
2421
import {
2522
DocumentSnapshot as LiteDocumentSnapshot,
2623
fieldPathFromArgument
2724
} from '../../../lite/src/api/snapshot';
2825
import { FirebaseFirestore } from './database';
2926
import {
3027
DocumentData,
31-
DocumentReference,
3228
Query,
3329
queryEqual,
3430
SetOptions
@@ -41,9 +37,7 @@ import { Code, FirestoreError } from '../../../src/util/error';
4137
import { ViewSnapshot } from '../../../src/core/view_snapshot';
4238
import { FieldPath } from '../../../lite/src/api/field_path';
4339
import { SnapshotListenOptions } from './reference';
44-
import { Bytes } from '../../../lite/src/api/bytes';
45-
46-
const DEFAULT_SERVER_TIMESTAMP_BEHAVIOR: ServerTimestampBehavior = 'none';
40+
import { UntypedFirestoreDataConverter } from '../../../src/api/user_data_reader';
4741

4842
/**
4943
* Converter used by `withConverter()` to transform user objects of type `T`
@@ -187,12 +181,13 @@ export class DocumentSnapshot<T = DocumentData> extends LiteDocumentSnapshot<
187181

188182
constructor(
189183
readonly _firestore: FirebaseFirestore,
184+
userDataWriter: AbstractUserDataWriter,
190185
key: DocumentKey,
191186
document: Document | null,
192187
metadata: SnapshotMetadata,
193-
converter: FirestoreDataConverter<T> | null
188+
converter: UntypedFirestoreDataConverter<T> | null
194189
) {
195-
super(_firestore, key, document, converter);
190+
super(_firestore, userDataWriter, key, document, converter);
196191
this._firestoreImpl = _firestore;
197192
this.metadata = metadata;
198193
}
@@ -219,29 +214,26 @@ export class DocumentSnapshot<T = DocumentData> extends LiteDocumentSnapshot<
219214
* @return An `Object` containing all fields in the document or `undefined` if
220215
* the document doesn't exist.
221216
*/
222-
data(options?: SnapshotOptions): T | undefined {
217+
data(options: SnapshotOptions = {}): T | undefined {
223218
if (!this._document) {
224219
return undefined;
225220
} else if (this._converter) {
226221
// We only want to use the converter and create a new DocumentSnapshot
227222
// if a converter has been provided.
228223
const snapshot = new QueryDocumentSnapshot(
229224
this._firestore,
225+
this._userDataWriter,
230226
this._key,
231227
this._document,
232228
this.metadata,
233229
/* converter= */ null
234230
);
235231
return this._converter.fromFirestore(snapshot, options);
236232
} else {
237-
const userDataWriter = new UserDataWriter(
238-
this._firestoreImpl._databaseId,
239-
options?.serverTimestamps || DEFAULT_SERVER_TIMESTAMP_BEHAVIOR,
240-
key =>
241-
new DocumentReference(this._firestore, /* converter= */ null, key),
242-
bytes => new Bytes(bytes)
243-
);
244-
return userDataWriter.convertValue(this._document.toProto()) as T;
233+
return this._userDataWriter.convertValue(
234+
this._document.toProto(),
235+
options.serverTimestamps
236+
) as T;
245237
}
246238
}
247239

@@ -269,13 +261,10 @@ export class DocumentSnapshot<T = DocumentData> extends LiteDocumentSnapshot<
269261
.data()
270262
.field(fieldPathFromArgument('DocumentSnapshot.get', fieldPath));
271263
if (value !== null) {
272-
const userDataWriter = new UserDataWriter(
273-
this._firestoreImpl._databaseId,
274-
options.serverTimestamps || DEFAULT_SERVER_TIMESTAMP_BEHAVIOR,
275-
key => new DocumentReference(this._firestore, this._converter, key),
276-
bytes => new Bytes(bytes)
264+
return this._userDataWriter.convertValue(
265+
value,
266+
options.serverTimestamps
277267
);
278-
return userDataWriter.convertValue(value);
279268
}
280269
}
281270
return undefined;
@@ -339,6 +328,7 @@ export class QuerySnapshot<T = DocumentData> {
339328

340329
constructor(
341330
readonly _firestore: FirebaseFirestore,
331+
readonly _userDataWriter: AbstractUserDataWriter,
342332
query: Query<T>,
343333
readonly _snapshot: ViewSnapshot
344334
) {
@@ -431,6 +421,7 @@ export class QuerySnapshot<T = DocumentData> {
431421
): QueryDocumentSnapshot<T> {
432422
return new QueryDocumentSnapshot<T>(
433423
this._firestore,
424+
this._userDataWriter,
434425
doc.key,
435426
doc,
436427
new SnapshotMetadata(hasPendingWrites, fromCache),

packages/firestore/exp/src/api/transaction.ts

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { Transaction as InternalTransaction } from '../../../src/core/transactio
2929
import { validateReference } from '../../../lite/src/api/write_batch';
3030
import { getDatastore } from '../../../lite/src/api/components';
3131
import { DocumentReference } from '../../../lite/src/api/reference';
32+
import { ExpUserDataWriter } from './reference';
3233

3334
/**
3435
* A reference to a transaction.
@@ -56,12 +57,14 @@ export class Transaction extends LiteTransaction {
5657
*/
5758
get<T>(documentRef: DocumentReference<T>): Promise<DocumentSnapshot<T>> {
5859
const ref = validateReference<T>(documentRef, this._firestore);
60+
const userDataWriter = new ExpUserDataWriter(this._firestore);
5961
return super
6062
.get(documentRef)
6163
.then(
6264
liteDocumentSnapshot =>
6365
new DocumentSnapshot(
6466
this._firestore,
67+
userDataWriter,
6568
ref._key,
6669
liteDocumentSnapshot._document,
6770
new SnapshotMetadata(

packages/firestore/exp/test/shim.ts

+2-66
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ import { Compat } from '../../src/compat/compat';
4949
import {
5050
Firestore,
5151
DocumentReference,
52+
DocumentSnapshot,
53+
QueryDocumentSnapshot,
5254
wrapObserver,
5355
extractSnapshotOptions
5456
} from '../../src/api/database';
@@ -184,48 +186,6 @@ export class WriteBatch
184186
}
185187
}
186188

187-
export class DocumentSnapshot<T = legacy.DocumentData>
188-
extends Compat<exp.DocumentSnapshot<T>>
189-
implements legacy.DocumentSnapshot<T> {
190-
constructor(
191-
private readonly _firestore: Firestore,
192-
delegate: exp.DocumentSnapshot<T>
193-
) {
194-
super(delegate);
195-
}
196-
197-
readonly ref = new DocumentReference<T>(this._firestore, this._delegate.ref);
198-
readonly id = this._delegate.id;
199-
readonly metadata = this._delegate.metadata;
200-
201-
get exists(): boolean {
202-
return this._delegate.exists();
203-
}
204-
205-
data(options?: legacy.SnapshotOptions): T | undefined {
206-
return wrap(this._firestore, this._delegate.data(options));
207-
}
208-
209-
get(fieldPath: string | FieldPath, options?: legacy.SnapshotOptions): any {
210-
return wrap(
211-
this._firestore,
212-
this._delegate.get(unwrap(fieldPath), options)
213-
);
214-
}
215-
216-
isEqual(other: DocumentSnapshot<T>): boolean {
217-
return snapshotEqual(this._delegate, other._delegate);
218-
}
219-
}
220-
221-
export class QueryDocumentSnapshot<T = legacy.DocumentData>
222-
extends DocumentSnapshot<T>
223-
implements legacy.QueryDocumentSnapshot<T> {
224-
data(options?: legacy.SnapshotOptions): T {
225-
return this._delegate.data(options)!;
226-
}
227-
}
228-
229189
export class Query<T = legacy.DocumentData>
230190
extends Compat<exp.Query<T>>
231191
implements legacy.Query<T> {
@@ -496,30 +456,6 @@ export class Blob extends Compat<BytesExp> implements legacy.Blob {
496456
}
497457
}
498458

499-
/**
500-
* Takes document data that uses the firestore-exp API types and replaces them
501-
* with the API types defined in this shim.
502-
*/
503-
function wrap(firestore: Firestore, value: any): any {
504-
if (Array.isArray(value)) {
505-
return value.map(v => wrap(firestore, v));
506-
} else if (value instanceof FieldPathExp) {
507-
return new FieldPath(...value._internalPath.toArray());
508-
} else if (value instanceof BytesExp) {
509-
return new Blob(value);
510-
} else if (isPlainObject(value)) {
511-
const obj: any = {};
512-
for (const key in value) {
513-
if (value.hasOwnProperty(key)) {
514-
obj[key] = wrap(firestore, value[key]);
515-
}
516-
}
517-
return obj;
518-
} else {
519-
return value;
520-
}
521-
}
522-
523459
/**
524460
* Takes user data that uses API types from this shim and replaces them
525461
* with the the firestore-exp API types.

0 commit comments

Comments
 (0)