Skip to content

Commit ffff490

Browse files
committed
Successfully tested bundling a QuerySnapshot
Also includes: - format - lint fixes. - Comments for bundler methods - Addition of DocumentSnapshotBundleData & QuerySnapshotBundleData types.
1 parent 5e3620d commit ffff490

File tree

2 files changed

+73
-97
lines changed

2 files changed

+73
-97
lines changed

packages/firestore/src/api/snapshot.ts

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ import { AbstractUserDataWriter } from '../lite-api/user_data_writer';
3636
import { Document } from '../model/document';
3737
import { DocumentKey } from '../model/document_key';
3838
import { debugAssert, fail } from '../util/assert';
39-
import { BundleBuilder, DocumentBundleData } from '../util/bundle_builder_impl';
39+
import {
40+
BundleBuilder,
41+
DocumentSnapshotBundleData,
42+
QuerySnapshotBundleData
43+
} from '../util/bundle_builder_impl';
4044
import { Code, FirestoreError } from '../util/error';
4145
import { AutoId } from '../util/misc';
4246

@@ -500,34 +504,26 @@ export class DocumentSnapshot<
500504
}
501505

502506
toJSON(): object {
507+
const document = this._document;
503508
if (
504-
!this._document ||
505-
!this._document.isValidDocument() ||
506-
!this._document.isFoundDocument()
509+
!document ||
510+
!document.isValidDocument() ||
511+
!document.isFoundDocument()
507512
) {
508513
return { bundle: '' };
509514
}
510-
511515
const builder: BundleBuilder = new BundleBuilder(
512516
this._firestore,
513517
AutoId.newId()
514518
);
515519
const documentData = this._userDataWriter.convertObjectMap(
516-
this._document.data.value.mapValue.fields,
520+
document.data.value.mapValue.fields,
517521
'previous'
518522
);
519-
520523
builder.addBundleDocument(
521-
DocumentToDocumentBundleData(
522-
this._firestore,
523-
this.ref.path,
524-
documentData,
525-
this._document
526-
)
524+
ToDocumentSnapshotBundleData(this.ref.path, documentData, document)
527525
);
528-
return {
529-
bundle: builder.build()
530-
};
526+
return { bundle: builder.build() };
531527
}
532528
}
533529

@@ -690,7 +686,10 @@ export class QuerySnapshot<
690686
this._firestore,
691687
AutoId.newId()
692688
);
693-
const docBundleDataArray: DocumentBundleData[] = [];
689+
const databaseId = this._firestore._databaseId.database;
690+
const projectId = this._firestore._databaseId.projectId;
691+
const parent = `projects/${projectId}/databases/${databaseId}/documents`;
692+
const docBundleDataArray: DocumentSnapshotBundleData[] = [];
694693
const docArray = this.docs;
695694
docArray.forEach(doc => {
696695
if (doc._document === null) {
@@ -701,16 +700,15 @@ export class QuerySnapshot<
701700
'previous'
702701
);
703702
docBundleDataArray.push(
704-
DocumentToDocumentBundleData(
705-
this._firestore,
706-
doc.ref.path,
707-
documentData,
708-
doc._document
709-
)
703+
ToDocumentSnapshotBundleData(doc.ref.path, documentData, doc._document)
710704
);
711705
});
712-
713-
builder.addBundleQuery(this.query._query, docBundleDataArray);
706+
const bundleData: QuerySnapshotBundleData = {
707+
query: this.query._query,
708+
parent,
709+
docBundleDataArray
710+
};
711+
builder.addBundleQuery(bundleData);
714712
return { bundle: builder.build() };
715713
}
716714
}
@@ -853,12 +851,12 @@ export function snapshotEqual<AppModelType, DbModelType extends DocumentData>(
853851
return false;
854852
}
855853

