Add files via upload

This commit is contained in:
jlevine18
2019-01-06 13:02:35 -06:00
committed by GitHub
parent 752b981e37
commit d7301e26c3
78 changed files with 63595 additions and 0 deletions

View File

@@ -0,0 +1,197 @@
/*!
* 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';
Object.defineProperty(exports, "__esModule", { value: true });
const logger_1 = require("./logger");
/*
* @module firestore/backoff
* @private
*
* Contains backoff logic to facilitate RPC error handling. This class derives
* its implementation from the Firestore Mobile Web Client.
*
* @see https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/remote/backoff.ts
*/
/*!
* The default initial backoff time in milliseconds after an error.
* Set to 1s according to https://cloud.google.com/apis/design/errors.
*/
const DEFAULT_BACKOFF_INITIAL_DELAY_MS = 1000;
/*!
* The default maximum backoff time in milliseconds.
*/
const DEFAULT_BACKOFF_MAX_DELAY_MS = 60 * 1000;
/*!
* The default factor to increase the backup by after each failed attempt.
*/
const DEFAULT_BACKOFF_FACTOR = 1.5;
/*!
* The default jitter to distribute the backoff attempts by (0 means no
* randomization, 1.0 means +/-50% randomization).
*/
const DEFAULT_JITTER_FACTOR = 1.0;
/*!
* The timeout handler used by `ExponentialBackoff`.
*/
let delayExecution = setTimeout;
/**
* Allows overriding of the timeout handler used by the exponential backoff
* implementation. If not invoked, we default to `setTimeout()`.
*
* Used only in testing.
*
* @private
* @param {function} handler A handler than matches the API of `setTimeout()`.
*/
function setTimeoutHandler(handler) {
delayExecution = handler;
}
exports.setTimeoutHandler = setTimeoutHandler;
/**
* A helper for running delayed tasks following an exponential backoff curve
* between attempts.
*
* Each delay is made up of a "base" delay which follows the exponential
* backoff curve, and a "jitter" (+/- 50% by default) that is calculated and
* added to the base delay. This prevents clients from accidentally
* synchronizing their delays causing spikes of load to the backend.
*
* @private
*/
class ExponentialBackoff {
/**
* @param {number=} options.initialDelayMs Optional override for the initial
* retry delay.
* @param {number=} options.backoffFactor Optional override for the
* exponential backoff factor.
* @param {number=} options.maxDelayMs Optional override for the maximum
* retry delay.
* @param {number=} options.jitterFactor Optional override to control the
* jitter factor by which to randomize attempts (0 means no randomization,
* 1.0 means +/-50% randomization). It is suggested not to exceed this range.
*/
constructor(options) {
options = options || {};
/**
* The initial delay (used as the base delay on the first retry attempt).
* Note that jitter will still be applied, so the actual delay could be as
* little as 0.5*initialDelayMs (based on a jitter factor of 1.0).
*
* @type {number}
* @private
*/
this._initialDelayMs = options.initialDelayMs !== undefined ?
options.initialDelayMs :
DEFAULT_BACKOFF_INITIAL_DELAY_MS;
/**
* The multiplier to use to determine the extended base delay after each
* attempt.
* @type {number}
* @private
*/
this._backoffFactor = options.backoffFactor !== undefined ?
options.backoffFactor :
DEFAULT_BACKOFF_FACTOR;
/**
* The maximum base delay after which no further backoff is performed.
* Note that jitter will still be applied, so the actual delay could be as
* much as 1.5*maxDelayMs (based on a jitter factor of 1.0).
*
* @type {number}
* @private
*/
this._maxDelayMs = options.maxDelayMs !== undefined ?
options.maxDelayMs :
DEFAULT_BACKOFF_MAX_DELAY_MS;
/**
* The jitter factor that controls the random distribution of the backoff
* points.
*
* @type {number}
* @private
*/
this._jitterFactor = options.jitterFactor !== undefined ?
options.jitterFactor :
DEFAULT_JITTER_FACTOR;
/**
* The backoff delay of the current attempt.
* @type {number}
* @private
*/
this._currentBaseMs = 0;
}
/**
* Resets the backoff delay.
*
* The very next backoffAndWait() will have no delay. If it is called again
* (i.e. due to an error), initialDelayMs (plus jitter) will be used, and
* subsequent ones will increase according to the backoffFactor.
*
* @private
*/
reset() {
this._currentBaseMs = 0;
}
/**
* Resets the backoff delay to the maximum delay (e.g. for use after a
* RESOURCE_EXHAUSTED error).
*
* @private
*/
resetToMax() {
this._currentBaseMs = this._maxDelayMs;
}
/**
* Returns a promise that resolves after currentDelayMs, and increases the
* delay for any subsequent attempts.
*
* @private
* @return {Promise.<void>} A Promise that resolves when the current delay
* elapsed.
*/
backoffAndWait() {
// First schedule using the current base (which may be 0 and should be
// honored as such).
const delayWithJitterMs = this._currentBaseMs + this._jitterDelayMs();
if (this._currentBaseMs > 0) {
logger_1.logger('ExponentialBackoff.backoffAndWait', `Backing off for ${delayWithJitterMs} ms ` +
`(base delay: ${this._currentBaseMs} ms)`);
}
// Apply backoff factor to determine next delay and ensure it is within
// bounds.
this._currentBaseMs *= this._backoffFactor;
if (this._currentBaseMs < this._initialDelayMs) {
this._currentBaseMs = this._initialDelayMs;
}
if (this._currentBaseMs > this._maxDelayMs) {
this._currentBaseMs = this._maxDelayMs;
}
return new Promise(resolve => {
delayExecution(resolve, delayWithJitterMs);
});
}
/**
* Returns a randomized "jitter" delay based on the current base and jitter
* factor.
*
* @private
* @returns {number} The jitter to apply based on the current delay.
*/
_jitterDelayMs() {
return (Math.random() - 0.5) * this._jitterFactor * this._currentBaseMs;
}
}
exports.ExponentialBackoff = ExponentialBackoff;

View File

@@ -0,0 +1,213 @@
/*!
* 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = __importDefault(require("is"));
const validate_1 = require("./validate");
const validate = validate_1.createValidator();
/*!
* @module firestore/convert
* @private
*
* This module contains utility functions to convert
* `firestore.v1beta1.Documents` from Proto3 JSON to their equivalent
* representation in Protobuf JS. Protobuf JS is the only encoding supported by
* this client, and dependencies that use Proto3 JSON (such as the Google Cloud
* Functions SDK) are supported through this conversion and its usage in
* {@see Firestore#snapshot_}.
*/
/**
* Converts an ISO 8601 or google.protobuf.Timestamp proto into Protobuf JS.
*
* @private
* @param {*=} timestampValue - The value to convert.
* @param {string=} argumentName - The argument name to use in the error message
* if the conversion fails. If omitted, 'timestampValue' is used.
* @return {{nanos,seconds}|undefined} The value as expected by Protobuf JS or
* undefined if no input was provided.
*/
function convertTimestamp(timestampValue, argumentName) {
let timestampProto = undefined;
if (is_1.default.string(timestampValue)) {
let date = new Date(timestampValue);
let seconds = Math.floor(date.getTime() / 1000);
let nanos = 0;
if (timestampValue.length > 20) {
const nanoString = timestampValue.substring(20, timestampValue.length - 1);
const trailingZeroes = 9 - nanoString.length;
nanos = parseInt(nanoString, 10) * Math.pow(10, trailingZeroes);
}
if (isNaN(seconds) || isNaN(nanos)) {
argumentName = argumentName || 'timestampValue';
throw new Error(`Specify a valid ISO 8601 timestamp for "${argumentName}".`);
}
timestampProto = {
seconds: seconds || undefined,
nanos: nanos || undefined,
};
}
else if (is_1.default.defined(timestampValue)) {
validate.isObject('timestampValue', timestampValue);
timestampProto = {
seconds: timestampValue.seconds || undefined,
nanos: timestampValue.nanos || undefined,
};
}
return timestampProto;
}
/**
* Converts a Proto3 JSON 'bytesValue' field into Protobuf JS.
*
* @private
* @param {*} bytesValue - The value to convert.
* @return {Buffer} The value as expected by Protobuf JS.
*/
function convertBytes(bytesValue) {
if (typeof bytesValue === 'string') {
return Buffer.from(bytesValue, 'base64');
}
else {
return bytesValue;
}
}
/**
* Detects 'valueType' from a Proto3 JSON `firestore.v1beta1.Value` proto.
*
* @private
* @param {object} proto - The `firestore.v1beta1.Value` proto.
* @return {string} - The string value for 'valueType'.
*/
function detectValueType(proto) {
if (proto.valueType) {
return proto.valueType;
}
let detectedValues = [];
if (is_1.default.defined(proto.stringValue)) {
detectedValues.push('stringValue');
}
if (is_1.default.defined(proto.booleanValue)) {
detectedValues.push('booleanValue');
}
if (is_1.default.defined(proto.integerValue)) {
detectedValues.push('integerValue');
}
if (is_1.default.defined(proto.doubleValue)) {
detectedValues.push('doubleValue');
}
if (is_1.default.defined(proto.timestampValue)) {
detectedValues.push('timestampValue');
}
if (is_1.default.defined(proto.referenceValue)) {
detectedValues.push('referenceValue');
}
if (is_1.default.defined(proto.arrayValue)) {
detectedValues.push('arrayValue');
}
if (is_1.default.defined(proto.nullValue)) {
detectedValues.push('nullValue');
}
if (is_1.default.defined(proto.mapValue)) {
detectedValues.push('mapValue');
}
if (is_1.default.defined(proto.geoPointValue)) {
detectedValues.push('geoPointValue');
}
if (is_1.default.defined(proto.bytesValue)) {
detectedValues.push('bytesValue');
}
if (detectedValues.length !== 1) {
throw new Error(`Unable to infer type value fom '${JSON.stringify(proto)}'.`);
}
return detectedValues[0];
}
/**
* Converts a `firestore.v1beta1.Value` in Proto3 JSON encoding into the
* Protobuf JS format expected by this client.
*
* @private
* @param {object} fieldValue - The `firestore.v1beta1.Value` in Proto3 JSON
* format.
* @return {object} The `firestore.v1beta1.Value` in Protobuf JS format.
*/
function convertValue(fieldValue) {
let valueType = detectValueType(fieldValue);
switch (valueType) {
case 'timestampValue':
return {
timestampValue: convertTimestamp(fieldValue.timestampValue),
};
case 'bytesValue':
return {
bytesValue: convertBytes(fieldValue.bytesValue),
};
case 'arrayValue': {
let arrayValue = [];
if (is_1.default.array(fieldValue.arrayValue.values)) {
for (let value of fieldValue.arrayValue.values) {
arrayValue.push(convertValue(value));
}
}
return {
arrayValue: {
values: arrayValue,
},
};
}
case 'mapValue': {
let mapValue = {};
for (let prop in fieldValue.mapValue.fields) {
if (fieldValue.mapValue.fields.hasOwnProperty(prop)) {
mapValue[prop] = convertValue(fieldValue.mapValue.fields[prop]);
}
}
return {
mapValue: {
fields: mapValue,
},
};
}
default:
return fieldValue;
}
}
/**
* Converts a `firestore.v1beta1.Document` in Proto3 JSON encoding into the
* Protobuf JS format expected by this client. This conversion creates a copy of
* the underlying document.
*
* @private
* @param {object} document - The `firestore.v1beta1.Document` in Proto3 JSON
* format.
* @return {object} The `firestore.v1beta1.Document` in Protobuf JS format.
*/
function convertDocument(document) {
let result = {};
for (let prop in document) {
if (document.hasOwnProperty(prop)) {
result[prop] = convertValue(document[prop]);
}
}
return result;
}
module.exports = {
documentFromJson: convertDocument,
timestampFromJson: convertTimestamp,
valueFromJson: convertValue,
detectValueType: detectValueType,
};

View File

@@ -0,0 +1,173 @@
/*!
* Copyright 2018 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 __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 is = __importStar(require("is"));
/**
* A DocumentChange represents a change to the documents matching a query.
* It contains the document affected and the type of change that occurred.
*
* @class
*/
class DocumentChange {
/**
* @private
* @hideconstructor
*
* @param {string} type - 'added' | 'removed' | 'modified'.
* @param {QueryDocumentSnapshot} document - The document.
* @param {number} oldIndex - The index in the documents array prior to this
* change.
* @param {number} newIndex - The index in the documents array after this
* change.
*/
constructor(type, document, oldIndex, newIndex) {
this._type = type;
this._document = document;
this._oldIndex = oldIndex;
this._newIndex = newIndex;
}
/**
* The type of change ('added', 'modified', or 'removed').
*
* @type {string}
* @name DocumentChange#type
* @readonly
*
* @example
* let query = firestore.collection('col').where('foo', '==', 'bar');
* let docsArray = [];
*
* let unsubscribe = query.onSnapshot(querySnapshot => {
* for (let change of querySnapshot.docChanges) {
* console.log(`Type of change is ${change.type}`);
* }
* });
*
* // Remove this listener.
* unsubscribe();
*/
get type() {
return this._type;
}
/**
* The document affected by this change.
*
* @type {QueryDocumentSnapshot}
* @name DocumentChange#doc
* @readonly
*
* @example
* let query = firestore.collection('col').where('foo', '==', 'bar');
*
* let unsubscribe = query.onSnapshot(querySnapshot => {
* for (let change of querySnapshot.docChanges) {
* console.log(change.doc.data());
* }
* });
*
* // Remove this listener.
* unsubscribe();
*/
get doc() {
return this._document;
}
/**
* The index of the changed document in the result set immediately prior to
* this DocumentChange (i.e. supposing that all prior DocumentChange objects
* have been applied). Is -1 for 'added' events.
*
* @type {number}
* @name DocumentChange#oldIndex
* @readonly
*
* @example
* let query = firestore.collection('col').where('foo', '==', 'bar');
* let docsArray = [];
*
* let unsubscribe = query.onSnapshot(querySnapshot => {
* for (let change of querySnapshot.docChanges) {
* if (change.oldIndex !== -1) {
* docsArray.splice(change.oldIndex, 1);
* }
* if (change.newIndex !== -1) {
* docsArray.splice(change.newIndex, 0, change.doc);
* }
* }
* });
*
* // Remove this listener.
* unsubscribe();
*/
get oldIndex() {
return this._oldIndex;
}
/**
* The index of the changed document in the result set immediately after
* this DocumentChange (i.e. supposing that all prior DocumentChange
* objects and the current DocumentChange object have been applied).
* Is -1 for 'removed' events.
*
* @type {number}
* @name DocumentChange#newIndex
* @readonly
*
* @example
* let query = firestore.collection('col').where('foo', '==', 'bar');
* let docsArray = [];
*
* let unsubscribe = query.onSnapshot(querySnapshot => {
* for (let change of querySnapshot.docChanges) {
* if (change.oldIndex !== -1) {
* docsArray.splice(change.oldIndex, 1);
* }
* if (change.newIndex !== -1) {
* docsArray.splice(change.newIndex, 0, change.doc);
* }
* }
* });
*
* // Remove this listener.
* unsubscribe();
*/
get newIndex() {
return this._newIndex;
}
/**
* Returns true if the data in this `DocumentChange` is equal to the provided
* value.
*
* @param {*} other The value to compare against.
* @return true if this `DocumentChange` is equal to the provided value.
*/
isEqual(other) {
if (this === other) {
return true;
}
return (is.instanceof(other, DocumentChange) && this._type === other._type &&
this._oldIndex === other._oldIndex &&
this._newIndex === other._newIndex &&
this._document.isEqual(other._document));
}
}
exports.DocumentChange = DocumentChange;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,324 @@
/*!
* Copyright 2018 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const deep_equal_1 = __importDefault(require("deep-equal"));
const validate_1 = require("./validate");
const firestore_proto_api_1 = require("../protos/firestore_proto_api");
var api = firestore_proto_api_1.google.firestore.v1beta1;
const validate = validate_1.createValidator();
/**
* Sentinel values that can be used when writing documents with set(), create()
* or update().
*
* @class
*/
class FieldValue {
/**
* @private
* @hideconstructor
*/
constructor() { }
/**
* Returns a sentinel for use with update() or set() with {merge:true} to mark
* a field for deletion.
*
* @returns {FieldValue} The sentinel value to use in your objects.
*
* @example
* let documentRef = firestore.doc('col/doc');
* let data = { a: 'b', c: 'd' };
*
* documentRef.set(data).then(() => {
* return documentRef.update({a: Firestore.FieldValue.delete()});
* }).then(() => {
* // Document now only contains { c: 'd' }
* });
*/
static delete() {
return DeleteTransform.DELETE_SENTINEL;
}
/**
* Returns a sentinel used with set(), create() or update() to include a
* server-generated timestamp in the written data.
*
* @return {FieldValue} The FieldValue sentinel for use in a call to set(),
* create() or update().
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.set({
* time: Firestore.FieldValue.serverTimestamp()
* }).then(() => {
* return documentRef.get();
* }).then(doc => {
* console.log(`Server time set to ${doc.get('time')}`);
* });
*/
static serverTimestamp() {
return ServerTimestampTransform.SERVER_TIMESTAMP_SENTINEL;
}
/**
* Returns a special value that can be used with set(), create() or update()
* that tells the server to union the given elements with any array value that
* already exists on the server. Each specified element that doesn't already
* exist in the array will be added to the end. If the field being modified is
* not already an array it will be overwritten with an array containing
* exactly the specified elements.
*
* @param {...*} elements The elements to union into the array.
* @return {FieldValue} The FieldValue sentinel for use in a call to set(),
* create() or update().
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.update(
* 'array', Firestore.FieldValue.arrayUnion('foo')
* ).then(() => {
* return documentRef.get();
* }).then(doc => {
* // doc.get('array') contains field 'foo'
* });
*/
static arrayUnion(...elements) {
validate.minNumberOfArguments('FieldValue.arrayUnion', arguments, 1);
return new ArrayUnionTransform(elements);
}
/**
* Returns a special value that can be used with set(), create() or update()
* that tells the server to remove the given elements from any array value
* that already exists on the server. All instances of each element specified
* will be removed from the array. If the field being modified is not already
* an array it will be overwritten with an empty array.
*
* @param {...*} elements The elements to remove from the array.
* @return {FieldValue} The FieldValue sentinel for use in a call to set(),
* create() or update().
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.update(
* 'array', Firestore.FieldValue.arrayRemove('foo')
* ).then(() => {
* return documentRef.get();
* }).then(doc => {
* // doc.get('array') no longer contains field 'foo'
* });
*/
static arrayRemove(...elements) {
validate.minNumberOfArguments('FieldValue.arrayRemove', arguments, 1);
return new ArrayRemoveTransform(elements);
}
/**
* Returns true if this `FieldValue` is equal to the provided value.
*
* @param {*} other The value to compare against.
* @return {boolean} true if this `FieldValue` is equal to the provided value.
*/
isEqual(other) {
return this === other;
}
}
exports.FieldValue = FieldValue;
/**
* An internal interface shared by all field transforms.
*
* A 'FieldTransform` subclass should implement '.includeInDocumentMask',
* '.includeInDocumentTransform' and 'toProto' (if '.includeInDocumentTransform'
* is 'true').
*
* @private
* @abstract
*/
class FieldTransform extends FieldValue {
}
exports.FieldTransform = FieldTransform;
/**
* A transform that deletes a field from a Firestore document.
*
* @private
*/
class DeleteTransform extends FieldTransform {
constructor() {
super();
}
/**
* Deletes are included in document masks.
*/
get includeInDocumentMask() {
return true;
}
/**
* Deletes are are omitted from document transforms.
*/
get includeInDocumentTransform() {
return false;
}
get methodName() {
return 'FieldValue.delete';
}
validate() {
return true;
}
toProto(serializer, fieldPath) {
throw new Error('FieldValue.delete() should not be included in a FieldTransform');
}
}
/**
* Sentinel value for a field delete.
*/
DeleteTransform.DELETE_SENTINEL = new DeleteTransform();
exports.DeleteTransform = DeleteTransform;
/**
* A transform that sets a field to the Firestore server time.
*
* @private
*/
class ServerTimestampTransform extends FieldTransform {
constructor() {
super();
}
/**
* Server timestamps are omitted from document masks.
*
* @private
*/
get includeInDocumentMask() {
return false;
}
/**
* Server timestamps are included in document transforms.
*
* @private
*/
get includeInDocumentTransform() {
return true;
}
get methodName() {
return 'FieldValue.serverTimestamp';
}
validate() {
return true;
}
toProto(serializer, fieldPath) {
return {
fieldPath: fieldPath.formattedName,
setToServerValue: api.DocumentTransform.FieldTransform.ServerValue.REQUEST_TIME,
};
}
}
/**
* Sentinel value for a server timestamp.
*
* @private
*/
ServerTimestampTransform.SERVER_TIMESTAMP_SENTINEL = new ServerTimestampTransform();
/**
* Transforms an array value via a union operation.
*
* @private
*/
class ArrayUnionTransform extends FieldTransform {
constructor(elements) {
super();
this.elements = elements;
}
/**
* Array transforms are omitted from document masks.
*/
get includeInDocumentMask() {
return false;
}
/**
* Array transforms are included in document transforms.
*/
get includeInDocumentTransform() {
return true;
}
get methodName() {
return 'FieldValue.arrayUnion';
}
validate(validator) {
let valid = true;
for (let i = 0; valid && i < this.elements.length; ++i) {
valid = validator.isArrayElement(i, this.elements[i], { allowDeletes: 'none', allowTransforms: false });
}
return valid;
}
toProto(serializer, fieldPath) {
const encodedElements = serializer.encodeValue(this.elements).arrayValue;
return {
fieldPath: fieldPath.formattedName,
appendMissingElements: encodedElements
};
}
isEqual(other) {
return (this === other ||
(other instanceof ArrayUnionTransform &&
deep_equal_1.default(this.elements, other.elements, { strict: true })));
}
}
/**
* Transforms an array value via a remove operation.
*
* @private
*/
class ArrayRemoveTransform extends FieldTransform {
constructor(elements) {
super();
this.elements = elements;
}
/**
* Array transforms are omitted from document masks.
*/
get includeInDocumentMask() {
return false;
}
/**
* Array transforms are included in document transforms.
*/
get includeInDocumentTransform() {
return true;
}
get methodName() {
return 'FieldValue.arrayRemove';
}
validate(validator) {
let valid = true;
for (let i = 0; valid && i < this.elements.length; ++i) {
valid = validator.isArrayElement(i, this.elements[i], { allowDeletes: 'none', allowTransforms: false });
}
return valid;
}
toProto(serializer, fieldPath) {
const encodedElements = serializer.encodeValue(this.elements).arrayValue;
return {
fieldPath: fieldPath.formattedName,
removeAllFromArray: encodedElements
};
}
isEqual(other) {
return (this === other ||
(other instanceof ArrayRemoveTransform &&
deep_equal_1.default(this.elements, other.elements, { strict: true })));
}
}

