Skip to content

Commit

Permalink
Using timestamps in the API
Browse files Browse the repository at this point in the history
  • Loading branch information
schmidt-sebastian committed Jun 26, 2018
1 parent c5e5d18 commit 564bc37
Show file tree
Hide file tree
Showing 16 changed files with 214 additions and 183 deletions.
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
116 changes: 42 additions & 74 deletions src/document.js
Expand Up @@ -195,10 +195,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.
* @param {Timestamp=} createTime - The time when the document was created
* (or undefined if the document does not exist).
* @param {Timestamp=} updateTime - The time when the document was last
* updated (or undefined if the document does not exist).
*/
constructor(ref, fieldsProto, readTime, createTime, updateTime) {
Expand Down Expand Up @@ -364,16 +364,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 +386,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 +407,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 +550,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 +631,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 +641,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 +816,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 +828,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 +848,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 +922,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 +1381,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 +1395,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 +1407,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 +1545,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 +1569,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;

0 comments on commit 564bc37

Please sign in to comment.