Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using the new Timestamp class in the public API #226

Merged
merged 14 commits into from Jun 28, 2018
6 changes: 4 additions & 2 deletions conformance/runner.js
Expand Up @@ -95,7 +95,9 @@ const convertInput = {
precondition: precondition => {
const deepCopy = JSON.parse(JSON.stringify(precondition));
if (deepCopy.updateTime) {
deepCopy.lastUpdateTime = DocumentSnapshot.toISOTime(deepCopy.updateTime);
deepCopy.lastUpdateTime = Firestore.Timestamp.fromProto(
deepCopy.updateTime
);
delete deepCopy.updateTime;
}
return deepCopy;
Expand Down Expand Up @@ -136,7 +138,7 @@ const convertInput = {
snapshot: snapshot => {
const docs = [];
const changes = [];
const readTime = DocumentSnapshot.toISOTime(snapshot.readTime);
const readTime = Firestore.Timestamp.fromProto(snapshot.readTime);

for (const doc of snapshot.docs) {
const deepCopy = JSON.parse(JSON.stringify(doc));
Expand Down
117 changes: 42 additions & 75 deletions src/document.js
Expand Up @@ -22,7 +22,6 @@ const is = require('is');

const fieldValue = require('./field-value');
const path = require('./path');
const timestampFromJson = require('./convert').timestampFromJson;

/*!
* @see {ResourcePath}
Expand Down Expand Up @@ -195,10 +194,10 @@ class DocumentSnapshot {
* @param {object=} fieldsProto - The fields of the Firestore `Document`
* Protobuf backing this document (or undefined if the document does not
* exist).
* @param {string} readTime - The ISO 8601 time when this snapshot was read.
* @param {string=} createTime - The ISO 8601 time when the document was
* created (or undefined if the document does not exist).
* @param {string=} updateTime - The ISO 8601 time when the document was last
* @param {Timestamp} readTime - The time when this snapshot was read.

This comment was marked as spam.

This comment was marked as spam.

* @param {Timestamp=} createTime - The time when the document was created
* (or undefined if the document does not exist).

This comment was marked as spam.

This comment was marked as spam.

* @param {Timestamp=} updateTime - The time when the document was last
* updated (or undefined if the document does not exist).

This comment was marked as spam.

This comment was marked as spam.

*/
constructor(ref, fieldsProto, readTime, createTime, updateTime) {
Expand Down Expand Up @@ -364,16 +363,17 @@ class DocumentSnapshot {
* The time the document was created. Undefined for documents that don't
* exist.
*
* @type {string|undefined}
* @type {Timestamp|undefined}
* @name DocumentSnapshot#createTime
* @readonly
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.get().then((documentSnapshot) => {
* documentRef.get().then(documentSnapshot => {
* if (documentSnapshot.exists) {
* console.log(`Document created at '${documentSnapshot.createTime}'`);
* let createTime = documentSnapshot.createTime;
* console.log(`Document created at '${createTime.toDate()}'`);
* }
* });
*/
Expand All @@ -385,16 +385,17 @@ class DocumentSnapshot {
* The time the document was last updated (at the time the snapshot was
* generated). Undefined for documents that don't exist.
*
* @type {string|undefined}
* @type {Timestamp|undefined}
* @name DocumentSnapshot#updateTime
* @readonly
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.get().then((documentSnapshot) => {
* documentRef.get().then(documentSnapshot => {
* if (documentSnapshot.exists) {
* console.log(`Document updated at '${documentSnapshot.updateTime}'`);
* let updateTime = documentSnapshot.updateTime;
* console.log(`Document updated at '${updateTime.toDate()}'`);
* }
* });
*/
Expand All @@ -405,15 +406,16 @@ class DocumentSnapshot {
/**
* The time this snapshot was read.
*
* @type {string}
* @type {Timestamp}
* @name DocumentSnapshot#readTime
* @readonly
*
* @example
* let documentRef = firestore.doc('col/doc');
*
* documentRef.get().then((documentSnapshot) => {
* console.log(`Document read at '${documentSnapshot.readTime}'`);
* documentRef.get().then(documentSnapshot => {
* let readTime = documentSnapshot.readTime;
* console.log(`Document read at '${readTime.toDate()}'`);
* });
*/
get readTime() {
Expand Down Expand Up @@ -547,10 +549,7 @@ class DocumentSnapshot {
return parseFloat(proto.doubleValue, 10);
}
case 'timestampValue': {
const timestamp = new Timestamp(
Number(proto.timestampValue.seconds || 0),
Number(proto.timestampValue.nanos || 0)
);
const timestamp = Timestamp.fromProto(proto.timestampValue);
return timestampsInSnapshotsEnabled ? timestamp : timestamp.toDate();
}
case 'referenceValue': {
Expand Down Expand Up @@ -631,6 +630,8 @@ class DocumentSnapshot {
* value.
*/
isEqual(other) {
// Since the read time is different on every document read, we explicitly
// ignore all document metadata in this comparison.
return (
this === other ||
(is.instance(other, DocumentSnapshot) &&
Expand All @@ -639,37 +640,6 @@ class DocumentSnapshot {
);
}

/**
* Converts a Google Protobuf timestamp to an ISO 8601 string.
*
* @private
* @param {{seconds:number=,nanos:number=}=} timestamp The Google Protobuf
* timestamp.
* @returns {string|undefined} The representation in ISO 8601 or undefined if
* the input is empty.
*/
static toISOTime(timestamp) {
if (timestamp) {
let isoSubstring = new Date(
(timestamp.seconds || 0) * 1000
).toISOString();

// Strip milliseconds from JavaScript ISO representation
// (YYYY-MM-DDTHH:mm:ss.sssZ or ±YYYYYY-MM-DDTHH:mm:ss.sssZ)
isoSubstring = isoSubstring.substr(0, isoSubstring.length - 4);

// Append nanoseconds as per ISO 8601
let nanoString = (timestamp.nanos || '') + '';
while (nanoString.length < 9) {
nanoString = '0' + nanoString;
}

return isoSubstring + nanoString + 'Z';
}

return undefined;
}

/**
* Encodes a JavaScrip object into the Firestore 'Fields' representation.
*
Expand Down Expand Up @@ -845,10 +815,9 @@ class QueryDocumentSnapshot extends DocumentSnapshot {
* @param {firestore/DocumentReference} ref - The reference to the document.
* @param {object} fieldsProto - The fields of the Firestore `Document`
* Protobuf backing this document.
* @param {string} readTime - The ISO 8601 time when this snapshot was read.
* @param {string} createTime - The ISO 8601 time when the document was
* created.
* @param {string} updateTime - The ISO 8601 time when the document was last
* @param {Timestamp} readTime - The time when this snapshot was read.
* @param {Timestamp} createTime - The time when the document was created.
* @param {Timestamp} updateTime - The time when the document was last
* updated.
*/
constructor(ref, fieldsProto, readTime, createTime, updateTime) {
Expand All @@ -858,16 +827,16 @@ class QueryDocumentSnapshot extends DocumentSnapshot {
/**
* The time the document was created.
*
* @type {string}
* @type {Timestamp}
* @name QueryDocumentSnapshot#createTime
* @readonly
* @override
*
* @example
* let query = firestore.collection('col');
*
* query.get().forEach(documentSnapshot => {
* console.log(`Document created at '${documentSnapshot.createTime}'`);
* query.get().forEach(snapshot => {
* console.log(`Document created at '${snapshot.createTime.toDate()}'`);
* });
*/
get createTime() {
Expand All @@ -878,16 +847,16 @@ class QueryDocumentSnapshot extends DocumentSnapshot {
* The time the document was last updated (at the time the snapshot was
* generated).
*
* @type {string}
* @type {Timestamp}
* @name QueryDocumentSnapshot#updateTime
* @readonly
* @override
*
* @example
* let query = firestore.collection('col');
*
* query.get().forEach(documentSnapshot => {
* console.log(`Document updated at '${documentSnapshot.updateTime}'`);
* query.get().forEach(snapshot => {
* console.log(`Document updated at '${snapshot.updateTime.toDate()}'`);
* });
*/
get updateTime() {
Expand Down Expand Up @@ -952,23 +921,23 @@ class DocumentSnapshotBuilder {
this.fieldsProto = snapshot._fieldsProto;

/**
* The ISO 8601 time when this document was read.
* The time when this document was read.
*
* @type {string}
* @type {Timestamp}
*/
this.readTime = snapshot._readTime;

/**
* The ISO 8601 time when this document was created.
* The time when this document was created.
*
* @type {string}
* @type {Timestamp}
*/
this.createTime = snapshot._createTime;

/**
* The ISO 8601 time when this document was last updated.
* The time when this document was last updated.
*
* @type {string}
* @type {Timestamp}
*/
this.updateTime = snapshot._updateTime;
}
Expand Down Expand Up @@ -1411,8 +1380,8 @@ class Precondition {
*
* @param {boolean=} options.exists - Whether the referenced document should
* exist in Firestore,
* @param {string=} options.lastUpdateTime - The last update time
* of the referenced document in Firestore (as ISO 8601 string).
* @param {Timestamp=} options.lastUpdateTime - The last update time of the
* referenced document in Firestore.
* @param options
*/
constructor(options) {
Expand All @@ -1425,6 +1394,7 @@ class Precondition {
/**
* Generates the Protobuf `Preconditon` object for this precondition.
*
* @private
* @returns {Object|null} The `Preconditon` Protobuf object or 'null' if there
* are no preconditions.
*/
Expand All @@ -1436,10 +1406,7 @@ class Precondition {
let proto = {};

if (is.defined(this._lastUpdateTime)) {
proto.updateTime = timestampFromJson(
this._lastUpdateTime,
'lastUpdateTime'
);
proto.updateTime = this._lastUpdateTime.toProto();
} else {
proto.exists = this._exists;
}
Expand Down Expand Up @@ -1577,8 +1544,8 @@ function validateFieldValue(val, options, depth) {
*
* @param {boolean=} options.exists - Whether the referenced document
* should exist.
* @param {string=} options.lastUpdateTime - The last update time
* of the referenced document in Firestore (as ISO 8601 string).
* @param {Timestamp=} options.lastUpdateTime - The last update time
* of the referenced document in Firestore.
* @param {boolean} allowExist Whether to allow the 'exists' preconditions.
* @returns {boolean} 'true' if the input is a valid Precondition.
*/
Expand All @@ -1601,8 +1568,8 @@ function validatePrecondition(precondition, allowExist) {

if (is.defined(precondition.lastUpdateTime)) {
++conditions;
if (!is.string(precondition.lastUpdateTime)) {
throw new Error('"lastUpdateTime" is not a string.');
if (!is.instance(precondition.lastUpdateTime, Timestamp)) {
throw new Error('"lastUpdateTime" is not a Firestore Timestamp.');
}
}

Expand Down
19 changes: 13 additions & 6 deletions src/index.js
Expand Up @@ -58,6 +58,11 @@ const FieldPath = path.FieldPath;
*/
const FieldValue = require('./field-value').FieldValue;

/*!
* @see Timestamp
*/
const Timestamp = require('./timestamp');

/*!
* @see CollectionReference
*/
Expand Down Expand Up @@ -440,17 +445,19 @@ follow these steps, YOUR APP MAY BREAK.`);
document.fieldsProto = documentOrName.fields
? convertDocument(documentOrName.fields)
: {};
document.createTime = DocumentSnapshot.toISOTime(
document.createTime = Timestamp.fromProto(
convertTimestamp(documentOrName.createTime, 'documentOrName.createTime')
);
document.updateTime = DocumentSnapshot.toISOTime(
document.updateTime = Timestamp.fromProto(
convertTimestamp(documentOrName.updateTime, 'documentOrName.updateTime')
);
}

document.readTime = DocumentSnapshot.toISOTime(
convertTimestamp(readTime, 'readTime')
);
if (readTime) {
document.readTime = Timestamp.fromProto(
convertTimestamp(readTime, 'readTime')
);
}

return document.build();
}
Expand Down Expand Up @@ -1373,4 +1380,4 @@ module.exports.FieldPath = FieldPath;
* @see Timestamp
* @type Timestamp
*/
module.exports.Timestamp = require('./timestamp');
module.exports.Timestamp = Timestamp;