Skip to content

Commit fccd633

Browse files
committed
QuerySnapshot jsonSchema
1 parent 011ac37 commit fccd633

File tree

1 file changed

+72
-86
lines changed

1 file changed

+72
-86
lines changed

packages/firestore/src/api/snapshot.ts

Lines changed: 72 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
QuerySnapshotBundleData
5252
} from '../util/bundle_builder_impl';
5353
import { Code, FirestoreError } from '../util/error';
54+
import { Property, property, validateJSON } from '../util/json_validation';
5455
import { AutoId } from '../util/misc';
5556

5657
import { Firestore } from './database';
@@ -544,7 +545,7 @@ export class DocumentSnapshot<
544545
throw new FirestoreError(
545546
Code.FAILED_PRECONDITION,
546547
'DocumentSnapshot.toJSON() attempted to serialize a document with pending writes. ' +
547-
'Await waitForPendingWrites() before invoking toJSON().'
548+
'Await waitForPendingWrites() before invoking toJSON().'
548549
);
549550
}
550551
builder.addBundleDocument(
@@ -612,8 +613,7 @@ export class DocumentSnapshot<
612613
).getElements();
613614
if (elements.length === 0) {
614615
error = 'No snapshat data was found in the bundle.';
615-
}
616-
if (
616+
} else if (
617617
elements.length !== 2 ||
618618
!elements[0].payload.documentMetadata ||
619619
!elements[1].payload.document
@@ -782,7 +782,7 @@ export class QuerySnapshot<
782782
throw new FirestoreError(
783783
Code.INVALID_ARGUMENT,
784784
'To include metadata changes with your document changes, you must ' +
785-
'also pass { includeMetadataChanges:true } to onSnapshot().'
785+
'also pass { includeMetadataChanges:true } to onSnapshot().'
786786
);
787787
}
788788

@@ -797,6 +797,14 @@ export class QuerySnapshot<
797797
return this._cachedChanges;
798798
}
799799

