Edit File: bundle.js
"use strict"; // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. Object.defineProperty(exports, "__esModule", { value: true }); const document_1 = require("./document"); const reference_1 = require("./reference"); const timestamp_1 = require("./timestamp"); const validate_1 = require("./validate"); const BUNDLE_VERSION = 1; /** * Builds a Firestore data bundle with results from the given document and query snapshots. */ class BundleBuilder { constructor(bundleId) { this.bundleId = bundleId; // Resulting documents for the bundle, keyed by full document path. this.documents = new Map(); // Named queries saved in the bundle, keyed by query name. this.namedQueries = new Map(); // The latest read time among all bundled documents and queries. this.latestReadTime = new timestamp_1.Timestamp(0, 0); } /** * Adds a Firestore document snapshot or query snapshot to the bundle. * Both the documents data and the query read time will be included in the bundle. * * @param {DocumentSnapshot | string} documentOrName A document snapshot to add or a name of a query. * @param {Query=} querySnapshot A query snapshot to add to the bundle, if provided. * @returns {BundleBuilder} This instance. * * @example * const bundle = firestore.bundle('data-bundle'); * const docSnapshot = await firestore.doc('abc/123').get(); * const querySnapshot = await firestore.collection('coll').get(); * * const bundleBuffer = bundle.add(docSnapshot); // Add a document * .add('coll-query', querySnapshot) // Add a named query. * .build() * // Save `bundleBuffer` to CDN or stream it to clients. */ add(documentOrName, querySnapshot) { // eslint-disable-next-line prefer-rest-params validate_1.validateMinNumberOfArguments('BundleBuilder.add', arguments, 1); // eslint-disable-next-line prefer-rest-params validate_1.validateMaxNumberOfArguments('BundleBuilder.add', arguments, 2); if (arguments.length === 1) { validateDocumentSnapshot('documentOrName', documentOrName); this.addBundledDocument(documentOrName); } else { validate_1.validateString('documentOrName', documentOrName); validateQuerySnapshot('querySnapshot', querySnapshot); this.addNamedQuery(documentOrName, querySnapshot); } return this; } addBundledDocument(snap, queryName) { const originalDocument = this.documents.get(snap.ref.path); const originalQueries = originalDocument === null || originalDocument === void 0 ? void 0 : originalDocument.metadata.queries; // Update with document built from `snap` because it is newer. if (!originalDocument || timestamp_1.Timestamp.fromProto(originalDocument.metadata.readTime) < snap.readTime) { const docProto = snap.toDocumentProto(); this.documents.set(snap.ref.path, { document: snap.exists ? docProto : undefined, metadata: { name: docProto.name, readTime: snap.readTime.toProto().timestampValue, exists: snap.exists, }, }); } // Update `queries` to include both original and `queryName`. const newDocument = this.documents.get(snap.ref.path); newDocument.metadata.queries = originalQueries || []; if (queryName) { newDocument.metadata.queries.push(queryName); } if (snap.readTime > this.latestReadTime) { this.latestReadTime = snap.readTime; } } addNamedQuery(name, querySnap) { if (this.namedQueries.has(name)) { throw new Error(`Query name conflict: ${name} has already been added.`); } this.namedQueries.set(name, { name, bundledQuery: querySnap.query._toBundledQuery(), readTime: querySnap.readTime.toProto().timestampValue, }); for (const snap of querySnap.docs) { this.addBundledDocument(snap, name); } if (querySnap.readTime > this.latestReadTime) { this.latestReadTime = querySnap.readTime; } } /** * Converts a IBundleElement to a Buffer whose content is the length prefixed JSON representation * of the element. * @private * @internal */ elementToLengthPrefixedBuffer(bundleElement) { // Convert to a valid proto message object then take its JSON representation. // This take cares of stuff like converting internal byte array fields // to Base64 encodings. // We lazy-load the Proto file to reduce cold-start times. const message = require('../protos/firestore_v1_proto_api') .firestore.BundleElement.fromObject(bundleElement) .toJSON(); const buffer = Buffer.from(JSON.stringify(message), 'utf-8'); const lengthBuffer = Buffer.from(buffer.length.toString()); return Buffer.concat([lengthBuffer, buffer]); } build() { let bundleBuffer = Buffer.alloc(0); for (const namedQuery of this.namedQueries.values()) { bundleBuffer = Buffer.concat([ bundleBuffer, this.elementToLengthPrefixedBuffer({ namedQuery }), ]); } for (const bundledDocument of this.documents.values()) { const documentMetadata = bundledDocument.metadata; bundleBuffer = Buffer.concat([ bundleBuffer, this.elementToLengthPrefixedBuffer({ documentMetadata }), ]); // Write to the bundle if document exists. const document = bundledDocument.document; if (document) { bundleBuffer = Buffer.concat([ bundleBuffer, this.elementToLengthPrefixedBuffer({ document }), ]); } } const metadata = { id: this.bundleId, createTime: this.latestReadTime.toProto().timestampValue, version: BUNDLE_VERSION, totalDocuments: this.documents.size, totalBytes: bundleBuffer.length, }; // Prepends the metadata element to the bundleBuffer: `bundleBuffer` is the second argument to `Buffer.concat`. bundleBuffer = Buffer.concat([ this.elementToLengthPrefixedBuffer({ metadata }), bundleBuffer, ]); return bundleBuffer; } } exports.BundleBuilder = BundleBuilder; /** * Convenient class to hold both the metadata and the actual content of a document to be bundled. * @private * @internal */ class BundledDocument { constructor(metadata, document) { this.metadata = metadata; this.document = document; } } /** * Validates that 'value' is DocumentSnapshot. * * @private * @internal * @param arg The argument name or argument index (for varargs methods). * @param value The input to validate. */ function validateDocumentSnapshot(arg, value) { if (!(value instanceof document_1.DocumentSnapshot)) { throw new Error(validate_1.invalidArgumentMessage(arg, 'DocumentSnapshot')); } } /** * Validates that 'value' is QuerySnapshot. * * @private * @internal * @param arg The argument name or argument index (for varargs methods). * @param value The input to validate. */ function validateQuerySnapshot(arg, value) { if (!(value instanceof reference_1.QuerySnapshot)) { throw new Error(validate_1.invalidArgumentMessage(arg, 'QuerySnapshot')); } } //# sourceMappingURL=bundle.js.map
Back to File Manager