View File

@@ -0,0 +1,108 @@
/*!
* Copyright 2018 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';
Object.defineProperty(exports, "__esModule", { value: true });
const validate_1 = require("./validate");
const validate = validate_1.createValidator();
/**
* An immutable object representing a geographic location in Firestore. The
* location is represented as a latitude/longitude pair.
*
* @class
*/
class GeoPoint {
/**
* Creates a [GeoPoint]{@link GeoPoint}.
*
* @param {number} latitude The latitude as a number between -90 and 90.
* @param {number} longitude The longitude as a number between -180 and 180.
*
* @example
* let data = {
* google: new Firestore.GeoPoint(37.422, 122.084)
* };
*
* firestore.doc('col/doc').set(data).then(() => {
* console.log(`Location is ${data.google.latitude}, ` +
* `${data.google.longitude}`);
* });
*/
constructor(latitude, longitude) {
validate.isNumber('latitude', latitude, -90, 90);
validate.isNumber('longitude', longitude, -180, 180);
this._latitude = latitude;
this._longitude = longitude;
}
/**
* The latitude as a number between -90 and 90.
*
* @type {number}
* @name GeoPoint#latitude
* @readonly
*/
get latitude() {
return this._latitude;
}
/**
* The longitude as a number between -180 and 180.
*
* @type {number}
* @name GeoPoint#longitude
* @readonly
*/
get longitude() {
return this._longitude;
}
/**
* Returns a string representation for this GeoPoint.
*
* @return {string} The string representation.
*/
toString() {
return `GeoPoint { latitude: ${this.latitude}, longitude: ${this.longitude} }`;
}
/**
* Returns true if this `GeoPoint` is equal to the provided value.
*
* @param {*} other The value to compare against.
* @return {boolean} true if this `GeoPoint` is equal to the provided value.
*/
isEqual(other) {
return (this === other ||
(other instanceof GeoPoint && this.latitude === other.latitude &&
this.longitude === other.longitude));
}
/**
* Converts the GeoPoint to a google.type.LatLng proto.
* @private
*/
toProto() {
return {
geoPointValue: {
latitude: this.latitude,
longitude: this.longitude,
}
};
}
/**
* Converts a google.type.LatLng proto to its GeoPoint representation.
* @private
*/
static fromProto(proto) {
return new GeoPoint(proto.latitude || 0, proto.longitude || 0);
}
}
exports.GeoPoint = GeoPoint;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
/*!
* Copyright 2018 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const util_1 = __importDefault(require("util"));
const validate_1 = require("./validate");
const validate = validate_1.createValidator();
/*! The Firestore library version */
let libVersion;
/*! The external function used to emit logs. */
let logFunction = (msg) => { };
/**
* Log function to use for debug output. By default, we don't perform any
* logging.
*
* @private
*/
function logger(methodName, requestTag, logMessage, ...additionalArgs) {
requestTag = requestTag || '#####';
const formattedMessage = util_1.default.format(logMessage, ...additionalArgs);
const time = new Date().toISOString();
logFunction(`Firestore (${libVersion}) ${time} ${requestTag} [${methodName}]: ` +
formattedMessage);
}
exports.logger = logger;
/**
* Sets the log function for all active Firestore instances.
*
* @private
*/
function setLogFunction(logger) {
validate.isFunction('logger', logger);
logFunction = logger;
}
exports.setLogFunction = setLogFunction;
/**
* Sets the log function for all active Firestore instances.
*
* @private
*/
function setLibVersion(version) {
libVersion = version;
}
exports.setLibVersion = setLibVersion;

View File

@@ -0,0 +1,238 @@
/*!
* 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 __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 is = __importStar(require("is"));
const convert_1 = require("./convert");
const path_1 = require("./path");
const validate_1 = require("./validate");
/*!
* The type order as defined by the backend.
*/
var TypeOrder;
(function (TypeOrder) {
TypeOrder[TypeOrder["NULL"] = 0] = "NULL";
TypeOrder[TypeOrder["BOOLEAN"] = 1] = "BOOLEAN";
TypeOrder[TypeOrder["NUMBER"] = 2] = "NUMBER";
TypeOrder[TypeOrder["TIMESTAMP"] = 3] = "TIMESTAMP";
TypeOrder[TypeOrder["STRING"] = 4] = "STRING";
TypeOrder[TypeOrder["BLOB"] = 5] = "BLOB";
TypeOrder[TypeOrder["REF"] = 6] = "REF";
TypeOrder[TypeOrder["GEO_POINT"] = 7] = "GEO_POINT";
TypeOrder[TypeOrder["ARRAY"] = 8] = "ARRAY";
TypeOrder[TypeOrder["OBJECT"] = 9] = "OBJECT";
})(TypeOrder || (TypeOrder = {}));
/*!
* @private
*/
function typeOrder(val) {
const valueType = convert_1.detectValueType(val);
switch (valueType) {
case 'nullValue':
return TypeOrder.NULL;
case 'integerValue':
return TypeOrder.NUMBER;
case 'doubleValue':
return TypeOrder.NUMBER;
case 'stringValue':
return TypeOrder.STRING;
case 'booleanValue':
return TypeOrder.BOOLEAN;
case 'arrayValue':
return TypeOrder.ARRAY;
case 'timestampValue':
return TypeOrder.TIMESTAMP;
case 'geoPointValue':
return TypeOrder.GEO_POINT;
case 'bytesValue':
return TypeOrder.BLOB;
case 'referenceValue':
return TypeOrder.REF;
case 'mapValue':
return TypeOrder.OBJECT;
default:
throw validate_1.customObjectError(val);
}
}
/*!
* @private
*/
function primitiveComparator(left, right) {
if (left < right) {
return -1;
}
if (left > right) {
return 1;
}
return 0;
}
exports.primitiveComparator = primitiveComparator;
/*!
* Utility function to compare doubles (using Firestore semantics for NaN).
* @private
*/
function compareNumbers(left, right) {
if (left < right) {
return -1;
}
if (left > right) {
return 1;
}
if (left === right) {
return 0;
}
// one or both are NaN.
if (isNaN(left)) {
return isNaN(right) ? 0 : -1;
}
return 1;
}
/*!
* @private
*/
function compareNumberProtos(left, right) {
let leftValue, rightValue;
if (left.integerValue !== undefined) {
leftValue = Number(left.integerValue);
}
else {
leftValue = Number(left.doubleValue);
}
if (right.integerValue !== undefined) {
rightValue = Number(right.integerValue);
}
else {
rightValue = Number(right.doubleValue);
}
return compareNumbers(leftValue, rightValue);
}
/*!
* @private
*/
function compareTimestamps(left, right) {
const seconds = primitiveComparator(left.seconds || 0, right.seconds || 0);
if (seconds !== 0) {
return seconds;
}
return primitiveComparator(left.nanos || 0, right.nanos || 0);
}
/*!
* @private
*/
function compareBlobs(left, right) {
if (!is.instanceof(left, Buffer) || !is.instanceof(right, Buffer)) {
throw new Error('Blobs can only be compared if they are Buffers.');
}
return Buffer.compare(left, right);
}
/*!
* @private
*/
function compareReferenceProtos(left, right) {
const leftPath = path_1.ResourcePath.fromSlashSeparatedString(left.referenceValue);
const rightPath = path_1.ResourcePath.fromSlashSeparatedString(right.referenceValue);
return leftPath.compareTo(rightPath);
}
/*!
* @private
*/
function compareGeoPoints(left, right) {
return (primitiveComparator(left.latitude || 0, right.latitude || 0) ||
primitiveComparator(left.longitude || 0, right.longitude || 0));
}
/*!
* @private
*/
function compareArrays(left, right) {
for (let i = 0; i < left.length && i < right.length; i++) {
const valueComparison = compare(left[i], right[i]);
if (valueComparison !== 0) {
return valueComparison;
}
}
// If all the values matched so far, just check the length.
return primitiveComparator(left.length, right.length);
}
/*!
* @private
*/
function compareObjects(left, right) {
// This requires iterating over the keys in the object in order and doing a
// deep comparison.
const leftKeys = Object.keys(left);
const rightKeys = Object.keys(right);
leftKeys.sort();
rightKeys.sort();
for (let i = 0; i < leftKeys.length && i < rightKeys.length; i++) {
const keyComparison = primitiveComparator(leftKeys[i], rightKeys[i]);
if (keyComparison !== 0) {
return keyComparison;
}
const key = leftKeys[i];
const valueComparison = compare(left[key], right[key]);
if (valueComparison !== 0) {
return valueComparison;
}
}
// If all the keys matched so far, just check the length.
return primitiveComparator(leftKeys.length, rightKeys.length);
}
/*!
* @private
*/
function compare(left, right) {
// First compare the types.
const leftType = typeOrder(left);
const rightType = typeOrder(right);
const typeComparison = primitiveComparator(leftType, rightType);
if (typeComparison !== 0) {
return typeComparison;
}
// So they are the same type.
switch (leftType) {
case TypeOrder.NULL:
// Nulls are all equal.
return 0;
case TypeOrder.BOOLEAN:
return primitiveComparator(left.booleanValue, right.booleanValue);
case TypeOrder.STRING:
return primitiveComparator(left.stringValue, right.stringValue);
case TypeOrder.NUMBER:
return compareNumberProtos(left, right);
case TypeOrder.TIMESTAMP:
return compareTimestamps(left.timestampValue, right.timestampValue);
case TypeOrder.BLOB:
return compareBlobs(left.bytesValue, right.bytesValue);
case TypeOrder.REF:
return compareReferenceProtos(left, right);
case TypeOrder.GEO_POINT:
return compareGeoPoints(left.geoPointValue, right.geoPointValue);
case TypeOrder.ARRAY:
return compareArrays(left.arrayValue.values || [], right.arrayValue.values || []);
case TypeOrder.OBJECT:
return compareObjects(left.mapValue.fields || {}, right.mapValue.fields || {});
default:
throw new Error(`Encountered unknown type order: ${leftType}`);
}
}
exports.compare = compare;

View File

@@ -0,0 +1,510 @@
/*!
* 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = __importDefault(require("is"));
const validate_1 = require("./validate");
const validate = validate_1.createValidator();
/*!
* A regular expression to verify an absolute Resource Path in Firestore. It
* extracts the project ID, the database name and the relative resource path
* if available.
*
* @type {RegExp}
*/
const RESOURCE_PATH_RE =
// Note: [\s\S] matches all characters including newlines.
/^projects\/([^/]*)\/databases\/([^/]*)(?:\/documents\/)?([\s\S]*)$/;
/*!
* A regular expression to verify whether a field name can be passed to the
* backend without escaping.
*
* @type {RegExp}
*/
const UNESCAPED_FIELD_NAME_RE = /^[_a-zA-Z][_a-zA-Z0-9]*$/;
/*!
* A regular expression to verify field paths that are passed to the API as
* strings. Field paths that do not match this expression have to be provided
* as a [FieldPath]{@link FieldPath} object.
*
* @type {RegExp}
*/
const FIELD_PATH_RE = /^[^*~/[\]]+$/;
/**
* An abstract class representing a Firestore path.
*
* Subclasses have to implement `split()` and `canonicalString()`.
*
* @private
* @class
*/
class Path {
/**
* Creates a new Path with the given segments.
*
* @private
* @hideconstructor
* @param {string[]} segments - Sequence of parts of a path.
*/
constructor(segments) {
this.segments = segments;
}
/**
* String representation as expected by the proto API.
*
* @private
* @type {string}
*/
get formattedName() {
return this.canonicalString();
}
/**
* Create a child path beneath the current level.
*
* @private
* @param {string|T} relativePath - Relative path to append to the current
* path.
* @returns {T} The new path.
* @template T
*/
append(relativePath) {
if (relativePath instanceof Path) {
return this.construct(this.segments.concat(relativePath.segments));
}
return this.construct(this.segments.concat(this.split(relativePath)));
}
/**
* Returns the path of the parent node.
*
* @private
* @returns {T|null} The new path or null if we are already at the root.
* @returns {T} The new path.
* @template T
*/
parent() {
if (this.segments.length === 0) {
return null;
}
return this.construct(this.segments.slice(0, this.segments.length - 1));
}
/**
* Checks whether the current path is a prefix of the specified path.
*
* @private
* @param {Path} other - The path to check against.
* @returns {boolean} 'true' iff the current path is a prefix match with
* 'other'.
*/
isPrefixOf(other) {
if (other.segments.length < this.segments.length) {
return false;
}
for (let i = 0; i < this.segments.length; i++) {
if (this.segments[i] !== other.segments[i]) {
return false;
}
}
return true;
}
/**
* Returns a string representation of this path.
*
* @private
* @returns {string} A string representing this path.
*/
toString() {
return this.formattedName;
}
/**
* Compare the current path against another Path object.
*
* @private
* @param {Path} other - The path to compare to.
* @returns {number} -1 if current < other, 1 if current > other, 0 if equal
*/
compareTo(other) {
const len = Math.min(this.segments.length, other.segments.length);
for (let i = 0; i < len; i++) {
if (this.segments[i] < other.segments[i]) {
return -1;
}
if (this.segments[i] > other.segments[i]) {
return 1;
}
}
if (this.segments.length < other.segments.length) {
return -1;
}
if (this.segments.length > other.segments.length) {
return 1;
}
return 0;
}
/**
* Returns a copy of the underlying segments.
*
* @private
* @returns {Array.<string>} A copy of the segments that make up this path.
*/
toArray() {
return this.segments.slice();
}
/**
* Returns true if this `Path` is equal to the provided value.
*
* @private
* @param {*} other The value to compare against.
* @return {boolean} true if this `Path` is equal to the provided value.
*/
isEqual(other) {
return (this === other ||
(is_1.default.instanceof(other, this.constructor) &&
this.compareTo(other) === 0));
}
}
/**
* A slash-separated path for navigating resources (documents and collections)
* within Firestore.
*
* @private
* @class
*/
class ResourcePath extends Path {
/**
* Constructs a Firestore Resource Path.
*
* @private
* @hideconstructor
*
* @param {string} projectId - The Firestore project id.
* @param {string} databaseId - The Firestore database id.
* @param {string[]?} segments - Sequence of names of the parts of
* the path.
*/
constructor(projectId, databaseId, segments) {
const elements = segments instanceof Array ?
segments :
Array.prototype.slice.call(arguments, 2);
super(elements);
this.projectId = projectId;
this.databaseId = databaseId;
}
/**
* String representation of the path relative to the database root.
*
* @private
* @type {string}
*/
get relativeName() {
return this.segments.join('/');
}
/**
* Indicates whether this ResourcePath points to a document.
*
* @private
* @type {boolean}
*/
get isDocument() {
return this.segments.length > 0 && this.segments.length % 2 === 0;
}
/**
* Indicates whether this ResourcePath points to a collection.
*
* @private
* @type {boolean}
*/
get isCollection() {
return this.segments.length % 2 === 1;
}
/**
* The last component of the path.
*
* @private
* @type {string|null}
*/
get id() {
if (this.segments.length > 0) {
return this.segments[this.segments.length - 1];
}
return null;
}
/**
* Returns true if the given string can be used as a relative or absolute
* resource path.
*
* @private
* @param {string} resourcePath - The path to validate.
* @throws if the string can't be used as a resource path.
* @returns {boolean} 'true' when the path is valid.
*/
static validateResourcePath(resourcePath) {
if (!is_1.default.string(resourcePath) || resourcePath === '') {
throw new Error(`Path must be a non-empty string.`);
}
if (resourcePath.indexOf('//') >= 0) {
throw new Error('Paths must not contain //.');
}
return true;
}
/**
* Creates a resource path from an absolute Firestore path.
*
* @private
* @param {string} absolutePath - A string representation of a Resource Path.
* @returns {ResourcePath} The new ResourcePath.
*/
static fromSlashSeparatedString(absolutePath) {
const elements = RESOURCE_PATH_RE.exec(absolutePath);
if (elements) {
const project = elements[1];
const database = elements[2];
const path = elements[3];
return new ResourcePath(project, database).append(path);
}
throw new Error(`Resource name '${absolutePath}' is not valid.`);
}
/**
* Splits a string into path segments, using slashes as separators.
*
* @private
* @override
* @param {string} relativePath - The path to split.
* @returns {Array.<string>} - The split path segments.
*/
split(relativePath) {
// We may have an empty segment at the beginning or end if they had a
// leading or trailing slash (which we allow).
return relativePath.split('/').filter(segment => segment.length > 0);
}
/**
* String representation of a ResourcePath as expected by the API.
*
* @private
* @override
* @returns {string} The representation as expected by the API.
*/
canonicalString() {
let components = [
'projects',
this.projectId,
'databases',
this.databaseId,
];
if (this.segments.length > 0) {
components = components.concat('documents', this.segments);
}
return components.join('/');
}
/**
* Constructs a new instance of ResourcePath. We need this instead of using
* the normal constructor because polymorphic 'this' doesn't work on static
* methods.
*
* @private
* @override
* @param {Array.<string>} segments - Sequence of names of the parts of the
* path.
* @returns {ResourcePath} The newly created ResourcePath.
*/
construct(segments) {
return new ResourcePath(this.projectId, this.databaseId, segments);
}
/**
* Compare the current path against another ResourcePath object.
*
* @private
* @override
* @param {ResourcePath} other - The path to compare to.
* @returns {number} -1 if current < other, 1 if current > other, 0 if equal
*/
compareTo(other) {
// Ignore DocumentReference with {{projectId}} placeholders and assume that
// the resolved IDs match the provided ResourcePath. We could alternatively
// try to resolve the Project ID here, but this is asynchronous as it
// requires Disk I/O.
if (this.projectId !== '{{projectId}}' &&
other.projectId !== '{{projectId}}') {
if (this.projectId < other.projectId) {
return -1;
}
if (this.projectId > other.projectId) {
return 1;
}
}
if (this.databaseId < other.databaseId) {
return -1;
}
if (this.databaseId > other.databaseId) {
return 1;
}
return super.compareTo(other);
}
/**
* Converts this ResourcePath to the Firestore Proto representation.
*
* @private
*/
toProto() {
return {
referenceValue: this.formattedName,
};
}
}
exports.ResourcePath = ResourcePath;
/**
* A dot-separated path for navigating sub-objects within a document.
*
* @class
*/
class FieldPath extends Path {
/**
* Constructs a Firestore Field Path.
*
* @param {...string|string[]} segments - Sequence of field names that form
* this path.
*
* @example
* let query = firestore.collection('col');
* let fieldPath = new FieldPath('f.o.o', 'bar');
*
* query.where(fieldPath, '==', 42).get().then(snapshot => {
* snapshot.forEach(document => {
* console.log(`Document contains {'f.o.o' : {'bar' : 42}}`);
* });
* });
*/
constructor(segments) {
validate.minNumberOfArguments('FieldPath', arguments, 1);
const elements = segments instanceof Array ?
segments :
Array.prototype.slice.call(arguments);
for (let i = 0; i < elements.length; ++i) {
validate.isString(i, elements[i]);
if (elements[i].length === 0) {
throw new Error(`Argument at index ${i} should not be empty.`);
}
}
super(elements);
}
/**
* A special FieldPath value to refer to the ID of a document. It can be used
* in queries to sort or filter by the document ID.
*
* @returns {FieldPath}
*/
static documentId() {
return FieldPath._DOCUMENT_ID;
}
/**
* Returns true if the provided value can be used as a field path argument.
*
* @private
* @param {string|FieldPath} fieldPath - The value to verify.
* @throws if the string can't be used as a field path.
* @returns {boolean} 'true' when the path is valid.
*/
static validateFieldPath(fieldPath) {
if (!(fieldPath instanceof FieldPath)) {
if (!is_1.default.string(fieldPath)) {
throw validate_1.customObjectError(fieldPath);
}
if (fieldPath.indexOf('..') >= 0) {
throw new Error(`Paths must not contain '..' in them.`);
}
if (fieldPath.startsWith('.') || fieldPath.endsWith('.')) {
throw new Error(`Paths must not start or end with '.'.`);
}
if (!FIELD_PATH_RE.test(fieldPath)) {
throw new Error(`Paths can't be empty and must not contain '*~/[]'.`);
}
}
return true;
}
/**
* Turns a field path argument into a [FieldPath]{@link FieldPath}.
* Supports FieldPaths as input (which are passed through) and dot-separated
* strings.
*
* @private
* @param {string|FieldPath} fieldPath - The FieldPath to create.
* @returns {FieldPath} A field path representation.
*/
static fromArgument(fieldPath) {
// validateFieldPath() is used in all public API entry points to validate
// that fromArgument() is only called with a Field Path or a string.
return fieldPath instanceof FieldPath ? fieldPath :
new FieldPath(fieldPath.split('.'));
}
/**
* String representation of a FieldPath as expected by the API.
*
* @private
* @override
* @returns {string} The representation as expected by the API.
*/
canonicalString() {
return this.segments
.map(str => {
return UNESCAPED_FIELD_NAME_RE.test(str) ?
str :
'`' + str.replace('\\', '\\\\').replace('`', '\\`') + '`';
})
.join('.');
}
/**
* Splits a string into path segments, using dots as separators.
*
* @private
* @override
* @param {string} fieldPath - The path to split.
* @returns {Array.<string>} - The split path segments.
*/
split(fieldPath) {
return fieldPath.split('.');
}
/**
* Constructs a new instance of FieldPath. We need this instead of using
* the normal constructor because polymorphic 'this' doesn't work on static
* methods.
*
* @private
* @override
* @param {Array.<string>} segments - Sequence of field names.
* @returns {ResourcePath} The newly created FieldPath.
*/
construct(segments) {
return new FieldPath(segments);
}
/**
* Returns true if this `FieldPath` is equal to the provided value.
*
* @param {*} other The value to compare against.
* @return {boolean} true if this `FieldPath` is equal to the provided value.
*/
isEqual(other) {
return super.isEqual(other);
}
}
/**
* A special sentinel value to refer to the ID of a document.
*
* @private
*/
FieldPath._DOCUMENT_ID = new FieldPath('__name__');
exports.FieldPath = FieldPath;

