Skip to content

Commit 467ecc0

Browse files
authored
Merge pull request #27 from dmurvihill/timestamp
Timestamp
2 parents 496b37f + d65329d commit 467ecc0

14 files changed

+268
-47
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"lodash.clone": "^4.5.0",
3737
"lodash.clonedeep": "^4.5.0",
3838
"lodash.clonedeepwith": "^4.5.0",
39+
"lodash.clonewith": "^4.5.0",
3940
"lodash.compact": "^3.0.1",
4041
"lodash.difference": "^4.5.0",
4142
"lodash.every": "^4.6.0",
@@ -53,6 +54,7 @@
5354
"lodash.isfunction": "^3.0.9",
5455
"lodash.isnumber": "^3.0.3",
5556
"lodash.isobject": "^3.0.2",
57+
"lodash.isplainobject": "^4.0.6",
5658
"lodash.isstring": "^4.0.1",
5759
"lodash.isundefined": "^3.0.1",
5860
"lodash.keys": "^4.2.0",

src/firebase.js

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,12 @@ MockFirebase.ServerValue = {
4444
}
4545
};
4646

47-
var getServerTime, defaultClock;
48-
getServerTime = defaultClock = function () {
49-
return new Date().getTime();
50-
};
51-
5247
MockFirebase.setClock = function (fn) {
53-
getServerTime = fn;
48+
utils.setServerClock(fn);
5449
};
5550

5651
MockFirebase.restoreClock = function () {
57-
getServerTime = defaultClock;
52+
utils.restoreServerClock();
5853
};
5954