800+
static _jsonSchemaVersion: string = 'firestore/querySnapshot/1.0';
801+
static _jsonSchema = {
802+
type: property('string', QuerySnapshot._jsonSchemaVersion),
803+
bundleSource: property('string'),
804+
bundleName: property('string'),
805+
bundle: property('string')
806+
};
807+
800808
/**
801809
* Returns a JSON-serializable representation of this `QuerySnapshot` instance.
802810
*
@@ -805,6 +813,7 @@ export class QuerySnapshot<
805813
toJSON(): object {
806814
// eslint-disable-next-line @typescript-eslint/no-explicit-any
807815
const result: any = {};
816+
result['type'] = QuerySnapshot._jsonSchemaVersion;
808817
result['bundleSource'] = 'QuerySnapshot';
809818
result['bundleName'] = AutoId.newId();
810819

@@ -829,7 +838,7 @@ export class QuerySnapshot<
829838
throw new FirestoreError(
830839
Code.FAILED_PRECONDITION,
831840
'QuerySnapshot.toJSON() attempted to serialize a document with pending writes. ' +
832-
'Await waitForPendingWrites() before invoking toJSON().'
841+
'Await waitForPendingWrites() before invoking toJSON().'
833842
);
834843
}
835844
docBundleDataArray.push(
@@ -867,92 +876,69 @@ export class QuerySnapshot<
867876
db: Firestore,
868877
json: object
869878
): QuerySnapshot<AppModelType, DbModelType> | null {
870-
const requiredFields = ['bundle', 'bundleName', 'bundleSource'];
871-
let error: string | undefined = undefined;
872-
let bundleString: string = '';
873-
for (const key of requiredFields) {
874-
if (!(key in json)) {
875-
error = `json missing required field: ${key}`;
879+
if (validateJSON(json, QuerySnapshot._jsonSchema)) {
880+
if (json.bundleSource !== 'QuerySnapshot') {
881+
throw new FirestoreError(Code.INVALID_ARGUMENT, "Expected 'bundleSource' field to equal 'QuerySnapshot'");
876882
}
877-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
878-
const value = (json as any)[key];
879-
if (key === 'bundleSource') {
880-
if (typeof value !== 'string') {
881-
error = `json field 'bundleSource' must be a string.`;
882-
break;
883-
} else if (value !== 'QuerySnapshot') {
884-
error = "Expected 'bundleSource' field to equal 'QuerySnapshot'";
885-
break;
886-
}
887-
} else if (key === 'bundle') {
888-
if (typeof value !== 'string') {
889-
error = `json field 'bundle' must be a string.`;
890-
break;
891-
}
892-
bundleString = value;
893-
}
894-
}
895-
if (error) {
896-
throw new FirestoreError(Code.INVALID_ARGUMENT, error);
897-
}
898-
// Parse the bundle data.
899-
const serializer = newSerializer(db._databaseId);
900-
const bundleReader = createBundleReaderSync(bundleString, serializer);
901-
const bundleMetadata = bundleReader.getMetadata();
902-
const elements = bundleReader.getElements();
903-
if (elements.length === 0) {
904-
throw new FirestoreError(
905-
Code.INVALID_ARGUMENT,
906-
'No snapshat data was found in the bundle.'
883+
// Parse the bundle data.
884+
const serializer = newSerializer(db._databaseId);
885+
const bundleReader = createBundleReaderSync(json.bundle, serializer);
886+
const bundleLoader: BundleLoader = new BundleLoader(
887+
bundleReader.getMetadata(),
888+
serializer
907889
);
908-
}
890+
const elements = bundleReader.getElements();
891+
for (const element of elements) {
892+
bundleLoader.addSizedElement(element);
893+
}
894+
const parsedNamedQueries = bundleLoader.queries;
895+
if (parsedNamedQueries.length !== 1) {
896+
throw new FirestoreError(
897+
Code.INVALID_ARGUMENT,
898+
`Snapshot data expected 1 query but found ${parsedNamedQueries.length} queries.`
899+
);
900+
}
909901

910-
const bundleLoader: BundleLoader = new BundleLoader(
911-
bundleMetadata,
912-
serializer
913-
);
914-
for (const element of elements) {
915-
bundleLoader.addSizedElement(element);
916-
}
917-
const parsedNamedQueries = bundleLoader.queries;
918-
if (parsedNamedQueries.length !== 1) {
919-
throw new FirestoreError(
920-
Code.INVALID_ARGUMENT,
921-
'Snapshot data contained more than one named query.'
922-
);
923-
}
902+
// Create an internal Query object from the named query in the budnle.
903+
const query = fromBundledQuery(parsedNamedQueries[0].bundledQuery!);
924904

925-
const query = fromBundledQuery(parsedNamedQueries[0].bundledQuery!);
926-
// convert bundle data into the types that the DocumentSnapshot constructore requires.
927-
const liteUserDataWriter = new LiteUserDataWriter(db);
905+
// Construct the arrays of document data for the query.
906+
const bundledDocuments = bundleLoader.documents;
907+
const documentSet = new DocumentSet();
908+
const documentKeys = documentKeySet();
909+
for (const bundledDocumet of bundledDocuments) {
910+
const document = fromDocument(serializer, bundledDocumet.document!);
911+
documentSet.add(document);
912+
const documentPath = ResourcePath.fromString(
913+
bundledDocumet.metadata.name!
914+
);
915+
documentKeys.add(new DocumentKey(documentPath));
916+
}
928917

929-
const bundledDocuments = bundleLoader.documents;
930-
const documentSet = new DocumentSet();
931-
const documentKeys = documentKeySet();
932-
for (const bundledDocumet of bundledDocuments) {
933-
const document = fromDocument(serializer, bundledDocumet.document!);
934-
documentSet.add(document);
935-
const documentPath = ResourcePath.fromString(
936-
bundledDocumet.metadata.name!
918+
// Create a view snapshot of the query and documents.
919+
const viewSnapshot = ViewSnapshot.fromInitialDocuments(
920+
query,
921+
documentSet,
922+
documentKeys,
923+
/* fromCache= */ false,
924+
/* hasCachedResults= */ false
937925
);
938-
documentKeys.add(new DocumentKey(documentPath));
939-
}
940926

941-
const viewSnapshot = ViewSnapshot.fromInitialDocuments(
942-
query,
943-
documentSet,
944-
documentKeys,
945-
false, // fromCache
946-
false // hasCachedResults
947-
);
927+
// Create an external Query object, required to construct the QuerySnapshot.
928+
const externalQuery = new Query<AppModelType, DbModelType>(db, null, query);
948929

949-
const externalQuery = new Query<AppModelType, DbModelType>(db, null, query);
930+
// Return a new QuerySnapshot with all of the collected data.
931+
return new QuerySnapshot<AppModelType, DbModelType>(
932+
db,
933+
new LiteUserDataWriter(db),
934+
externalQuery,
935+
viewSnapshot
936+
);
937+
}
950938

951-
return new QuerySnapshot<AppModelType, DbModelType>(
952-
db,
953-
liteUserDataWriter,
954-
externalQuery,
955-
viewSnapshot
939+
throw new FirestoreError(
940+
Code.INTERNAL,
941+
'Unexpected error creating QuerySnapshot from JSON.'
956942
);
957943
}
958944
}
@@ -977,10 +963,10 @@ export function changesFromSnapshot<
977963
);
978964
debugAssert(
979965
!lastDoc ||
980-
newQueryComparator(querySnapshot._snapshot.query)(
981-
lastDoc,
982-
change.doc
983-
) < 0,
966+
newQueryComparator(querySnapshot._snapshot.query)(
967+
lastDoc,
968+
change.doc
969+
) < 0,
984970
'Got added events in wrong order'
985971
);
986972
const doc = new QueryDocumentSnapshot<AppModelType, DbModelType>(

0 commit comments

Comments
 (0)