View File

@@ -0,0 +1,124 @@
/*!
* Copyright 2018 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
/**
* An auto-resizing pool that distributes concurrent operations over multiple
* clients of type `T`.
*
* ClientPool is used within Firestore to manage a pool of GAPIC clients and
* automatically initializes multiple clients if we issue more than 100
* concurrent operations.
*
* @private
*/
class ClientPool {
/**
* @param concurrentOperationLimit - The number of operations that each client
* can handle.
* @param clientFactory - A factory function called as needed when new clients
* are required.
*/
constructor(concurrentOperationLimit, clientFactory) {
this.concurrentOperationLimit = concurrentOperationLimit;
this.clientFactory = clientFactory;
/** Stores each active clients and how many operations it has outstanding. */
this.activeClients = new Map();
}
/**
* Returns an already existing client if it has less than the maximum number
* of concurrent operations or initializes and returns a new client.
*/
acquire() {
let selectedClient = null;
let selectedRequestCount = 0;
this.activeClients.forEach((requestCount, client) => {
if (!selectedClient && requestCount < this.concurrentOperationLimit) {
selectedClient = client;
selectedRequestCount = requestCount;
}
});
if (!selectedClient) {
selectedClient = this.clientFactory();
assert_1.default(!this.activeClients.has(selectedClient), 'The provided client factory returned an existing instance');
}
this.activeClients.set(selectedClient, selectedRequestCount + 1);
return selectedClient;
}
/**
* Reduces the number of operations for the provided client, potentially
* removing it from the pool of active clients.
*/
release(client) {
let requestCount = this.activeClients.get(client) || 0;
assert_1.default(requestCount > 0, 'No active request');
requestCount = requestCount - 1;
this.activeClients.set(client, requestCount);
if (requestCount === 0) {
this.garbageCollect();
}
}
/**
* The number of currently registered clients.
*
* @return Number of currently registered clients.
*/
// Visible for testing.
get size() {
return this.activeClients.size;
}
/**
* Runs the provided operation in this pool. This function may create an
* additional client if all existing clients already operate at the concurrent
* operation limit.
*
* @param op - A callback function that returns a Promise. The client T will
* be returned to the pool when callback finishes.
* @return A Promise that resolves with the result of `op`.
*/
run(op) {
const client = this.acquire();
return op(client)
.catch(err => {
this.release(client);
return Promise.reject(err);
})
.then(res => {
this.release(client);
return res;
});
}
/**
* Deletes clients that are no longer executing operations. Keeps up to one
* idle client to reduce future initialization costs.
*/
garbageCollect() {
let idleClients = 0;
this.activeClients.forEach((requestCount, client) => {
if (requestCount === 0) {
++idleClients;
if (idleClients > 1) {
this.activeClients.delete(client);
}
}
});
}
}
exports.ClientPool = ClientPool;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,228 @@
/*!
* Copyright 2018 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = __importDefault(require("is"));
const firestore_proto_api_1 = require("../protos/firestore_proto_api");
const timestamp_1 = require("./timestamp");
const field_value_1 = require("./field-value");
const validate_1 = require("./validate");
const path_1 = require("./path");
const convert_1 = require("./convert");
const geo_point_1 = require("./geo-point");
/**
* Serializer that is used to convert between JavaScripts types and their
* Firestore Protobuf representation.
*
* @private
*/
class Serializer {
constructor(firestore, timestampsInSnapshotsEnabled) {
this.timestampsInSnapshotsEnabled = timestampsInSnapshotsEnabled;
// Instead of storing the `firestore` object, we store just a reference to
// its `.doc()` method. This avoid a circular reference, which breaks
// JSON.stringify().
this.createReference = path => firestore.doc(path);
}
/**
* Encodes a JavaScrip object into the Firestore 'Fields' representation.
*
* @private
* @param obj The object to encode.
* @returns The Firestore 'Fields' representation
*/
encodeFields(obj) {
const fields = {};
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
const val = this.encodeValue(obj[prop]);
if (val) {
fields[prop] = val;
}
}
}
return fields;
}
/**
* Encodes a JavaScript value into the Firestore 'Value' representation.
*
* @private
* @param val The object to encode
* @returns The Firestore Proto or null if we are deleting a field.
*/
encodeValue(val) {
if (val instanceof field_value_1.FieldTransform) {
return null;
}
if (is_1.default.string(val)) {
return {
stringValue: val,
};
}
if (is_1.default.bool(val)) {
return {
booleanValue: val,
};
}
if (is_1.default.integer(val)) {
return {
integerValue: val,
};
}
// Integers are handled above, the remaining numbers are treated as doubles
if (is_1.default.number(val)) {
return {
doubleValue: val,
};
}
if (is_1.default.date(val)) {
const timestamp = timestamp_1.Timestamp.fromDate(val);
return {
timestampValue: {
seconds: timestamp.seconds,
nanos: timestamp.nanoseconds,
},
};
}
if (val === null) {
return {
nullValue: firestore_proto_api_1.google.protobuf.NullValue.NULL_VALUE,
};
}
if (val instanceof Buffer || val instanceof Uint8Array) {
return {
bytesValue: val,
};
}
if (typeof val === 'object' && 'toProto' in val &&
typeof val.toProto === 'function') {
return val.toProto();
}
if (val instanceof Array) {
const array = {
arrayValue: {},
};
if (val.length > 0) {
array.arrayValue.values = [];
for (let i = 0; i < val.length; ++i) {
const enc = this.encodeValue(val[i]);
if (enc) {
array.arrayValue.values.push(enc);
}
}
}
return array;
}
if (typeof val === 'object' && isPlainObject(val)) {
const map = {
mapValue: {},
};
// If we encounter an empty object, we always need to send it to make sure
// the server creates a map entry.
if (!is_1.default.empty(val)) {
map.mapValue.fields = this.encodeFields(val);
if (is_1.default.empty(map.mapValue.fields)) {
return null;
}
}
return map;
}
throw validate_1.customObjectError(val);
}
/**
* Decodes a single Firestore 'Value' Protobuf.
*
* @private
* @param proto - A Firestore 'Value' Protobuf.
* @returns The converted JS type.
*/
decodeValue(proto) {
const valueType = convert_1.detectValueType(proto);
switch (valueType) {
case 'stringValue': {
return proto.stringValue;
}
case 'booleanValue': {
return proto.booleanValue;
}
case 'integerValue': {
return Number(proto.integerValue);
}
case 'doubleValue': {
return Number(proto.doubleValue);
}
case 'timestampValue': {
const timestamp = timestamp_1.Timestamp.fromProto(proto.timestampValue);
return this.timestampsInSnapshotsEnabled ? timestamp :
timestamp.toDate();
}
case 'referenceValue': {
const resourcePath = path_1.ResourcePath.fromSlashSeparatedString(proto.referenceValue);
return this.createReference(resourcePath.relativeName);
}
case 'arrayValue': {
const array = [];
if (is_1.default.array(proto.arrayValue.values)) {
for (const value of proto.arrayValue.values) {
array.push(this.decodeValue(value));
}
}
return array;
}
case 'nullValue': {
return null;
}
case 'mapValue': {
const obj = {};
const fields = proto.mapValue.fields;
for (const prop in fields) {
if (fields.hasOwnProperty(prop)) {
obj[prop] = this.decodeValue(fields[prop]);
}
}
return obj;
}
case 'geoPointValue': {
return geo_point_1.GeoPoint.fromProto(proto.geoPointValue);
}
case 'bytesValue': {
return proto.bytesValue;
}
default: {
throw new Error('Cannot decode type from Firestore Value: ' +
JSON.stringify(proto));
}
}
}
}
exports.Serializer = Serializer;
/**
* Verifies that 'obj' is a plain JavaScript object that can be encoded as a
* 'Map' in Firestore.
*
* @private
* @param input - The argument to verify.
* @returns 'true' if the input can be a treated as a plain object.
*/
function isPlainObject(input) {
return (typeof input === 'object' && input !== null &&
(Object.getPrototypeOf(input) === Object.prototype ||
Object.getPrototypeOf(input) === null));
}
exports.isPlainObject = isPlainObject;

View File

@@ -0,0 +1,225 @@
/*!
* Copyright 2018 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = __importDefault(require("is"));
const validate_1 = require("./validate");
const validate = validate_1.createValidator();
/*!
* Number of nanoseconds in a millisecond.
*
* @type {number}
*/
const MS_TO_NANOS = 1000000;
/**
* A Timestamp represents a point in time independent of any time zone or
* calendar, represented as seconds and fractions of seconds at nanosecond
* resolution in UTC Epoch time. It is encoded using the Proleptic Gregorian
* Calendar which extends the Gregorian calendar backwards to year one. It is
* encoded assuming all minutes are 60 seconds long, i.e. leap seconds are
* "smeared" so that no leap second table is needed for interpretation. Range is
* from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
*
* @see https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto
*/
class Timestamp {
/**
* Creates a new timestamp with the current date, with millisecond precision.
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.set({ updateTime:Firestore.Timestamp.now() });
*
* @return {Timestamp} A new `Timestamp` representing the current date.
*/
static now() {
return Timestamp.fromMillis(Date.now());
}
/**
* Creates a new timestamp from the given date.
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* let date = Date.parse('01 Jan 2000 00:00:00 GMT');
* documentRef.set({ startTime:Firestore.Timestamp.fromDate(date) });
*
* @param {Date} date The date to initialize the `Timestamp` from.
* @return {Timestamp} A new `Timestamp` representing the same point in time
* as the given date.
*/
static fromDate(date) {
return Timestamp.fromMillis(date.getTime());
}
/**
* Creates a new timestamp from the given number of milliseconds.
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.set({ startTime:Firestore.Timestamp.fromMillis(42) });
*
* @param {number} milliseconds Number of milliseconds since Unix epoch
* 1970-01-01T00:00:00Z.
* @return {Timestamp} A new `Timestamp` representing the same point in time
* as the given number of milliseconds.
*/
static fromMillis(milliseconds) {
const seconds = Math.floor(milliseconds / 1000);
const nanos = (milliseconds - seconds * 1000) * MS_TO_NANOS;
return new Timestamp(seconds, nanos);
}
/**
* Generates a `Timestamp` object from a Timestamp proto.
*
* @private
* @param {Object} timestamp The `Timestamp` Protobuf object.
*/
static fromProto(timestamp) {
return new Timestamp(Number(timestamp.seconds || 0), Number(timestamp.nanos || 0));
}
/**
* Creates a new timestamp.
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.set({ startTime:new Firestore.Timestamp(42, 0) });
*
* @param {number} seconds The number of seconds of UTC time since Unix epoch
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59Z inclusive.
* @param {number} nanoseconds The non-negative fractions of a second at
* nanosecond resolution. Negative second values with fractions must still
* have non-negative nanoseconds values that count forward in time. Must be
* from 0 to 999,999,999 inclusive.
*/
constructor(seconds, nanoseconds) {
validate.isInteger('seconds', seconds);
validate.isInteger('nanoseconds', nanoseconds, 0, 999999999);
this._seconds = seconds;
this._nanoseconds = nanoseconds;
}
/**
* The number of seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.get().then(snap => {
* let updatedAt = snap.updateTime;
* console.log(`Updated at ${updated.seconds}s ${updated.nanoseconds}ns`);
* });
*
* @type {number}
*/
get seconds() {
return this._seconds;
}
/**
* The non-negative fractions of a second at nanosecond resolution.
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.get().then(snap => {
* let updated = snap.updateTime;
* console.log(`Updated at ${updated.seconds}s ${updated.nanoseconds}ns`);
* });
*
* @type {number}
*/
get nanoseconds() {
return this._nanoseconds;
}
/**
* Returns a new `Date` corresponding to this timestamp. This may lose
* precision.
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.get().then(snap => {
* console.log(`Document updated at: ${snap.updateTime.toDate()}`);
* });
*
* @return {Date} JavaScript `Date` object representing the same point in time
* as this `Timestamp`, with millisecond precision.
*/
toDate() {
return new Date(this._seconds * 1000 + Math.round(this._nanoseconds / MS_TO_NANOS));
}
/**
* Returns the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.get().then(snap => {
* let startTime = snap.get('startTime');
* let endTime = snap.get('endTime');
* console.log(`Duration: ${endTime - startTime}`);
* });
*
* @return {number} The point in time corresponding to this timestamp,
* represented as the number of milliseconds since Unix epoch
* 1970-01-01T00:00:00Z.
*/
toMillis() {
return this._seconds * 1000 + Math.floor(this._nanoseconds / MS_TO_NANOS);
}
/**
* Returns 'true' if this `Timestamp` is equal to the provided one.
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.get().then(snap => {
* if (snap.createTime.isEqual(snap.updateTime)) {
* console.log('Document is in its initial state.');
* }
* });
*
* @param {any} other The `Timestamp` to compare against.
* @return {boolean} 'true' if this `Timestamp` is equal to the provided one.
*/
isEqual(other) {
return (this === other ||
(is_1.default.instanceof(other, Timestamp) && this._seconds === other.seconds &&
this._nanoseconds === other.nanoseconds));
}
/**
* Generates the Protobuf `Timestamp` object for this timestamp.
*
* @private
* @returns {Object} The `Timestamp` Protobuf object.
*/
toProto() {
const timestamp = {};
if (this.seconds) {
timestamp.seconds = this.seconds;
}
if (this.nanoseconds) {
timestamp.nanos = this.nanoseconds;
}
return { timestampValue: timestamp };
}
}
exports.Timestamp = Timestamp;

View File