6055
MockFirebase.autoId = function () {
@@ -449,7 +444,7 @@ MockFirebase.prototype._dataChanged = function (unparsedData) {
449444
var data = utils.cleanData(unparsedData);
450445

451446
if (utils.isServerTimestamp(data)) {
452-
data = getServerTime();
447+
data = utils.getServerTime();
453448
}
454449

455450
if (pri !== this.priority) {
@@ -475,7 +470,7 @@ MockFirebase.prototype._dataChanged = function (unparsedData) {
475470
keysToChange.forEach(function (key) {
476471
var childData = unparsedData[key];
477472
if (utils.isServerTimestamp(childData)) {
478-
childData = getServerTime();
473+
childData = utils.getServerTime();
479474
}
480475
self._updateOrAdd(key, childData, events);
481476
});
@@ -493,7 +488,7 @@ MockFirebase.prototype._dataChanged = function (unparsedData) {
493488

494489
MockFirebase.prototype._priChanged = function (newPriority) {
495490
if (utils.isServerTimestamp(newPriority)) {
496-
newPriority = getServerTime();
491+
newPriority = utils.getServerTime();
497492
}
498493
this.priority = newPriority;
499494
if (this.parent) {
@@ -719,10 +714,10 @@ function render(datum) {
719714
return array;
720715
}
721716
} else {
722-
return _.cloneDeep(datum);
717+
return _.cloneDeepWith(datum, utils.cloneCustomizer);
723718
}
724719
} else {
725-
return _.clone(datum);
720+
return _.cloneWith(datum, utils.cloneCustomizer);
726721
}
727722
}
728723

src/firestore-document-snapshot.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
var _ = require('./lodash');
44
var FieldPath = require('./firestore-field-path');
5+
var utils = require('./utils');
56

67
function MockFirestoreDocumentSnapshot (id, ref, data) {
78
this.id = id;
89
this.ref = ref;
910
this._snapshotdata = _.cloneDeep(data) || null;
1011
this.data = function() {
11-
return _.cloneDeep(this._snapshotdata);
12+
return _.cloneDeepWith(this._snapshotdata, utils.cloneCustomizer);
1213
};
1314
this.exists = this._snapshotdata !== null;
1415
this.metadata = {

src/firestore-document.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ var Promise = require('rsvp').Promise;
66
var autoId = require('firebase-auto-ids');
77
var DocumentSnapshot = require('./firestore-document-snapshot');
88
var Queue = require('./queue').Queue;
9+
var Timestamp = require('./timestamp');
910
var utils = require('./utils');
1011
var validate = require('./validators');
12+
var WriteResult = require('./write-result');
1113

1214
function MockFirestoreDocument(path, data, parent, name, CollectionReference) {
1315
this.ref = this;
@@ -108,9 +110,12 @@ MockFirestoreDocument.prototype.create = function (data, callback) {
108110

109111
var base = self._getData();
110112
err = err || self._validateDoesNotExist(base);
111-
if (err === null) {
113+
if (err === null) {
114+
var serverTime = utils.getServerTime();
115+
var result = new WriteResult(Timestamp.fromMillis(serverTime));
116+
data = utils.removeEmptyFirestoreProperties(data, serverTime);
112117
self._dataChanged(data);
113-
resolve();
118+
resolve(result);
114119
} else {
115120
if (callback) {
116121
callback(err);
@@ -132,6 +137,7 @@ MockFirestoreDocument.prototype.set = function (data, opts, callback) {
132137
return new Promise(function (resolve, reject) {
133138
self._defer('set', _.toArray(arguments), function () {
134139
if (err === null) {
140+
data = utils.removeEmptyFirestoreProperties(data, utils.getServerTime());
135141
self._dataChanged(data);
136142
resolve();
137143
} else {
@@ -167,7 +173,7 @@ MockFirestoreDocument.prototype._update = function (changes, opts, callback) {
167173
data = _.assign(_.isObject(base) ? base : {}, utils.updateToFirestoreObject(changes));
168174
}
169175
}
170-
data = utils.removeEmptyFirestoreProperties(data, original);
176+
data = utils.removeEmptyFirestoreProperties(data, original, utils.getServerTime());
171177
self._dataChanged(data);
172178
resolve(data);
173179
} else {

src/firestore-query.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ MockFirestoreQuery.prototype._results = function () {
268268
if (this.orderedProperties.length === 0) {
269269
_.forEach(this.data, function(data, key) {
270270
if (inRange(data, key) && (self.limited <= 0 || limit < self.limited)) {
271-
results[key] = _.cloneDeep(data);
271+
results[key] = _.cloneDeepWith(data, utils.cloneCustomizer);
272272
limit++;
273273
}
274274
});
@@ -285,7 +285,7 @@ MockFirestoreQuery.prototype._results = function () {
285285
queryable = _.orderBy(queryable, orderBy, self.orderedDirections);
286286
queryable.forEach(function(q) {
287287
if (inRange(q.data, q.key) && (self.limited <= 0 || limit < self.limited)) {
288-
results[q.key] = _.cloneDeep(q.data);
288+
results[q.key] = _.cloneDeepWith(q.data, utils.cloneCustomizer);
289289
limit++;
290290
}
291291
});

src/lodash.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module.exports = {
22
assign: require('lodash.assign'),
33
bind: require('lodash.bind'),
44
clone: require('lodash.clone'),
5+
cloneWith: require('lodash.clonewith'),
56
cloneDeep: require('lodash.clonedeep'),
67
cloneDeepWith: require('lodash.clonedeepwith'),
78
compact: require('lodash.compact'),
@@ -22,6 +23,7 @@ module.exports = {
2223
isEqual: require('lodash.isequal'),
2324
isFunction: require('lodash.isfunction'),
2425
isNumber: require('lodash.isnumber'),
26+
isPlainObject: require('lodash.isplainobject'),
2527
isObject: require('lodash.isobject'),
2628
isString: require('lodash.isstring'),
2729
isUndefined: require('lodash.isundefined'),

src/sdk.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var MockFieldPath = require('./firestore-field-path');
55
var MockFieldValue = require('./firestore-field-value');
66
var MockMessaging = require('./messaging');
77
var MockStorage = require('./storage');
8+
var MockTimestamp = require('./timestamp');
89

910
var EmailAuthProvider = function() {
1011
this.providerId = EmailAuthProvider.PROVIDER_ID;
@@ -79,6 +80,7 @@ function MockFirebaseSdk(createDatabase, createAuth, createFirestore, createStor
7980
}
8081
MockFirebaseFirestore.FieldPath = MockFieldPath;
8182
MockFirebaseFirestore.FieldValue = MockFieldValue;
83+
MockFirebaseFirestore.Timestamp = MockTimestamp;
8284

8385
function MockFirebaseStorage() {
8486
return createStorage ? createStorage() : new MockStorage();

src/timestamp.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
function Timestamp(seconds, nanoseconds) {
4+
this.seconds = seconds;
5+
this.nanoseconds = nanoseconds;
6+
}
7+
8+
Timestamp.fromDate = function(date) {
9+
return Timestamp.fromMillis(date.getTime());
10+
};
11+
12+
Timestamp.fromMillis = function(ms) {
13+
var sec = Math.floor(ms / 1000);
14+
var ns = (ms % 1000) * 1000 * 1000;
15+
return new Timestamp(sec, ns);
16+
};
17+
18+
Timestamp.prototype.toDate = function () {
19+
var millis = this.seconds * 1000 + this.nanoseconds / (1000 * 1000);
20+
return new Date(millis);
21+
};
22+
23+
module.exports = Timestamp;

src/utils.js

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
var Snapshot = require('./snapshot');
4+
var Timestamp = require('./timestamp');
45
var FieldValue = require('./firestore-field-value');
56
var _ = require('./lodash');
67

@@ -81,6 +82,24 @@ exports.priorityComparator = function priorityComparator(a, b) {
8182
return 0;
8283
};
8384

85+
var serverClock, defaultClock;
86+
87+
serverClock = defaultClock = function () {
88+
return new Date().getTime();
89+
};
90+
91+
exports.getServerTime = function getServerTime() {
92+
return serverClock();
93+
};
94+
95+
exports.setServerClock = function setServerTime(fn) {
96+
serverClock = fn;
97+
};
98+
99+
exports.restoreServerClock = function restoreServerTime() {
100+
serverClock = defaultClock;
101+
};
102+
84103
exports.isServerTimestamp = function isServerTimestamp(data) {
85104
return _.isObject(data) && data['.sv'] === 'timestamp';
86105
};
@@ -114,12 +133,10 @@ exports.removeEmptyRtdbProperties = function removeEmptyRtdbProperties(obj) {
114133
}
115134
};
116135

117-
exports.removeEmptyFirestoreProperties = function removeEmptyFirestoreProperties(obj, current) {
118-
var t = typeof obj;
119-
if (t === 'boolean' || t === 'string' || t === 'number' || t === 'undefined') {
136+
exports.removeEmptyFirestoreProperties = function removeEmptyFirestoreProperties(obj, current, serverTime) {
137+
if (!_.isPlainObject(obj)) {
120138
return obj;
121139
}
122-
if (obj instanceof Date) return obj;
123140

124141
var keys = getKeys(obj);
125142

@@ -131,13 +148,13 @@ exports.removeEmptyFirestoreProperties = function removeEmptyFirestoreProperties
131148

132149
if (keys.length > 0) {
133150
for (var s in obj) {
134-
135-
var value = removeEmptyFirestoreProperties(obj[s]);
151+
var value = removeEmptyFirestoreProperties(obj[s], serverTime);
136152
if (FieldValue.delete().isEqual(value)) {
137153
delete obj[s];
138-
}
139-
if (FieldValue.serverTimestamp().isEqual(value)) {
140-
obj[s] = new Date();
154+
} else if (FieldValue.serverTimestamp().isEqual(value)) {
155+
obj[s] = new Date(serverTime);
156+
} else if (value instanceof Timestamp) {
157+
obj[s] = value.toDate();
141158
}
142159
if (FieldValue.arrayRemove().isEqual(value)) {
143160
const replacement = Array.isArray(value.arg) ? value.arg : [value.arg];
@@ -228,3 +245,9 @@ exports.createThenableReference = function(reference, promise) {
228245
};
229246
return reference;
230247
};
248+
249+
exports.cloneCustomizer = function(value) {
250+
if (value instanceof Date) {
251+
return Timestamp.fromMillis(value.getTime());
252+
}
253+
};

src/write-result.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
function WriteResult(writeTime) {
4+
this.writeTime = writeTime;
5+
}
6+
7+
module.exports = WriteResult;

0 commit comments

Comments
 (0)