From 6b3d1db51918aa20c8c5cf51becff12a83fbb039 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Mon, 5 Nov 2018 11:48:08 -0800 Subject: [PATCH] Add Node 8 Firestore reactive sample (#798) --- functions/node8/index.js | 22 ++++++++++ functions/node8/package.json | 5 +++ functions/node8/test/index.test.js | 64 +++++++++++++++++++++++++++--- 3 files changed, 86 insertions(+), 5 deletions(-) diff --git a/functions/node8/index.js b/functions/node8/index.js index 58f41f14c7..cf76b9ed5a 100644 --- a/functions/node8/index.js +++ b/functions/node8/index.js @@ -207,3 +207,25 @@ exports.helloAnalytics = (data, context) => { console.log(`Location: ${userObj.geoInfo.city}, ${userObj.geoInfo.country}`); }; // [END functions_firebase_analytics_node8] + +// [START functions_firebase_reactive_node8] +const Firestore = require('@google-cloud/firestore'); + +const firestore = new Firestore({ + projectId: process.env.GCP_PROJECT +}); + +// Converts strings added to /messages/{pushId}/original to uppercase +exports.makeUpperCase = (data, context) => { + const resource = context.resource; + const affectedDoc = firestore.doc(resource.split('/documents/')[1]); + + const curValue = data.value.fields.original.stringValue; + const newValue = curValue.toUpperCase(); + console.log(`Replacing value: ${curValue} --> ${newValue}`); + + return affectedDoc.set({ + 'original': newValue + }); +}; +// [END functions_firebase_reactive_node8] diff --git a/functions/node8/package.json b/functions/node8/package.json index d9e5ffaa4e..071c641294 100644 --- a/functions/node8/package.json +++ b/functions/node8/package.json @@ -21,7 +21,12 @@ "devDependencies": { "@google-cloud/nodejs-repo-tools": "^2.3.3", "ava": "^0.25.0", + "proxyquire": "^2.1.0", "semistandard": "^12.0.1", + "sinon": "^7.0.0", "uuid": "^3.3.2" + }, + "dependencies": { + "@google-cloud/firestore": "^0.18.0" } } diff --git a/functions/node8/test/index.test.js b/functions/node8/test/index.test.js index c7e3d40d90..37f3ad75fc 100644 --- a/functions/node8/test/index.test.js +++ b/functions/node8/test/index.test.js @@ -15,16 +15,34 @@ 'use strict'; +const sinon = require('sinon'); const uuid = require('uuid'); const test = require('ava'); const utils = require('@google-cloud/nodejs-repo-tools'); +const proxyquire = require(`proxyquire`).noCallThru(); -const program = require('../'); +function getSample () { + const firestoreMock = { + doc: sinon.stub().returnsThis(), + set: sinon.stub() + }; + + return { + program: proxyquire(`../`, { + '@google-cloud/firestore': sinon.stub().returns(firestoreMock) + }), + mocks: { + firestore: firestoreMock + } + }; +} test.beforeEach(utils.stubConsole); test.afterEach.always(utils.restoreConsole); test.serial('should monitor Firebase RTDB', t => { + const sample = getSample(); + const dataId = uuid.v4(); const resourceId = uuid.v4(); @@ -38,7 +56,7 @@ test.serial('should monitor Firebase RTDB', t => { resource: resourceId }; - program.helloRTDB(data, context); + sample.program.helloRTDB(data, context); t.true(console.log.firstCall.args[0].includes(resourceId)); t.deepEqual(console.log.secondCall.args, ['Admin?: true']); @@ -46,6 +64,8 @@ test.serial('should monitor Firebase RTDB', t => { }); test.serial('should monitor Firestore', t => { + const sample = getSample(); + const resourceId = uuid.v4(); const context = { @@ -56,7 +76,7 @@ test.serial('should monitor Firestore', t => { value: { uuid: uuid.v4() } }; - program.helloFirestore(data, context); + sample.program.helloFirestore(data, context); t.true(console.log.firstCall.args[0].includes(resourceId)); t.true(console.log.calledWith(JSON.stringify(data.oldValue, null, 2))); @@ -64,6 +84,8 @@ test.serial('should monitor Firestore', t => { }); test.serial('should monitor Auth', t => { + const sample = getSample(); + const userId = uuid.v4(); const dateString = (new Date()).toISOString(); const emailString = `${uuid.v4()}@${uuid.v4()}.com`; @@ -76,7 +98,7 @@ test.serial('should monitor Auth', t => { email: emailString }; - program.helloAuth(data, null); + sample.program.helloAuth(data, null); t.true(console.log.firstCall.args[0].includes(userId)); t.true(console.log.secondCall.args[0].includes(dateString)); @@ -84,6 +106,8 @@ test.serial('should monitor Auth', t => { }); test.serial('should monitor Analytics', t => { + const sample = getSample(); + const date = new Date(); const data = { eventDim: [{ @@ -105,10 +129,40 @@ test.serial('should monitor Analytics', t => { resource: 'my-resource' }; - program.helloAnalytics(data, context); + sample.program.helloAnalytics(data, context); + t.is(console.log.args[0][0], `Function triggered by the following event: my-resource`); t.is(console.log.args[1][0], `Name: my-event`); t.is(console.log.args[2][0], `Timestamp: ${date}`); t.is(console.log.args[3][0], `Device Model: Pixel`); t.is(console.log.args[4][0], `Location: London, UK`); }); + +test(`should update data in response to Firestore events`, t => { + const sample = getSample(); + + const date = Date.now(); + const data = { + email: 'me@example.com', + metadata: { + createdAt: date + }, + value: { + fields: { + original: { + stringValue: 'foobar' + } + } + } + }; + + const context = { + resource: '/documents/some/path' + }; + + sample.program.makeUpperCase(data, context); + + t.true(sample.mocks.firestore.doc.calledWith('some/path')); + t.true(console.log.calledWith(`Replacing value: foobar --> FOOBAR`)); + t.true(sample.mocks.firestore.set.calledWith({'original': 'FOOBAR'})); +});