From 749c7f3d985f978cd2a204cbc28c3fff09458b5b Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Tue, 26 Jan 2021 14:06:51 -0800 Subject: [PATCH] Add toJSON() support for Firestore classes (#4298) --- .changeset/clean-meals-double.md | 7 ++++++ packages-exp/app-compat/src/firebaseApp.ts | 8 +++++++ packages/app/src/firebaseApp.ts | 8 +++++++ packages/app/src/lite/firebaseAppLite.ts | 8 +++++++ packages/app/test/firebaseApp.test.ts | 5 +++++ packages/firestore/src/exp/snapshot.ts | 12 +++++----- packages/firestore/src/lite/database.ts | 8 +++++++ packages/firestore/src/lite/snapshot.ts | 6 ++--- .../test/integration/api/query.test.ts | 4 +--- .../test/integration/api/type.test.ts | 4 +--- .../firestore/test/unit/api/database.test.ts | 22 +++++++++++++++++++ .../firestore/test/unit/specs/spec_builder.ts | 6 ++--- 12 files changed, 80 insertions(+), 18 deletions(-) create mode 100644 .changeset/clean-meals-double.md diff --git a/.changeset/clean-meals-double.md b/.changeset/clean-meals-double.md new file mode 100644 index 00000000000..dab4a1cd7ab --- /dev/null +++ b/.changeset/clean-meals-double.md @@ -0,0 +1,7 @@ +--- +'@firebase/app-compat': patch +'@firebase/app': patch +'@firebase/firestore': patch +--- + +Firestore classes like DocumentReference and Query can now be serialized to JSON (#4258) diff --git a/packages-exp/app-compat/src/firebaseApp.ts b/packages-exp/app-compat/src/firebaseApp.ts index 19fe04d0cd0..ade5cbc11e5 100644 --- a/packages-exp/app-compat/src/firebaseApp.ts +++ b/packages-exp/app-compat/src/firebaseApp.ts @@ -134,6 +134,14 @@ export class FirebaseAppImpl implements FirebaseApp { _addOrOverwriteComponent(component: Component): void { _addOrOverwriteComponent(this.app, component); } + + toJSON(): object { + return { + name: this.name, + automaticDataCollectionEnabled: this.automaticDataCollectionEnabled, + options: this.options + }; + } } // TODO: investigate why the following needs to be commented out diff --git a/packages/app/src/firebaseApp.ts b/packages/app/src/firebaseApp.ts index 64ad5e21716..81d27eea699 100644 --- a/packages/app/src/firebaseApp.ts +++ b/packages/app/src/firebaseApp.ts @@ -163,6 +163,14 @@ export class FirebaseAppImpl implements FirebaseApp { this.container.addOrOverwriteComponent(component); } + toJSON(): object { + return { + name: this.name, + automaticDataCollectionEnabled: this.automaticDataCollectionEnabled, + options: this.options + }; + } + /** * This function will throw an Error if the App has already been deleted - * use before performing API actions on the App. diff --git a/packages/app/src/lite/firebaseAppLite.ts b/packages/app/src/lite/firebaseAppLite.ts index bc1be941a97..14795bb104c 100644 --- a/packages/app/src/lite/firebaseAppLite.ts +++ b/packages/app/src/lite/firebaseAppLite.ts @@ -141,4 +141,12 @@ export class FirebaseAppLiteImpl implements FirebaseApp { throw ERROR_FACTORY.create(AppError.APP_DELETED, { appName: this.name_ }); } } + + toJSON(): object { + return { + name: this.name, + automaticDataCollectionEnabled: this.automaticDataCollectionEnabled, + options: this.options + }; + } } diff --git a/packages/app/test/firebaseApp.test.ts b/packages/app/test/firebaseApp.test.ts index cf465b04ab8..da16c6c8411 100644 --- a/packages/app/test/firebaseApp.test.ts +++ b/packages/app/test/firebaseApp.test.ts @@ -336,6 +336,11 @@ function firebaseAppTests( expect(() => firebase.app('toString')).throws(/'toString'.*created/i); }); + it('JSON.stringify() does not throw', () => { + const app = firebase.initializeApp({}, 'new-app'); + JSON.stringify(app); + }); + describe('Check for bad app names', () => { const tests = ['', 123, false, null]; for (const data of tests) { diff --git a/packages/firestore/src/exp/snapshot.ts b/packages/firestore/src/exp/snapshot.ts index bda734a8ecb..414f0300ada 100644 --- a/packages/firestore/src/exp/snapshot.ts +++ b/packages/firestore/src/exp/snapshot.ts @@ -214,9 +214,9 @@ export interface DocumentChange { * access will return 'undefined'. You can use the `exists()` method to * explicitly verify a document's existence. */ -export class DocumentSnapshot extends LiteDocumentSnapshot< - T -> { +export class DocumentSnapshot< + T = DocumentData +> extends LiteDocumentSnapshot { private readonly _firestoreImpl: FirebaseFirestore; /** @@ -329,9 +329,9 @@ export class DocumentSnapshot extends LiteDocumentSnapshot< * `exists` property will always be true and `data()` will never return * 'undefined'. */ -export class QueryDocumentSnapshot extends DocumentSnapshot< - T -> { +export class QueryDocumentSnapshot< + T = DocumentData +> extends DocumentSnapshot { /** * Retrieves all fields in the document as an `Object`. * diff --git a/packages/firestore/src/lite/database.ts b/packages/firestore/src/lite/database.ts index 9d0d2906171..84fd0a993a9 100644 --- a/packages/firestore/src/lite/database.ts +++ b/packages/firestore/src/lite/database.ts @@ -134,6 +134,14 @@ export class FirebaseFirestore implements FirestoreService { return this._terminateTask; } + toJSON(): object { + return { + app: this._app, + databaseId: this._databaseId, + settings: this._settings + }; + } + /** * Terminates all components used by this client. Subclasses can override * this method to clean up their own dependencies, but must also call this diff --git a/packages/firestore/src/lite/snapshot.ts b/packages/firestore/src/lite/snapshot.ts index 5aeef50947c..350dd109b13 100644 --- a/packages/firestore/src/lite/snapshot.ts +++ b/packages/firestore/src/lite/snapshot.ts @@ -212,9 +212,9 @@ export class DocumentSnapshot { * `exists` property will always be true and `data()` will never return * 'undefined'. */ -export class QueryDocumentSnapshot extends DocumentSnapshot< - T -> { +export class QueryDocumentSnapshot< + T = DocumentData +> extends DocumentSnapshot { /** * Retrieves all fields in the document as an `Object`. * diff --git a/packages/firestore/test/integration/api/query.test.ts b/packages/firestore/test/integration/api/query.test.ts index 10bab8cc329..eab9995dd4a 100644 --- a/packages/firestore/test/integration/api/query.test.ts +++ b/packages/firestore/test/integration/api/query.test.ts @@ -155,9 +155,7 @@ apiDescribe('Queries', (persistence: boolean) => { .onSnapshot(storeLimitEvent.storeEvent); // Setup mirroring `limitToLast` query - const storeLimitToLastEvent = new EventsAccumulator< - firestore.QuerySnapshot - >(); + const storeLimitToLastEvent = new EventsAccumulator(); let limitToLastUnlisten = collection .orderBy('sort', 'desc') .limitToLast(2) diff --git a/packages/firestore/test/integration/api/type.test.ts b/packages/firestore/test/integration/api/type.test.ts index 310b1906054..9c48dd40c68 100644 --- a/packages/firestore/test/integration/api/type.test.ts +++ b/packages/firestore/test/integration/api/type.test.ts @@ -53,9 +53,7 @@ apiDescribe('Firestore', (persistence: boolean) => { docSnapshot = querySnapshot.docs[0]; expect(docSnapshot.data()).to.deep.equal(data); - const eventsAccumulator = new EventsAccumulator< - firestore.QuerySnapshot - >(); + const eventsAccumulator = new EventsAccumulator(); const unlisten = collection.onSnapshot(eventsAccumulator.storeEvent); querySnapshot = await eventsAccumulator.awaitEvent(); docSnapshot = querySnapshot.docs[0]; diff --git a/packages/firestore/test/unit/api/database.test.ts b/packages/firestore/test/unit/api/database.test.ts index 0aff83ac970..f01c5d509eb 100644 --- a/packages/firestore/test/unit/api/database.test.ts +++ b/packages/firestore/test/unit/api/database.test.ts @@ -32,6 +32,10 @@ describe('CollectionReference', () => { expectEqual(collectionReference('foo'), collectionReference('foo')); expectNotEqual(collectionReference('foo'), collectionReference('bar')); }); + + it('JSON.stringify() does not throw', () => { + JSON.stringify(collectionReference('foo')); + }); }); describe('DocumentReference', () => { @@ -42,6 +46,10 @@ describe('DocumentReference', () => { documentReference('rooms/bar') ); }); + + it('JSON.stringify() does not throw', () => { + JSON.stringify(documentReference('foo/bar')); + }); }); describe('DocumentSnapshot', () => { @@ -72,6 +80,10 @@ describe('DocumentSnapshot', () => { documentSnapshot('rooms/bar', { a: 1 }, false) ); }); + + it('JSON.stringify() does not throw', () => { + JSON.stringify(documentSnapshot('foo/bar', { a: 1 }, true)); + }); }); describe('Query', () => { @@ -79,6 +91,10 @@ describe('Query', () => { expectEqual(query('foo'), query('foo')); expectNotEqual(query('foo'), query('bar')); }); + + it('JSON.stringify() does not throw', () => { + JSON.stringify(query('foo')); + }); }); describe('QuerySnapshot', () => { @@ -123,6 +139,12 @@ describe('QuerySnapshot', () => { querySnapshot('foo', {}, { a: { a: 1 } }, keys('foo/a'), false, true) ); }); + + it('JSON.stringify() does not throw', () => { + JSON.stringify( + querySnapshot('foo', {}, { a: { a: 1 } }, keys(), false, false) + ); + }); }); describe('SnapshotMetadata', () => { diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index 500a558fb6c..d5b82968fc0 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -1047,9 +1047,9 @@ export class SpecBuilder { return { key: SpecBuilder.keyToSpec(doc.key), version: doc.version.toMicroseconds(), - value: userDataWriter.convertValue(doc.toProto()) as JsonObject< - unknown - >, + value: userDataWriter.convertValue( + doc.toProto() + ) as JsonObject, options: { hasLocalMutations: doc.hasLocalMutations, hasCommittedMutations: doc.hasCommittedMutations