Skip to content

Commit c361963

Browse files
committed
Format and coalesce utils.
1 parent 3d45407 commit c361963

File tree

3 files changed

+115
-99
lines changed

3 files changed

+115
-99
lines changed

packages/firestore/src/util/bundle_builder.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

packages/firestore/src/util/bundle_builder_impl.ts

Lines changed: 114 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -15,51 +15,45 @@
1515
* limitations under the License.
1616
*/
1717

18-
import {queryToTarget} from '../../src/core/query';
18+
import { queryToTarget } from '../../src/core/query';
1919
import {
2020
JsonProtoSerializer,
21-
toDocument,
2221
toName,
2322
toQueryTarget,
24-
toTimestamp,
23+
toTimestamp
2524
} from '../../src/remote/serializer';
26-
import {Firestore} from '../api/database';
27-
import {DatabaseId} from '../core/database_info';
28-
import {DocumentSnapshot, QuerySnapshot} from '../lite-api/snapshot';
29-
import {Timestamp} from '../lite-api/timestamp';
25+
import { encoder } from '../../test/unit/util/bundle_data';
26+
import { Firestore } from '../api/database';
27+
import { ExpUserDataWriter } from '../api/reference_impl';
28+
import { DatabaseId } from '../core/database_info';
29+
import { DocumentSnapshot, QuerySnapshot } from '../lite-api/snapshot';
30+
import { Timestamp } from '../lite-api/timestamp';
31+
import {
32+
parseObject,
33+
UserDataReader,
34+
UserDataSource
35+
} from '../lite-api/user_data_reader';
36+
import { AbstractUserDataWriter } from '../lite-api/user_data_writer';
37+
import { MutableDocument } from '../model/document';
3038
import {
3139
BundledDocumentMetadata as ProtoBundledDocumentMetadata,
3240
BundleElement as ProtoBundleElement,
3341
BundleMetadata as ProtoBundleMetadata,
34-
NamedQuery as ProtoNamedQuery,
42+
NamedQuery as ProtoNamedQuery
3543
} from '../protos/firestore_bundle_proto';
3644
import {
3745
Document as ProtoDocument,
3846
Document
3947
} from '../protos/firestore_proto_api';
4048

41-
import {
42-
invalidArgumentMessage,
43-
validateString,
44-
} from './bundle_builder_validation_utils';
45-
import {encoder} from "../../test/unit/util/bundle_data";
46-
import {
47-
parseData, parseObject,
48-
UserDataReader,
49-
UserDataSource
50-
} from "../lite-api/user_data_reader";
51-
import {AbstractUserDataWriter} from "../lite-api/user_data_writer";
52-
import {ExpUserDataWriter} from "../api/reference_impl";
53-
import {MutableDocument} from "../model/document";
54-
import {debugAssert} from "./assert";
49+
import { debugAssert } from './assert';
5550

5651
const BUNDLE_VERSION = 1;
5752

