From 9169fae692d219b5fb42004a4eb82e5a5919f087 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Tue, 14 Jan 2020 09:36:54 -0800 Subject: [PATCH] feat: support serialization of Moment.js (#879) --- dev/src/serializer.ts | 29 ++++++++++++++++++++++++++++- dev/test/document.ts | 31 +++++++++++++++++++++++++++++++ package.json | 3 ++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/dev/src/serializer.ts b/dev/src/serializer.ts index 1b25b12bd..db6b3de30 100644 --- a/dev/src/serializer.ts +++ b/dev/src/serializer.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import {Moment} from 'moment'; + import * as proto from '../protos/firestore_v1_proto_api'; import {detectValueType} from './convert'; @@ -119,7 +121,17 @@ export class Serializer { } if (val instanceof Date) { - const timestamp = Timestamp.fromDate(val as Date); + const timestamp = Timestamp.fromDate(val); + return { + timestampValue: { + seconds: timestamp.seconds, + nanos: timestamp.nanoseconds, + }, + }; + } + + if (isMomentJsType(val)) { + const timestamp = Timestamp.fromDate(val.toDate()); return { timestampValue: { seconds: timestamp.seconds, @@ -369,6 +381,8 @@ export function validateUserInput( // Ok. } else if (value instanceof Timestamp || value instanceof Date) { // Ok. + } else if (isMomentJsType(value)) { + // Ok. } else if (value instanceof Buffer || value instanceof Uint8Array) { // Ok. } else if (value === null) { @@ -377,3 +391,16 @@ export function validateUserInput( throw new Error(customObjectMessage(arg, value, path)); } } + +/** + * Returns true if value is a MomentJs date object. + * @private + */ +function isMomentJsType(value: unknown): value is Moment { + return ( + typeof value === 'object' && + value !== null && + value.constructor.name === 'Moment' && + typeof (value as Moment).toDate === 'function' + ); +} diff --git a/dev/test/document.ts b/dev/test/document.ts index 43c764a4d..8d9a8594b 100644 --- a/dev/test/document.ts +++ b/dev/test/document.ts @@ -244,6 +244,37 @@ describe('serialize document', () => { }); }); + it('supports Moment.js', () => { + class Moment { + toDate(): Date { + return new Date('Jul 20 1969 20:18:00.123 UTC'); + } + } + + const overrides: ApiOverride = { + commit: request => { + requestEquals( + request, + set({ + document: document('documentId', 'moonLanding', { + timestampValue: { + nanos: 123000000, + seconds: -14182920, + }, + }), + }) + ); + return response(writeResult(1)); + }, + }; + + return createInstance(overrides).then(firestore => { + return firestore.doc('collectionId/documentId').set({ + moonLanding: new Moment(), + }); + }); + }); + it('serializes unicode keys', () => { const overrides: ApiOverride = { commit: request => { diff --git a/package.json b/package.json index 6e0c8d7a2..d74da7697 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,10 @@ "@types/duplexify": "^3.5.0", "@types/extend": "^3.0.0", "@types/mocha": "^5.2.3", + "@types/moment": "^2.13.0", "@types/node": "^12.12.17", "@types/through2": "^2.0.34", + "c8": "^7.0.0", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "codecov": "^3.6.1", @@ -76,7 +78,6 @@ "jsdoc-region-tag": "^1.0.2", "linkinator": "^1.8.0", "mocha": "^7.0.0", - "c8": "^7.0.0", "power-assert": "^1.6.1", "protobufjs": "^6.8.6", "proxyquire": "^2.1.3",