mirror of
https://github.com/titanscouting/tra-analysis.git
synced 2025-09-26 07:10:17 +00:00
1276 lines
52 KiB
JavaScript
1276 lines
52 KiB
JavaScript
/*!
|
|
* Copyright 2017 Google Inc. All Rights Reserved.
|
|
*
|
|
* 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
|
|
*
|
|
* http://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.
|
|
*/
|
|
'use strict';
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
result["default"] = mod;
|
|
return result;
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const assert_1 = __importDefault(require("assert"));
|
|
const bun_1 = __importDefault(require("bun"));
|
|
const extend_1 = __importDefault(require("extend"));
|
|
const is_1 = __importDefault(require("is"));
|
|
const through2_1 = __importDefault(require("through2"));
|
|
const projectify_1 = require("@google-cloud/projectify");
|
|
const reference_1 = require("./reference");
|
|
const document_1 = require("./document");
|
|
const field_value_1 = require("./field-value");
|
|
const validate_1 = require("./validate");
|
|
const write_batch_1 = require("./write-batch");
|
|
const transaction_1 = require("./transaction");
|
|
const timestamp_1 = require("./timestamp");
|
|
const path_1 = require("./path");
|
|
const pool_1 = require("./pool");
|
|
const document_change_1 = require("./document-change");
|
|
const serializer_1 = require("./serializer");
|
|
const geo_point_1 = require("./geo-point");
|
|
const logger_1 = require("./logger");
|
|
const util_1 = require("./util");
|
|
const convert = __importStar(require("./convert"));
|
|
const libVersion = require('../../package.json').version;
|
|
logger_1.setLibVersion(libVersion);
|
|
/*!
|
|
* DO NOT REMOVE THE FOLLOWING NAMESPACE DEFINITIONS
|
|
*/
|
|
/**
|
|
* @namespace google.protobuf
|
|
*/
|
|
/**
|
|
* @namespace google.rpc
|
|
*/
|
|
/**
|
|
* @namespace google.firestore.v1beta1
|
|
*/
|
|
/*!
|
|
* @see v1beta1
|
|
*/
|
|
let v1beta1; // Lazy-loaded in `_runRequest()`
|
|
/*!
|
|
* HTTP header for the resource prefix to improve routing and project isolation
|
|
* by the backend.
|
|
* @type {string}
|
|
*/
|
|
const CLOUD_RESOURCE_HEADER = 'google-cloud-resource-prefix';
|
|
/*!
|
|
* The maximum number of times to retry idempotent requests.
|
|
* @type {number}
|
|
*/
|
|
const MAX_REQUEST_RETRIES = 5;
|
|
/*!
|
|
* The maximum number of concurrent requests supported by a single GRPC channel,
|
|
* as enforced by Google's Frontend. If the SDK issues more than 100 concurrent
|
|
* operations, we need to use more than one GAPIC client since these clients
|
|
* multiplex all requests over a single channel.
|
|
*
|
|
* @type {number}
|
|
*/
|
|
const MAX_CONCURRENT_REQUESTS_PER_CLIENT = 100;
|
|
/*!
|
|
* GRPC Error code for 'UNAVAILABLE'.
|
|
* @type {number}
|
|
*/
|
|
const GRPC_UNAVAILABLE = 14;
|
|
/*!
|
|
* The maximum depth of a Firestore object.
|
|
*
|
|
* @type {number}
|
|
*/
|
|
const MAX_DEPTH = 20;
|
|
/**
|
|
* Document data (e.g. for use with
|
|
* [set()]{@link DocumentReference#set}) consisting of fields mapped
|
|
* to values.
|
|
*
|
|
* @typedef {Object.<string, *>} DocumentData
|
|
*/
|
|
/**
|
|
* Update data (for use with [update]{@link DocumentReference#update})
|
|
* that contains paths (e.g. 'foo' or 'foo.baz') mapped to values. Fields that
|
|
* contain dots reference nested fields within the document.
|
|
*
|
|
* @typedef {Object.<string, *>} UpdateData
|
|
*/
|
|
/**
|
|
* An options object that configures conditional behavior of
|
|
* [update()]{@link DocumentReference#update} and
|
|
* [delete()]{@link DocumentReference#delete} calls in
|
|
* [DocumentReference]{@link DocumentReference},
|
|
* [WriteBatch]{@link WriteBatch}, and
|
|
* [Transaction]{@link Transaction}. Using Preconditions, these calls
|
|
* can be restricted to only apply to documents that match the specified
|
|
* conditions.
|
|
*
|
|
* @property {string} lastUpdateTime - The update time to enforce (specified as
|
|
* an ISO 8601 string).
|
|
* @typedef {Object} Precondition
|
|
*/
|
|
/**
|
|
* An options object that configures the behavior of
|
|
* [set()]{@link DocumentReference#set} calls in
|
|
* [DocumentReference]{@link DocumentReference},
|
|
* [WriteBatch]{@link WriteBatch}, and
|
|
* [Transaction]{@link Transaction}. These calls can be
|
|
* configured to perform granular merges instead of overwriting the target
|
|
* documents in their entirety by providing a SetOptions object with
|
|
* { merge : true }.
|
|
*
|
|
* @property {boolean} merge - Changes the behavior of a set() call to only
|
|
* replace the values specified in its data argument. Fields omitted from the
|
|
* set() call remain untouched.
|
|
* @typedef {Object} SetOptions
|
|
*/
|
|
/**
|
|
* The Firestore client represents a Firestore Database and is the entry point
|
|
* for all Firestore operations.
|
|
*
|
|
* @see [Firestore Documentation]{@link https://firebase.google.com/docs/firestore/}
|
|
*
|
|
* @class
|
|
*
|
|
* @example <caption>Install the client library with <a
|
|
* href="https://www.npmjs.com/">npm</a>:</caption> npm install --save
|
|
* @google-cloud/firestore
|
|
*
|
|
* @example <caption>Import the client library</caption>
|
|
* var Firestore = require('@google-cloud/firestore');
|
|
*
|
|
* @example <caption>Create a client that uses <a
|
|
* href="https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application">Application
|
|
* Default Credentials (ADC)</a>:</caption> var firestore = new Firestore();
|
|
*
|
|
* @example <caption>Create a client with <a
|
|
* href="https://cloud.google.com/docs/authentication/production#obtaining_and_providing_service_account_credentials_manually">explicit
|
|
* credentials</a>:</caption> var firestore = new Firestore({ projectId:
|
|
* 'your-project-id', keyFilename: '/path/to/keyfile.json'
|
|
* });
|
|
*
|
|
* @example <caption>include:samples/quickstart.js</caption>
|
|
* region_tag:firestore_quickstart
|
|
* Full quickstart example:
|
|
*/
|
|
class Firestore {
|
|
/**
|
|
* @param {Object=} settings - [Configuration object](#/docs).
|
|
* @param {string=} settings.projectId The Firestore Project ID. Can be
|
|
* omitted in environments that support `Application Default Credentials`
|
|
* {@see https://cloud.google.com/docs/authentication}
|
|
* @param {string=} settings.keyFilename Local file containing the Service
|
|
* Account credentials. Can be omitted in environments that support
|
|
* `Application Default Credentials`
|
|
* {@see https://cloud.google.com/docs/authentication}
|
|
* @param {boolean=} settings.timestampsInSnapshots Enables the use of
|
|
* `Timestamp`s for timestamp fields in `DocumentSnapshots`.<br/>
|
|
* Currently, Firestore returns timestamp fields as `Date` but `Date` only
|
|
* supports millisecond precision, which leads to truncation and causes
|
|
* unexpected behavior when using a timestamp from a snapshot as a part
|
|
* of a subsequent query.
|
|
* <br/>Setting `timestampsInSnapshots` to true will cause Firestore to return
|
|
* `Timestamp` values instead of `Date` avoiding this kind of problem. To
|
|
* make this work you must also change any code that uses `Date` to use
|
|
* `Timestamp` instead.
|
|
* <br/>NOTE: in the future `timestampsInSnapshots: true` will become the
|
|
* default and this option will be removed so you should change your code to
|
|
* use `Timestamp` now and opt-in to this new behavior as soon as you can.
|
|
*/
|
|
constructor(settings) {
|
|
settings = extend_1.default({}, settings, {
|
|
libName: 'gccl',
|
|
libVersion: libVersion,
|
|
});
|
|
this._validator = new validate_1.Validator({
|
|
ArrayElement: (name, value) => validateFieldValue(name, value, /* depth */ 0, /*inArray=*/ true),
|
|
DeletePrecondition: precondition => document_1.validatePrecondition(precondition, /* allowExists= */ true),
|
|
Document: validateDocumentData,
|
|
DocumentReference: reference_1.validateDocumentReference,
|
|
FieldPath: path_1.FieldPath.validateFieldPath,
|
|
FieldValue: validateFieldValue,
|
|
FieldOrder: reference_1.validateFieldOrder,
|
|
QueryComparison: reference_1.validateComparisonOperator,
|
|
QueryValue: validateFieldValue,
|
|
ResourcePath: path_1.ResourcePath.validateResourcePath,
|
|
SetOptions: document_1.validateSetOptions,
|
|
UpdateMap: write_batch_1.validateUpdateMap,
|
|
UpdatePrecondition: precondition => document_1.validatePrecondition(precondition, /* allowExists= */ false),
|
|
});
|
|
/**
|
|
* A client pool to distribute requests over multiple GAPIC clients in order
|
|
* to work around a connection limit of 100 concurrent requests per client.
|
|
* @private
|
|
* @type {ClientPool|null}
|
|
*/
|
|
this._clientPool = null;
|
|
/**
|
|
* Whether the initialization settings can still be changed by invoking
|
|
* `settings()`.
|
|
* @private
|
|
* @type {boolean}
|
|
*/
|
|
this._settingsFrozen = false;
|
|
/**
|
|
* A Promise that resolves when client initialization completes. Can be
|
|
* 'null' if initialization hasn't started yet.
|
|
* @private
|
|
* @type {Promise|null}
|
|
*/
|
|
this._clientInitialized = null;
|
|
/**
|
|
* The configuration options for the GAPIC client.
|
|
* @private
|
|
* @type {Object}
|
|
*/
|
|
this._initalizationSettings = null;
|
|
/**
|
|
* The serializer to use for the Protobuf transformation.
|
|
* @private
|
|
* @type {Serializer}
|
|
*/
|
|
this._serializer = null;
|
|
this.validateAndApplySettings(settings);
|
|
// GCF currently tears down idle connections after two minutes. Requests
|
|
// that are issued after this period may fail. On GCF, we therefore issue
|
|
// these requests as part of a transaction so that we can safely retry until
|
|
// the network link is reestablished.
|
|
//
|
|
// The environment variable FUNCTION_TRIGGER_TYPE is used to detect the GCF
|
|
// environment.
|
|
this._preferTransactions = is_1.default.defined(process.env.FUNCTION_TRIGGER_TYPE);
|
|
this._lastSuccessfulRequest = null;
|
|
if (this._preferTransactions) {
|
|
logger_1.logger('Firestore', null, 'Detected GCF environment');
|
|
}
|
|
logger_1.logger('Firestore', null, 'Initialized Firestore');
|
|
}
|
|
/**
|
|
* Specifies custom settings to be used to configure the `Firestore`
|
|
* instance. Can only be invoked once and before any other Firestore method.
|
|
*
|
|
* If settings are provided via both `settings()` and the `Firestore`
|
|
* constructor, both settings objects are merged and any settings provided via
|
|
* `settings()` take precedence.
|
|
*
|
|
* @param {object} settings The settings to use for all Firestore operations.
|
|
*/
|
|
settings(settings) {
|
|
this._validator.isObject('settings', settings);
|
|
this._validator.isOptionalString('settings.projectId', settings.projectId);
|
|
this._validator.isOptionalBoolean('settings.timestampsInSnapshots', settings.timestampsInSnapshots);
|
|
if (this._clientInitialized) {
|
|
throw new Error('Firestore has already been started and its settings can no longer ' +
|
|
'be changed. You can only call settings() before calling any other ' +
|
|
'methods on a Firestore object.');
|
|
}
|
|
if (this._settingsFrozen) {
|
|
throw new Error('Firestore.settings() has already be called. You can only call ' +
|
|
'settings() once, and only before calling any other methods on a ' +
|
|
'Firestore object.');
|
|
}
|
|
const mergedSettings = extend_1.default({}, this._initalizationSettings, settings);
|
|
this.validateAndApplySettings(mergedSettings);
|
|
this._settingsFrozen = true;
|
|
}
|
|
validateAndApplySettings(settings) {
|
|
this._validator.isOptionalBoolean('settings.timestampsInSnapshots', settings.timestampsInSnapshots);
|
|
this._timestampsInSnapshotsEnabled = !!settings.timestampsInSnapshots;
|
|
if (settings && settings.projectId) {
|
|
this._validator.isString('settings.projectId', settings.projectId);
|
|
this._referencePath = new path_1.ResourcePath(settings.projectId, '(default)');
|
|
}
|
|
else {
|
|
// Initialize a temporary reference path that will be overwritten during
|
|
// project ID detection.
|
|
this._referencePath = new path_1.ResourcePath('{{projectId}}', '(default)');
|
|
}
|
|
this._initalizationSettings = settings;
|
|
this._serializer = new serializer_1.Serializer(this, this._timestampsInSnapshotsEnabled);
|
|
}
|
|
/**
|
|
* The root path to the database.
|
|
*
|
|
* @private
|
|
* @type {string}
|
|
*/
|
|
get formattedName() {
|
|
return this._referencePath.formattedName;
|
|
}
|
|
/**
|
|
* Gets a [DocumentReference]{@link DocumentReference} instance that
|
|
* refers to the document at the specified path.
|
|
*
|
|
* @param {string} documentPath - A slash-separated path to a document.
|
|
* @returns {DocumentReference} The
|
|
* [DocumentReference]{@link DocumentReference} instance.
|
|
*
|
|
* @example
|
|
* let documentRef = firestore.doc('collection/document');
|
|
* console.log(`Path of document is ${documentRef.path}`);
|
|
*/
|
|
doc(documentPath) {
|
|
this._validator.isResourcePath('documentPath', documentPath);
|
|
let path = this._referencePath.append(documentPath);
|
|
if (!path.isDocument) {
|
|
throw new Error(`Argument "documentPath" must point to a document, but was "${documentPath}". Your path does not contain an even number of components.`);
|
|
}
|
|
return new reference_1.DocumentReference(this, path);
|
|
}
|
|
/**
|
|
* Gets a [CollectionReference]{@link CollectionReference} instance
|
|
* that refers to the collection at the specified path.
|
|
*
|
|
* @param {string} collectionPath - A slash-separated path to a collection.
|
|
* @returns {CollectionReference} The
|
|
* [CollectionReference]{@link CollectionReference} instance.
|
|
*
|
|
* @example
|
|
* let collectionRef = firestore.collection('collection');
|
|
*
|
|
* // Add a document with an auto-generated ID.
|
|
* collectionRef.add({foo: 'bar'}).then((documentRef) => {
|
|
* console.log(`Added document at ${documentRef.path})`);
|
|
* });
|
|
*/
|
|
collection(collectionPath) {
|
|
this._validator.isResourcePath('collectionPath', collectionPath);
|
|
let path = this._referencePath.append(collectionPath);
|
|
if (!path.isCollection) {
|
|
throw new Error(`Argument "collectionPath" must point to a collection, but was "${collectionPath}". Your path does not contain an odd number of components.`);
|
|
}
|
|
return new reference_1.CollectionReference(this, path);
|
|
}
|
|
/**
|
|
* Creates a [WriteBatch]{@link WriteBatch}, used for performing
|
|
* multiple writes as a single atomic operation.
|
|
*
|
|
* @returns {WriteBatch} A WriteBatch that operates on this Firestore
|
|
* client.
|
|
*
|
|
* @example
|
|
* let writeBatch = firestore.batch();
|
|
*
|
|
* // Add two documents in an atomic batch.
|
|
* let data = { foo: 'bar' };
|
|
* writeBatch.set(firestore.doc('col/doc1'), data);
|
|
* writeBatch.set(firestore.doc('col/doc2'), data);
|
|
*
|
|
* writeBatch.commit().then(res => {
|
|
* console.log(`Added document at ${res.writeResults[0].updateTime}`);
|
|
* });
|
|
*/
|
|
batch() {
|
|
return new write_batch_1.WriteBatch(this);
|
|
}
|
|
/**
|
|
* Creates a [DocumentSnapshot]{@link DocumentSnapshot} or a
|
|
* [QueryDocumentSnapshot]{@link QueryDocumentSnapshot} from a
|
|
* `firestore.v1beta1.Document` proto (or from a resource name for missing
|
|
* documents).
|
|
*
|
|
* This API is used by Google Cloud Functions and can be called with both
|
|
* 'Proto3 JSON' and 'Protobuf JS' encoded data.
|
|
*
|
|
* @private
|
|
* @param {object|string} documentOrName - The Firestore 'Document' proto or
|
|
* the resource name of a missing document.
|
|
* @param {object=} readTime - A 'Timestamp' proto indicating the time this
|
|
* document was read.
|
|
* @param {string=} encoding - One of 'json' or 'protobufJS'. Applies to both
|
|
* the 'document' Proto and 'readTime'. Defaults to 'protobufJS'.
|
|
* @returns {DocumentSnapshot|QueryDocumentSnapshot} - A QueryDocumentSnapshot
|
|
* for existing documents, otherwise a DocumentSnapshot.
|
|
*/
|
|
snapshot_(documentOrName, readTime, encoding) {
|
|
// TODO: Assert that Firestore Project ID is valid.
|
|
let convertTimestamp;
|
|
let convertDocument;
|
|
if (!is_1.default.defined(encoding) || encoding === 'protobufJS') {
|
|
convertTimestamp = data => data;
|
|
convertDocument = data => data;
|
|
}
|
|
else if (encoding === 'json') {
|
|
// Google Cloud Functions calls us with Proto3 JSON format data, which we
|
|
// must convert to Protobuf JS.
|
|
convertTimestamp = convert.timestampFromJson;
|
|
convertDocument = convert.documentFromJson;
|
|
}
|
|
else {
|
|
throw new Error(`Unsupported encoding format. Expected 'json' or 'protobufJS', ` +
|
|
`but was '${encoding}'.`);
|
|
}
|
|
const document = new document_1.DocumentSnapshot.Builder();
|
|
if (is_1.default.string(documentOrName)) {
|
|
document.ref = new reference_1.DocumentReference(this, path_1.ResourcePath.fromSlashSeparatedString(documentOrName));
|
|
}
|
|
else {
|
|
document.ref = new reference_1.DocumentReference(this, path_1.ResourcePath.fromSlashSeparatedString(documentOrName.name));
|
|
document.fieldsProto =
|
|
documentOrName.fields ? convertDocument(documentOrName.fields) : {};
|
|
document.createTime = timestamp_1.Timestamp.fromProto(convertTimestamp(documentOrName.createTime, 'documentOrName.createTime'));
|
|
document.updateTime = timestamp_1.Timestamp.fromProto(convertTimestamp(documentOrName.updateTime, 'documentOrName.updateTime'));
|
|
}
|
|
if (readTime) {
|
|
document.readTime =
|
|
timestamp_1.Timestamp.fromProto(convertTimestamp(readTime, 'readTime'));
|
|
}
|
|
return document.build();
|
|
}
|
|
/**
|
|
* Executes the given updateFunction and commits the changes applied within
|
|
* the transaction.
|
|
*
|
|
* You can use the transaction object passed to 'updateFunction' to read and
|
|
* modify Firestore documents under lock. Transactions are committed once
|
|
* 'updateFunction' resolves and attempted up to five times on failure.
|
|
*
|
|
* @param {function(Transaction)} updateFunction - The
|
|
* function to execute within the transaction
|
|
* context.
|
|
* @param {object=} transactionOptions - Transaction options.
|
|
* @param {number=} transactionOptions.maxAttempts - The maximum number of
|
|
* attempts for this transaction.
|
|
* @returns {Promise} If the transaction completed successfully or was
|
|
* explicitly aborted (by the updateFunction returning a failed Promise), the
|
|
* Promise returned by the updateFunction will be returned here. Else if the
|
|
* transaction failed, a rejected Promise with the corresponding failure
|
|
* error will be returned.
|
|
*
|
|
* @example
|
|
* let counterTransaction = firestore.runTransaction(transaction => {
|
|
* let documentRef = firestore.doc('col/doc');
|
|
* return transaction.get(documentRef).then(doc => {
|
|
* if (doc.exists) {
|
|
* let count = doc.get('count') || 0;
|
|
* if (count > 10) {
|
|
* return Promise.reject('Reached maximum count');
|
|
* }
|
|
* transaction.update(documentRef, { count: ++count });
|
|
* return Promise.resolve(count);
|
|
* }
|
|
*
|
|
* transaction.create(documentRef, { count: 1 });
|
|
* return Promise.resolve(1);
|
|
* });
|
|
* });
|
|
*
|
|
* counterTransaction.then(res => {
|
|
* console.log(`Count updated to ${res}`);
|
|
* });
|
|
*/
|
|
runTransaction(updateFunction, transactionOptions) {
|
|
this._validator.isFunction('updateFunction', updateFunction);
|
|
const defaultAttempts = 5;
|
|
let attemptsRemaining = defaultAttempts;
|
|
let previousTransaction;
|
|
if (is_1.default.defined(transactionOptions)) {
|
|
this._validator.isObject('transactionOptions', transactionOptions);
|
|
this._validator.isOptionalInteger('transactionOptions.maxAttempts', transactionOptions.maxAttempts, 1);
|
|
attemptsRemaining = transactionOptions.maxAttempts || attemptsRemaining;
|
|
previousTransaction = transactionOptions.previousTransaction;
|
|
}
|
|
const transaction = new transaction_1.Transaction(this, previousTransaction);
|
|
const requestTag = transaction.requestTag;
|
|
let result;
|
|
--attemptsRemaining;
|
|
return transaction.begin()
|
|
.then(() => {
|
|
let promise = updateFunction(transaction);
|
|
result = is_1.default.instanceof(promise, Promise) ?
|
|
promise :
|
|
Promise.reject(new Error('You must return a Promise in your transaction()-callback.'));
|
|
return result.catch(err => {
|
|
logger_1.logger('Firestore.runTransaction', requestTag, 'Rolling back transaction after callback error:', err);
|
|
// Rollback the transaction and return the failed result.
|
|
return transaction.rollback().then(() => {
|
|
return result;
|
|
});
|
|
});
|
|
})
|
|
.then(() => {
|
|
return transaction.commit().then(() => result).catch(err => {
|
|
if (attemptsRemaining > 0) {
|
|
logger_1.logger('Firestore.runTransaction', requestTag, `Retrying transaction after error: ${JSON.stringify(err)}.`);
|
|
return this.runTransaction(updateFunction, {
|
|
previousTransaction: transaction,
|
|
maxAttempts: attemptsRemaining,
|
|
});
|
|
}
|
|
logger_1.logger('Firestore.runTransaction', requestTag, 'Exhausted transaction retries, returning error: %s', err);
|
|
return Promise.reject(err);
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Fetches the root collections that are associated with this Firestore
|
|
* database.
|
|
*
|
|
* @returns {Promise.<Array.<CollectionReference>>} A Promise that resolves
|
|
* with an array of CollectionReferences.
|
|
*
|
|
* @example
|
|
* firestore.getCollections().then(collections => {
|
|
* for (let collection of collections) {
|
|
* console.log(`Found collection with id: ${collection.id}`);
|
|
* }
|
|
* });
|
|
*/
|
|
getCollections() {
|
|
let rootDocument = new reference_1.DocumentReference(this, this._referencePath);
|
|
return rootDocument.getCollections();
|
|
}
|
|
/**
|
|
* Retrieves multiple documents from Firestore.
|
|
*
|
|
* @param {...DocumentReference} documents - The document references
|
|
* to receive.
|
|
* @returns {Promise<Array.<DocumentSnapshot>>} A Promise that
|
|
* contains an array with the resulting document snapshots.
|
|
*
|
|
* @example
|
|
* let documentRef1 = firestore.doc('col/doc1');
|
|
* let documentRef2 = firestore.doc('col/doc2');
|
|
*
|
|
* firestore.getAll(documentRef1, documentRef2).then(docs => {
|
|
* console.log(`First document: ${JSON.stringify(docs[0])}`);
|
|
* console.log(`Second document: ${JSON.stringify(docs[1])}`);
|
|
* });
|
|
*/
|
|
getAll(documents) {
|
|
documents = is_1.default.array(arguments[0]) ? arguments[0].slice() :
|
|
Array.prototype.slice.call(arguments);
|
|
for (let i = 0; i < documents.length; ++i) {
|
|
this._validator.isDocumentReference(i, documents[i]);
|
|
}
|
|
return this.getAll_(documents, util_1.requestTag());
|
|
}
|
|
/**
|
|
* Internal method to retrieve multiple documents from Firestore, optionally
|
|
* as part of a transaction.
|
|
*
|
|
* @private
|
|
* @param {Array.<DocumentReference>} docRefs - The documents
|
|
* to receive.
|
|
* @param {string} requestTag A unique client-assigned identifier for this
|
|
* request.
|
|
* @param {bytes=} transactionId - transactionId - The transaction ID to use
|
|
* for this read.
|
|
* @returns {Promise<Array.<DocumentSnapshot>>} A Promise that contains an array with
|
|
* the resulting documents.
|
|
*/
|
|
getAll_(docRefs, requestTag, transactionId) {
|
|
const requestedDocuments = new Set();
|
|
const retrievedDocuments = new Map();
|
|
let request = {
|
|
database: this.formattedName,
|
|
transaction: transactionId,
|
|
};
|
|
for (let docRef of docRefs) {
|
|
requestedDocuments.add(docRef.formattedName);
|
|
}
|
|
request.documents = Array.from(requestedDocuments);
|
|
const self = this;
|
|
return self.readStream('batchGetDocuments', request, requestTag, true)
|
|
.then(stream => {
|
|
return new Promise((resolve, reject) => {
|
|
stream
|
|
.on('error', err => {
|
|
logger_1.logger('Firestore.getAll_', requestTag, 'GetAll failed with error:', err);
|
|
reject(err);
|
|
})
|
|
.on('data', response => {
|
|
try {
|
|
let document;
|
|
if (response.found) {
|
|
logger_1.logger('Firestore.getAll_', requestTag, 'Received document: %s', response.found.name);
|
|
document =
|
|
self.snapshot_(response.found, response.readTime);
|
|
}
|
|
else {
|
|
logger_1.logger('Firestore.getAll_', requestTag, 'Document missing: %s', response.missing);
|
|
document = self.snapshot_(response.missing, response.readTime);
|
|
}
|
|
let path = document.ref.path;
|
|
retrievedDocuments.set(path, document);
|
|
}
|
|
catch (err) {
|
|
logger_1.logger('Firestore.getAll_', requestTag, 'GetAll failed with exception:', err);
|
|
reject(err);
|
|
}
|
|
})
|
|
.on('end', () => {
|
|
logger_1.logger('Firestore.getAll_', requestTag, 'Received %d results', retrievedDocuments.size);
|
|
// BatchGetDocuments doesn't preserve document order. We use
|
|
// the request order to sort the resulting documents.
|
|
const orderedDocuments = [];
|
|
for (let docRef of docRefs) {
|
|
let document = retrievedDocuments.get(docRef.path);
|
|
if (!is_1.default.defined(document)) {
|
|
reject(new Error(`Did not receive document for "${docRef.path}".`));
|
|
}
|
|
orderedDocuments.push(document);
|
|
}
|
|
resolve(orderedDocuments);
|
|
});
|
|
stream.resume();
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Executes a new request using the first available GAPIC client.
|
|
*
|
|
* @private
|
|
*/
|
|
_runRequest(op) {
|
|
// Initialize the client pool if this is the first request.
|
|
if (!this._clientInitialized) {
|
|
if (!this._timestampsInSnapshotsEnabled) {
|
|
console.error(`
|
|
The behavior for Date objects stored in Firestore is going to change
|
|
AND YOUR APP MAY BREAK.
|
|
To hide this warning and ensure your app does not break, you need to add the
|
|
following code to your app before calling any other Cloud Firestore methods:
|
|
|
|
const firestore = new Firestore();
|
|
const settings = {/* your settings... */ timestampsInSnapshots: true};
|
|
firestore.settings(settings);
|
|
|
|
With this change, timestamps stored in Cloud Firestore will be read back as
|
|
Firebase Timestamp objects instead of as system Date objects. So you will also
|
|
need to update code expecting a Date to instead expect a Timestamp. For example:
|
|
|
|
// Old:
|
|
const date = snapshot.get('created_at');
|
|
// New:
|
|
const timestamp = snapshot.get('created_at');
|
|
const date = timestamp.toDate();
|
|
|
|
Please audit all existing usages of Date when you enable the new behavior. In a
|
|
future release, the behavior will change to the new behavior, so if you do not
|
|
follow these steps, YOUR APP MAY BREAK.`);
|
|
}
|
|
this._clientInitialized = this._initClientPool().then(clientPool => {
|
|
this._clientPool = clientPool;
|
|
});
|
|
}
|
|
return this._clientInitialized.then(() => this._clientPool.run(op));
|
|
}
|
|
/**
|
|
* Initializes the client pool and invokes Project ID detection. Returns a
|
|
* Promise on completion.
|
|
*
|
|
* @private
|
|
* @return {Promise<ClientPool>}
|
|
*/
|
|
_initClientPool() {
|
|
assert_1.default(!this._clientInitialized, 'Client pool already initialized');
|
|
const clientPool = new pool_1.ClientPool(MAX_CONCURRENT_REQUESTS_PER_CLIENT, () => {
|
|
const gapicClient = module.exports.v1beta1(this._initalizationSettings);
|
|
logger_1.logger('Firestore', null, 'Initialized Firestore GAPIC Client');
|
|
return gapicClient;
|
|
});
|
|
const projectIdProvided = this._referencePath.projectId !== '{{projectId}}';
|
|
if (projectIdProvided) {
|
|
return Promise.resolve(clientPool);
|
|
}
|
|
else {
|
|
return clientPool.run(client => this._detectProjectId(client))
|
|
.then(projectId => {
|
|
this._referencePath =
|
|
new path_1.ResourcePath(projectId, this._referencePath.databaseId);
|
|
return clientPool;
|
|
});
|
|
}
|
|
}
|
|
/**
|
|
* Auto-detects the Firestore Project ID.
|
|
*
|
|
* @private
|
|
* @param {object} gapicClient - The Firestore GAPIC client.
|
|
* @return {Promise<string>} A Promise that resolves with the Project ID.
|
|
*/
|
|
_detectProjectId(gapicClient) {
|
|
return new Promise((resolve, reject) => {
|
|
gapicClient.getProjectId((err, projectId) => {
|
|
if (err) {
|
|
logger_1.logger('Firestore._detectProjectId', null, 'Failed to detect project ID: %s', err);
|
|
reject(err);
|
|
}
|
|
else {
|
|
logger_1.logger('Firestore._detectProjectId', null, 'Detected project ID: %s', projectId);
|
|
resolve(projectId);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Decorate the request options of an API request. This is used to replace
|
|
* any `{{projectId}}` placeholders with the value detected from the user's
|
|
* environment, if one wasn't provided manually.
|
|
*
|
|
* @private
|
|
*/
|
|
_decorateRequest(request) {
|
|
let decoratedRequest = extend_1.default(true, {}, request);
|
|
decoratedRequest =
|
|
projectify_1.replaceProjectIdToken(decoratedRequest, this._referencePath.projectId);
|
|
let decoratedGax = { otherArgs: { headers: {} } };
|
|
decoratedGax.otherArgs.headers[CLOUD_RESOURCE_HEADER] = this.formattedName;
|
|
return { request: decoratedRequest, gax: decoratedGax };
|
|
}
|
|
/**
|
|
* A function returning a Promise that can be retried.
|
|
*
|
|
* @private
|
|
* @callback retryFunction
|
|
* @returns {Promise} A Promise indicating the function's success.
|
|
*/
|
|
/**
|
|
* Helper method that retries failed Promises.
|
|
*
|
|
* If 'delayMs' is specified, waits 'delayMs' between invocations. Otherwise,
|
|
* schedules the first attempt immediately, and then waits 100 milliseconds
|
|
* for further attempts.
|
|
*
|
|
* @private
|
|
* @param {number} attemptsRemaining - The number of available attempts.
|
|
* @param {string} requestTag - A unique client-assigned identifier for this
|
|
* request.
|
|
* @param {retryFunction} func - Method returning a Promise than can be
|
|
* retried.
|
|
* @param {number=} delayMs - How long to wait before issuing a this retry.
|
|
* Defaults to zero.
|
|
* @returns {Promise} - A Promise with the function's result if successful
|
|
* within `attemptsRemaining`. Otherwise, returns the last rejected Promise.
|
|
*/
|
|
_retry(attemptsRemaining, requestTag, func, delayMs) {
|
|
let self = this;
|
|
let currentDelay = delayMs || 0;
|
|
let nextDelay = delayMs || 100;
|
|
--attemptsRemaining;
|
|
return new Promise(resolve => {
|
|
setTimeout(resolve, currentDelay);
|
|
})
|
|
.then(func)
|
|
.then(result => {
|
|
self._lastSuccessfulRequest = new Date().getTime();
|
|
return result;
|
|
})
|
|
.catch(err => {
|
|
if (is_1.default.defined(err.code) && err.code !== GRPC_UNAVAILABLE) {
|
|
logger_1.logger('Firestore._retry', requestTag, 'Request failed with unrecoverable error:', err);
|
|
return Promise.reject(err);
|
|
}
|
|
if (attemptsRemaining === 0) {
|
|
logger_1.logger('Firestore._retry', requestTag, 'Request failed with error:', err);
|
|
return Promise.reject(err);
|
|
}
|
|
logger_1.logger('Firestore._retry', requestTag, 'Retrying request that failed with error:', err);
|
|
return self._retry(attemptsRemaining, requestTag, func, nextDelay);
|
|
});
|
|
}
|
|
/**
|
|
* Opens the provided stream and waits for it to become healthy. If an error
|
|
* occurs before the first byte is read, the method rejects the returned
|
|
* Promise.
|
|
*
|
|
* @private
|
|
* @param {Stream} resultStream - The Node stream to monitor.
|
|
* @param {string} requestTag A unique client-assigned identifier for this
|
|
* request.
|
|
* @param {Object=} request - If specified, the request that should be written
|
|
* to the stream after it opened.
|
|
* @returns {Promise.<Stream>} The given Stream once it is considered healthy.
|
|
*/
|
|
_initializeStream(resultStream, requestTag, request) {
|
|
/** The last error we received and have not forwarded yet. */
|
|
let errorReceived = null;
|
|
/**
|
|
* Whether we have resolved the Promise and returned the stream to the
|
|
* caller.
|
|
*/
|
|
let streamReleased = false;
|
|
/**
|
|
* Whether the stream end has been reached. This has to be forwarded to the
|
|
* caller..
|
|
*/
|
|
let endCalled = false;
|
|
return new Promise((resolve, reject) => {
|
|
const releaseStream = () => {
|
|
if (errorReceived) {
|
|
logger_1.logger('Firestore._initializeStream', requestTag, 'Emit error:', errorReceived);
|
|
resultStream.emit('error', errorReceived);
|
|
errorReceived = null;
|
|
}
|
|
else if (!streamReleased) {
|
|
logger_1.logger('Firestore._initializeStream', requestTag, 'Releasing stream');
|
|
streamReleased = true;
|
|
resultStream.pause();
|
|
// Calling 'stream.pause()' only holds up 'data' events and not the
|
|
// 'end' event we intend to forward here. We therefore need to wait
|
|
// until the API consumer registers their listeners (in the .then()
|
|
// call) before emitting any further events.
|
|
resolve(resultStream);
|
|
// We execute the forwarding of the 'end' event via setTimeout() as
|
|
// V8 guarantees that the above the Promise chain is resolved before
|
|
// any calls invoked via setTimeout().
|
|
setTimeout(() => {
|
|
if (endCalled) {
|
|
logger_1.logger('Firestore._initializeStream', requestTag, 'Forwarding stream close');
|
|
resultStream.emit('end');
|
|
}
|
|
}, 0);
|
|
}
|
|
};
|
|
// We capture any errors received and buffer them until the caller has
|
|
// registered a listener. We register our event handler as early as
|
|
// possible to avoid the default stream behavior (which is just to log and
|
|
// continue).
|
|
resultStream.on('readable', () => {
|
|
releaseStream();
|
|
});
|
|
resultStream.on('end', () => {
|
|
logger_1.logger('Firestore._initializeStream', requestTag, 'Received stream end');
|
|
endCalled = true;
|
|
releaseStream();
|
|
});
|
|
resultStream.on('error', err => {
|
|
logger_1.logger('Firestore._initializeStream', requestTag, 'Received stream error:', err);
|
|
// If we receive an error before we were able to receive any data,
|
|
// reject this stream.
|
|
if (!streamReleased) {
|
|
logger_1.logger('Firestore._initializeStream', requestTag, 'Received initial error:', err);
|
|
streamReleased = true;
|
|
reject(err);
|
|
}
|
|
else {
|
|
errorReceived = err;
|
|
}
|
|
});
|
|
if (is_1.default.defined(request)) {
|
|
logger_1.logger('Firestore._initializeStream', requestTag, 'Sending request: %j', request);
|
|
resultStream.write(request, 'utf-8', () => {
|
|
logger_1.logger('Firestore._initializeStream', requestTag, 'Marking stream as healthy');
|
|
releaseStream();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* A funnel for all non-streaming API requests, assigning a project ID where
|
|
* necessary within the request options.
|
|
*
|
|
* @private
|
|
* @param {function} methodName - Name of the veneer API endpoint that takes a
|
|
* request and GAX options.
|
|
* @param {Object} request - The Protobuf request to send.
|
|
* @param {string} requestTag A unique client-assigned identifier for this
|
|
* request.
|
|
* @param {boolean} allowRetries - Whether this is an idempotent request that
|
|
* can be retried.
|
|
* @returns {Promise.<Object>} A Promise with the request result.
|
|
*/
|
|
request(methodName, request, requestTag, allowRetries) {
|
|
let attempts = allowRetries ? MAX_REQUEST_RETRIES : 1;
|
|
return this._runRequest(gapicClient => {
|
|
const decorated = this._decorateRequest(request);
|
|
return this._retry(attempts, requestTag, () => {
|
|
return new Promise((resolve, reject) => {
|
|
logger_1.logger('Firestore.request', requestTag, 'Sending request: %j', decorated.request);
|
|
gapicClient[methodName](decorated.request, decorated.gax, (err, result) => {
|
|
if (err) {
|
|
logger_1.logger('Firestore.request', requestTag, 'Received error:', err);
|
|
reject(err);
|
|
}
|
|
else {
|
|
logger_1.logger('Firestore.request', requestTag, 'Received response: %j', result);
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* A funnel for read-only streaming API requests, assigning a project ID where
|
|
* necessary within the request options.
|
|
*
|
|
* The stream is returned in paused state and needs to be resumed once all
|
|
* listeners are attached.
|
|
*
|
|
* @private
|
|
* @param {string} methodName - Name of the streaming Veneer API endpoint that
|
|
* takes a request and GAX options.
|
|
* @param {Object} request - The Protobuf request to send.
|
|
* @param {string} requestTag A unique client-assigned identifier for this
|
|
* request.
|
|
* @param {boolean} allowRetries - Whether this is an idempotent request that
|
|
* can be retried.
|
|
* @returns {Promise.<Stream>} A Promise with the resulting read-only stream.
|
|
*/
|
|
readStream(methodName, request, requestTag, allowRetries) {
|
|
let attempts = allowRetries ? MAX_REQUEST_RETRIES : 1;
|
|
return this._runRequest(gapicClient => {
|
|
const decorated = this._decorateRequest(request);
|
|
return this._retry(attempts, requestTag, () => {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
logger_1.logger('Firestore.readStream', requestTag, 'Sending request: %j', decorated.request);
|
|
let stream = gapicClient[methodName](decorated.request, decorated.gax);
|
|
let logStream = through2_1.default.obj(function (chunk, enc, callback) {
|
|
logger_1.logger('Firestore.readStream', requestTag, 'Received response: %j', chunk);
|
|
this.push(chunk);
|
|
callback();
|
|
});
|
|
resolve(bun_1.default([stream, logStream]));
|
|
}
|
|
catch (err) {
|
|
logger_1.logger('Firestore.readStream', requestTag, 'Received error:', err);
|
|
reject(err);
|
|
}
|
|
})
|
|
.then(stream => this._initializeStream(stream, requestTag));
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* A funnel for read-write streaming API requests, assigning a project ID
|
|
* where necessary for all writes.
|
|
*
|
|
* The stream is returned in paused state and needs to be resumed once all
|
|
* listeners are attached.
|
|
*
|
|
* @private
|
|
* @param {string} methodName - Name of the streaming Veneer API endpoint that
|
|
* takes GAX options.
|
|
* @param {Object} request - The Protobuf request to send as the first stream
|
|
* message.
|
|
* @param {string} requestTag A unique client-assigned identifier for this
|
|
* request.
|
|
* @param {boolean} allowRetries - Whether this is an idempotent request that
|
|
* can be retried.
|
|
* @returns {Promise.<Stream>} A Promise with the resulting read/write stream.
|
|
*/
|
|
readWriteStream(methodName, request, requestTag, allowRetries) {
|
|
let self = this;
|
|
let attempts = allowRetries ? MAX_REQUEST_RETRIES : 1;
|
|
return this._runRequest(gapicClient => {
|
|
const decorated = this._decorateRequest(request);
|
|
return this._retry(attempts, requestTag, () => {
|
|
return Promise.resolve().then(() => {
|
|
logger_1.logger('Firestore.readWriteStream', requestTag, 'Opening stream');
|
|
// The generated bi-directional streaming API takes the list of GAX
|
|
// headers as its second argument.
|
|
let requestStream = gapicClient[methodName]({}, decorated.gax);
|
|
// The transform stream to assign the project ID.
|
|
let transform = through2_1.default.obj(function (chunk, encoding, callback) {
|
|
let decoratedChunk = extend_1.default(true, {}, chunk);
|
|
projectify_1.replaceProjectIdToken(decoratedChunk, self._referencePath.projectId);
|
|
logger_1.logger('Firestore.readWriteStream', requestTag, 'Streaming request: %j', decoratedChunk);
|
|
requestStream.write(decoratedChunk, encoding, callback);
|
|
});
|
|
let logStream = through2_1.default.obj(function (chunk, enc, callback) {
|
|
logger_1.logger('Firestore.readWriteStream', requestTag, 'Received response: %j', chunk);
|
|
this.push(chunk);
|
|
callback();
|
|
});
|
|
let resultStream = bun_1.default([transform, requestStream, logStream]);
|
|
return this._initializeStream(resultStream, requestTag, request);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
/**
|
|
* Validates a JavaScript value for usage as a Firestore value.
|
|
*
|
|
* @private
|
|
* @param {Object} val JavaScript value to validate.
|
|
* @param {string} options.allowDeletes At what level field deletes are
|
|
* supported (acceptable values are 'none', 'root' or 'all').
|
|
* @param {boolean} options.allowServerTimestamps Whether server timestamps
|
|
* are supported.
|
|
* @param {number=} depth The current depth of the traversal.
|
|
* @param {number=} inArray Whether we are inside an array.
|
|
* @returns {boolean} 'true' when the object is valid.
|
|
* @throws {Error} when the object is invalid.
|
|
*/
|
|
function validateFieldValue(val, options, depth, inArray) {
|
|
assert_1.default(['none', 'root', 'all'].indexOf(options.allowDeletes) !== -1, 'Expected \'none\', \'root\', or \'all\' for \'options.allowDeletes\'');
|
|
assert_1.default(typeof options.allowTransforms === 'boolean', 'Expected boolean for \'options.allowTransforms\'');
|
|
if (!depth) {
|
|
depth = 1;
|
|
}
|
|
else if (depth > MAX_DEPTH) {
|
|
throw new Error(`Input object is deeper than ${MAX_DEPTH} levels or contains a cycle.`);
|
|
}
|
|
inArray = inArray || false;
|
|
if (is_1.default.array(val)) {
|
|
for (let prop of val) {
|
|
validateFieldValue(val[prop], options, depth + 1, /* inArray= */ true);
|
|
}
|
|
}
|
|
else if (serializer_1.isPlainObject(val)) {
|
|
for (let prop in val) {
|
|
if (val.hasOwnProperty(prop)) {
|
|
validateFieldValue(val[prop], options, depth + 1, inArray);
|
|
}
|
|
}
|
|
}
|
|
else if (val instanceof field_value_1.DeleteTransform) {
|
|
if (inArray) {
|
|
throw new Error(`${val.methodName}() cannot be used inside of an array.`);
|
|
}
|
|
else if ((options.allowDeletes === 'root' && depth > 1) ||
|
|
options.allowDeletes === 'none') {
|
|
throw new Error(`${val.methodName}() must appear at the top-level and can only be used in update() or set() with {merge:true}.`);
|
|
}
|
|
}
|
|
else if (val instanceof field_value_1.FieldTransform) {
|
|
if (inArray) {
|
|
throw new Error(`${val.methodName}() cannot be used inside of an array.`);
|
|
}
|
|
else if (!options.allowTransforms) {
|
|
throw new Error(`${val.methodName}() can only be used in set(), create() or update().`);
|
|
}
|
|
}
|
|
else if (is_1.default.instanceof(val, reference_1.DocumentReference)) {
|
|
return true;
|
|
}
|
|
else if (is_1.default.instanceof(val, geo_point_1.GeoPoint)) {
|
|
return true;
|
|
}
|
|
else if (is_1.default.instanceof(val, timestamp_1.Timestamp)) {
|
|
return true;
|
|
}
|
|
else if (is_1.default.instanceof(val, path_1.FieldPath)) {
|
|
throw new Error('Cannot use object of type "FieldPath" as a Firestore value.');
|
|
}
|
|
else if (is_1.default.object(val)) {
|
|
throw validate_1.customObjectError(val);
|
|
}
|
|
return true;
|
|
}
|
|
/**
|
|
* Validates a JavaScript object for usage as a Firestore document.
|
|
*
|
|
* @private
|
|
* @param {Object} obj JavaScript object to validate.
|
|
* @param {string} options.allowDeletes At what level field deletes are
|
|
* supported (acceptable values are 'none', 'root' or 'all').
|
|
* @param {boolean} options.allowServerTimestamps Whether server timestamps
|
|
* are supported.
|
|
* @param {boolean} options.allowEmpty Whether empty documents are supported.
|
|
* @returns {boolean} 'true' when the object is valid.
|
|
* @throws {Error} when the object is invalid.
|
|
*/
|
|
function validateDocumentData(obj, options) {
|
|
assert_1.default(typeof options.allowEmpty === 'boolean', 'Expected boolean for \'options.allowEmpty\'');
|
|
if (!serializer_1.isPlainObject(obj)) {
|
|
throw new Error('Input is not a plain JavaScript object.');
|
|
}
|
|
options = options || {};
|
|
let isEmpty = true;
|
|
for (let prop in obj) {
|
|
if (obj.hasOwnProperty(prop)) {
|
|
isEmpty = false;
|
|
validateFieldValue(obj[prop], options, /* depth= */ 1);
|
|
}
|
|
}
|
|
if (options.allowEmpty === false && isEmpty) {
|
|
throw new Error('At least one field must be updated.');
|
|
}
|
|
return true;
|
|
}
|
|
/**
|
|
* A logging function that takes a single string.
|
|
*
|
|
* @callback Firestore~logFunction
|
|
* @param {string} Log message
|
|
*/
|
|
/**
|
|
* Sets the log function for all active Firestore instances.
|
|
*
|
|
* @method Firestore.setLogFunction
|
|
* @param {Firestore~logFunction} logger - A log function that takes a single
|
|
* string.
|
|
*/
|
|
Firestore.setLogFunction = logger_1.setLogFunction;
|
|
/**
|
|
* The default export of the `@google-cloud/firestore` package is the
|
|
* {@link Firestore} class.
|
|
*
|
|
* See {@link Firestore} and {@link ClientConfig} for client methods and
|
|
* configuration options.
|
|
*
|
|
* @module {Firestore} @google-cloud/firestore
|
|
* @alias nodejs-firestore
|
|
*
|
|
* @example <caption>Install the client library with <a
|
|
* href="https://www.npmjs.com/">npm</a>:</caption> npm install --save
|
|
* @google-cloud/firestore
|
|
*
|
|
* @example <caption>Import the client library</caption>
|
|
* var Firestore = require('@google-cloud/firestore');
|
|
*
|
|
* @example <caption>Create a client that uses <a
|
|
* href="https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application">Application
|
|
* Default Credentials (ADC)</a>:</caption> var firestore = new Firestore();
|
|
*
|
|
* @example <caption>Create a client with <a
|
|
* href="https://cloud.google.com/docs/authentication/production#obtaining_and_providing_service_account_credentials_manually">explicit
|
|
* credentials</a>:</caption> var firestore = new Firestore({ projectId:
|
|
* 'your-project-id', keyFilename: '/path/to/keyfile.json'
|
|
* });
|
|
*
|
|
* @example <caption>include:samples/quickstart.js</caption>
|
|
* region_tag:firestore_quickstart
|
|
* Full quickstart example:
|
|
*/
|
|
module.exports = Firestore;
|
|
module.exports.default = Firestore;
|
|
module.exports.Firestore = Firestore;
|
|
/**
|
|
* {@link v1beta1} factory function.
|
|
*
|
|
* @name Firestore.v1beta1
|
|
* @see v1beta1
|
|
* @type {function}
|
|
*/
|
|
Object.defineProperty(module.exports, 'v1beta1', {
|
|
// The v1beta1 module is very large. To avoid pulling it in from static
|
|
// scope, we lazy-load and cache the module.
|
|
get: () => {
|
|
if (!v1beta1) {
|
|
v1beta1 = require('./v1beta1');
|
|
}
|
|
return v1beta1;
|
|
},
|
|
});
|
|
/**
|
|
* {@link GeoPoint} class.
|
|
*
|
|
* @name Firestore.GeoPoint
|
|
* @see GeoPoint
|
|
* @type {Constructor}
|
|
*/
|
|
module.exports.GeoPoint = geo_point_1.GeoPoint;
|
|
/**
|
|
* {@link Transaction} class.
|
|
*
|
|
* @name Firestore.Transaction
|
|
* @see Transaction
|
|
* @type Transaction
|
|
*/
|
|
module.exports.Transaction = transaction_1.Transaction;
|
|
/**
|
|
* {@link WriteBatch} class.
|
|
*
|
|
* @name Firestore.WriteBatch
|
|
* @see WriteBatch
|
|
* @type WriteBatch
|
|
*/
|
|
module.exports.WriteBatch = write_batch_1.WriteBatch;
|
|
/**
|
|
* {@link DocumentReference} class.
|
|
*
|
|
* @name Firestore.DocumentReference
|
|
* @see DocumentReference
|
|
* @type DocumentReference
|
|
*/
|
|
module.exports.DocumentReference = reference_1.DocumentReference;
|
|
/**
|
|
* {@link WriteResult} class.
|
|
*
|
|
* @name Firestore.WriteResult
|
|
* @see WriteResult
|
|
* @type WriteResult
|
|
*/
|
|
module.exports.WriteResult = write_batch_1.WriteResult;
|
|
/**
|
|
* {@link DocumentSnapshot} DocumentSnapshot.
|
|
*
|
|
* @name Firestore.DocumentSnapshot
|
|
* @see DocumentSnapshot
|
|
* @type DocumentSnapshot
|
|
*/
|
|
module.exports.DocumentSnapshot = document_1.DocumentSnapshot;
|
|
/**
|
|
* {@link QueryDocumentSnapshot} class.
|
|
*
|
|
* @name Firestore.QueryDocumentSnapshot
|
|
* @see QueryDocumentSnapshot
|
|
* @type QueryDocumentSnapshot
|
|
*/
|
|
module.exports.QueryDocumentSnapshot = document_1.QueryDocumentSnapshot;
|
|
/**
|
|
* {@link CollectionReference} class.
|
|
*
|
|
* @name Firestore.CollectionReference
|
|
* @see CollectionReference
|
|
* @type CollectionReference
|
|
*/
|
|
module.exports.CollectionReference = reference_1.CollectionReference;
|
|
/**
|
|
* {@link QuerySnapshot} class.
|
|
*
|
|
* @name Firestore.QuerySnapshot
|
|
* @see QuerySnapshot
|
|
* @type QuerySnapshot
|
|
*/
|
|
module.exports.QuerySnapshot = reference_1.QuerySnapshot;
|
|
/**
|
|
* {@link DocumentChange} class.
|
|
*
|
|
* @name Firestore.DocumentChange
|
|
* @see DocumentChange
|
|
* @type DocumentChange
|
|
*/
|
|
module.exports.DocumentChange = document_change_1.DocumentChange;
|
|
/**
|
|
* {@link Query} class.
|
|
*
|
|
* @name Firestore.Query
|
|
* @see Query
|
|
* @type Query
|
|
*/
|
|
module.exports.Query = reference_1.Query;
|
|
/**
|
|
* {@link FieldValue} class.
|
|
*
|
|
* @name Firestore.FieldValue
|
|
* @see FieldValue
|
|
*/
|
|
module.exports.FieldValue = field_value_1.FieldValue;
|
|
/**
|
|
* {@link FieldPath} class.
|
|
*
|
|
* @name Firestore.FieldPath
|
|
* @see FieldPath
|
|
* @type {Constructor}
|
|
*/
|
|
module.exports.FieldPath = path_1.FieldPath;
|
|
/**
|
|
* {@link Timestamp} class.
|
|
*
|
|
* @name Firestore.Timestamp
|
|
* @see Timestamp
|
|
* @type Timestamp
|
|
*/
|
|
module.exports.Timestamp = timestamp_1.Timestamp;
|