Skip to content

Commit 517f865

Browse files
authored
Merge 158b491 into a49774f
2 parents a49774f + 158b491 commit 517f865

File tree

11 files changed

+823
-454
lines changed

11 files changed

+823
-454
lines changed

packages/firestore/src/api/snapshot.ts

Lines changed: 54 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,12 @@ 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';
5352
import { Code, FirestoreError } from '../util/error';
5453
// API extractor fails importing 'property' unless we also explicitly import 'Property'.
5554
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports-ts
@@ -529,6 +528,13 @@ export class DocumentSnapshot<
529528
* @returns a JSON representation of this object.
530529
*/
531530
toJSON(): object {
531+
if (this.metadata.hasPendingWrites) {
532+
throw new FirestoreError(
533+
Code.FAILED_PRECONDITION,
534+
'DocumentSnapshot.toJSON() attempted to serialize a document with pending writes. ' +
535+
'Await waitForPendingWrites() before invoking toJSON().'
536+
);
537+
}
532538
const document = this._document;
533539
// eslint-disable-next-line @typescript-eslint/no-explicit-any
534540
const result: any = {};
@@ -544,29 +550,16 @@ export class DocumentSnapshot<
544550
) {
545551
return result;
546552
}
547-
const builder: BundleBuilder = new BundleBuilder(
548-
this._firestore,
549-
AutoId.newId()
550-
);
551553
const documentData = this._userDataWriter.convertObjectMap(
552554
document.data.value.mapValue.fields,
553555
'previous'
554556
);
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-
)
557+
result['bundle'] = buildDocumentSnapshotJsonBundle(
558+
this._firestore,
559+
document,
560+
documentData,
561+
this.ref.path
568562
);
569-
result['bundle'] = builder.build();
570563
return result;
571564
}
572565
}
@@ -611,6 +604,12 @@ export function documentSnapshotFromJSON<
611604
converter?: FirestoreDataConverter<AppModelType, DbModelType>
612605
): DocumentSnapshot<AppModelType, DbModelType> {
613606
if (validateJSON(json, DocumentSnapshot._jsonSchema)) {
607+
if (json.bundle === 'NOT SUPPORTED') {
608+
throw new FirestoreError(
609+
Code.INVALID_ARGUMENT,
610+
'The provided JSON object was created in a client environment, which is not supported.'
611+
);
612+
}
614613
// Parse the bundle data.
615614
const serializer = newSerializer(db._databaseId);
616615
const bundleReader = createBundleReaderSync(json.bundle, serializer);
@@ -825,52 +824,48 @@ export class QuerySnapshot<
825824
* @returns a JSON representation of this object.
826825
*/
827826
toJSON(): object {
827+
if (this.metadata.hasPendingWrites) {
828+
throw new FirestoreError(
829+
Code.FAILED_PRECONDITION,
830+
'QuerySnapshot.toJSON() attempted to serialize a document with pending writes. ' +
831+
'Await waitForPendingWrites() before invoking toJSON().'
832+
);
833+
}
828834
// eslint-disable-next-line @typescript-eslint/no-explicit-any
829835
const result: any = {};
830836
result['type'] = QuerySnapshot._jsonSchemaVersion;
831837
result['bundleSource'] = 'QuerySnapshot';
832838
result['bundleName'] = AutoId.newId();
833839

834-
const builder: BundleBuilder = new BundleBuilder(
835-
this._firestore,
836-
result['bundleName']
837-
);
838840
const databaseId = this._firestore._databaseId.database;
839841
const projectId = this._firestore._databaseId.projectId;
840842
const parent = `projects/${projectId}/databases/${databaseId}/documents`;
841-
const docBundleDataArray: DocumentSnapshotBundleData[] = [];
842-
const docArray = this.docs;
843-
docArray.forEach(doc => {
843+
const documents: Document[] = [];
844+
const documentData: DocumentData[] = [];
845+
const paths: string[] = [];
846+
847+
this.docs.forEach(doc => {
844848
if (doc._document === null) {
845849
return;
846850
}
847-
const documentData = this._userDataWriter.convertObjectMap(
848-
doc._document.data.value.mapValue.fields,
849-
'previous'
850-
);
851-
if (this.metadata.hasPendingWrites) {
852-
throw new FirestoreError(
853-
Code.FAILED_PRECONDITION,
854-
'QuerySnapshot.toJSON() attempted to serialize a document with pending writes. ' +
855-
'Await waitForPendingWrites() before invoking toJSON().'
856-
);
857-
}
858-
docBundleDataArray.push(
859-
documentToDocumentSnapshotBundleData(
860-
doc.ref.path,
861-
documentData,
862-
doc._document
851+
documents.push(doc._document);
852+
documentData.push(
853+
this._userDataWriter.convertObjectMap(
854+
doc._document.data.value.mapValue.fields,
855+
'previous'
863856
)
864857
);
858+
paths.push(doc.ref.path);
865859
});
866-
const bundleData: QuerySnapshotBundleData = {
867-
name: result['bundleName'],
868-
query: this.query._query,
860+
result['bundle'] = buildQuerySnapshotJsonBundle(
861+
this._firestore,
862+
this.query._query,
863+
result['bundleName'],
869864
parent,
870-
docBundleDataArray
871-
};
872-
builder.addBundleQuery(bundleData);
873-
result['bundle'] = builder.build();
865+
paths,
866+
documents,
867+
documentData
868+
);
874869
return result;
875870
}
876871
}
@@ -915,6 +910,12 @@ export function querySnapshotFromJSON<
915910
converter?: FirestoreDataConverter<AppModelType, DbModelType>
916911
): QuerySnapshot<AppModelType, DbModelType> {
917912
if (validateJSON(json, QuerySnapshot._jsonSchema)) {
913+
if (json.bundle === 'NOT SUPPORTED') {
914+
throw new FirestoreError(
915+
Code.INVALID_ARGUMENT,
916+
'The provided JSON object was created in a client environment, which is not supported.'
917+
);
918+
}
918919
// Parse the bundle data.
919920
const serializer = newSerializer(db._databaseId);
920921
const bundleReader = createBundleReaderSync(json.bundle, serializer);
@@ -1111,20 +1112,3 @@ export function snapshotEqual<AppModelType, DbModelType extends DocumentData>(
11111112

11121113
return false;
11131114
}
1114-
1115-
// Formats Document data for bundling a DocumentSnapshot.
1116-
function documentToDocumentSnapshotBundleData(
1117-
path: string,
1118-
documentData: DocumentData,
1119-
document: Document
1120-
): DocumentSnapshotBundleData {
1121-
return {
1122-
documentData,
1123-
documentKey: document.mutableCopy().key,
1124-
documentPath: path,
1125-
documentExists: true,
1126-
createdTime: document.createTime.toTimestamp(),
1127-
readTime: document.readTime.toTimestamp(),
1128-
versionTime: document.version.toTimestamp()
1129-
};
1130-
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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 { DocumentData } from '../../lite-api/reference';
22+
import { Document } from '../../model/document';
23+
24+
export function buildDocumentSnapshotJsonBundle(
25+
db: Firestore,
26+
document: Document,
27+
docData: DocumentData,
28+
path: string
29+
): string {
30+
return 'NOT SUPPORTED';
31+
}
32+
33+
export function buildQuerySnapshotJsonBundle(
34+
db: Firestore,
35+
query: Query,
36+
bundleName: string,
37+
parent: string,
38+
paths: string[],
39+
docs: Document[],
40+
documentData: DocumentData[]
41+
): string {
42+
return 'NOT SUPPORTED';
43+
}
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: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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 { DocumentData } from '../../lite-api/reference';
22+
import { Document } from '../../model/document';
23+
import {
24+
BundleBuilder,
25+
DocumentSnapshotBundleData,
26+
QuerySnapshotBundleData
27+
} from '../../util/bundle_builder_impl';
28+
import { AutoId } from '../../util/misc';
29+
30+
export function buildDocumentSnapshotJsonBundle(
31+
db: Firestore,
32+
document: Document,
33+
docData: DocumentData,
34+
path: string
35+
): string {
36+
const builder: BundleBuilder = new BundleBuilder(db, AutoId.newId());
37+
builder.addBundleDocument(
38+
documentToDocumentSnapshotBundleData(path, docData, document)
39+
);
40+
return builder.build();
41+
}
42+
43+
export function buildQuerySnapshotJsonBundle(
44+
db: Firestore,
45+
query: Query,
46+
bundleName: string,
47+
parent: string,
48+
paths: string[],
49+
docs: Document[],
50+
documentData: DocumentData[]
51+
): string {
52+
const docBundleDataArray: DocumentSnapshotBundleData[] = [];
53+
for (let i = 0; i < docs.length; i++) {
54+
docBundleDataArray.push(
55+
documentToDocumentSnapshotBundleData(paths[i], documentData[i], docs[i])
56+
);
57+
}
58+
const bundleData: QuerySnapshotBundleData = {
59+
name: bundleName,
60+
query,
61+
parent,
62+
docBundleDataArray
63+
};
64+
const builder: BundleBuilder = new BundleBuilder(db, bundleName);
65+
builder.addBundleQuery(bundleData);
66+
return builder.build();
67+
}
68+
69+
// Formats Document data for bundling a DocumentSnapshot.
70+
function documentToDocumentSnapshotBundleData(
71+
path: string,
72+
documentData: DocumentData,
73+
document: Document
74+
): DocumentSnapshotBundleData {
75+
return {
76+
documentData,
77+
documentKey: document.mutableCopy().key,
78+
documentPath: path,
79+
documentExists: true,
80+
createdTime: document.createTime.toTimestamp(),
81+
readTime: document.readTime.toTimestamp(),
82+
versionTime: document.version.toTimestamp()
83+
};
84+
}
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';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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 {
19+
buildDocumentSnapshotJsonBundle,
20+
buildQuerySnapshotJsonBundle
21+
} from '../browser/snapshot_to_json';

0 commit comments

Comments
 (0)