From 7788aa9bfbb4397e2f89147c157d04351dcf2e26 Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Fri, 10 Nov 2017 17:44:33 -0800 Subject: [PATCH] Parse Firestore data values and allow param substitution --- lib/localFunction.js | 190 +++++++++++++++++-------------------------- 1 file changed, 74 insertions(+), 116 deletions(-) diff --git a/lib/localFunction.js b/lib/localFunction.js index 3150840086a..ca2e7385c6b 100644 --- a/lib/localFunction.js +++ b/lib/localFunction.js @@ -4,7 +4,10 @@ var _ = require('lodash'); var is = require('is'); var request = require('request'); +var instance; + var LocalFunction = function(trigger, urls, controller) { + instance = this; this.name = trigger.name; this.eventTrigger = trigger.eventTrigger; this.httpsTrigger = trigger.httpsTrigger; @@ -45,162 +48,89 @@ LocalFunction.prototype._requestCallBack = function(err, response, body) { return console.log('\nRESPONSE RECEIVED FROM FUNCTION: ' + status + body); }; -LocalFunction.prototype.encodeFirestoreValue = function(data) { - - var isPlainObject= function(input) { - return ( - typeof input === 'object' && +LocalFunction.prototype._encodeFirestoreValue = function(data) { + var isPlainObject = function(input) { + return typeof input === 'object' && input !== null && - Object.getPrototypeOf(input) === Object.prototype - ); + _.isEqual(Object.getPrototypeOf(input), Object.prototype); }; var encodeHelper = function(val) { - if (is.string(val)) { return { stringValue: val }; } - if (is.boolean(val)) { return { booleanValue: val }; } - if (is.integer(val)) { return { integerValue: val }; } - // Integers are handled above, the remaining numbers are treated as doubles if (is.number(val)) { return { doubleValue: val }; } - - // if (is.date(val)) { - // let epochSeconds = Math.floor(val.getTime() / 1000); - // let timestamp = { - // seconds: epochSeconds, - // nanos: (val.getTime() - epochSeconds * 1000) * 1000000, - // }; - // return { - // timestampValue: timestamp - // }; - // } - - // if (is.array(val)) { - // let encodedElements = []; - // for (let i = 0; i < val.length; ++i) { - // let enc = this.encodeValue(val[i]); - // if (enc) { - // encodedElements.push(enc); - // } - // } - // return { - // arrayValue: { - // values: encodedElements - // }, - // }; - // } - + if (is.date(val)) { + return { + timestampValue: val.toISOString() + }; + } + if (is.array(val)) { + var encodedElements = []; + for (var i = 0; i < val.length; ++i) { + var enc = encodeHelper(val[i]); + if (enc) { + encodedElements.push(enc); + } + } + return { + arrayValue: { + values: encodedElements + } + }; + } if (is.nil(val)) { return { nullValue: 'NULL_VALUE' }; } - - // if (is.instance(val, DocumentReference) || is.instance(val, ResourcePath)) { - // return { - // referenceValue: val.formattedName, - // }; - // } - - // if (is.instance(val, GeoPoint)) { - // return { - // valueType: 'geoPointValue', - // geoPointValue: val.toProto(), - // }; - // } - if (is.instanceof(val, Buffer) || is.instanceof(val, Uint8Array)) { return { - bytesValue: val, + bytesValue: val + }; + } + if (isPlainObject(val)) { + return { + mapValue: { + fields: instance._encodeFirestoreValue(val) + } }; } - - // if (isPlainObject(val)) { - // console.log(val + 'is a plain object') - // return { - // mapValue: { - // fields: this.encodeFields(val) - // }, - // }; - // } - throw new Error( - 'Cannot encode ' + val + 'to a Firestore Value' + 'Cannot encode ' + val + 'to a Firestore Value.' + + ' The emulator does not yet support Firestore document reference values or geo points.' ); }; return _.mapValues(data, encodeHelper); }; - -// LocalFunction.prototype._convertToFirestoreWireFormat = function(fields) { -// if (!fields) { -// return {}; -// } -// function convertHelper(data) { -// let result; -// _.forEach(data, (value, key) => { -// let dataPart; -// // var valueType = -// if (valueType === 'arrayValue') { -// let array = _.get(value, 'values', []); -// dataPart = { -// arrayValue: { -// values: _.map(array, (elem) => { -// return convertHelper(elem); -// }), -// }, -// }; -// } else if (valueType === 'mapValue') { -// let map = _.get(value, 'fields', {}); -// dataPart = { -// mapValue: { -// fields: _.mapValues(map, (val) => { -// return convertHelper(val); -// }), -// }, -// }; -// } else if (valueType === 'timestampValue') { -// dataPart = {timestampValue: dateToTimestampProto(value)}; -// } else { -// dataPart = data; -// } -// result = _.merge({}, dataPart, {valueType: valueType}); -// }); -// return result; -// } - -// return _.mapValues(fields, (data) => { -// return convertHelper(data); -// }); -// }; - LocalFunction.prototype._call = function(data, opts) { opts = opts || {}; + var operationType; + var dataPayload; if (this.httpsTrigger) { this.controller.call(this.name, data || {}); } else if (this.eventTrigger) { if (this._isDatabaseFunc(this.eventTrigger)) { - var operationType = _.last(this.eventTrigger.eventType.split('.')); - var dataPayload; + operationType = _.last(this.eventTrigger.eventType.split('.')); if (operationType === 'create') { dataPayload = { data: null, @@ -219,14 +149,42 @@ LocalFunction.prototype._call = function(data, opts) { } opts.resource = this._substituteParams(this.eventTrigger.resource, opts.params); this.controller.call(this.name, dataPayload, opts); - } else if (this._isFirestoreFunc(this.eventTrigger)){ - var dataPayload = { - value: { - "createTime": "2017-06-02T18:48:58.920638Z", - fields: this.encodeFirestoreValue(data), - "updateTime": "2017-06-16T04:50:48.293439Z" - } + } else if (this._isFirestoreFunc(this.eventTrigger)) { + operationType = _.last(this.eventTrigger.eventType.split('.')); + var currentTime = (new Date()).toISOString(); + if (operationType === 'create') { + dataPayload = { + value: { + fields: this._encodeFirestoreValue(data), + createTime: currentTime, + updateTime: currentTime + }, + oldValue: {} + }; + } else if (operationType === 'update' || operationType === 'write') { + dataPayload = { + value: { + fields: this._encodeFirestoreValue(data.after), + createTime: currentTime, + updateTime: currentTime + }, + oldValue: { + fields: this._encodeFirestoreValue(data.before), + createTime: currentTime, + updateTime: currentTime + } + }; + } else if (operationType === 'delete') { + dataPayload = { + value: {}, + oldValue: { + fields: this._encodeFirestoreValue(data), + createTime: currentTime, + updateTime: currentTime + } + }; } + opts.resource = this._substituteParams(this.eventTrigger.resource, opts.params); this.controller.call(this.name, dataPayload, opts); } else { this.controller.call(this.name, data || {}, opts);