856-
function DocumentToDocumentBundleData(
857-
firestore: Firestore,
854+
// Formats Document data for bundling a DocumentSnapshot.
855+
function ToDocumentSnapshotBundleData(
858856
path: string,
859857
documentData: DocumentData,
860858
document: Document
861-
): DocumentBundleData {
859+
): DocumentSnapshotBundleData {
862860
return {
863861
documentData,
864862
documentKey: document.mutableCopy().key,

packages/firestore/src/util/bundle_builder_impl.ts

Lines changed: 47 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {
4343
Document as ProtoDocument,
4444
Document
4545
} from '../protos/firestore_proto_api';
46+
import { AutoId } from '../util/misc';
4647

4748
import { debugAssert } from './assert';
4849

@@ -82,7 +83,9 @@ export class BundleBuilder {
8283
);
8384
}
8485

85-
toBundleDocument(docBundleData: DocumentBundleData): ProtoDocument {
86+
private toBundleDocument(
87+
docBundleData: DocumentSnapshotBundleData
88+
): ProtoDocument {
8689
// TODO handle documents that have mutations
8790
debugAssert(
8891
!docBundleData.documentData.hasLocalMutations,
@@ -105,8 +108,16 @@ export class BundleBuilder {
105108
};
106109
}
107110

111+
/**
112+
* Adds data from a DocumentSnapshot to the bundle.
113+
* @internal
114+
* @param docBundleData A DocumentSnapshotBundleData containing information from the
115+
* DocumentSnapshot. Note we cannot accept a DocumentSnapshot directly due to a circular
116+
* dependency error.
117+
* @param queryName The name of the QuerySnapshot if this document is part of a Query.
118+
*/
108119
addBundleDocument(
109-
docBundleData: DocumentBundleData,
120+
docBundleData: DocumentSnapshotBundleData,
110121
queryName?: string
111122
): void {
112123
const originalDocument = this.documents.get(docBundleData.documentPath);
@@ -130,7 +141,6 @@ export class BundleBuilder {
130141
}
131142
});
132143
}
133-
134144
// Update `queries` to include both original and `queryName`.
135145
if (queryName) {
136146
const newDocument = this.documents.get(docBundleData.documentPath)!;
@@ -144,28 +154,33 @@ export class BundleBuilder {
144154
}
145155
}
146156

147-
addBundleQuery(query: Query, docBundleDataArray: DocumentBundleData[]): void {
148-
const queryTarget = toQueryTarget(this.serializer, queryToTarget(query));
149-
const name = queryTarget.parent.canonicalString();
150-
157+
/**
158+
* Adds data from a QuerySnapshot to the bundle.
159+
* @internal
160+
* @param docBundleData A QuerySnapshotBundleData containing information from the
161+
* QuerySnapshot. Note we cannot accept a QuerySnapshot directly due to a circular
162+
* dependency error.
163+
*/
164+
addBundleQuery(queryBundleData: QuerySnapshotBundleData): void {
165+
const name = AutoId.newId();
151166
if (this.namedQueries.has(name)) {
152167
throw new Error(`Query name conflict: ${name} has already been added.`);
153168
}
154-
155169
let latestReadTime = new Timestamp(0, 0);
156-
for (const docBundleData of docBundleDataArray) {
170+
for (const docBundleData of queryBundleData.docBundleDataArray) {
157171
this.addBundleDocument(docBundleData, name);
158172
if (docBundleData.readTime && docBundleData.readTime > latestReadTime) {
159173
latestReadTime = docBundleData.readTime;
160174
}
161175
}
162-
176+
const queryTarget = toQueryTarget(
177+
this.serializer,
178+
queryToTarget(queryBundleData.query)
179+
);
163180
const bundledQuery = {
164-
parent: queryTarget.parent.canonicalString(),
165-
structuredQuery: queryTarget.queryTarget.structuredQuery,
166-
limitType: null
181+
parent: queryBundleData.parent,
182+
structuredQuery: queryTarget.queryTarget.structuredQuery
167183
};
168-
169184
this.namedQueries.set(name, {
170185
name,
171186
bundledQuery,
@@ -226,19 +241,26 @@ export class BundleBuilder {
226241

227242
/**
228243
* Interface for an object that contains data required to bundle a DocumentSnapshot.
229-
* Accessing the methods of DocumentSnapshot directly to retreivew this data in this
230-
* implementation would create a circular dependency.
231-
*
232244
* @internal
233245
*/
234-
export interface DocumentBundleData {
235-
readonly documentData: DocumentData;
236-
readonly documentKey: DocumentKey;
237-
readonly documentPath: string;
238-
readonly documentExists: boolean;
239-
readonly createdTime: Timestamp;
240-
readonly readTime?: Timestamp;
241-
readonly versionTime: Timestamp;
246+
export interface DocumentSnapshotBundleData {
247+
documentData: DocumentData;
248+
documentKey: DocumentKey;
249+
documentPath: string;
250+
documentExists: boolean;
251+
createdTime: Timestamp;
252+
readTime?: Timestamp;
253+
versionTime: Timestamp;
254+
}
255+
256+
/**
257+
* Interface for an object that contains data required to bundle a QuerySnapshot.
258+
* @internal
259+
*/
260+
export interface QuerySnapshotBundleData {
261+
query: Query;
262+
parent: string;
263+
docBundleDataArray: DocumentSnapshotBundleData[];
242264
}
243265

244266
/**
@@ -252,47 +274,3 @@ class BundledDocument {
252274
readonly document?: Document
253275
) {}
254276
}
255-
256-
/**
257-
* Validates that 'value' is a string.
258-
*
259-
* @private
260-
* @internal
261-
* @param arg The argument name or argument index (for varargs methods).
262-
* @param value The input to validate.
263-
* @param options Options that specify whether the string can be omitted.
264-
*/
265-
export function validateString(arg: string | number, value: unknown): void {
266-
if (typeof value !== 'string') {
267-
throw new Error(invalidArgumentMessage(arg, 'string'));
268-
}
269-
}
270-
271-
/**
272-
* Generates an error message to use with invalid arguments.
273-
*
274-
* @private
275-
* @internal
276-
* @param arg The argument name or argument index (for varargs methods).
277-
* @param expectedType The expected input type.
278-
*/
279-
export function invalidArgumentMessage(
280-
arg: string | number,
281-
expectedType: string
282-
): string {
283-
return `${formatArgumentName(arg)} is not a valid ${expectedType}.`;
284-
}
285-
286-
/**
287-
* Creates a descriptive name for the provided argument name or index.
288-
*
289-
* @private
290-
* @internal
291-
* @param arg The argument name or argument index (for varargs methods).
292-
* @return Either the argument name or its index description.
293-
*/
294-
function formatArgumentName(arg: string | number): string {
295-
return typeof arg === 'string'
296-
? `Value for argument "${arg}"`
297-
: `Element at index ${arg}`;
298-
}

0 commit comments

Comments
 (0)