@@ -0,0 +1,311 @@
/*!
* 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = __importDefault(require("is"));
const reference_1 = require("./reference");
const util_1 = require("./util");
/*!
* Error message for transactional reads that were executed after performing
* writes.
*/
const READ_AFTER_WRITE_ERROR_MSG = 'Firestore transactions require all reads to be executed before all writes.';
/*!
* Transactions can be retried if the initial stream opening errors out.
*/
const ALLOW_RETRIES = true;
/**
* A reference to a transaction.
*
* The Transaction object passed to a transaction's updateFunction provides
* the methods to read and write data within the transaction context. See
* [runTransaction()]{@link Firestore#runTransaction}.
*
* @class
*/
class Transaction {
/**
* @private
* @hideconstructor
*
* @param {Firestore} firestore - The Firestore Database client.
* @param {Transaction=} previousTransaction - If
* available, the failed transaction that is being retried.
*/
constructor(firestore, previousTransaction) {
this._firestore = firestore;
this._validator = firestore._validator;
this._previousTransaction = previousTransaction;
this._writeBatch = firestore.batch();
this._requestTag =
previousTransaction ? previousTransaction.requestTag : util_1.requestTag();
}
/**
* Retrieve a document or a query result from the database. Holds a
* pessimistic lock on all returned documents.
*
* @param {DocumentReference|Query} refOrQuery - The
* document or query to return.
* @returns {Promise} A Promise that resolves with a DocumentSnapshot or
* QuerySnapshot for the returned documents.
*
* @example
* firestore.runTransaction(transaction => {
* let documentRef = firestore.doc('col/doc');
* return transaction.get(documentRef).then(doc => {
* if (doc.exists) {
* transaction.update(documentRef, { count: doc.get('count') + 1 });
* } else {
* transaction.create(documentRef, { count: 1 });
* }
* });
* });
*/
get(refOrQuery) {
if (!this._writeBatch.isEmpty) {
throw new Error(READ_AFTER_WRITE_ERROR_MSG);
}
if (is_1.default.instance(refOrQuery, reference_1.DocumentReference)) {
return this._firestore
.getAll_([refOrQuery], this._requestTag, this._transactionId)
.then(res => {
return Promise.resolve(res[0]);
});
}
if (is_1.default.instance(refOrQuery, reference_1.Query)) {
return refOrQuery._get({ transactionId: this._transactionId });
}
throw new Error('Argument "refOrQuery" must be a DocumentRef or a Query.');
}
/**
* Retrieves multiple documents from Firestore. Holds a pessimistic lock on
* all returned documents.
*
* @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 firstDoc = firestore.doc('col/doc1');
* let secondDoc = firestore.doc('col/doc2');
* let resultDoc = firestore.doc('col/doc3');
*
* firestore.runTransaction(transaction => {
* return transaction.getAll(firstDoc, secondDoc).then(docs => {
* transaction.set(resultDoc, {
* sum: docs[1].get('count') + docs[2].get('count')
* });
* });
* });
*/
getAll(documents) {
if (!this._writeBatch.isEmpty) {
throw new Error(READ_AFTER_WRITE_ERROR_MSG);
}
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._firestore.getAll_(documents, this._requestTag, this._transactionId);
}
/**
* Create the document referred to by the provided
* [DocumentReference]{@link DocumentReference}. The operation will
* fail the transaction if a document exists at the specified location.
*
* @param {DocumentReference} documentRef - A reference to the
* document to be created.
* @param {DocumentData} data - The object data to serialize as the document.
* @returns {Transaction} This Transaction instance. Used for
* chaining method calls.
*
* @example
* firestore.runTransaction(transaction => {
* let documentRef = firestore.doc('col/doc');
* return transaction.get(documentRef).then(doc => {
* if (!doc.exists) {
* transaction.create(documentRef, { foo: 'bar' });
* }
* });
* });
*/
create(documentRef, data) {
this._writeBatch.create(documentRef, data);
return this;
}
/**
* Writes to the document referred to by the provided
* [DocumentReference]{@link DocumentReference}. If the document
* does not exist yet, it will be created. If you pass
* [SetOptions]{@link SetOptions}, the provided data can be merged into the
* existing document.
*
* @param {DocumentReference} documentRef - A reference to the
* document to be set.
* @param {DocumentData} data - The object to serialize as the document.
* @param {SetOptions=} options - An object to configure the set behavior.
* @param {boolean=} options.merge - If true, set() merges the values
* specified in its data argument. Fields omitted from this set() call
* remain untouched.
* @param {Array.<string|FieldPath>=} options.mergeFields - If provided,
* set() only replaces the specified field paths. Any field path that is not
* specified is ignored and remains untouched.
* @returns {Transaction} This Transaction instance. Used for
* chaining method calls.
*
* @example
* firestore.runTransaction(transaction => {
* let documentRef = firestore.doc('col/doc');
* transaction.set(documentRef, { foo: 'bar' });
* return Promise.resolve();
* });
*/
set(documentRef, data, options) {
this._writeBatch.set(documentRef, data, options);
return this;
}
/**
* Updates fields in the document referred to by the provided
* [DocumentReference]{@link DocumentReference}. The update will
* fail if applied to a document that does not exist.
*
* The update() method accepts either an object with field paths encoded as
* keys and field values encoded as values, or a variable number of arguments
* that alternate between field paths and field values. Nested fields can be
* updated by providing dot-separated field path strings or by providing
* FieldPath objects.
*
* A Precondition restricting this update can be specified as the last
* argument.
*
* @param {DocumentReference} documentRef - A reference to the
* document to be updated.
* @param {UpdateData|string|FieldPath} dataOrField - An object
* containing the fields and values with which to update the document
* or the path of the first field to update.
* @param {
* ...(Precondition|*|string|FieldPath)} preconditionOrValues -
* An alternating list of field paths and values to update or a Precondition
* to to enforce on this update.
* @returns {Transaction} This Transaction instance. Used for
* chaining method calls.
*
* @example
* firestore.runTransaction(transaction => {
* let documentRef = firestore.doc('col/doc');
* return transaction.get(documentRef).then(doc => {
* if (doc.exists) {
* transaction.update(documentRef, { count: doc.get('count') + 1 });
* } else {
* transaction.create(documentRef, { count: 1 });
* }
* });
* });
*/
update(documentRef, dataOrField, preconditionOrValues) {
this._validator.minNumberOfArguments('update', arguments, 2);
preconditionOrValues = Array.prototype.slice.call(arguments, 2);
this._writeBatch.update.apply(this._writeBatch, [documentRef, dataOrField].concat(preconditionOrValues));
return this;
}
/**
* Deletes the document referred to by the provided [DocumentReference]
* {@link DocumentReference}.
*
* @param {DocumentReference} documentRef - A reference to the
* document to be deleted.
* @param {Precondition=} precondition - A precondition to enforce for this
* delete.
* @param {Timestamp=} precondition.lastUpdateTime If set, enforces that the
* document was last updated at lastUpdateTime. Fails the transaction if the
* document doesn't exist or was last updated at a different time.
* @returns {Transaction} This Transaction instance. Used for
* chaining method calls.
*
* @example
* firestore.runTransaction(transaction => {
* let documentRef = firestore.doc('col/doc');
* transaction.delete(documentRef);
* return Promise.resolve();
* });
*/
delete(documentRef, precondition) {
this._writeBatch.delete(documentRef, precondition);
return this;
}
/**
* Starts a transaction and obtains the transaction id from the server.
*
* @private
* @returns {Promise} An empty Promise.
*/
begin() {
let request = {
database: this._firestore.formattedName,
};
if (this._previousTransaction) {
request.options = {
readWrite: {
retryTransaction: this._previousTransaction._transactionId,
},
};
}
return this._firestore
.request('beginTransaction', request, this._requestTag, ALLOW_RETRIES)
.then(resp => {
this._transactionId = resp.transaction;
});
}
/**
* Commits all queued-up changes in this transaction and releases all locks.
*
* @private
* @returns {Promise} An empty Promise.
*/
commit() {
return this._writeBatch.commit_({
transactionId: this._transactionId,
requestTag: this._requestTag,
});
}
/**
* Releases all locks and rolls back this transaction.
*
* @private
* @returns {Promise} An empty Promise.
*/
rollback() {
let request = {
database: this._firestore.formattedName,
transaction: this._transactionId,
};
return this._firestore.request('rollback', request, this._requestTag);
}
/**
* Returns the tag to use with with all request for this Transaction.
* @private
* @return {string} A unique client-generated identifier for this transaction.
*/
get requestTag() {
return this._requestTag;
}
}
exports.Transaction = Transaction;

View File

@@ -0,0 +1,17 @@
/*!
* Copyright 2018 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';
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,46 @@
/*!
* Copyright 2018 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';
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Generate a unique client-side identifier.
*
* Used for the creation of new documents.
*
* @private
* @returns {string} A unique 20-character wide identifier.
*/
function autoId() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let autoId = '';
for (let i = 0; i < 20; i++) {
autoId += chars.charAt(Math.floor(Math.random() * chars.length));
}
return autoId;
}
exports.autoId = autoId;
/**
* Generate a short and semi-random client-side identifier.
*
* Used for the creation of request tags.
*
* @private
* @returns {string} A random 5-character wide identifier.
*/
function requestTag() {
return autoId().substr(0, 5);
}
exports.requestTag = requestTag;

View File

@@ -0,0 +1,107 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* A set of field paths on a document.
* Used to restrict a get or update operation on a document to a subset of its
* fields.
* This is different from standard field masks, as this is always scoped to a
* Document, and takes in account the dynamic nature of Value.
*
* @property {string[]} fieldPaths
* The list of field paths in the mask. See Document.fields for a field
* path syntax reference.
*
* @typedef DocumentMask
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.DocumentMask definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/common.proto}
*/
var DocumentMask = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A precondition on a document, used for conditional operations.
*
* @property {boolean} exists
* When set to `true`, the target document must exist.
* When set to `false`, the target document must not exist.
*
* @property {Object} updateTime
* When set, the target document must exist and have been last updated at
* that time.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef Precondition
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.Precondition definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/common.proto}
*/
var Precondition = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* Options for creating a new transaction.
*
* @property {Object} readOnly
* The transaction can only be used for read operations.
*
* This object should have the same structure as [ReadOnly]{@link
* google.firestore.v1beta1.ReadOnly}
*
* @property {Object} readWrite
* The transaction can be used for both read and write operations.
*
* This object should have the same structure as [ReadWrite]{@link
* google.firestore.v1beta1.ReadWrite}
*
* @typedef TransactionOptions
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.TransactionOptions definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/common.proto}
*/
var TransactionOptions = {
// This is for documentation. Actual contents will be loaded by gRPC.
/**
* Options for a transaction that can be used to read and write documents.
*
* @property {string} retryTransaction
* An optional transaction to retry.
*
* @typedef ReadWrite
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.TransactionOptions.ReadWrite definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/common.proto}
*/
ReadWrite: {
// This is for documentation. Actual contents will be loaded by gRPC.
},
/**
* Options for a transaction that can only be used to read documents.
*
* @property {Object} readTime
* Reads documents at the given time.
* This may not be older than 60 seconds.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef ReadOnly
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.TransactionOptions.ReadOnly definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/common.proto}
*/
ReadOnly: {
// This is for documentation. Actual contents will be loaded by gRPC.
}
};

View File

