Skip to content

Commit df70734

Browse files
committed
Implementation. Test fails client side.
Can't easily test bundles client side. They can't be generated on the client. I tried pre-generating some but the bundle must align with the Firebase project name, and the Firebase project name varies across test targets (prod, local, etc).
1 parent 96a49f7 commit df70734

File tree

12 files changed

+673
-219
lines changed

12 files changed

+673
-219
lines changed

packages/firestore/src/api/reference_impl.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,10 +1005,7 @@ export function onSnapshotResume<
10051005
let curArg = 0;
10061006
let options: SnapshotListenOptions | undefined = undefined;
10071007
if (typeof args[curArg] === 'object' && !isPartialObserver(args[curArg])) {
1008-
console.error('DEDB arg 0 is SnapsotLsitenOptions');
10091008
options = args[curArg++] as SnapshotListenOptions;
1010-
} else {
1011-
console.error('DEDB arg 0 is NOT SnapsotLsitenOptions');
10121009
}
10131010

10141011
if (json.bundleSource === 'QuerySnapshot') {
@@ -1275,6 +1272,10 @@ function onSnapshotDocumentSnapshotBundle<
12751272
converter ? converter : null,
12761273
DocumentKey.fromPath(json.bundleName)
12771274
);
1275+
console.error(
1276+
'DEDB onSnapshotDocumentSnapshotBundle callong onSnapshot with docRef: ',
1277+
docReference.path.toString()
1278+
);
12781279
internalUnsubscribe = onSnapshot(
12791280
docReference as DocumentReference<AppModelType, DbModelType>,
12801281
options ? options : {},

packages/firestore/src/api/snapshot.ts

Lines changed: 55 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ import { DocumentKey } from '../model/document_key';
4343
import { DocumentSet } from '../model/document_set';
4444
import { ResourcePath } from '../model/path';
4545
import { newSerializer } from '../platform/serializer';
46+
import {
47+
buildQuerySnapshotJsonBundle,
48+
buildDocumentSnapshotJsonBundle
49+
} from '../platform/snapshot_to_json';
4650
import { fromDocument } from '../remote/serializer';
4751
import { debugAssert, fail } from '../util/assert';
48-
import {
49-
BundleBuilder,
50-
DocumentSnapshotBundleData,
51-
QuerySnapshotBundleData
52-
} from '../util/bundle_builder_impl';
52+
5353
import { Code, FirestoreError } from '../util/error';
5454
// API extractor fails importing 'property' unless we also explicitly import 'Property'.
5555
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports-ts
@@ -529,6 +529,13 @@ export class DocumentSnapshot<
529529
* @returns a JSON representation of this object.
530530
*/
531531
toJSON(): object {
532+
if (this.metadata.hasPendingWrites) {
533+
throw new FirestoreError(
534+
Code.FAILED_PRECONDITION,
535+
'DocumentSnapshot.toJSON() attempted to serialize a document with pending writes. ' +
536+
'Await waitForPendingWrites() before invoking toJSON().'
537+
);
538+
}
532539
const document = this._document;
533540
// eslint-disable-next-line @typescript-eslint/no-explicit-any
534541
const result: any = {};
@@ -544,29 +551,16 @@ export class DocumentSnapshot<
544551
) {
545552
return result;
546553
}
547-
const builder: BundleBuilder = new BundleBuilder(
548-
this._firestore,
549-
AutoId.newId()
550-
);
551554
const documentData = this._userDataWriter.convertObjectMap(
552555
document.data.value.mapValue.fields,
553556
'previous'
554557
);
555-
if (this.metadata.hasPendingWrites) {
556-
throw new FirestoreError(
557-
Code.FAILED_PRECONDITION,
558-
'DocumentSnapshot.toJSON() attempted to serialize a document with pending writes. ' +
559-
'Await waitForPendingWrites() before invoking toJSON().'
560-
);
561-
}
562-
builder.addBundleDocument(
563-
documentToDocumentSnapshotBundleData(
564-
this.ref.path,
565-
documentData,
566-
document
567-
)
558+
result['bundle'] = buildDocumentSnapshotJsonBundle(
559+
this._firestore,
560+
document,
561+
documentData,
562+
this.ref.path
568563
);
569-
result['bundle'] = builder.build();
570564
return result;
571565
}
572566
}
@@ -611,6 +605,12 @@ export function documentSnapshotFromJSON<
611605
...args: unknown[]
612606
): DocumentSnapshot<AppModelType, DbModelType> {
613607
if (validateJSON(json, DocumentSnapshot._jsonSchema)) {
608+
if (json.bundle === 'NOT SUPPORTED') {
609+
throw new FirestoreError(
610+
Code.INVALID_ARGUMENT,
611+
'The provided JSON object was created in a client environment, which is not supported.'
612+
);
613+
}
614614
// Parse the bundle data.
615615
const serializer = newSerializer(db._databaseId);
616616
const bundleReader = createBundleReaderSync(json.bundle, serializer);
@@ -831,52 +831,48 @@ export class QuerySnapshot<
831831
* @returns a JSON representation of this object.
832832
*/
833833
toJSON(): object {
834+
if (this.metadata.hasPendingWrites) {
835+
throw new FirestoreError(
836+
Code.FAILED_PRECONDITION,
837+
'QuerySnapshot.toJSON() attempted to serialize a document with pending writes. ' +
838+
'Await waitForPendingWrites() before invoking toJSON().'
839+
);
840+
}
834841
// eslint-disable-next-line @typescript-eslint/no-explicit-any
835842
const result: any = {};
836843
result['type'] = QuerySnapshot._jsonSchemaVersion;
837844
result['bundleSource'] = 'QuerySnapshot';
838845
result['bundleName'] = AutoId.newId();
839846

840-
const builder: BundleBuilder = new BundleBuilder(
841-
this._firestore,
842-
result['bundleName']
843-
);
844847
const databaseId = this._firestore._databaseId.database;
845848
const projectId = this._firestore._databaseId.projectId;
846849
const parent = `projects/${projectId}/databases/${databaseId}/documents`;
847-
const docBundleDataArray: DocumentSnapshotBundleData[] = [];
848-
const docArray = this.docs;
849-
docArray.forEach(doc => {
850+
const documents: Document[] = [];
851+
const documentData: DocumentData[] = [];
852+
const paths: string[] = [];
853+
854+
this.docs.forEach(doc => {
850855
if (doc._document === null) {
851856
return;
852857
}
853-
const documentData = this._userDataWriter.convertObjectMap(
854-
doc._document.data.value.mapValue.fields,
855-
'previous'
856-
);
857-
if (this.metadata.hasPendingWrites) {
858-
throw new FirestoreError(
859-
Code.FAILED_PRECONDITION,
860-
'QuerySnapshot.toJSON() attempted to serialize a document with pending writes. ' +
861-
'Await waitForPendingWrites() before invoking toJSON().'
862-
);
863-
}
864-
docBundleDataArray.push(
865-
documentToDocumentSnapshotBundleData(
866-
doc.ref.path,
867-
documentData,
868-
doc._document
858+
documents.push(doc._document);
859+
documentData.push(
860+
this._userDataWriter.convertObjectMap(
861+
doc._document.data.value.mapValue.fields,
862+
'previous'
869863
)
870864
);
865+
paths.push(doc.ref.path);
871866
});
872-
const bundleData: QuerySnapshotBundleData = {
873-
name: result['bundleName'],
874-
query: this.query._query,
867+
result['bundle'] = buildQuerySnapshotJsonBundle(
868+
this._firestore,
869+
this.query._query,
870+
result['bundleName'],
875871
parent,
876-
docBundleDataArray
877-
};
878-
builder.addBundleQuery(bundleData);
879-
result['bundle'] = builder.build();
872+
paths,
873+
documents,
874+
documentData
875+
);
880876
return result;
881877
}
882878
}
@@ -921,6 +917,12 @@ export function querySnapshotFromJSON<
921917
...args: unknown[]
922918
): QuerySnapshot<AppModelType, DbModelType> {
923919
if (validateJSON(json, QuerySnapshot._jsonSchema)) {
920+
if (json.bundle === 'NOT SUPPORTED') {
921+
throw new FirestoreError(
922+
Code.INVALID_ARGUMENT,
923+
'The provided JSON object was created in a client environment, which is not supported.'
924+
);
925+
}
924926
// Parse the bundle data.
925927
const serializer = newSerializer(db._databaseId);
926928
const bundleReader = createBundleReaderSync(json.bundle, serializer);
@@ -1123,20 +1125,3 @@ export function snapshotEqual<AppModelType, DbModelType extends DocumentData>(
11231125

11241126
return false;
11251127
}
1126-
1127-
// Formats Document data for bundling a DocumentSnapshot.
1128-
function documentToDocumentSnapshotBundleData(
1129-
path: string,
1130-
documentData: DocumentData,
1131-
document: Document
1132-
): DocumentSnapshotBundleData {
1133-
return {
1134-
documentData,
1135-
documentKey: document.mutableCopy().key,
1136-
documentPath: path,
1137-
documentExists: true,
1138-
createdTime: document.createTime.toTimestamp(),
1139-
readTime: document.readTime.toTimestamp(),
1140-
versionTime: document.version.toTimestamp()
1141-
};
1142-
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/** Return the Platform-specific build JSON bundle implementations. */
19+
import { Query } from '../../core/query';
20+
import { DocumentData } from '../../lite-api/reference';
21+
import { Document } from '../../model/document';
22+
import { Firestore } from '../../api/database';
23+
24+
25+
export function buildDocumentSnapshotJsonBundle(
26+
db: Firestore,
27+
document: Document,
28+
docData: DocumentData,
29+
path: string
30+
): string {
31+
return "NOT SUPPORTED";
32+
}
33+
34+
export function buildQuerySnapshotJsonBundle(
35+
db: Firestore,
36+
query: Query,
37+
bundleName: string,
38+
parent: string,
39+
paths: string[],
40+
docs: Document[],
41+
documentData: DocumentData[]
42+
) : string {
43+
return "NOT SUPPORTED";
44+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export * from '../browser/snapshot_to_json';
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/** Return the Platform-specific build JSON bundle implementations. */
19+
import { Firestore } from '../../api/database';
20+
import { Query } from '../../core/query';
21+
import {
22+
DocumentData
23+
} from '../../lite-api/reference';
24+
import { Document } from '../../model/document';
25+
import {
26+
BundleBuilder,
27+
DocumentSnapshotBundleData,
28+
QuerySnapshotBundleData
29+
} from '../../util/bundle_builder_impl';
30+
import { AutoId } from '../../util/misc';
31+
32+
33+
export function buildDocumentSnapshotJsonBundle(
34+
db: Firestore,
35+
document: Document,
36+
docData: DocumentData,
37+
path: string
38+
): string {
39+
const builder: BundleBuilder = new BundleBuilder(
40+
db,
41+
AutoId.newId()
42+
);
43+
builder.addBundleDocument(documentToDocumentSnapshotBundleData(
44+
path,
45+
docData,
46+
document
47+
));
48+
return builder.build();
49+
}
50+
51+
export function buildQuerySnapshotJsonBundle(
52+
db: Firestore,
53+
query: Query,
54+
bundleName: string,
55+
parent: string,
56+
paths: string[],
57+
docs: Document[],
58+
documentData: DocumentData[]
59+
) : string {
60+
const docBundleDataArray: DocumentSnapshotBundleData[] = [];
61+
for (let i = 0; i < docs.length; i++) {
62+
docBundleDataArray.push(
63+
documentToDocumentSnapshotBundleData(
64+
paths[i],
65+
documentData[i],
66+
docs[i]
67+
)
68+
);
69+
}
70+
const bundleData: QuerySnapshotBundleData = {
71+
name: bundleName,
72+
query,
73+
parent,
74+
docBundleDataArray
75+
};
76+
const builder: BundleBuilder = new BundleBuilder(db, bundleName);
77+
builder.addBundleQuery(bundleData);
78+
return builder.build();
79+
}
80+
81+
// Formats Document data for bundling a DocumentSnapshot.
82+
function documentToDocumentSnapshotBundleData(
83+
path: string,
84+
documentData: DocumentData,
85+
document: Document
86+
): DocumentSnapshotBundleData {
87+
return {
88+
documentData,
89+
documentKey: document.mutableCopy().key,
90+
documentPath: path,
91+
documentExists: true,
92+
createdTime: document.createTime.toTimestamp(),
93+
readTime: document.readTime.toTimestamp(),
94+
versionTime: document.version.toTimestamp()
95+
};
96+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export * from '../node/snapshot_to_json';

0 commit comments

Comments
 (0)