From 9cb1a304a447585a6355bc0f2a2fcd38c459fd1b Mon Sep 17 00:00:00 2001 From: Billy Jacobson Date: Wed, 22 Apr 2020 12:44:51 -0400 Subject: [PATCH 1/6] Working function! --- functions/README.md | 1 + functions/bigtable/index.js | 67 +++++++++++++++++ functions/bigtable/package.json | 31 ++++++++ functions/bigtable/test/index.test.js | 101 ++++++++++++++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 functions/bigtable/index.js create mode 100644 functions/bigtable/package.json create mode 100644 functions/bigtable/test/index.test.js diff --git a/functions/README.md b/functions/README.md index 548d87d3a2..26943129f8 100644 --- a/functions/README.md +++ b/functions/README.md @@ -22,6 +22,7 @@ environment. * [Hello World](helloworld/) * [Background](background/) * [Callbacks](messages/) +* [Cloud Bigtable](bigtable/) * [Cloud Pub/Sub](pubsub/) * [Cloud Spanner](spanner/) * [Dependencies](uuid/) diff --git a/functions/bigtable/index.js b/functions/bigtable/index.js new file mode 100644 index 0000000000..6352902669 --- /dev/null +++ b/functions/bigtable/index.js @@ -0,0 +1,67 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// Imports the Google Cloud client library +const {Bigtable} = require('@google-cloud/bigtable'); + +// Instantiates a client +const bigtable = new Bigtable(); + +// Your Cloud Bigtable instance ID +const instanceId = 'testing-instance'; + +// Your Cloud Bigtable table ID +const tableId = 'mobile-time-series'; + +/** + * HTTP Cloud Function. + * + * @param {Object} req Cloud Function request context. + * @param {Object} res Cloud Function response context. + */ +exports.get = async (req, res) => { + // Gets a reference to a Cloud Bigtable instance and database + const instance = bigtable.instance(instanceId); + const table = instance.table(tableId); + + // Execute the query + try { + const prefix = 'phone#'; + let rows = []; + + await table + .createReadStream({ + prefix, + }) + .on('error', err => { + res.send(`Error querying Bigtable: ${err}`); + res.status(500).end(); + }) + .on('data', row => { + rows.push( + `rowkey: ${row.id}, ` + + `os_build: ${row.data["stats_summary"]["os_build"][0].value}\n` + ) + }).on('end', () => { + rows.forEach(r => res.write(r)); + res.status(200).end(); + }); + } catch (err) { + res.send(`Error querying Bigtable: ${err}`); + res.status(500).end(); + } + +}; \ No newline at end of file diff --git a/functions/bigtable/package.json b/functions/bigtable/package.json new file mode 100644 index 0000000000..315164f4e3 --- /dev/null +++ b/functions/bigtable/package.json @@ -0,0 +1,31 @@ +{ + "name": "nodejs-docs-samples-functions-bigtable", + "version": "0.0.1", + "private": true, + "license": "Apache-2.0", + "author": "Google Inc.", + "repository": { + "type": "git", + "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" + }, + "engines": { + "node": ">=8.0.0" + }, + "scripts": { + "test": "mocha test/*.test.js --timeout=20000", + "start": "functions-framework --target=get" + }, + "dependencies": { + "@google-cloud/bigtable": "^2.3.2", + "@google-cloud/functions-framework": "^1.5.1" + }, + "devDependencies": { + "mocha": "^7.0.0", + "proxyquire": "^2.1.0", + "sinon": "^9.0.0" + }, + "cloud-repo-tools": { + "requiresKeyFile": true, + "requiresProjectId": true + } +} diff --git a/functions/bigtable/test/index.test.js b/functions/bigtable/test/index.test.js new file mode 100644 index 0000000000..b9432b8563 --- /dev/null +++ b/functions/bigtable/test/index.test.js @@ -0,0 +1,101 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const proxyquire = require('proxyquire').noCallThru(); +const sinon = require('sinon'); +const assert = require('assert'); + +const rows = [ + { + id: "phone#4c410523#20190501", + data: { + stats_summary: { + os_build: [{ + value: "PQ2A.190405.003" + }] + } + }, + }, + { + id: "phone#5c10102#20190501", + data: { + stats_summary: { + os_build: [{ + value: "PQ2A.190406.000" + }] + } + }, + }, +]; + +const query = { + prefix: "phone#" +}; + +const getSample = () => { + const resultsMock = rows.map((row) => { + return {toJSON: sinon.stub().returns(row)}; + }); + const tableMock = { + createReadStream: sinon.stub().returns(Promise.resolve([resultsMock])), + }; + const instanceMock = { + table: sinon.stub().returns(tableMock), + }; + const bigtableMock = { + instance: sinon.stub().returns(instanceMock), + }; + + const BigtableMock = sinon.stub().returns(bigtableMock); + + return { + program: proxyquire('../', { + '@google-cloud/bigtable': {Bigtable: BigtableMock}, + }), + mocks: { + bigtable: bigtableMock, + table: tableMock, + instance: instanceMock, + results: resultsMock, + res: { + status: sinon.stub().returnsThis(), + send: sinon.stub().returnsThis(), + end: sinon.stub().returnsThis(), + write: sinon.stub().returnsThis(), + }, + }, + }; +}; + +describe('bigtable_functions_quickstart', () => { + it('get: Gets rows', async () => { + const sample = getSample(); + const {mocks} = sample; + + await sample.program.get(mocks.req, mocks.res); + assert.strictEqual(mocks.bigtable.instance.called, true); + assert.strictEqual(mocks.instance.table.called, true); + assert.strictEqual(mocks.table.createReadStream.calledWith(query), true); + assert.strictEqual(mocks.results[0].toJSON.called, true); + assert.strictEqual( + mocks.res.write.calledWith( + 'SingerId: 1, AlbumId: 2, AlbumTitle: Total Junk\n' + ), + true + ); + assert.strictEqual(mocks.res.end.called, true); + }); +}); From 5c87ab3e1e617e5df97164432d5a662a13c0d285 Mon Sep 17 00:00:00 2001 From: Billy Jacobson Date: Wed, 22 Apr 2020 14:33:23 -0400 Subject: [PATCH 2/6] mocked stream --- functions/bigtable/test/index.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/functions/bigtable/test/index.test.js b/functions/bigtable/test/index.test.js index b9432b8563..7c3326f3ee 100644 --- a/functions/bigtable/test/index.test.js +++ b/functions/bigtable/test/index.test.js @@ -17,6 +17,7 @@ const proxyquire = require('proxyquire').noCallThru(); const sinon = require('sinon'); const assert = require('assert'); +const {PassThrough} = require('stream'); const rows = [ { @@ -46,11 +47,10 @@ const query = { }; const getSample = () => { - const resultsMock = rows.map((row) => { - return {toJSON: sinon.stub().returns(row)}; - }); + const mockedStream = require('stream').Readable.from(rows); + const tableMock = { - createReadStream: sinon.stub().returns(Promise.resolve([resultsMock])), + createReadStream: sinon.stub().returns(mockedStream), }; const instanceMock = { table: sinon.stub().returns(tableMock), @@ -69,7 +69,6 @@ const getSample = () => { bigtable: bigtableMock, table: tableMock, instance: instanceMock, - results: resultsMock, res: { status: sinon.stub().returnsThis(), send: sinon.stub().returnsThis(), @@ -89,7 +88,8 @@ describe('bigtable_functions_quickstart', () => { assert.strictEqual(mocks.bigtable.instance.called, true); assert.strictEqual(mocks.instance.table.called, true); assert.strictEqual(mocks.table.createReadStream.calledWith(query), true); - assert.strictEqual(mocks.results[0].toJSON.called, true); + // assert.strictEqual(mocks.results[0].toJSON.called, true); + console.log(mocks.res.write.getCalls()) assert.strictEqual( mocks.res.write.calledWith( 'SingerId: 1, AlbumId: 2, AlbumTitle: Total Junk\n' From f4f1a50ea0674d32cda8c247b487ca68f1f4f7d3 Mon Sep 17 00:00:00 2001 From: Billy Jacobson Date: Wed, 22 Apr 2020 21:32:31 -0400 Subject: [PATCH 3/6] working tests --- functions/bigtable/index.js | 1 - functions/bigtable/test/index.test.js | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/functions/bigtable/index.js b/functions/bigtable/index.js index 6352902669..3c6aaecb6e 100644 --- a/functions/bigtable/index.js +++ b/functions/bigtable/index.js @@ -41,7 +41,6 @@ exports.get = async (req, res) => { try { const prefix = 'phone#'; let rows = []; - await table .createReadStream({ prefix, diff --git a/functions/bigtable/test/index.test.js b/functions/bigtable/test/index.test.js index 7c3326f3ee..bf96645634 100644 --- a/functions/bigtable/test/index.test.js +++ b/functions/bigtable/test/index.test.js @@ -19,6 +19,7 @@ const sinon = require('sinon'); const assert = require('assert'); const {PassThrough} = require('stream'); +let mockedStream; const rows = [ { id: "phone#4c410523#20190501", @@ -47,7 +48,7 @@ const query = { }; const getSample = () => { - const mockedStream = require('stream').Readable.from(rows); + mockedStream = require('stream').Readable.from(rows); const tableMock = { createReadStream: sinon.stub().returns(mockedStream), @@ -83,16 +84,23 @@ describe('bigtable_functions_quickstart', () => { it('get: Gets rows', async () => { const sample = getSample(); const {mocks} = sample; - await sample.program.get(mocks.req, mocks.res); + rows.forEach((row) => { + mockedStream.emit('data', row); + }); + mockedStream.emit("end"); assert.strictEqual(mocks.bigtable.instance.called, true); assert.strictEqual(mocks.instance.table.called, true); assert.strictEqual(mocks.table.createReadStream.calledWith(query), true); - // assert.strictEqual(mocks.results[0].toJSON.called, true); - console.log(mocks.res.write.getCalls()) assert.strictEqual( mocks.res.write.calledWith( - 'SingerId: 1, AlbumId: 2, AlbumTitle: Total Junk\n' + 'rowkey: phone#4c410523#20190501, os_build: PQ2A.190405.003\n' + ), + true + ); + assert.strictEqual( + mocks.res.write.calledWith( + 'rowkey: phone#5c10102#20190501, os_build: PQ2A.190406.000\n' ), true ); From fc3069bef78aa541cf068403ccc63c5e856db11d Mon Sep 17 00:00:00 2001 From: Billy Jacobson Date: Thu, 23 Apr 2020 11:02:53 -0400 Subject: [PATCH 4/6] add CODEOWNERS --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index a7b461f303..260b6cf92a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -15,6 +15,7 @@ functions @ace-n @grant run @GoogleCloudPlatform/cdpe-cbr-containers # Other functions samples +functions/bigtable @billyjacobson functions/composer-storage-trigger @leahecole functions/scheduleinstance @djmailhot functions/speech-to-speech @ricalo From 4a3093c8c8876785c2983bb48c916ab7e01b76ad Mon Sep 17 00:00:00 2001 From: Billy Jacobson Date: Thu, 23 Apr 2020 11:56:39 -0400 Subject: [PATCH 5/6] change sample instance id --- functions/bigtable/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/bigtable/index.js b/functions/bigtable/index.js index 3c6aaecb6e..c8ba4887ef 100644 --- a/functions/bigtable/index.js +++ b/functions/bigtable/index.js @@ -21,7 +21,7 @@ const {Bigtable} = require('@google-cloud/bigtable'); const bigtable = new Bigtable(); // Your Cloud Bigtable instance ID -const instanceId = 'testing-instance'; +const instanceId = 'your-instance'; // Your Cloud Bigtable table ID const tableId = 'mobile-time-series'; From d2c7277c3feef4757cdcfe7ef86fd3efa5a51487 Mon Sep 17 00:00:00 2001 From: Billy Jacobson Date: Thu, 23 Apr 2020 11:58:11 -0400 Subject: [PATCH 6/6] add region tags --- functions/bigtable/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/functions/bigtable/index.js b/functions/bigtable/index.js index c8ba4887ef..ef71a14363 100644 --- a/functions/bigtable/index.js +++ b/functions/bigtable/index.js @@ -14,6 +14,7 @@ 'use strict'; +// [START bigtable_functions_quickstart] // Imports the Google Cloud client library const {Bigtable} = require('@google-cloud/bigtable'); @@ -62,5 +63,5 @@ exports.get = async (req, res) => { res.send(`Error querying Bigtable: ${err}`); res.status(500).end(); } - -}; \ No newline at end of file +}; +// [END bigtable_functions_quickstart] \ No newline at end of file