@@ -0,0 +1,183 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* A Firestore document.
*
* Must not exceed 1 MiB - 4 bytes.
*
* @property {string} name
* The resource name of the document, for example
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
*
* @property {Object.<string, Object>} fields
* The document's fields.
*
* The map keys represent field names.
*
* A simple field name contains only characters `a` to `z`, `A` to `Z`,
* `0` to `9`, or `_`, and must not start with `0` to `9` or `_`. For example,
* `foo_bar_17`.
*
* Field names matching the regular expression `__.*__` are reserved. Reserved
* field names are forbidden except in certain documented contexts. The map
* keys, represented as UTF-8, must not exceed 1,500 bytes and cannot be
* empty.
*
* Field paths may be used in other contexts to refer to structured fields
* defined here. For `map_value`, the field path is represented by the simple
* or quoted field names of the containing fields, delimited by `.`. For
* example, the structured field
* `"foo" : { map_value: { "x&y" : { string_value: "hello" }}}` would be
* represented by the field path `foo.x&y`.
*
* Within a field path, a quoted field name starts and ends with `` ` `` and
* may contain any character. Some characters, including `` ` ``, must be
* escaped using a `\`. For example, `` `x&y` `` represents `x&y` and
* `` `bak\`tik` `` represents `` bak`tik ``.
*
* @property {Object} createTime
* Output only. The time at which the document was created.
*
* This value increases monotonically when a document is deleted then
* recreated. It can also be compared to values from other documents and
* the `read_time` of a query.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @property {Object} updateTime
* Output only. The time at which the document was last changed.
*
* This value is initally set to the `create_time` then increases
* monotonically with each change to the document. It can also be
* compared to values from other documents and the `read_time` of a query.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef Document
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.Document definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/document.proto}
*/
var Document = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A message that can hold any of the supported value types.
*
* @property {number} nullValue
* A null value.
*
* The number should be among the values of [NullValue]{@link
* google.protobuf.NullValue}
*
* @property {boolean} booleanValue
* A boolean value.
*
* @property {number} integerValue
* An integer value.
*
* @property {number} doubleValue
* A double value.
*
* @property {Object} timestampValue
* A timestamp value.
*
* Precise only to microseconds. When stored, any additional precision is
* rounded down.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @property {string} stringValue
* A string value.
*
* The string, represented as UTF-8, must not exceed 1 MiB - 89 bytes.
* Only the first 1,500 bytes of the UTF-8 representation are considered by
* queries.
*
* @property {string} bytesValue
* A bytes value.
*
* Must not exceed 1 MiB - 89 bytes.
* Only the first 1,500 bytes are considered by queries.
*
* @property {string} referenceValue
* A reference to a document. For example:
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
*
* @property {Object} geoPointValue
* A geo point value representing a point on the surface of Earth.
*
* This object should have the same structure as [LatLng]{@link
* google.type.LatLng}
*
* @property {Object} arrayValue
* An array value.
*
* Cannot contain another array value.
*
* This object should have the same structure as [ArrayValue]{@link
* google.firestore.v1beta1.ArrayValue}
*
* @property {Object} mapValue
* A map value.
*
* This object should have the same structure as [MapValue]{@link
* google.firestore.v1beta1.MapValue}
*
* @typedef Value
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.Value definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/document.proto}
*/
var Value = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* An array value.
*
* @property {Object[]} values
* Values in the array.
*
* This object should have the same structure as [Value]{@link
* google.firestore.v1beta1.Value}
*
* @typedef ArrayValue
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.ArrayValue definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/document.proto}
*/
var ArrayValue = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A map value.
*
* @property {Object.<string, Object>} fields
* The map's fields.
*
* The map keys represent field names. Field names matching the regular
* expression `__.*__` are reserved. Reserved field names are forbidden except
* in certain documented contexts. The map keys, represented as UTF-8, must
* not exceed 1,500 bytes and cannot be empty.
*
* @typedef MapValue
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.MapValue definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/document.proto}
*/
var MapValue = {
// This is for documentation. Actual contents will be loaded by gRPC.
};

View File

@@ -0,0 +1,881 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* The request for Firestore.GetDocument.
*
* @property {string} name
* The resource name of the Document to get. In the format:
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
*
* @property {Object} mask
* The fields to return. If not set, returns all fields.
*
* If the document has a field that is not present in this mask, that field
* will not be returned in the response.
*
* This object should have the same structure as [DocumentMask]{@link
* google.firestore.v1beta1.DocumentMask}
*
* @property {string} transaction
* Reads the document in a transaction.
*
* @property {Object} readTime
* Reads the version of the document at the given time.
* This may not be older than 60 seconds.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef GetDocumentRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.GetDocumentRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var GetDocumentRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.ListDocuments.
*
* @property {string} parent
* The parent resource name. In the format:
* `projects/{project_id}/databases/{database_id}/documents` or
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
* For example:
* `projects/my-project/databases/my-database/documents` or
* `projects/my-project/databases/my-database/documents/chatrooms/my-chatroom`
*
* @property {string} collectionId
* The collection ID, relative to `parent`, to list. For example: `chatrooms`
* or `messages`.
*
* @property {number} pageSize
* The maximum number of documents to return.
*
* @property {string} pageToken
* The `next_page_token` value returned from a previous List request, if any.
*
* @property {string} orderBy
* The order to sort results by. For example: `priority desc, name`.
*
* @property {Object} mask
* The fields to return. If not set, returns all fields.
*
* If a document has a field that is not present in this mask, that field
* will not be returned in the response.
*
* This object should have the same structure as [DocumentMask]{@link
* google.firestore.v1beta1.DocumentMask}
*
* @property {string} transaction
* Reads documents in a transaction.
*
* @property {Object} readTime
* Reads documents as they were at the given time.
* This may not be older than 60 seconds.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @property {boolean} showMissing
* If the list should show missing documents. A missing document is a
* document that does not exist but has sub-documents. These documents will
* be returned with a key but will not have fields, Document.create_time,
* or Document.update_time set.
*
* Requests with `show_missing` may not specify `where` or
* `order_by`.
*
* @typedef ListDocumentsRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.ListDocumentsRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var ListDocumentsRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The response for Firestore.ListDocuments.
*
* @property {Object[]} documents
* The Documents found.
*
* This object should have the same structure as [Document]{@link
* google.firestore.v1beta1.Document}
*
* @property {string} nextPageToken
* The next page token.
*
* @typedef ListDocumentsResponse
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.ListDocumentsResponse definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var ListDocumentsResponse = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.CreateDocument.
*
* @property {string} parent
* The parent resource. For example:
* `projects/{project_id}/databases/{database_id}/documents` or
* `projects/{project_id}/databases/{database_id}/documents/chatrooms/{chatroom_id}`
*
* @property {string} collectionId
* The collection ID, relative to `parent`, to list. For example: `chatrooms`.
*
* @property {string} documentId
* The client-assigned document ID to use for this document.
*
* Optional. If not specified, an ID will be assigned by the service.
*
* @property {Object} document
* The document to create. `name` must not be set.
*
* This object should have the same structure as [Document]{@link
* google.firestore.v1beta1.Document}
*
* @property {Object} mask
* The fields to return. If not set, returns all fields.
*
* If the document has a field that is not present in this mask, that field
* will not be returned in the response.
*
* This object should have the same structure as [DocumentMask]{@link
* google.firestore.v1beta1.DocumentMask}
*
* @typedef CreateDocumentRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.CreateDocumentRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var CreateDocumentRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.UpdateDocument.
*
* @property {Object} document
* The updated document.
* Creates the document if it does not already exist.
*
* This object should have the same structure as [Document]{@link
* google.firestore.v1beta1.Document}
*
* @property {Object} updateMask
* The fields to update.
* None of the field paths in the mask may contain a reserved name.
*
* If the document exists on the server and has fields not referenced in the
* mask, they are left unchanged.
* Fields referenced in the mask, but not present in the input document, are
* deleted from the document on the server.
*
* This object should have the same structure as [DocumentMask]{@link
* google.firestore.v1beta1.DocumentMask}
*
* @property {Object} mask
* The fields to return. If not set, returns all fields.
*
* If the document has a field that is not present in this mask, that field
* will not be returned in the response.
*
* This object should have the same structure as [DocumentMask]{@link
* google.firestore.v1beta1.DocumentMask}
*
* @property {Object} currentDocument
* An optional precondition on the document.
* The request will fail if this is set and not met by the target document.
*
* This object should have the same structure as [Precondition]{@link
* google.firestore.v1beta1.Precondition}
*
* @typedef UpdateDocumentRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.UpdateDocumentRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var UpdateDocumentRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.DeleteDocument.
*
* @property {string} name
* The resource name of the Document to delete. In the format:
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
*
* @property {Object} currentDocument
* An optional precondition on the document.
* The request will fail if this is set and not met by the target document.
*
* This object should have the same structure as [Precondition]{@link
* google.firestore.v1beta1.Precondition}
*
* @typedef DeleteDocumentRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.DeleteDocumentRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var DeleteDocumentRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.BatchGetDocuments.
*
* @property {string} database
* The database name. In the format:
* `projects/{project_id}/databases/{database_id}`.
*
* @property {string[]} documents
* The names of the documents to retrieve. In the format:
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
* The request will fail if any of the document is not a child resource of the
* given `database`. Duplicate names will be elided.
*
* @property {Object} mask
* The fields to return. If not set, returns all fields.
*
* If a document has a field that is not present in this mask, that field will
* not be returned in the response.
*
* This object should have the same structure as [DocumentMask]{@link
* google.firestore.v1beta1.DocumentMask}
*
* @property {string} transaction
* Reads documents in a transaction.
*
* @property {Object} newTransaction
* Starts a new transaction and reads the documents.
* Defaults to a read-only transaction.
* The new transaction ID will be returned as the first response in the
* stream.
*
* This object should have the same structure as [TransactionOptions]{@link
* google.firestore.v1beta1.TransactionOptions}
*
* @property {Object} readTime
* Reads documents as they were at the given time.
* This may not be older than 60 seconds.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef BatchGetDocumentsRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.BatchGetDocumentsRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var BatchGetDocumentsRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The streamed response for Firestore.BatchGetDocuments.
*
* @property {Object} found
* A document that was requested.
*
* This object should have the same structure as [Document]{@link
* google.firestore.v1beta1.Document}
*
* @property {string} missing
* A document name that was requested but does not exist. In the format:
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
*
* @property {string} transaction
* The transaction that was started as part of this request.
* Will only be set in the first response, and only if
* BatchGetDocumentsRequest.new_transaction was set in the request.
*
* @property {Object} readTime
* The time at which the document was read.
* This may be monotically increasing, in this case the previous documents in
* the result stream are guaranteed not to have changed between their
* read_time and this one.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef BatchGetDocumentsResponse
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.BatchGetDocumentsResponse definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var BatchGetDocumentsResponse = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.BeginTransaction.
*
* @property {string} database
* The database name. In the format:
* `projects/{project_id}/databases/{database_id}`.
*
* @property {Object} options
* The options for the transaction.
* Defaults to a read-write transaction.
*
* This object should have the same structure as [TransactionOptions]{@link
* google.firestore.v1beta1.TransactionOptions}
*
* @typedef BeginTransactionRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.BeginTransactionRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var BeginTransactionRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The response for Firestore.BeginTransaction.
*
* @property {string} transaction
* The transaction that was started.
*
* @typedef BeginTransactionResponse
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.BeginTransactionResponse definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var BeginTransactionResponse = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.Commit.
*
* @property {string} database
* The database name. In the format:
* `projects/{project_id}/databases/{database_id}`.
*
* @property {Object[]} writes
* The writes to apply.
*
* Always executed atomically and in order.
*
* This object should have the same structure as [Write]{@link
* google.firestore.v1beta1.Write}
*
* @property {string} transaction
* If set, applies all writes in this transaction, and commits it.
*
* @typedef CommitRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.CommitRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var CommitRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The response for Firestore.Commit.
*
* @property {Object[]} writeResults
* The result of applying the writes.
*
* This i-th write result corresponds to the i-th write in the
* request.
*
* This object should have the same structure as [WriteResult]{@link
* google.firestore.v1beta1.WriteResult}
*
* @property {Object} commitTime
* The time at which the commit occurred.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef CommitResponse
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.CommitResponse definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var CommitResponse = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.Rollback.
*
* @property {string} database
* The database name. In the format:
* `projects/{project_id}/databases/{database_id}`.
*
* @property {string} transaction
* The transaction to roll back.
*
* @typedef RollbackRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.RollbackRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var RollbackRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.RunQuery.
*
* @property {string} parent
* The parent resource name. In the format:
* `projects/{project_id}/databases/{database_id}/documents` or
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
* For example:
* `projects/my-project/databases/my-database/documents` or
* `projects/my-project/databases/my-database/documents/chatrooms/my-chatroom`
*
* @property {Object} structuredQuery
* A structured query.
*
* This object should have the same structure as [StructuredQuery]{@link
* google.firestore.v1beta1.StructuredQuery}
*
* @property {string} transaction
* Reads documents in a transaction.
*
* @property {Object} newTransaction
* Starts a new transaction and reads the documents.
* Defaults to a read-only transaction.
* The new transaction ID will be returned as the first response in the
* stream.
*
* This object should have the same structure as [TransactionOptions]{@link
* google.firestore.v1beta1.TransactionOptions}
*
* @property {Object} readTime
* Reads documents as they were at the given time.
* This may not be older than 60 seconds.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef RunQueryRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.RunQueryRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var RunQueryRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The response for Firestore.RunQuery.
*
* @property {string} transaction
* The transaction that was started as part of this request.
* Can only be set in the first response, and only if
* RunQueryRequest.new_transaction was set in the request.
* If set, no other fields will be set in this response.
*
* @property {Object} document
* A query result.
* Not set when reporting partial progress.
*
* This object should have the same structure as [Document]{@link
* google.firestore.v1beta1.Document}
*
* @property {Object} readTime
* The time at which the document was read. This may be monotonically
* increasing; in this case, the previous documents in the result stream are
* guaranteed not to have changed between their `read_time` and this one.
*
* If the query returns no results, a response with `read_time` and no
* `document` will be sent, and this represents the time at which the query
* was run.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @property {number} skippedResults
* The number of results that have been skipped due to an offset between
* the last response and the current response.
*
* @typedef RunQueryResponse
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.RunQueryResponse definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var RunQueryResponse = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The request for Firestore.Write.
*
* The first request creates a stream, or resumes an existing one from a token.
*
* When creating a new stream, the server replies with a response containing
* only an ID and a token, to use in the next request.
*
* When resuming a stream, the server first streams any responses later than the
* given token, then a response containing only an up-to-date token, to use in
* the next request.
*
* @property {string} database
* The database name. In the format:
* `projects/{project_id}/databases/{database_id}`.
* This is only required in the first message.
*
* @property {string} streamId
* The ID of the write stream to resume.
* This may only be set in the first message. When left empty, a new write
* stream will be created.
*
* @property {Object[]} writes
* The writes to apply.
*
* Always executed atomically and in order.
* This must be empty on the first request.
* This may be empty on the last request.
* This must not be empty on all other requests.
*
* This object should have the same structure as [Write]{@link
* google.firestore.v1beta1.Write}
*
* @property {string} streamToken
* A stream token that was previously sent by the server.
*
* The client should set this field to the token from the most recent
* WriteResponse it has received. This acknowledges that the client has
* received responses up to this token. After sending this token, earlier
* tokens may not be used anymore.
*
* The server may close the stream if there are too many unacknowledged
* responses.
*
* Leave this field unset when creating a new stream. To resume a stream at
* a specific point, set this field and the `stream_id` field.
*
* Leave this field unset when creating a new stream.
*
* @property {Object.<string, string>} labels
* Labels associated with this write request.
*
* @typedef WriteRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.WriteRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var WriteRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The response for Firestore.Write.
*
* @property {string} streamId
* The ID of the stream.
* Only set on the first message, when a new stream was created.
*
* @property {string} streamToken
* A token that represents the position of this response in the stream.
* This can be used by a client to resume the stream at this point.
*
* This field is always set.
*
* @property {Object[]} writeResults
* The result of applying the writes.
*
* This i-th write result corresponds to the i-th write in the
* request.
*
* This object should have the same structure as [WriteResult]{@link
* google.firestore.v1beta1.WriteResult}
*
* @property {Object} commitTime
* The time at which the commit occurred.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef WriteResponse
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.WriteResponse definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var WriteResponse = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A request for Firestore.Listen
*
* @property {string} database
* The database name. In the format:
* `projects/{project_id}/databases/{database_id}`.
*
* @property {Object} addTarget
* A target to add to this stream.
*
* This object should have the same structure as [Target]{@link
* google.firestore.v1beta1.Target}
*
* @property {number} removeTarget
* The ID of a target to remove from this stream.
*
* @property {Object.<string, string>} labels
* Labels associated with this target change.
*
* @typedef ListenRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.ListenRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var ListenRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The response for Firestore.Listen.
*
* @property {Object} targetChange
* Targets have changed.
*
* This object should have the same structure as [TargetChange]{@link
* google.firestore.v1beta1.TargetChange}
*
* @property {Object} documentChange
* A Document has changed.
*
* This object should have the same structure as [DocumentChange]{@link
* google.firestore.v1beta1.DocumentChange}
*
* @property {Object} documentDelete
* A Document has been deleted.
*
* This object should have the same structure as [DocumentDelete]{@link
* google.firestore.v1beta1.DocumentDelete}
*
* @property {Object} documentRemove
* A Document has been removed from a target (because it is no longer
* relevant to that target).
*
* This object should have the same structure as [DocumentRemove]{@link
* google.firestore.v1beta1.DocumentRemove}
*
* @property {Object} filter
* A filter to apply to the set of documents previously returned for the
* given target.
*
* Returned when documents may have been removed from the given target, but
* the exact documents are unknown.
*
* This object should have the same structure as [ExistenceFilter]{@link
* google.firestore.v1beta1.ExistenceFilter}
*
* @typedef ListenResponse
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.ListenResponse definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var ListenResponse = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A specification of a set of documents to listen to.
*
* @property {Object} query
* A target specified by a query.
*
* This object should have the same structure as [QueryTarget]{@link
* google.firestore.v1beta1.QueryTarget}
*
* @property {Object} documents
* A target specified by a set of document names.
*
* This object should have the same structure as [DocumentsTarget]{@link
* google.firestore.v1beta1.DocumentsTarget}
*
* @property {string} resumeToken
* A resume token from a prior TargetChange for an identical target.
*
* Using a resume token with a different target is unsupported and may fail.
*
* @property {Object} readTime
* Start listening after a specific `read_time`.
*
* The client must know the state of matching documents at this time.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @property {number} targetId
* A client provided target ID.
*
* If not set, the server will assign an ID for the target.
*
* Used for resuming a target without changing IDs. The IDs can either be
* client-assigned or be server-assigned in a previous stream. All targets
* with client provided IDs must be added before adding a target that needs
* a server-assigned id.
*
* @property {boolean} once
* If the target should be removed once it is current and consistent.
*
* @typedef Target
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.Target definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var Target = {
// This is for documentation. Actual contents will be loaded by gRPC.
/**
* A target specified by a set of documents names.
*
* @property {string[]} documents
* The names of the documents to retrieve. In the format:
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
* The request will fail if any of the document is not a child resource of
* the given `database`. Duplicate names will be elided.
*
* @typedef DocumentsTarget
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.Target.DocumentsTarget definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
DocumentsTarget: {
// This is for documentation. Actual contents will be loaded by gRPC.
},
/**
* A target specified by a query.
*
* @property {string} parent
* The parent resource name. In the format:
* `projects/{project_id}/databases/{database_id}/documents` or
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
* For example:
* `projects/my-project/databases/my-database/documents` or
* `projects/my-project/databases/my-database/documents/chatrooms/my-chatroom`
*
* @property {Object} structuredQuery
* A structured query.
*
* This object should have the same structure as [StructuredQuery]{@link
* google.firestore.v1beta1.StructuredQuery}
*
* @typedef QueryTarget
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.Target.QueryTarget definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
QueryTarget: {
// This is for documentation. Actual contents will be loaded by gRPC.
}
};
/**
* Targets being watched have changed.
*
* @property {number} targetChangeType
* The type of change that occurred.
*
* The number should be among the values of [TargetChangeType]{@link
* google.firestore.v1beta1.TargetChangeType}
*
* @property {number[]} targetIds
* The target IDs of targets that have changed.
*
* If empty, the change applies to all targets.
*
* For `target_change_type=ADD`, the order of the target IDs matches the order
* of the requests to add the targets. This allows clients to unambiguously
* associate server-assigned target IDs with added targets.
*
* For other states, the order of the target IDs is not defined.
*
* @property {Object} cause
* The error that resulted in this change, if applicable.
*
* This object should have the same structure as [Status]{@link
* google.rpc.Status}
*
* @property {string} resumeToken
* A token that can be used to resume the stream for the given `target_ids`,
* or all targets if `target_ids` is empty.
*
* Not set on every target change.
*
* @property {Object} readTime
* The consistent `read_time` for the given `target_ids` (omitted when the
* target_ids are not at a consistent snapshot).
*
* The stream is guaranteed to send a `read_time` with `target_ids` empty
* whenever the entire stream reaches a new consistent snapshot. ADD,
* CURRENT, and RESET messages are guaranteed to (eventually) result in a
* new consistent snapshot (while NO_CHANGE and REMOVE messages are not).
*
* For a given stream, `read_time` is guaranteed to be monotonically
* increasing.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef TargetChange
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.TargetChange definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var TargetChange = {
// This is for documentation. Actual contents will be loaded by gRPC.
/**
* The type of change.
*
* @enum {number}
* @memberof google.firestore.v1beta1
*/
TargetChangeType: {
/**
* No change has occurred. Used only to send an updated `resume_token`.
*/
NO_CHANGE: 0,
/**
* The targets have been added.
*/
ADD: 1,
/**
* The targets have been removed.
*/
REMOVE: 2,
/**
* The targets reflect all changes committed before the targets were added
* to the stream.
*
* This will be sent after or with a `read_time` that is greater than or
* equal to the time at which the targets were added.
*
* Listeners can wait for this change if read-after-write semantics
* are desired.
*/
CURRENT: 3,
/**
* The targets have been reset, and a new initial state for the targets
* will be returned in subsequent changes.
*
* After the initial state is complete, `CURRENT` will be returned even
* if the target was previously indicated to be `CURRENT`.
*/
RESET: 4
}
};
/**
* The request for Firestore.ListCollectionIds.
*
* @property {string} parent
* The parent document. In the format:
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
* For example:
* `projects/my-project/databases/my-database/documents/chatrooms/my-chatroom`
*
* @property {number} pageSize
* The maximum number of results to return.
*
* @property {string} pageToken
* A page token. Must be a value from
* ListCollectionIdsResponse.
*
* @typedef ListCollectionIdsRequest
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.ListCollectionIdsRequest definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var ListCollectionIdsRequest = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* The response from Firestore.ListCollectionIds.
*
* @property {string[]} collectionIds
* The collection ids.
*
* @property {string} nextPageToken
* A page token that may be used to continue the list.
*
* @typedef ListCollectionIdsResponse
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.ListCollectionIdsResponse definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto}
*/
var ListCollectionIdsResponse = {
// This is for documentation. Actual contents will be loaded by gRPC.
};

View File

@@ -0,0 +1,379 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* A Firestore query.
*
* @property {Object} select
* The projection to return.
*
* This object should have the same structure as [Projection]{@link
* google.firestore.v1beta1.Projection}
*
* @property {Object[]} from
* The collections to query.
*
* This object should have the same structure as [CollectionSelector]{@link
* google.firestore.v1beta1.CollectionSelector}
*
* @property {Object} where
* The filter to apply.
*
* This object should have the same structure as [Filter]{@link
* google.firestore.v1beta1.Filter}
*
* @property {Object[]} orderBy
* The order to apply to the query results.
*
* Firestore guarantees a stable ordering through the following rules:
*
* * Any field required to appear in `order_by`, that is not already
* specified in `order_by`, is appended to the order in field name order
* by default.
* * If an order on `__name__` is not specified, it is appended by default.
*
* Fields are appended with the same sort direction as the last order
* specified, or 'ASCENDING' if no order was specified. For example:
*
* * `SELECT * FROM Foo ORDER BY A` becomes
* `SELECT * FROM Foo ORDER BY A, __name__`
* * `SELECT * FROM Foo ORDER BY A DESC` becomes
* `SELECT * FROM Foo ORDER BY A DESC, __name__ DESC`
* * `SELECT * FROM Foo WHERE A > 1` becomes
* `SELECT * FROM Foo WHERE A > 1 ORDER BY A, __name__`
*
* This object should have the same structure as [Order]{@link
* google.firestore.v1beta1.Order}
*
* @property {Object} startAt
* A starting point for the query results.
*
* This object should have the same structure as [Cursor]{@link
* google.firestore.v1beta1.Cursor}
*
* @property {Object} endAt
* A end point for the query results.
*
* This object should have the same structure as [Cursor]{@link
* google.firestore.v1beta1.Cursor}
*
* @property {number} offset
* The number of results to skip.
*
* Applies before limit, but after all other constraints. Must be >= 0 if
* specified.
*
* @property {Object} limit
* The maximum number of results to return.
*
* Applies after all other constraints.
* Must be >= 0 if specified.
*
* This object should have the same structure as [Int32Value]{@link
* google.protobuf.Int32Value}
*
* @typedef StructuredQuery
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.StructuredQuery definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
var StructuredQuery = {
// This is for documentation. Actual contents will be loaded by gRPC.
/**
* A selection of a collection, such as `messages as m1`.
*
* @property {string} collectionId
* The collection ID.
* When set, selects only collections with this ID.
*
* @property {boolean} allDescendants
* When false, selects only collections that are immediate children of
* the `parent` specified in the containing `RunQueryRequest`.
* When true, selects all descendant collections.
*
* @typedef CollectionSelector
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.StructuredQuery.CollectionSelector definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
CollectionSelector: {
// This is for documentation. Actual contents will be loaded by gRPC.
},
/**
* A filter.
*
* @property {Object} compositeFilter
* A composite filter.
*
* This object should have the same structure as [CompositeFilter]{@link
* google.firestore.v1beta1.CompositeFilter}
*
* @property {Object} fieldFilter
* A filter on a document field.
*
* This object should have the same structure as [FieldFilter]{@link
* google.firestore.v1beta1.FieldFilter}
*
* @property {Object} unaryFilter
* A filter that takes exactly one argument.
*
* This object should have the same structure as [UnaryFilter]{@link
* google.firestore.v1beta1.UnaryFilter}
*
* @typedef Filter
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.StructuredQuery.Filter definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
Filter: {
// This is for documentation. Actual contents will be loaded by gRPC.
},
/**
* A filter that merges multiple other filters using the given operator.
*
* @property {number} op
* The operator for combining multiple filters.
*
* The number should be among the values of [Operator]{@link
* google.firestore.v1beta1.Operator}
*
* @property {Object[]} filters
* The list of filters to combine.
* Must contain at least one filter.
*
* This object should have the same structure as [Filter]{@link
* google.firestore.v1beta1.Filter}
*
* @typedef CompositeFilter
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.StructuredQuery.CompositeFilter definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
CompositeFilter: {
// This is for documentation. Actual contents will be loaded by gRPC.
/**
* A composite filter operator.
*
* @enum {number}
* @memberof google.firestore.v1beta1
*/
Operator: {
/**
* Unspecified. This value must not be used.
*/
OPERATOR_UNSPECIFIED: 0,
/**
* The results are required to satisfy each of the combined filters.
*/
AND: 1
}
},
/**
* A filter on a specific field.
*
* @property {Object} field
* The field to filter by.
*
* This object should have the same structure as [FieldReference]{@link
* google.firestore.v1beta1.FieldReference}
*
* @property {number} op
* The operator to filter by.
*
* The number should be among the values of [Operator]{@link
* google.firestore.v1beta1.Operator}
*
* @property {Object} value
* The value to compare to.
*
* This object should have the same structure as [Value]{@link
* google.firestore.v1beta1.Value}
*
* @typedef FieldFilter
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.StructuredQuery.FieldFilter definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
FieldFilter: {
// This is for documentation. Actual contents will be loaded by gRPC.
/**
* A field filter operator.
*
* @enum {number}
* @memberof google.firestore.v1beta1
*/
Operator: {
/**
* Unspecified. This value must not be used.
*/
OPERATOR_UNSPECIFIED: 0,
/**
* Less than. Requires that the field come first in `order_by`.
*/
LESS_THAN: 1,
/**
* Less than or equal. Requires that the field come first in `order_by`.
*/
LESS_THAN_OR_EQUAL: 2,
/**
* Greater than. Requires that the field come first in `order_by`.
*/
GREATER_THAN: 3,
/**
* Greater than or equal. Requires that the field come first in
* `order_by`.
*/
GREATER_THAN_OR_EQUAL: 4,
/**
* Equal.
*/
EQUAL: 5
}
},
/**
* A filter with a single operand.
*
* @property {number} op
* The unary operator to apply.
*
* The number should be among the values of [Operator]{@link
* google.firestore.v1beta1.Operator}
*
* @property {Object} field
* The field to which to apply the operator.
*
* This object should have the same structure as [FieldReference]{@link
* google.firestore.v1beta1.FieldReference}
*
* @typedef UnaryFilter
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.StructuredQuery.UnaryFilter definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
UnaryFilter: {
// This is for documentation. Actual contents will be loaded by gRPC.
/**
* A unary operator.
*
* @enum {number}
* @memberof google.firestore.v1beta1
*/
Operator: {
/**
* Unspecified. This value must not be used.
*/
OPERATOR_UNSPECIFIED: 0,
/**
* Test if a field is equal to NaN.
*/
IS_NAN: 2,
/**
* Test if an exprestion evaluates to Null.
*/
IS_NULL: 3
}
},
/**
* An order on a field.
*
* @property {Object} field
* The field to order by.
*
* This object should have the same structure as [FieldReference]{@link
* google.firestore.v1beta1.FieldReference}
*
* @property {number} direction
* The direction to order by. Defaults to `ASCENDING`.
*
* The number should be among the values of [Direction]{@link
* google.firestore.v1beta1.Direction}
*
* @typedef Order
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.StructuredQuery.Order definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
Order: {
// This is for documentation. Actual contents will be loaded by gRPC.
},
/**
* A reference to a field, such as `max(messages.time) as max_time`.
*
* @property {string} fieldPath
*
* @typedef FieldReference
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.StructuredQuery.FieldReference definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
FieldReference: {
// This is for documentation. Actual contents will be loaded by gRPC.
},
/**
* The projection of document's fields to return.
*
* @property {Object[]} fields
* The fields to return.
*
* If empty, all fields are returned. To only return the name
* of the document, use `['__name__']`.
*
* This object should have the same structure as [FieldReference]{@link
* google.firestore.v1beta1.FieldReference}
*
* @typedef Projection
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.StructuredQuery.Projection definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
Projection: {
// This is for documentation. Actual contents will be loaded by gRPC.
},
/**
* A sort direction.
*
* @enum {number}
* @memberof google.firestore.v1beta1
*/
Direction: {
/**
* Unspecified.
*/
DIRECTION_UNSPECIFIED: 0,
/**
* Ascending.
*/
ASCENDING: 1,
/**
* Descending.
*/
DESCENDING: 2
}
};
/**
* A position in a query result set.
*
* @property {Object[]} values
* The values that represent a position, in the order they appear in
* the order by clause of a query.
*
* Can contain fewer values than specified in the order by clause.
*
* This object should have the same structure as [Value]{@link
* google.firestore.v1beta1.Value}
*
* @property {boolean} before
* If the position is just before or just after the given values, relative
* to the sort order defined by the query.
*
* @typedef Cursor
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.Cursor definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/query.proto}
*/
var Cursor = {
// This is for documentation. Actual contents will be loaded by gRPC.
};

View File

@@ -0,0 +1,262 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* A write on a document.
*
* @property {Object} update
* A document to write.
*
* This object should have the same structure as [Document]{@link
* google.firestore.v1beta1.Document}
*
* @property {string} delete
* A document name to delete. In the format:
* `projects/{project_id}/databases/{database_id}/documents/{document_path}`.
*
* @property {Object} transform
* Applies a tranformation to a document.
* At most one `transform` per document is allowed in a given request.
* An `update` cannot follow a `transform` on the same document in a given
* request.
*
* This object should have the same structure as [DocumentTransform]{@link
* google.firestore.v1beta1.DocumentTransform}
*
* @property {Object} updateMask
* The fields to update in this write.
*
* This field can be set only when the operation is `update`.
* None of the field paths in the mask may contain a reserved name.
* If the document exists on the server and has fields not referenced in the
* mask, they are left unchanged.
* Fields referenced in the mask, but not present in the input document, are
* deleted from the document on the server.
* The field paths in this mask must not contain a reserved field name.
*
* This object should have the same structure as [DocumentMask]{@link
* google.firestore.v1beta1.DocumentMask}
*
* @property {Object} currentDocument
* An optional precondition on the document.
*
* The write will fail if this is set and not met by the target document.
*
* This object should have the same structure as [Precondition]{@link
* google.firestore.v1beta1.Precondition}
*
* @typedef Write
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.Write definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/write.proto}
*/
var Write = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A transformation of a document.
*
* @property {string} document
* The name of the document to transform.
*
* @property {Object[]} fieldTransforms
* The list of transformations to apply to the fields of the document, in
* order.
* This must not be empty.
*
* This object should have the same structure as [FieldTransform]{@link
* google.firestore.v1beta1.FieldTransform}
*
* @typedef DocumentTransform
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.DocumentTransform definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/write.proto}
*/
var DocumentTransform = {
// This is for documentation. Actual contents will be loaded by gRPC.
/**
* A transformation of a field of the document.
*
* @property {string} fieldPath
* The path of the field. See Document.fields for the field path syntax
* reference.
*
* @property {number} setToServerValue
* Sets the field to the given server value.
*
* The number should be among the values of [ServerValue]{@link
* google.firestore.v1beta1.ServerValue}
*
* @typedef FieldTransform
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.DocumentTransform.FieldTransform definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/write.proto}
*/
FieldTransform: {
// This is for documentation. Actual contents will be loaded by gRPC.
/**
* A value that is calculated by the server.
*
* @enum {number}
* @memberof google.firestore.v1beta1
*/
ServerValue: {
/**
* Unspecified. This value must not be used.
*/
SERVER_VALUE_UNSPECIFIED: 0,
/**
* The time at which the server processed the request, with millisecond
* precision.
*/
REQUEST_TIME: 1
}
}
};
/**
* The result of applying a write.
*
* @property {Object} updateTime
* The last update time of the document after applying the write. Not set
* after a `delete`.
*
* If the write did not actually change the document, this will be the
* previous update_time.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @property {Object[]} transformResults
* The results of applying each DocumentTransform.FieldTransform, in the
* same order.
*
* This object should have the same structure as [Value]{@link
* google.firestore.v1beta1.Value}
*
* @typedef WriteResult
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.WriteResult definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/write.proto}
*/
var WriteResult = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A Document has changed.
*
* May be the result of multiple writes, including deletes, that
* ultimately resulted in a new value for the Document.
*
* Multiple DocumentChange messages may be returned for the same logical
* change, if multiple targets are affected.
*
* @property {Object} document
* The new state of the Document.
*
* If `mask` is set, contains only fields that were updated or added.
*
* This object should have the same structure as [Document]{@link
* google.firestore.v1beta1.Document}
*
* @property {number[]} targetIds
* A set of target IDs of targets that match this document.
*
* @property {number[]} removedTargetIds
* A set of target IDs for targets that no longer match this document.
*
* @typedef DocumentChange
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.DocumentChange definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/write.proto}
*/
var DocumentChange = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A Document has been deleted.
*
* May be the result of multiple writes, including updates, the
* last of which deleted the Document.
*
* Multiple DocumentDelete messages may be returned for the same logical
* delete, if multiple targets are affected.
*
* @property {string} document
* The resource name of the Document that was deleted.
*
* @property {number[]} removedTargetIds
* A set of target IDs for targets that previously matched this entity.
*
* @property {Object} readTime
* The read timestamp at which the delete was observed.
*
* Greater or equal to the `commit_time` of the delete.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef DocumentDelete
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.DocumentDelete definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/write.proto}
*/
var DocumentDelete = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A Document has been removed from the view of the targets.
*
* Sent if the document is no longer relevant to a target and is out of view.
* Can be sent instead of a DocumentDelete or a DocumentChange if the server
* can not send the new value of the document.
*
* Multiple DocumentRemove messages may be returned for the same logical
* write or delete, if multiple targets are affected.
*
* @property {string} document
* The resource name of the Document that has gone out of view.
*
* @property {number[]} removedTargetIds
* A set of target IDs for targets that previously matched this document.
*
* @property {Object} readTime
* The read timestamp at which the remove was observed.
*
* Greater or equal to the `commit_time` of the change/delete/remove.
*
* This object should have the same structure as [Timestamp]{@link
* google.protobuf.Timestamp}
*
* @typedef DocumentRemove
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.DocumentRemove definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/write.proto}
*/
var DocumentRemove = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* A digest of all the documents that match a given target.
*
* @property {number} targetId
* The target ID to which this filter applies.
*
* @property {number} count
* The total count of documents that match target_id.
*
* If different from the count of documents in the client that match, the
* client must manually determine which documents no longer match the target.
*
* @typedef ExistenceFilter
* @memberof google.firestore.v1beta1
* @see [google.firestore.v1beta1.ExistenceFilter definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/write.proto}
*/
var ExistenceFilter = {
// This is for documentation. Actual contents will be loaded by gRPC.
};

View File

@@ -0,0 +1,130 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* `Any` contains an arbitrary serialized protocol buffer message along with a
* URL that describes the type of the serialized message.
*
* Protobuf library provides support to pack/unpack Any values in the form
* of utility functions or additional generated methods of the Any type.
*
* Example 1: Pack and unpack a message in C++.
*
* Foo foo = ...;
* Any any;
* any.PackFrom(foo);
* ...
* if (any.UnpackTo(&foo)) {
* ...
* }
*
* Example 2: Pack and unpack a message in Java.
*
* Foo foo = ...;
* Any any = Any.pack(foo);
* ...
* if (any.is(Foo.class)) {
* foo = any.unpack(Foo.class);
* }
*
* Example 3: Pack and unpack a message in Python.
*
* foo = Foo(...)
* any = Any()
* any.Pack(foo)
* ...
* if any.Is(Foo.DESCRIPTOR):
* any.Unpack(foo)
* ...
*
* Example 4: Pack and unpack a message in Go
*
* foo := &pb.Foo{...}
* any, err := ptypes.MarshalAny(foo)
* ...
* foo := &pb.Foo{}
* if err := ptypes.UnmarshalAny(any, foo); err != nil {
* ...
* }
*
* The pack methods provided by protobuf library will by default use
* 'type.googleapis.com/full.type.name' as the type URL and the unpack
* methods only use the fully qualified type name after the last '/'
* in the type URL, for example "foo.bar.com/x/y.z" will yield type
* name "y.z".
*
*
* # JSON
*
* The JSON representation of an `Any` value uses the regular
* representation of the deserialized, embedded message, with an
* additional field `@type` which contains the type URL. Example:
*
* package google.profile;
* message Person {
* string first_name = 1;
* string last_name = 2;
* }
*
* {
* "@type": "type.googleapis.com/google.profile.Person",
* "firstName": <string>,
* "lastName": <string>
* }
*
* If the embedded message type is well-known and has a custom JSON
* representation, that representation will be embedded adding a field
* `value` which holds the custom JSON in addition to the `@type`
* field. Example (for message google.protobuf.Duration):
*
* {
* "@type": "type.googleapis.com/google.protobuf.Duration",
* "value": "1.212s"
* }
*
* @property {string} typeUrl
* A URL/resource name whose content describes the type of the
* serialized protocol buffer message.
*
* For URLs which use the scheme `http`, `https`, or no scheme, the
* following restrictions and interpretations apply:
*
* * If no scheme is provided, `https` is assumed.
* * The last segment of the URL's path must represent the fully
* qualified name of the type (as in `path/google.protobuf.Duration`).
* The name should be in a canonical form (e.g., leading "." is
* not accepted).
* * An HTTP GET on the URL must yield a google.protobuf.Type
* value in binary format, or produce an error.
* * Applications are allowed to cache lookup results based on the
* URL, or have them precompiled into a binary to avoid any
* lookup. Therefore, binary compatibility needs to be preserved
* on changes to types. (Use versioned type names to manage
* breaking changes.)
*
* Schemes other than `http`, `https` (or the empty scheme) might be
* used with implementation specific semantics.
*
* @property {string} value
* Must be a valid serialized protocol buffer of the above specified type.
*
* @typedef Any
* @memberof google.protobuf
* @see [google.protobuf.Any definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/any.proto}
*/
var Any = {
// This is for documentation. Actual contents will be loaded by gRPC.
};

View File

@@ -0,0 +1,33 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* A generic empty message that you can re-use to avoid defining duplicated
* empty messages in your APIs. A typical example is to use it as the request
* or the response type of an API method. For instance:
*
* service Foo {
* rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
* }
*
* The JSON representation for `Empty` is empty JSON object `{}`.
* @typedef Empty
* @memberof google.protobuf
* @see [google.protobuf.Empty definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/empty.proto}
*/
var Empty = {
// This is for documentation. Actual contents will be loaded by gRPC.
};

View File

@@ -0,0 +1,115 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* A Timestamp represents a point in time independent of any time zone
* or calendar, represented as seconds and fractions of seconds at
* nanosecond resolution in UTC Epoch time. It is encoded using the
* Proleptic Gregorian Calendar which extends the Gregorian calendar
* backwards to year one. It is encoded assuming all minutes are 60
* seconds long, i.e. leap seconds are "smeared" so that no leap second
* table is needed for interpretation. Range is from
* 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
* By restricting to that range, we ensure that we can convert to
* and from RFC 3339 date strings.
* See
* [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
*
* # Examples
*
* Example 1: Compute Timestamp from POSIX `time()`.
*
* Timestamp timestamp;
* timestamp.set_seconds(time(NULL));
* timestamp.set_nanos(0);
*
* Example 2: Compute Timestamp from POSIX `gettimeofday()`.
*
* struct timeval tv;
* gettimeofday(&tv, NULL);
*
* Timestamp timestamp;
* timestamp.set_seconds(tv.tv_sec);
* timestamp.set_nanos(tv.tv_usec * 1000);
*
* Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
*
* FILETIME ft;
* GetSystemTimeAsFileTime(&ft);
* UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
*
* // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
* // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
* Timestamp timestamp;
* timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
* timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
*
* Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
*
* long millis = System.currentTimeMillis();
*
* Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
* .setNanos((int) ((millis % 1000) * 1000000)).build();
*
*
* Example 5: Compute Timestamp from current time in Python.
*
* timestamp = Timestamp()
* timestamp.GetCurrentTime()
*
* # JSON Mapping
*
* In JSON format, the Timestamp type is encoded as a string in the
* [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
* format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
* where {year} is always expressed using four digits while {month}, {day},
* {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
* seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
* are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
* is required, though only UTC (as indicated by "Z") is presently supported.
*
* For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
* 01:30 UTC on January 15, 2017.
*
* In JavaScript, one can convert a Date object to this format using the
* standard
* [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
* method. In Python, a standard `datetime.datetime` object can be converted
* to this format using
* [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
* the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
* the Joda Time's [`ISODateTimeFormat.dateTime()`](https://cloud.google.com
* http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime())
* to obtain a formatter capable of generating timestamps in this format.
*
* @property {number} seconds
* Represents seconds of UTC time since Unix epoch
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59Z inclusive.
*
* @property {number} nanos
* Non-negative fractions of a second at nanosecond resolution. Negative
* second values with fractions must still have non-negative nanos values
* that count forward in time. Must be from 0 to 999,999,999
* inclusive.
*
* @typedef Timestamp
* @memberof google.protobuf
* @see [google.protobuf.Timestamp definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto}
*/
var Timestamp = {
// This is for documentation. Actual contents will be loaded by gRPC.
};

View File

@@ -0,0 +1,151 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* Wrapper message for `double`.
*
* The JSON representation for `DoubleValue` is JSON number.
*
* @property {number} value
* The double value.
*
* @typedef DoubleValue
* @memberof google.protobuf
* @see [google.protobuf.DoubleValue definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/wrappers.proto}
*/
var DoubleValue = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* Wrapper message for `float`.
*
* The JSON representation for `FloatValue` is JSON number.
*
* @property {number} value
* The float value.
*
* @typedef FloatValue
* @memberof google.protobuf
* @see [google.protobuf.FloatValue definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/wrappers.proto}
*/
var FloatValue = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* Wrapper message for `int64`.
*
* The JSON representation for `Int64Value` is JSON string.
*
* @property {number} value
* The int64 value.
*
* @typedef Int64Value
* @memberof google.protobuf
* @see [google.protobuf.Int64Value definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/wrappers.proto}
*/
var Int64Value = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* Wrapper message for `uint64`.
*
* The JSON representation for `UInt64Value` is JSON string.
*
* @property {number} value
* The uint64 value.
*
* @typedef UInt64Value
* @memberof google.protobuf
* @see [google.protobuf.UInt64Value definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/wrappers.proto}
*/
var UInt64Value = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* Wrapper message for `int32`.
*
* The JSON representation for `Int32Value` is JSON number.
*
* @property {number} value
* The int32 value.
*
* @typedef Int32Value
* @memberof google.protobuf
* @see [google.protobuf.Int32Value definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/wrappers.proto}
*/
var Int32Value = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* Wrapper message for `uint32`.
*
* The JSON representation for `UInt32Value` is JSON number.
*
* @property {number} value
* The uint32 value.
*
* @typedef UInt32Value
* @memberof google.protobuf
* @see [google.protobuf.UInt32Value definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/wrappers.proto}
*/
var UInt32Value = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* Wrapper message for `bool`.
*
* The JSON representation for `BoolValue` is JSON `true` and `false`.
*
* @property {boolean} value
* The bool value.
*
* @typedef BoolValue
* @memberof google.protobuf
* @see [google.protobuf.BoolValue definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/wrappers.proto}
*/
var BoolValue = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* Wrapper message for `string`.
*
* The JSON representation for `StringValue` is JSON string.
*
* @property {string} value
* The string value.
*
* @typedef StringValue
* @memberof google.protobuf
* @see [google.protobuf.StringValue definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/wrappers.proto}
*/
var StringValue = {
// This is for documentation. Actual contents will be loaded by gRPC.
};
/**
* Wrapper message for `bytes`.
*
* The JSON representation for `BytesValue` is JSON string.
*
* @property {string} value
* The bytes value.
*
* @typedef BytesValue
* @memberof google.protobuf
* @see [google.protobuf.BytesValue definition in proto format]{@link https://github.com/google/protobuf/blob/master/src/google/protobuf/wrappers.proto}
*/
var BytesValue = {
// This is for documentation. Actual contents will be loaded by gRPC.
};

View File

@@ -0,0 +1,92 @@
"use strict";
// Copyright 2018 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.
// Note: this file is purely for documentation. Any contents are not expected
// to be loaded as the JS file.
/**
* The `Status` type defines a logical error model that is suitable for
* different programming environments, including REST APIs and RPC APIs. It is
* used by [gRPC](https://github.com/grpc). The error model is designed to be:
*
* - Simple to use and understand for most users
* - Flexible enough to meet unexpected needs
*
* # Overview
*
* The `Status` message contains three pieces of data: error code, error
* message, and error details. The error code should be an enum value of
* google.rpc.Code, but it may accept additional error codes if needed. The
* error message should be a developer-facing English message that helps
* developers *understand* and *resolve* the error. If a localized user-facing
* error message is needed, put the localized message in the error details or
* localize it in the client. The optional error details may contain arbitrary
* information about the error. There is a predefined set of error detail types
* in the package `google.rpc` that can be used for common error conditions.
*
* # Language mapping
*
* The `Status` message is the logical representation of the error model, but it
* is not necessarily the actual wire format. When the `Status` message is
* exposed in different client libraries and different wire protocols, it can be
* mapped differently. For example, it will likely be mapped to some exceptions
* in Java, but more likely mapped to some error codes in C.
*
* # Other uses
*
* The error model and the `Status` message can be used in a variety of
* environments, either with or without APIs, to provide a
* consistent developer experience across different environments.
*
* Example uses of this error model include:
*
* - Partial errors. If a service needs to return partial errors to the client,
* it may embed the `Status` in the normal response to indicate the partial
* errors.
*
* - Workflow errors. A typical workflow has multiple steps. Each step may
* have a `Status` message for error reporting.
*
* - Batch operations. If a client uses batch request and batch response, the
* `Status` message should be used directly inside batch response, one for
* each error sub-response.
*
* - Asynchronous operations. If an API call embeds asynchronous operation
* results in its response, the status of those operations should be
* represented directly using the `Status` message.
*
* - Logging. If some API errors are stored in logs, the message `Status` could
* be used directly after any stripping needed for security/privacy reasons.
*
* @property {number} code
* The status code, which should be an enum value of google.rpc.Code.
*
* @property {string} message
* A developer-facing error message, which should be in English. Any
* user-facing error message should be localized and sent in the
* google.rpc.Status.details field, or localized by the client.
*
* @property {Object[]} details
* A list of messages that carry the error details. There is a common set of
* message types for APIs to use.
*
* This object should have the same structure as [Any]{@link
* google.protobuf.Any}
*
* @typedef Status
* @memberof google.rpc
* @see [google.rpc.Status definition in proto format]{@link https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto}
*/
var Status = {
// This is for documentation. Actual contents will be loaded by gRPC.
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,100 @@
{
"interfaces": {
"google.firestore.v1beta1.Firestore": {
"retry_codes": {
"idempotent": [
"DEADLINE_EXCEEDED",
"UNAVAILABLE"
],
"non_idempotent": []
},
"retry_params": {
"default": {
"initial_retry_delay_millis": 100,
"retry_delay_multiplier": 1.3,
"max_retry_delay_millis": 60000,
"initial_rpc_timeout_millis": 20000,
"rpc_timeout_multiplier": 1.0,
"max_rpc_timeout_millis": 20000,
"total_timeout_millis": 600000
},
"streaming": {
"initial_retry_delay_millis": 100,
"retry_delay_multiplier": 1.3,
"max_retry_delay_millis": 60000,
"initial_rpc_timeout_millis": 300000,
"rpc_timeout_multiplier": 1.0,
"max_rpc_timeout_millis": 86400000,
"total_timeout_millis": 86400000
}
},
"methods": {
"GetDocument": {
"timeout_millis": 60000,
"retry_codes_name": "idempotent",
"retry_params_name": "default"
},
"ListDocuments": {
"timeout_millis": 60000,
"retry_codes_name": "idempotent",
"retry_params_name": "default"
},
"CreateDocument": {
"timeout_millis": 60000,
"retry_codes_name": "non_idempotent",
"retry_params_name": "default"
},
"UpdateDocument": {
"timeout_millis": 60000,
"retry_codes_name": "non_idempotent",
"retry_params_name": "default"
},
"DeleteDocument": {
"timeout_millis": 60000,
"retry_codes_name": "idempotent",
"retry_params_name": "default"
},
"BatchGetDocuments": {
"timeout_millis": 86400000,
"retry_codes_name": "idempotent",
"retry_params_name": "streaming"
},
"BeginTransaction": {
"timeout_millis": 60000,
"retry_codes_name": "idempotent",
"retry_params_name": "default"
},
"Commit": {
"timeout_millis": 60000,
"retry_codes_name": "non_idempotent",
"retry_params_name": "default"
},
"Rollback": {
"timeout_millis": 60000,
"retry_codes_name": "idempotent",
"retry_params_name": "default"
},
"RunQuery": {
"timeout_millis": 86400000,
"retry_codes_name": "idempotent",
"retry_params_name": "default"
},
"Write": {
"timeout_millis": 300000,
"retry_codes_name": "non_idempotent",
"retry_params_name": "streaming"
},
"Listen": {
"timeout_millis": 86400000,
"retry_codes_name": "idempotent",
"retry_params_name": "streaming"
},
"ListCollectionIds": {
"timeout_millis": 60000,
"retry_codes_name": "idempotent",
"retry_params_name": "default"
}
}
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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 firestoreClient = require('./firestore_client');
var gax = require('google-gax');
var extend = require('extend');
function v1beta1(options) {
options = extend({
scopes: v1beta1.ALL_SCOPES,
}, options);
var gaxGrpc = new gax.GrpcClient(options);
return new firestoreClient(gaxGrpc);
}
v1beta1.SERVICE_ADDRESS = firestoreClient.SERVICE_ADDRESS;
v1beta1.ALL_SCOPES = firestoreClient.ALL_SCOPES;
module.exports = v1beta1;

View File

@@ -0,0 +1,184 @@
/*!
* 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 __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 is = __importStar(require("is"));
/**
* Formats the given word as plural conditionally given the preceding number.
*
* @private
*/
function formatPlural(num, str) {
return `${num} ${str}` + (num === 1 ? '' : 's');
}
/**
* Provides argument validation for the Firestore Public API. Exposes validators
* for strings, integers, numbers, objects and functions by default and can be
* extended to provide custom validators.
*
* The exported validation functions follow the naming convention is{Type} and
* isOptional{Type}, such as "isString" and "isOptionalString".
*
* To register custom validators, invoke the constructor with with a mapping
* from type names to validation functions. Validation functions return 'true'
* for valid inputs and may throw errors with custom validation messages for
* easier diagnosis.
*
* @private
*/
class Validator {
/**
* Create a new Validator, optionally registering the custom validators as
* provided.
*
* @param customValidators A list of custom validators to register.
*/
constructor(customValidators) {
const validators = Object.assign({
function: is.function,
integer: (value, min, max) => {
min = is.defined(min) ? min : -Infinity;
max = is.defined(max) ? max : Infinity;
if (!is.integer(value)) {
return false;
}
if (value < min || value > max) {
throw new Error(`Value must be within [${min}, ${max}] inclusive, but was: ${value}`);
}
return true;
},
number: (value, min, max) => {
min = is.defined(min) ? min : -Infinity;
max = is.defined(max) ? max : Infinity;
if (!is.number(value) || is.nan(value)) {
return false;
}
if (value < min || value > max) {
throw new Error(`Value must be within [${min}, ${max}] inclusive, but was: ${value}`);
}
return true;
},
object: is.object,
string: is.string,
boolean: is.boolean
}, customValidators);
const register = type => {
const camelCase = type.substring(0, 1).toUpperCase() + type.substring(1);
this[`is${camelCase}`] = (argumentName, ...values) => {
let valid = false;
let message = is.number(argumentName) ?
`Argument at index ${argumentName} is not a valid ${type}.` :
`Argument "${argumentName}" is not a valid ${type}.`;
try {
valid = validators[type].call(null, ...values);
}
catch (err) {
message += ` ${err.message}`;
}
if (valid !== true) {
throw new Error(message);
}
};
this[`isOptional${camelCase}`] = function (argumentName, value) {
if (is.defined(value)) {
this[`is${camelCase}`].apply(null, arguments);
}
};
};
for (const type in validators) {
if (validators.hasOwnProperty(type)) {
register(type);
}
}
}
/**
* Verifies that 'args' has at least 'minSize' elements.
*
* @param {string} funcName - The function name to use in the error message.
* @param {Array.<*>} args - The array (or array-like structure) to verify.
* @param {number} minSize - The minimum number of elements to enforce.
* @throws if the expectation is not met.
* @returns {boolean} 'true' when the minimum number of elements is available.
*/
minNumberOfArguments(funcName, args, minSize) {
if (args.length < minSize) {
throw new Error(`Function '${funcName}()' requires at least ` +
`${formatPlural(minSize, 'argument')}.`);
}
return true;
}
/**
* Verifies that 'args' has at most 'maxSize' elements.
*
* @param {string} funcName - The function name to use in the error message.
* @param {Array.<*>} args - The array (or array-like structure) to verify.
* @param {number} maxSize - The maximum number of elements to enforce.
* @throws if the expectation is not met.
* @returns {boolean} 'true' when only the maximum number of elements is
* specified.
*/
maxNumberOfArguments(funcName, args, maxSize) {
if (args.length > maxSize) {
throw new Error(`Function '${funcName}()' accepts at most ` +
`${formatPlural(maxSize, 'argument')}.`);
}
return true;
}
}
exports.Validator = Validator;
function customObjectError(val) {
if (is.object(val) && val.constructor.name !== 'Object') {
const typeName = val.constructor.name;
switch (typeName) {
case 'DocumentReference':
case 'FieldPath':
case 'FieldValue':
case 'GeoPoint':
case 'Timestamp':
return new Error(`Detected an object of type "${typeName}" that doesn't match the ` +
'expected instance. Please ensure that the Firestore types you ' +
'are using are from the same NPM package.');
default:
return new Error(`Couldn't serialize object of type "${typeName}". Firestore ` +
'doesn\'t support JavaScript objects with custom prototypes ' +
'(i.e. objects that were created via the \'new\' operator).');
}
}
else {
return new Error(`Invalid use of type "${typeof val}" as a Firestore argument.`);
}
}
exports.customObjectError = customObjectError;
/**
* Create a new Validator, optionally registering the custom validators as
* provided.
*
* @private
* @param customValidators A list of custom validators to register.
*/
function createValidator(customValidators) {
// This function exists to change the type of `Validator` to `any` so that
// consumers can call the custom validator functions.
return new Validator(customValidators);
}
exports.createValidator = createValidator;