5853
/**
5954
* Builds a Firestore data bundle with results from the given document and query snapshots.
6055
*/
6156
export class BundleBuilder {
62-
6357
// Resulting documents for the bundle, keyed by full document path.
6458
private documents: Map<string, BundledDocument> = new Map();
6559
// Named queries saved in the bundle, keyed by query name.
@@ -79,10 +73,17 @@ export class BundleBuilder {
7973

8074
// useProto3Json is true because the objects will be serialized to JSON string
8175
// before being written to the bundle buffer.
82-
this.serializer = new JsonProtoSerializer(this.databaseId, /*useProto3Json=*/ true);
76+
this.serializer = new JsonProtoSerializer(
77+
this.databaseId,
78+
/*useProto3Json=*/ true
79+
);
8380

8481
this.userDataWriter = new ExpUserDataWriter(firestore);
85-
this.userDataReader = new UserDataReader(this.databaseId, true, this.serializer);
82+
this.userDataReader = new UserDataReader(
83+
this.databaseId,
84+
true,
85+
this.serializer
86+
);
8687
}
8788

8889
/**
@@ -110,7 +111,9 @@ export class BundleBuilder {
110111
querySnapshot?: QuerySnapshot
111112
): BundleBuilder {
112113
if (arguments.length < 1 || arguments.length > 2) {
113-
throw new Error( 'Function BundleBuilder.add() requires 1 or 2 arguments.');
114+
throw new Error(
115+
'Function BundleBuilder.add() requires 1 or 2 arguments.'
116+
);
114117
}
115118
if (arguments.length === 1) {
116119
validateDocumentSnapshot('documentOrName', documentOrName);
@@ -123,9 +126,7 @@ export class BundleBuilder {
123126
return this;
124127
}
125128

126-
toBundleDocument(
127-
document: MutableDocument
128-
): ProtoDocument {
129+
toBundleDocument(document: MutableDocument): ProtoDocument {
129130
// TODO handle documents that have mutations
130131
debugAssert(
131132
!document.hasLocalMutations,
@@ -136,23 +137,35 @@ export class BundleBuilder {
136137
// to Proto3 JSON objects. This is the same approach used in
137138
// bundling in the nodejs-firestore SDK. It may not be the most
138139
// performant approach.
139-
const documentData = this.userDataWriter.convertObjectMap(document.data.value.mapValue.fields, 'previous');
140+
const documentData = this.userDataWriter.convertObjectMap(
141+
document.data.value.mapValue.fields,
142+
'previous'
143+
);
140144
// a parse context is typically used for validating and parsing user data, but in this
141145
// case we are using it internally to convert DocumentData to Proto3 JSON
142-
const context = this.userDataReader.createContext(UserDataSource.ArrayArgument, 'internal toBundledDocument');
146+
const context = this.userDataReader.createContext(
147+
UserDataSource.ArrayArgument,
148+
'internal toBundledDocument'
149+
);
143150
const proto3Fields = parseObject(documentData, context);
144151

145152
return {
146153
name: toName(this.serializer, document.key),
147154
fields: proto3Fields.mapValue.fields,
148155
updateTime: toTimestamp(this.serializer, document.version.toTimestamp()),
149-
createTime: toTimestamp(this.serializer, document.createTime.toTimestamp())
156+
createTime: toTimestamp(
157+
this.serializer,
158+
document.createTime.toTimestamp()
159+
)
150160
};
151161
}
152162

153163
private addBundledDocument(snap: DocumentSnapshot, queryName?: string): void {
154-
// TODO: is this a valid shortcircuit?
155-
if(!snap._document || !snap._document.isValidDocument()) {
164+
if (
165+
!snap._document ||
166+
!snap._document.isValidDocument() ||
167+
!snap._document.isFoundDocument()
168+
) {
156169
return;
157170
}
158171
const originalDocument = this.documents.get(snap.ref.path);
@@ -161,18 +174,20 @@ export class BundleBuilder {
161174

162175
// Update with document built from `snap` because it is newer.
163176
const snapReadTime = snap.readTime;
164-
if ( !originalDocument ||
165-
(!snapReadTime && !originalDocument.metadata.readTime) ||
166-
(snapReadTime && originalDocument.metadata.readTime! < snapReadTime)
177+
if (
178+
!originalDocument ||
179+
(!snapReadTime && !originalDocument.metadata.readTime) ||
180+
(snapReadTime && originalDocument.metadata.readTime! < snapReadTime)
167181
) {
168-
169182
this.documents.set(snap.ref.path, {
170-
document: snap._document.isFoundDocument() ? this.toBundleDocument(mutableCopy) : undefined,
183+
document: this.toBundleDocument(mutableCopy),
171184
metadata: {
172185
name: toName(this.serializer, mutableCopy.key),
173-
readTime: !!snapReadTime ? toTimestamp(this.serializer, snapReadTime) : undefined,
174-
exists: snap.exists(),
175-
},
186+
readTime: !!snapReadTime
187+
? toTimestamp(this.serializer, snapReadTime)
188+
: undefined,
189+
exists: snap.exists()
190+
}
176191
});
177192
}
178193

@@ -183,20 +198,21 @@ export class BundleBuilder {
183198
newDocument.metadata.queries!.push(queryName);
184199
}
185200

186-
const readTime = snap.readTime;
187-
if (readTime && readTime > this.latestReadTime) {
188-
this.latestReadTime = readTime;
201+
if (snapReadTime && snapReadTime > this.latestReadTime) {
202+
this.latestReadTime = snapReadTime;
189203
}
190204
}
191205

206+
// TODO: remove this since we're not planning to serialize named queries.
192207
private addNamedQuery(name: string, querySnap: QuerySnapshot): void {
193208
if (this.namedQueries.has(name)) {
194209
throw new Error(`Query name conflict: ${name} has already been added.`);
195210
}
196-
const queryTarget = toQueryTarget(this.serializer, queryToTarget(querySnap.query._query));
211+
const queryTarget = toQueryTarget(
212+
this.serializer,
213+
queryToTarget(querySnap.query._query)
214+
);
197215

198-
// TODO: if we can't resolve the query's readTime then can we set it to the latest
199-
// of the document collection?
200216
let latestReadTime = new Timestamp(0, 0);
201217
for (const snap of querySnap.docs) {
202218
const readTime = snap.readTime;
@@ -226,9 +242,7 @@ export class BundleBuilder {
226242
* @internal
227243
* @param bundleElement A ProtoBundleElement that is expected to be Proto3 JSON compatible.
228244
*/
229-
private lengthPrefixedString(
230-
bundleElement: ProtoBundleElement
231-
): string {
245+
private lengthPrefixedString(bundleElement: ProtoBundleElement): string {
232246
const str = JSON.stringify(bundleElement);
233247
// TODO: it's not ideal to have to re-encode all of these strings multiple times
234248
// It may be more performant to return a UInt8Array that is concatenated to other
@@ -242,18 +256,18 @@ export class BundleBuilder {
242256
let bundleString = '';
243257

244258
for (const namedQuery of this.namedQueries.values()) {
245-
bundleString += this.lengthPrefixedString({namedQuery});
259+
bundleString += this.lengthPrefixedString({ namedQuery });
246260
}
247261

248262
for (const bundledDocument of this.documents.values()) {
249263
const documentMetadata: ProtoBundledDocumentMetadata =
250264
bundledDocument.metadata;
251265

252-
bundleString += this.lengthPrefixedString({documentMetadata});
266+
bundleString += this.lengthPrefixedString({ documentMetadata });
253267
// Write to the bundle if document exists.
254268
const document = bundledDocument.document;
255269
if (document) {
256-
bundleString += this.lengthPrefixedString({document});
270+
bundleString += this.lengthPrefixedString({ document });
257271
}
258272
}
259273

@@ -263,10 +277,10 @@ export class BundleBuilder {
263277
version: BUNDLE_VERSION,
264278
totalDocuments: this.documents.size,
265279
// TODO: it's not ideal to have to re-encode all of these strings multiple times
266-
totalBytes: encoder.encode(bundleString).length,
280+
totalBytes: encoder.encode(bundleString).length
267281
};
268282
// Prepends the metadata element to the bundleBuffer: `bundleBuffer` is the second argument to `Buffer.concat`.
269-
bundleString = this.lengthPrefixedString({metadata}) + bundleString;
283+
bundleString = this.lengthPrefixedString({ metadata }) + bundleString;
270284

271285
// TODO: it's not ideal to have to re-encode all of these strings multiple times
272286
// the implementation in nodejs-firestore concatenates Buffers instead of
@@ -314,3 +328,47 @@ function validateQuerySnapshot(arg: string | number, value: unknown): void {
314328
throw new Error(invalidArgumentMessage(arg, 'QuerySnapshot'));
315329
}
316330
}
331+
332+
/**
333+
* Validates that 'value' is a string.
334+
*
335+
* @private
336+
* @internal
337+
* @param arg The argument name or argument index (for varargs methods).
338+
* @param value The input to validate.
339+
* @param options Options that specify whether the string can be omitted.
340+
*/
341+
export function validateString(arg: string | number, value: unknown): void {
342+
if (typeof value !== 'string') {
343+
throw new Error(invalidArgumentMessage(arg, 'string'));
344+
}
345+
}
346+
347+
/**
348+
* Generates an error message to use with invalid arguments.
349+
*
350+
* @private
351+
* @internal
352+
* @param arg The argument name or argument index (for varargs methods).
353+
* @param expectedType The expected input type.
354+
*/
355+
export function invalidArgumentMessage(
356+
arg: string | number,
357+
expectedType: string
358+
): string {
359+
return `${formatArgumentName(arg)} is not a valid ${expectedType}.`;
360+
}
361+
362+
/**
363+
* Creates a descriptive name for the provided argument name or index.
364+
*
365+
* @private
366+
* @internal
367+
* @param arg The argument name or argument index (for varargs methods).
368+
* @return Either the argument name or its index description.
369+
*/
370+
function formatArgumentName(arg: string | number): string {
371+
return typeof arg === 'string'
372+
? `Value for argument "${arg}"`
373+
: `Element at index ${arg}`;
374+
}

packages/firestore/src/util/bundle_builder_validation_utils.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* limitations under the License.
1616
*/
1717

18-
1918
/**
2019
* Validates that 'value' is a string.
2120
*
@@ -25,10 +24,7 @@
2524
* @param value The input to validate.
2625
* @param options Options that specify whether the string can be omitted.
2726
*/
28-
export function validateString(
29-
arg: string | number,
30-
value: unknown
31-
): void {
27+
export function validateString(arg: string | number, value: unknown): void {
3228
if (typeof value !== 'string') {
3329
throw new Error(invalidArgumentMessage(arg, 'string'));
3430
}

0 commit comments

Comments
 (0)