Skip to content

Commit 749c7f3

Browse files
author
Brian Chen
authored
Add toJSON() support for Firestore classes (#4298)
1 parent c2bd3db commit 749c7f3

File tree

12 files changed

+80
-18
lines changed

12 files changed

+80
-18
lines changed

.changeset/clean-meals-double.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@firebase/app-compat': patch
3+
'@firebase/app': patch
4+
'@firebase/firestore': patch
5+
---
6+
7+
Firestore classes like DocumentReference and Query can now be serialized to JSON (#4258)

packages-exp/app-compat/src/firebaseApp.ts

+8
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ export class FirebaseAppImpl implements FirebaseApp {
134134
_addOrOverwriteComponent(component: Component): void {
135135
_addOrOverwriteComponent(this.app, component);
136136
}
137+
138+
toJSON(): object {
139+
return {
140+
name: this.name,
141+
automaticDataCollectionEnabled: this.automaticDataCollectionEnabled,
142+
options: this.options
143+
};
144+
}
137145
}
138146

139147
// TODO: investigate why the following needs to be commented out

packages/app/src/firebaseApp.ts

+8
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,14 @@ export class FirebaseAppImpl implements FirebaseApp {
163163
this.container.addOrOverwriteComponent(component);
164164
}
165165

166+
toJSON(): object {
167+
return {
168+
name: this.name,
169+
automaticDataCollectionEnabled: this.automaticDataCollectionEnabled,
170+
options: this.options
171+
};
172+
}
173+
166174
/**
167175
* This function will throw an Error if the App has already been deleted -
168176
* use before performing API actions on the App.

packages/app/src/lite/firebaseAppLite.ts

+8
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,12 @@ export class FirebaseAppLiteImpl implements FirebaseApp {
141141
throw ERROR_FACTORY.create(AppError.APP_DELETED, { appName: this.name_ });
142142
}
143143
}
144+
145+
toJSON(): object {
146+
return {
147+
name: this.name,
148+
automaticDataCollectionEnabled: this.automaticDataCollectionEnabled,
149+
options: this.options
150+
};
151+
}
144152
}

packages/app/test/firebaseApp.test.ts

+5
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,11 @@ function firebaseAppTests(
336336
expect(() => firebase.app('toString')).throws(/'toString'.*created/i);
337337
});
338338

339+
it('JSON.stringify() does not throw', () => {
340+
const app = firebase.initializeApp({}, 'new-app');
341+
JSON.stringify(app);
342+
});
343+
339344
describe('Check for bad app names', () => {
340345
const tests = ['', 123, false, null];
341346
for (const data of tests) {

packages/firestore/src/exp/snapshot.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,9 @@ export interface DocumentChange<T = DocumentData> {
214214
* access will return 'undefined'. You can use the `exists()` method to
215215
* explicitly verify a document's existence.
216216
*/
217-
export class DocumentSnapshot<T = DocumentData> extends LiteDocumentSnapshot<
218-
T
219-
> {
217+
export class DocumentSnapshot<
218+
T = DocumentData
219+
> extends LiteDocumentSnapshot<T> {
220220
private readonly _firestoreImpl: FirebaseFirestore;
221221

222222
/**
@@ -329,9 +329,9 @@ export class DocumentSnapshot<T = DocumentData> extends LiteDocumentSnapshot<
329329
* `exists` property will always be true and `data()` will never return
330330
* 'undefined'.
331331
*/
332-
export class QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<
333-
T
334-
> {
332+
export class QueryDocumentSnapshot<
333+
T = DocumentData
334+
> extends DocumentSnapshot<T> {
335335
/**
336336
* Retrieves all fields in the document as an `Object`.
337337
*

packages/firestore/src/lite/database.ts

+8
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ export class FirebaseFirestore implements FirestoreService {
134134
return this._terminateTask;
135135
}
136136

137+
toJSON(): object {
138+
return {
139+
app: this._app,
140+
databaseId: this._databaseId,
141+
settings: this._settings
142+
};
143+
}
144+
137145
/**
138146
* Terminates all components used by this client. Subclasses can override
139147
* this method to clean up their own dependencies, but must also call this

packages/firestore/src/lite/snapshot.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ export class DocumentSnapshot<T = DocumentData> {
212212
* `exists` property will always be true and `data()` will never return
213213
* 'undefined'.
214214
*/
215-
export class QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<
216-
T
217-
> {
215+
export class QueryDocumentSnapshot<
216+
T = DocumentData
217+
> extends DocumentSnapshot<T> {
218218
/**
219219
* Retrieves all fields in the document as an `Object`.
220220
*

packages/firestore/test/integration/api/query.test.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,7 @@ apiDescribe('Queries', (persistence: boolean) => {
155155
.onSnapshot(storeLimitEvent.storeEvent);
156156

157157
// Setup mirroring `limitToLast` query
158-
const storeLimitToLastEvent = new EventsAccumulator<
159-
firestore.QuerySnapshot
160-
>();
158+
const storeLimitToLastEvent = new EventsAccumulator<firestore.QuerySnapshot>();
161159
let limitToLastUnlisten = collection
162160
.orderBy('sort', 'desc')
163161
.limitToLast(2)

packages/firestore/test/integration/api/type.test.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ apiDescribe('Firestore', (persistence: boolean) => {
5353
docSnapshot = querySnapshot.docs[0];
5454
expect(docSnapshot.data()).to.deep.equal(data);
5555

56-
const eventsAccumulator = new EventsAccumulator<
57-
firestore.QuerySnapshot
58-
>();
56+
const eventsAccumulator = new EventsAccumulator<firestore.QuerySnapshot>();
5957
const unlisten = collection.onSnapshot(eventsAccumulator.storeEvent);
6058
querySnapshot = await eventsAccumulator.awaitEvent();
6159
docSnapshot = querySnapshot.docs[0];

packages/firestore/test/unit/api/database.test.ts

+22
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ describe('CollectionReference', () => {
3232
expectEqual(collectionReference('foo'), collectionReference('foo'));
3333
expectNotEqual(collectionReference('foo'), collectionReference('bar'));
3434
});
35+
36+
it('JSON.stringify() does not throw', () => {
37+
JSON.stringify(collectionReference('foo'));
38+
});
3539
});
3640

3741
describe('DocumentReference', () => {
@@ -42,6 +46,10 @@ describe('DocumentReference', () => {
4246
documentReference('rooms/bar')
4347
);
4448
});
49+
50+
it('JSON.stringify() does not throw', () => {
51+
JSON.stringify(documentReference('foo/bar'));
52+
});
4553
});
4654

4755
describe('DocumentSnapshot', () => {
@@ -72,13 +80,21 @@ describe('DocumentSnapshot', () => {
7280
documentSnapshot('rooms/bar', { a: 1 }, false)
7381
);
7482
});
83+
84+
it('JSON.stringify() does not throw', () => {
85+
JSON.stringify(documentSnapshot('foo/bar', { a: 1 }, true));
86+
});
7587
});
7688

7789
describe('Query', () => {
7890
it('support equality checking with isEqual()', () => {
7991
expectEqual(query('foo'), query('foo'));
8092
expectNotEqual(query('foo'), query('bar'));
8193
});
94+
95+
it('JSON.stringify() does not throw', () => {
96+
JSON.stringify(query('foo'));
97+
});
8298
});
8399

84100
describe('QuerySnapshot', () => {
@@ -123,6 +139,12 @@ describe('QuerySnapshot', () => {
123139
querySnapshot('foo', {}, { a: { a: 1 } }, keys('foo/a'), false, true)
124140
);
125141
});
142+
143+
it('JSON.stringify() does not throw', () => {
144+
JSON.stringify(
145+
querySnapshot('foo', {}, { a: { a: 1 } }, keys(), false, false)
146+
);
147+
});
126148
});
127149

128150
describe('SnapshotMetadata', () => {

packages/firestore/test/unit/specs/spec_builder.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1047,9 +1047,9 @@ export class SpecBuilder {
10471047
return {
10481048
key: SpecBuilder.keyToSpec(doc.key),
10491049
version: doc.version.toMicroseconds(),
1050-
value: userDataWriter.convertValue(doc.toProto()) as JsonObject<
1051-
unknown
1052-
>,
1050+
value: userDataWriter.convertValue(
1051+
doc.toProto()
1052+
) as JsonObject<unknown>,
10531053
options: {
10541054
hasLocalMutations: doc.hasLocalMutations,
10551055
hasCommittedMutations: doc.hasCommittedMutations

0 commit comments

Comments
 (0)