View File

@@ -0,0 +1,664 @@
/*!
* 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const functional_red_black_tree_1 = __importDefault(require("functional-red-black-tree"));
const through2_1 = __importDefault(require("through2"));
const logger_1 = require("./logger");
const backoff_1 = require("./backoff");
const timestamp_1 = require("./timestamp");
const path_1 = require("./path");
const util_1 = require("./util");
const document_1 = require("./document");
const document_change_1 = require("./document-change");
/*!
* Target ID used by watch. Watch uses a fixed target id since we only support
* one target per stream.
*
* @private
* @type {number}
*/
const WATCH_TARGET_ID = 0x1;
/*!
* The change type for document change events.
*/
const ChangeType = {
added: 'added',
modified: 'modified',
removed: 'removed',
};
/*!
* List of GRPC Error Codes.
*
* This corresponds to
* {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md}.
*/
const GRPC_STATUS_CODE = {
// Not an error; returned on success.
OK: 0,
// The operation was cancelled (typically by the caller).
CANCELLED: 1,
// Unknown error. An example of where this error may be returned is if a
// Status value received from another address space belongs to an error-space
// that is not known in this address space. Also errors raised by APIs that
// do not return enough error information may be converted to this error.
UNKNOWN: 2,
// Client specified an invalid argument. Note that this differs from
// FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are
// problematic regardless of the state of the system (e.g., a malformed file
// name).
INVALID_ARGUMENT: 3,
// Deadline expired before operation could complete. For operations that
// change the state of the system, this error may be returned even if the
// operation has completed successfully. For example, a successful response
// from a server could have been delayed long enough for the deadline to
// expire.
DEADLINE_EXCEEDED: 4,
// Some requested entity (e.g., file or directory) was not found.
NOT_FOUND: 5,
// Some entity that we attempted to create (e.g., file or directory) already
// exists.
ALREADY_EXISTS: 6,
// The caller does not have permission to execute the specified operation.
// PERMISSION_DENIED must not be used for rejections caused by exhausting
// some resource (use RESOURCE_EXHAUSTED instead for those errors).
// PERMISSION_DENIED must not be used if the caller can not be identified
// (use UNAUTHENTICATED instead for those errors).
PERMISSION_DENIED: 7,
// The request does not have valid authentication credentials for the
// operation.
UNAUTHENTICATED: 16,
// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
// entire file system is out of space.
RESOURCE_EXHAUSTED: 8,
// Operation was rejected because the system is not in a state required for
// the operation's execution. For example, directory to be deleted may be
// non-empty, an rmdir operation is applied to a non-directory, etc.
//
// A litmus test that may help a service implementor in deciding
// between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
// (a) Use UNAVAILABLE if the client can retry just the failing call.
// (b) Use ABORTED if the client should retry at a higher-level
// (e.g., restarting a read-modify-write sequence).
// (c) Use FAILED_PRECONDITION if the client should not retry until
// the system state has been explicitly fixed. E.g., if an "rmdir"
// fails because the directory is non-empty, FAILED_PRECONDITION
// should be returned since the client should not retry unless
// they have first fixed up the directory by deleting files from it.
// (d) Use FAILED_PRECONDITION if the client performs conditional
// REST Get/Update/Delete on a resource and the resource on the
// server does not match the condition. E.g., conflicting
// read-modify-write on the same resource.
FAILED_PRECONDITION: 9,
// The operation was aborted, typically due to a concurrency issue like
// sequencer check failures, transaction aborts, etc.
//
// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
// and UNAVAILABLE.
ABORTED: 10,
// Operation was attempted past the valid range. E.g., seeking or reading
// past end of file.
//
// Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed
// if the system state changes. For example, a 32-bit file system will
// generate INVALID_ARGUMENT if asked to read at an offset that is not in the
// range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from
// an offset past the current file size.
//
// There is a fair bit of overlap between FAILED_PRECONDITION and
// OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error)
// when it applies so that callers who are iterating through a space can
// easily look for an OUT_OF_RANGE error to detect when they are done.
OUT_OF_RANGE: 11,
// Operation is not implemented or not supported/enabled in this service.
UNIMPLEMENTED: 12,
// Internal errors. Means some invariants expected by underlying System has
// been broken. If you see one of these errors, Something is very broken.
INTERNAL: 13,
// The service is currently unavailable. This is a most likely a transient
// condition and may be corrected by retrying with a backoff.
//
// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
// and UNAVAILABLE.
UNAVAILABLE: 14,
// Unrecoverable data loss or corruption.
DATA_LOSS: 15,
// Force users to include a default branch:
DO_NOT_USE: -1,
};
/*!
* The comparator used for document watches (which should always get called with
* the same document).
*/
const DOCUMENT_WATCH_COMPARATOR = (doc1, doc2) => {
assert_1.default(doc1 === doc2, 'Document watches only support one document.');
return 0;
};
/**
* @private
* @callback docsCallback
* @returns {Array.<QueryDocumentSnapshot>} An ordered list of documents.
*/
/**
* @private
* @callback changeCallback
* @returns {Array.<DocumentChange>} An ordered list of document
* changes.
*/
/**
* onSnapshot() callback that receives the updated query state.
*
* @private
* @callback watchSnapshotCallback
*
* @param {Timestamp} readTime - The time at which this snapshot was obtained.
* @param {number} size - The number of documents in the result set.
* @param {docsCallback} docs - A callback that returns the ordered list of
* documents stored in this snapshot.
* @param {changeCallback} changes - A callback that returns the list of
* changed documents since the last snapshot delivered for this watch.
*/
/**
* Watch provides listen functionality and exposes the 'onSnapshot' observer. It
* can be used with a valid Firestore Listen target.
*
* @class
* @private
*/
class Watch {
/**
* @private
* @hideconstructor
*
* @param {Firestore} firestore The Firestore Database client.
* @param {Object} target - A Firestore 'Target' proto denoting the target to
* listen on.
* @param {function} comparator - A comparator for QueryDocumentSnapshots that
* is used to order the document snapshots returned by this watch.
*/
constructor(firestore, target, comparator) {
this._firestore = firestore;
this._targets = target;
this._comparator = comparator;
this._backoff = new backoff_1.ExponentialBackoff();
this._requestTag = util_1.requestTag();
}
/**
* Creates a new Watch instance to listen on DocumentReferences.
*
* @private
* @param {DocumentReference} documentRef - The document
* reference for this watch.
* @returns {Watch} A newly created Watch instance.
*/
static forDocument(documentRef) {
return new Watch(documentRef.firestore, {
documents: {
documents: [documentRef.formattedName],
},
targetId: WATCH_TARGET_ID,
}, DOCUMENT_WATCH_COMPARATOR);
}
/**
* Creates a new Watch instance to listen on Queries.
*
* @private
* @param {Query} query - The query used for this watch.
* @returns {Watch} A newly created Watch instance.
*/
static forQuery(query) {
return new Watch(query.firestore, {
query: query.toProto(),
targetId: WATCH_TARGET_ID,
}, query.comparator());
}
/**
* Determines whether an error is considered permanent and should not be
* retried. Errors that don't provide a GRPC error code are always considered
* transient in this context.
*
* @private
* @param {Error} error An error object.
* @return {boolean} Whether the error is permanent.
*/
isPermanentError(error) {
if (error.code === undefined) {
logger_1.logger('Watch.onSnapshot', this._requestTag, 'Unable to determine error code: ', error);
return false;
}
switch (error.code) {
case GRPC_STATUS_CODE.CANCELLED:
case GRPC_STATUS_CODE.UNKNOWN:
case GRPC_STATUS_CODE.DEADLINE_EXCEEDED:
case GRPC_STATUS_CODE.RESOURCE_EXHAUSTED:
case GRPC_STATUS_CODE.INTERNAL:
case GRPC_STATUS_CODE.UNAVAILABLE:
case GRPC_STATUS_CODE.UNAUTHENTICATED:
return false;
default:
return true;
}
}
/**
* Determines whether we need to initiate a longer backoff due to system
* overload.
*
* @private
* @param {Error} error A GRPC Error object that exposes an error code.
* @return {boolean} Whether we need to back off our retries.
*/
isResourceExhaustedError(error) {
return error.code === GRPC_STATUS_CODE.RESOURCE_EXHAUSTED;
}
/**
* Starts a watch and attaches a listener for document change events.
*
* @private
* @param {watchSnapshotCallback} onNext - A callback to be called every time
* a new snapshot is available.
* @param {function(Error)} onError - A callback to be called if the listen
* fails or is cancelled. No further callbacks will occur.
*
* @returns {function()} An unsubscribe function that can be called to cancel
* the snapshot listener.
*/
onSnapshot(onNext, onError) {
let self = this;
// The sorted tree of QueryDocumentSnapshots as sent in the last snapshot.
// We only look at the keys.
let docTree = functional_red_black_tree_1.default(this._comparator);
// A map of document names to QueryDocumentSnapshots for the last sent
// snapshot.
let docMap = new Map();
// The accumulates map of document changes (keyed by document name) for the
// current snapshot.
let changeMap = new Map();
// The current state of the query results.
let current = false;
// We need this to track whether we've pushed an initial set of changes,
// since we should push those even when there are no changes, if there \
// aren't docs.
let hasPushed = false;
// The server assigns and updates the resume token.
let resumeToken = undefined;
// Indicates whether we are interested in data from the stream. Set to false
// in the 'unsubscribe()' callback.
let isActive = true;
// Sentinel value for a document remove.
const REMOVED = {};
const request = {
database: this._firestore.formattedName,
addTarget: this._targets,
};
// We may need to replace the underlying stream on reset events.
// This is the one that will be returned and proxy the current one.
const stream = through2_1.default.obj();
// The current stream to the backend.
let currentStream = null;
/** Helper to clear the docs on RESET or filter mismatch. */
const resetDocs = function () {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Resetting documents');
changeMap.clear();
resumeToken = undefined;
docTree.forEach(snapshot => {
// Mark each document as deleted. If documents are not deleted, they
// will be send again by the server.
changeMap.set(snapshot.ref.formattedName, REMOVED);
});
current = false;
};
/** Closes the stream and calls onError() if the stream is still active. */
const closeStream = function (err) {
if (currentStream) {
currentStream.unpipe(stream);
currentStream.end();
currentStream = null;
}
stream.end();
if (isActive) {
isActive = false;
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Invoking onError: ', err);
onError(err);
}
};
/**
* Re-opens the stream unless the specified error is considered permanent.
* Clears the change map.
*/
const maybeReopenStream = function (err) {
if (isActive && !self.isPermanentError(err)) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Stream ended, re-opening after retryable error: ', err);
request.addTarget.resumeToken = resumeToken;
changeMap.clear();
if (self.isResourceExhaustedError(err)) {
self._backoff.resetToMax();
}
resetStream();
}
else {
closeStream(err);
}
};
/** Helper to restart the outgoing stream to the backend. */
const resetStream = function () {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Opening new stream');
if (currentStream) {
currentStream.unpipe(stream);
currentStream.end();
currentStream = null;
initStream();
}
};
/**
* Initializes a new stream to the backend with backoff.
*/
const initStream = function () {
self._backoff.backoffAndWait().then(() => {
if (!isActive) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Not initializing inactive stream');
return;
}
// Note that we need to call the internal _listen API to pass additional
// header values in readWriteStream.
self._firestore
.readWriteStream('listen', request, self._requestTag, true)
.then(backendStream => {
if (!isActive) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Closing inactive stream');
backendStream.end();
return;
}
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Opened new stream');
currentStream = backendStream;
currentStream.on('error', err => {
maybeReopenStream(err);
});
currentStream.on('end', () => {
const err = new Error('Stream ended unexpectedly');
err.code = GRPC_STATUS_CODE.UNKNOWN;
maybeReopenStream(err);
});
currentStream.pipe(stream);
currentStream.resume();
})
.catch(closeStream);
});
};
/**
* Checks if the current target id is included in the list of target ids.
* If no targetIds are provided, returns true.
*/
const affectsTarget = function (targetIds, currentId) {
if (targetIds === undefined || targetIds.length === 0) {
return true;
}
for (let targetId of targetIds) {
if (targetId === currentId) {
return true;
}
}
return false;
};
/** Splits up document changes into removals, additions, and updates. */
const extractChanges = function (docMap, changes, readTime) {
let deletes = [];
let adds = [];
let updates = [];
changes.forEach((value, name) => {
if (value === REMOVED) {
if (docMap.has(name)) {
deletes.push(name);
}
}
else if (docMap.has(name)) {
value.readTime = readTime;
updates.push(value.build());
}
else {
value.readTime = readTime;
adds.push(value.build());
}
});
return { deletes, adds, updates };
};
/**
* Applies the mutations in changeMap to both the document tree and the
* document lookup map. Modified docMap in-place and returns the updated
* state.
*/
const computeSnapshot = function (docTree, docMap, changes) {
let updatedTree = docTree;
let updatedMap = docMap;
assert_1.default(docTree.length === docMap.size, 'The document tree and document ' +
'map should have the same number of entries.');
/**
* Applies a document delete to the document tree and the document map.
* Returns the corresponding DocumentChange event.
*/
const deleteDoc = function (name) {
assert_1.default(updatedMap.has(name), 'Document to delete does not exist');
let oldDocument = updatedMap.get(name);
let existing = updatedTree.find(oldDocument);
let oldIndex = existing.index;
updatedTree = existing.remove();
updatedMap.delete(name);
return new document_change_1.DocumentChange(ChangeType.removed, oldDocument, oldIndex, -1);
};
/**
* Applies a document add to the document tree and the document map.
* Returns the corresponding DocumentChange event.
*/
const addDoc = function (newDocument) {
let name = newDocument.ref.formattedName;
assert_1.default(!updatedMap.has(name), 'Document to add already exists');
updatedTree = updatedTree.insert(newDocument, null);
let newIndex = updatedTree.find(newDocument).index;
updatedMap.set(name, newDocument);
return new document_change_1.DocumentChange(ChangeType.added, newDocument, -1, newIndex);
};
/**
* Applies a document modification to the document tree and the document
* map. Returns the DocumentChange event for successful modifications.
*/
const modifyDoc = function (newDocument) {
let name = newDocument.ref.formattedName;
assert_1.default(updatedMap.has(name), 'Document to modify does not exist');
let oldDocument = updatedMap.get(name);
if (!oldDocument.updateTime.isEqual(newDocument.updateTime)) {
let removeChange = deleteDoc(name);
let addChange = addDoc(newDocument);
return new document_change_1.DocumentChange(ChangeType.modified, newDocument, removeChange.oldIndex, addChange.newIndex);
}
return null;
};
// Process the sorted changes in the order that is expected by our
// clients (removals, additions, and then modifications). We also need to
// sort the individual changes to assure that oldIndex/newIndex keep
// incrementing.
let appliedChanges = [];
changes.deletes.sort((name1, name2) => {
// Deletes are sorted based on the order of the existing document.
return self._comparator(updatedMap.get(name1), updatedMap.get(name2));
});
changes.deletes.forEach(name => {
let change = deleteDoc(name);
if (change) {
appliedChanges.push(change);
}
});
changes.adds.sort(self._comparator);
changes.adds.forEach(snapshot => {
let change = addDoc(snapshot);
if (change) {
appliedChanges.push(change);
}
});
changes.updates.sort(self._comparator);
changes.updates.forEach(snapshot => {
let change = modifyDoc(snapshot);
if (change) {
appliedChanges.push(change);
}
});
assert_1.default(updatedTree.length === updatedMap.size, 'The update document ' +
'tree and document map should have the same number of entries.');
return { updatedTree, updatedMap, appliedChanges };
};
/**
* Assembles a new snapshot from the current set of changes and invokes the
* user's callback. Clears the current changes on completion.
*/
const push = function (readTime, nextResumeToken) {
let changes = extractChanges(docMap, changeMap, readTime);
let diff = computeSnapshot(docTree, docMap, changes);
if (!hasPushed || diff.appliedChanges.length > 0) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Sending snapshot with %d changes and %d documents', diff.appliedChanges.length, diff.updatedTree.length);
onNext(readTime, diff.updatedTree.length, () => diff.updatedTree.keys, () => diff.appliedChanges);
hasPushed = true;
}
docTree = diff.updatedTree;
docMap = diff.updatedMap;
changeMap.clear();
resumeToken = nextResumeToken;
};
/**
* Returns the current count of all documents, including the changes from
* the current changeMap.
*/
const currentSize = function () {
let changes = extractChanges(docMap, changeMap);
return docMap.size + changes.adds.length - changes.deletes.length;
};
initStream();
stream
.on('data', proto => {
if (proto.targetChange) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Processing target change');
const change = proto.targetChange;
const noTargetIds = !change.targetIds || change.targetIds.length === 0;
if (change.targetChangeType === 'NO_CHANGE') {
if (noTargetIds && change.readTime && current) {
// This means everything is up-to-date, so emit the current
// set of docs as a snapshot, if there were changes.
push(timestamp_1.Timestamp.fromProto(change.readTime), change.resumeToken);
}
}
else if (change.targetChangeType === 'ADD') {
if (WATCH_TARGET_ID !== change.targetIds[0]) {
closeStream(Error('Unexpected target ID sent by server'));
}
}
else if (change.targetChangeType === 'REMOVE') {
let code = 13;
let message = 'internal error';
if (change.cause) {
code = change.cause.code;
message = change.cause.message;
}
// @todo: Surface a .code property on the exception.
closeStream(new Error('Error ' + code + ': ' + message));
}
else if (change.targetChangeType === 'RESET') {
// Whatever changes have happened so far no longer matter.
resetDocs();
}
else if (change.targetChangeType === 'CURRENT') {
current = true;
}
else {
closeStream(new Error('Unknown target change type: ' + JSON.stringify(change)));
}
if (change.resumeToken &&
affectsTarget(change.targetIds, WATCH_TARGET_ID)) {
this._backoff.reset();
}
}
else if (proto.documentChange) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Processing change event');
// No other targetIds can show up here, but we still need to see
// if the targetId was in the added list or removed list.
const targetIds = proto.documentChange.targetIds || [];
const removedTargetIds = proto.documentChange.removedTargetIds || [];
let changed = false;
let removed = false;
for (let i = 0; i < targetIds.length; i++) {
if (targetIds[i] === WATCH_TARGET_ID) {
changed = true;
}
}
for (let i = 0; i < removedTargetIds.length; i++) {
if (removedTargetIds[i] === WATCH_TARGET_ID) {
removed = true;
}
}
const document = proto.documentChange.document;
const name = document.name;
if (changed) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Received document change');
const snapshot = new document_1.DocumentSnapshot.Builder();
snapshot.ref = self._firestore.doc(path_1.ResourcePath.fromSlashSeparatedString(name).relativeName);
snapshot.fieldsProto = document.fields || {};
snapshot.createTime =
timestamp_1.Timestamp.fromProto(document.createTime);
snapshot.updateTime =
timestamp_1.Timestamp.fromProto(document.updateTime);
changeMap.set(name, snapshot);
}
else if (removed) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Received document remove');
changeMap.set(name, REMOVED);
}
}
else if (proto.documentDelete || proto.documentRemove) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Processing remove event');
const name = (proto.documentDelete || proto.documentRemove).document;
changeMap.set(name, REMOVED);
}
else if (proto.filter) {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Processing filter update');
if (proto.filter.count !== currentSize()) {
// We need to remove all the current results.
resetDocs();
// The filter didn't match, so re-issue the query.
resetStream();
}
}
else {
closeStream(new Error('Unknown listen response type: ' + JSON.stringify(proto)));
}
})
.on('end', () => {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Processing stream end');
if (currentStream) {
// Pass the event on to the underlying stream.
currentStream.end();
}
});
return () => {
logger_1.logger('Watch.onSnapshot', self._requestTag, 'Ending stream');
// Prevent further callbacks.
isActive = false;
onNext = () => { };
onError = () => { };
stream.end();
};
}
}
exports.Watch = Watch;

View File

@@ -0,0 +1,510 @@
/*!
* 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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const is_1 = __importDefault(require("is"));
const logger_1 = require("./logger");
const document_1 = require("./document");
const path_1 = require("./path");
const timestamp_1 = require("./timestamp");
const util_1 = require("./util");
/*!
* Google Cloud Functions terminates idle connections after two minutes. After
* longer periods of idleness, we issue transactional commits to allow for
* retries.
*
* @type {number}
*/
const GCF_IDLE_TIMEOUT_MS = 110 * 1000;
/**
* A WriteResult wraps the write time set by the Firestore servers on sets(),
* updates(), and creates().
*
* @class
*/
class WriteResult {
/**
* @private
* @hideconstructor
*
* @param {Timestamp} writeTime - The time of the corresponding document
* write.
*/
constructor(writeTime) {
this._writeTime = writeTime;
}
/**
* The write time as set by the Firestore servers.
*
* @type {Timestamp}
* @name WriteResult#writeTime
* @readonly
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.set({foo: 'bar'}).then(writeResult => {
* console.log(`Document written at: ${writeResult.toDate()}`);
* });
*/
get writeTime() {
return this._writeTime;
}
/**
* Returns true if this `WriteResult` is equal to the provided value.
*
* @param {*} other The value to compare against.
* @return true if this `WriteResult` is equal to the provided value.
*/
isEqual(other) {
return (this === other ||
(is_1.default.instanceof(other, WriteResult) &&
this._writeTime.isEqual(other._writeTime)));
}
}
exports.WriteResult = WriteResult;
/**
* A Firestore WriteBatch that can be used to atomically commit multiple write
* operations at once.
*
* @class
*/
class WriteBatch {
/**
* @private
* @hideconstructor
*
* @param {Firestore} firestore - The Firestore Database client.
*/
constructor(firestore) {
this._firestore = firestore;
this._validator = firestore._validator;
this._serializer = firestore._serializer;
this._writes = [];
this._committed = false;
}
/**
* Checks if this write batch has any pending operations.
*
* @private
* @returns {boolean}
*/
get isEmpty() {
return this._writes.length === 0;
}
/**
* Throws an error if this batch has already been committed.
*
* @private
*/
verifyNotCommitted() {
if (this._committed) {
throw new Error('Cannot modify a WriteBatch that has been committed.');
}
}
/**
* Create a document with the provided object values. This will fail the batch
* if a document exists at its location.
*
* @param {DocumentReference} documentRef - A reference to the
* document to be created.
* @param {DocumentData} data - The object to serialize as the document.
* @returns {WriteBatch} This WriteBatch instance. Used for chaining
* method calls.
*
* @example
* let writeBatch = firestore.batch();
* let documentRef = firestore.collection('col').doc();
*
* writeBatch.create(documentRef, {foo: 'bar'});
*
* writeBatch.commit().then(() => {
* console.log('Successfully executed batch.');
* });
*/
create(documentRef, data) {
this._validator.isDocumentReference('documentRef', documentRef);
this._validator.isDocument('data', data, {
allowEmpty: true,
allowDeletes: 'none',
allowTransforms: true,
});
this.verifyNotCommitted();
const document = document_1.DocumentSnapshot.fromObject(documentRef, data);
const precondition = new document_1.Precondition({ exists: false });
const transform = document_1.DocumentTransform.fromObject(documentRef, data);
transform.validate();
this._writes.push({
write: !document.isEmpty || transform.isEmpty ? document.toProto() : null,
transform: transform.toProto(this._serializer),
precondition: precondition.toProto(),
});
return this;
}
/**
* Deletes a document from the database.
*
* @param {DocumentReference} documentRef - A reference to the
* document to be deleted.
* @param {Precondition=} precondition - A precondition to enforce for this
* delete.
* @param {Timestamp=} precondition.lastUpdateTime If set, enforces that the
* document was last updated at lastUpdateTime. Fails the batch if the
* document doesn't exist or was last updated at a different time.
* @returns {WriteBatch} This WriteBatch instance. Used for chaining
* method calls.
*
* @example
* let writeBatch = firestore.batch();
* let documentRef = firestore.doc('col/doc');
*
* writeBatch.delete(documentRef);
*
* writeBatch.commit().then(() => {
* console.log('Successfully executed batch.');
* });
*/
delete(documentRef, precondition) {
this._validator.isDocumentReference('documentRef', documentRef);
this._validator.isOptionalDeletePrecondition('precondition', precondition);
this.verifyNotCommitted();
const conditions = new document_1.Precondition(precondition);
this._writes.push({
write: {
delete: documentRef.formattedName,
},
precondition: conditions.toProto(),
});
return this;
}
/**
* Write to the document referred to by the provided
* [DocumentReference]{@link DocumentReference}.
* If the document does not exist yet, it will be created. If you pass
* [SetOptions]{@link SetOptions}., the provided data can be merged
* into the existing document.
*
* @param {DocumentReference} documentRef - A reference to the
* document to be set.
* @param {DocumentData} data - The object to serialize as the document.
* @param {SetOptions=} options - An object to configure the set behavior.
* @param {boolean=} options.merge - If true, set() merges the values
* specified in its data argument. Fields omitted from this set() call
* remain untouched.
* @param {Array.<string|FieldPath>=} options.mergeFields - If provided,
* set() only replaces the specified field paths. Any field path that is not
* specified is ignored and remains untouched.
* @returns {WriteBatch} This WriteBatch instance. Used for chaining
* method calls.
*
* @example
* let writeBatch = firestore.batch();
* let documentRef = firestore.doc('col/doc');
*
* writeBatch.set(documentRef, {foo: 'bar'});
*
* writeBatch.commit().then(() => {
* console.log('Successfully executed batch.');
* });
*/
set(documentRef, data, options) {
this._validator.isOptionalSetOptions('options', options);
const mergeLeaves = options && options.merge === true;
const mergePaths = options && options.mergeFields;
this._validator.isDocumentReference('documentRef', documentRef);
this._validator.isDocument('data', data, {
allowEmpty: true,
allowDeletes: mergePaths || mergeLeaves ? 'all' : 'none',
allowTransforms: true,
});
this.verifyNotCommitted();
let documentMask;
if (mergePaths) {
documentMask = document_1.DocumentMask.fromFieldMask(options.mergeFields);
data = documentMask.applyTo(data);
}
const transform = document_1.DocumentTransform.fromObject(documentRef, data);
transform.validate();
const document = document_1.DocumentSnapshot.fromObject(documentRef, data);
if (mergePaths) {
documentMask.removeFields(transform.fields);
}
else {
documentMask = document_1.DocumentMask.fromObject(data);
}
const hasDocumentData = !document.isEmpty || !documentMask.isEmpty;
let write;
if (!mergePaths && !mergeLeaves) {
write = document.toProto();
}
else if (hasDocumentData || transform.isEmpty) {
write = document.toProto();
write.updateMask = documentMask.toProto(this._serializer);
}
this._writes.push({
write,
transform: transform.toProto(this._serializer),
});
return this;
}
/**
* Update fields of the document referred to by the provided
* [DocumentReference]{@link DocumentReference}. If the document
* doesn't yet exist, the update fails and the entire batch will be rejected.
*
* The update() method accepts either an object with field paths encoded as
* keys and field values encoded as values, or a variable number of arguments
* that alternate between field paths and field values. Nested fields can be
* updated by providing dot-separated field path strings or by providing
* FieldPath objects.
*
* A Precondition restricting this update can be specified as the last
* argument.
*
* @param {DocumentReference} documentRef - A reference to the
* document to be updated.
* @param {UpdateData|string|FieldPath} dataOrField - An object
* containing the fields and values with which to update the document
* or the path of the first field to update.
* @param {
* ...(Precondition|*|string|FieldPath)} preconditionOrValues -
* An alternating list of field paths and values to update or a Precondition
* to restrict this update.
* @returns {WriteBatch} This WriteBatch instance. Used for chaining
* method calls.
*
* @example
* let writeBatch = firestore.batch();
* let documentRef = firestore.doc('col/doc');
*
* writeBatch.update(documentRef, {foo: 'bar'});
*
* writeBatch.commit().then(() => {
* console.log('Successfully executed batch.');
* });
*/
update(documentRef, dataOrField, preconditionOrValues) {
this._validator.minNumberOfArguments('update', arguments, 2);
this._validator.isDocumentReference('documentRef', documentRef);
this.verifyNotCommitted();
const updateMap = new Map();
let precondition = new document_1.Precondition({ exists: true });
const argumentError = 'Update() requires either a single JavaScript ' +
'object or an alternating list of field/value pairs that can be ' +
'followed by an optional precondition.';
let usesVarargs = is_1.default.string(dataOrField) || is_1.default.instance(dataOrField, path_1.FieldPath);
if (usesVarargs) {
try {
for (let i = 1; i < arguments.length; i += 2) {
if (i === arguments.length - 1) {
this._validator.isUpdatePrecondition(i, arguments[i]);
precondition = new document_1.Precondition(arguments[i]);
}
else {
this._validator.isFieldPath(i, arguments[i]);
this._validator.minNumberOfArguments('update', arguments, i + 1);
this._validator.isFieldValue(i, arguments[i + 1], {
allowDeletes: 'root',
allowTransforms: true,
});
updateMap.set(path_1.FieldPath.fromArgument(arguments[i]), arguments[i + 1]);
}
}
}
catch (err) {
logger_1.logger('WriteBatch.update', null, 'Varargs validation failed:', err);
// We catch the validation error here and re-throw to provide a better
// error message.
throw new Error(`${argumentError} ${err.message}`);
}
}
else {
try {
this._validator.isDocument('dataOrField', dataOrField, {
allowEmpty: false,
allowDeletes: 'root',
allowTransforms: true,
});
this._validator.maxNumberOfArguments('update', arguments, 3);
Object.keys(dataOrField).forEach(key => {
this._validator.isFieldPath(key, key);
updateMap.set(path_1.FieldPath.fromArgument(key), dataOrField[key]);
});
if (is_1.default.defined(preconditionOrValues)) {
this._validator.isUpdatePrecondition('preconditionOrValues', preconditionOrValues);
precondition = new document_1.Precondition(preconditionOrValues);
}
}
catch (err) {
logger_1.logger('WriteBatch.update', null, 'Non-varargs validation failed:', err);
// We catch the validation error here and prefix the error with a custom
// message to describe the usage of update() better.
throw new Error(`${argumentError} ${err.message}`);
}
}
this._validator.isUpdateMap('dataOrField', updateMap);
let document = document_1.DocumentSnapshot.fromUpdateMap(documentRef, updateMap);
let documentMask = document_1.DocumentMask.fromUpdateMap(updateMap);
let write = null;
if (!document.isEmpty || !documentMask.isEmpty) {
write = document.toProto();
write.updateMask = documentMask.toProto();
}
let transform = document_1.DocumentTransform.fromUpdateMap(documentRef, updateMap);
transform.validate();
this._writes.push({
write: write,
transform: transform.toProto(this._serializer),
precondition: precondition.toProto(),
});
return this;
}
/**
* Atomically commits all pending operations to the database and verifies all
* preconditions. Fails the entire write if any precondition is not met.
*
* @returns {Promise.<Array.<WriteResult>>} A Promise that resolves
* when this batch completes.
*
* @example
* let writeBatch = firestore.batch();
* let documentRef = firestore.doc('col/doc');
*
* writeBatch.set(documentRef, {foo: 'bar'});
*
* writeBatch.commit().then(() => {
* console.log('Successfully executed batch.');
* });
*/
commit() {
return this.commit_();
}
/**
* Commit method that takes an optional transaction ID.
*
* @private
* @param {object=} commitOptions Options to use for this commit.
* @param {bytes=} commitOptions.transactionId The transaction ID of this
* commit.
* @param {string=} commitOptions.requestTag A unique client-assigned
* identifier for this request.
* @returns {Promise.<Array.<WriteResult>>} A Promise that resolves
* when this batch completes.
*/
commit_(commitOptions) {
// Note: We don't call `verifyNotCommitted()` to allow for retries.
let explicitTransaction = commitOptions && commitOptions.transactionId;
let tag = (commitOptions && commitOptions.requestTag) || util_1.requestTag();
let request = {
database: this._firestore.formattedName,
};
// On GCF, we periodically force transactional commits to allow for
// request retries in case GCF closes our backend connection.
if (!explicitTransaction && this._shouldCreateTransaction()) {
logger_1.logger('WriteBatch.commit', tag, 'Using transaction for commit');
return this._firestore.request('beginTransaction', request, tag, true)
.then(resp => {
return this.commit_({ transactionId: resp.transaction });
});
}
request.writes = [];
for (let req of this._writes) {
assert_1.default(req.write || req.transform, 'Either a write or transform must be set');
if (req.precondition) {
(req.write || req.transform).currentDocument = req.precondition;
}
if (req.write) {
request.writes.push(req.write);
}
if (req.transform) {
request.writes.push(req.transform);
}
}
logger_1.logger('WriteBatch.commit', tag, 'Sending %d writes', request.writes.length);
if (explicitTransaction) {
request.transaction = explicitTransaction;
}
this._committed = true;
return this._firestore.request('commit', request, tag).then(resp => {
const writeResults = [];
if (request.writes.length > 0) {
assert_1.default(resp.writeResults instanceof Array &&
request.writes.length === resp.writeResults.length, `Expected one write result per operation, but got ${resp.writeResults.length} results for ${request.writes.length} operations.`);
const commitTime = timestamp_1.Timestamp.fromProto(resp.commitTime);
let offset = 0;
for (let i = 0; i < this._writes.length; ++i) {
let writeRequest = this._writes[i];
// Don't return two write results for a write that contains a
// transform, as the fact that we have to split one write operation
// into two distinct write requests is an implementation detail.
if (writeRequest.write && writeRequest.transform) {
// The document transform is always sent last and produces the
// latest update time.
++offset;
}
let writeResult = resp.writeResults[i + offset];
writeResults.push(new WriteResult(writeResult.updateTime ?
timestamp_1.Timestamp.fromProto(writeResult.updateTime) :
commitTime));
}
}
return writeResults;
});
}
/**
* Determines whether we should issue a transactional commit. On GCF, this
* happens after two minutes of idleness.
*
* @private
* @returns {boolean} Whether to use a transaction.
*/
_shouldCreateTransaction() {
if (!this._firestore._preferTransactions) {
return false;
}
if (this._firestore._lastSuccessfulRequest) {
let now = new Date().getTime();
return now - this._firestore._lastSuccessfulRequest > GCF_IDLE_TIMEOUT_MS;
}
return true;
}
}
exports.WriteBatch = WriteBatch;
/*!
* Validates that the update data does not contain any ambiguous field
* definitions (such as 'a.b' and 'a').
*
* @param {Map.<FieldPath, *>} data - An update map with field/value pairs.
* @returns {boolean} 'true' if the input is a valid update map.
*/
function validateUpdateMap(data) {
const fields = [];
data.forEach((value, key) => {
fields.push(key);
});
fields.sort((left, right) => left.compareTo(right));
for (let i = 1; i < fields.length; ++i) {
if (fields[i - 1].isPrefixOf(fields[i])) {
throw new Error(`Field "${fields[i - 1]}" was specified multiple times.`);
}
}
return true;
}
exports.validateUpdateMap = validateUpdateMap;