diff --git a/functions/spanner/index.js b/functions/spanner/index.js new file mode 100644 index 0000000000..ae708d1599 --- /dev/null +++ b/functions/spanner/index.js @@ -0,0 +1,65 @@ +/** + * Copyright 2017, Google, Inc. + * 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'; + +// [START spanner_functions_quickstart] +// Imports the Google Cloud client library +const Spanner = require('@google-cloud/spanner'); + +// Instantiates a client +const spanner = Spanner(); + +// Your Cloud Spanner instance ID +const instanceId = 'my-instance'; + +// Your Cloud Spanner database ID +const databaseId = 'my-database'; + +/** + * HTTP Cloud Function. + * + * @param {Object} req Cloud Function request context. + * @param {Object} res Cloud Function response context. + */ +exports.get = (req, res) => { + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // The query to execute + const query = { + sql: 'SELECT * FROM Albums' + }; + + // Execute the query + return database.run(query) + .then((results) => { + const rows = results[0].map((row) => row.toJSON()); + rows.forEach((row) => { + res.write(`SingerId: ${row.SingerId.value}, AlbumId: ${row.AlbumId.value}, AlbumTitle: ${row.AlbumTitle}\n`); + }); + res + .status(200) + .end(); + }) + .catch((err) => { + res + .status(500) + .send(`Error querying Spanner: ${err}`) + .end(); + }); +}; +// [END spanner_functions_quickstart] diff --git a/functions/spanner/package.json b/functions/spanner/package.json new file mode 100644 index 0000000000..587f035ed7 --- /dev/null +++ b/functions/spanner/package.json @@ -0,0 +1,32 @@ +{ + "name": "nodejs-docs-samples-functions-spanner", + "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": ">=4.3.2" + }, + "scripts": { + "lint": "samples lint", + "pretest": "npm run lint", + "test": "ava -T 20s --verbose test/*.test.js" + }, + "dependencies": { + "@google-cloud/spanner": "0.4.2" + }, + "devDependencies": { + "@google-cloud/nodejs-repo-tools": "1.4.7", + "ava": "0.19.1", + "proxyquire": "1.7.11", + "sinon": "2.1.0" + }, + "cloud-repo-tools": { + "requiresKeyFile": true, + "requiresProjectId": true + } +} diff --git a/functions/spanner/test/index.test.js b/functions/spanner/test/index.test.js new file mode 100644 index 0000000000..46892cbe3a --- /dev/null +++ b/functions/spanner/test/index.test.js @@ -0,0 +1,85 @@ +/** + * Copyright 2017, Google, Inc. + * 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 test = require(`ava`); + +const entities = [ + { + SingerId: { value: 1 }, + AlbumId: { value: 1 }, + AlbumTitle: 'Go, Go, Go' + }, + { + SingerId: { value: 1 }, + AlbumId: { value: 2 }, + AlbumTitle: 'Total Junk' + } +]; + +const query = { + sql: 'SELECT * FROM Albums' +}; + +function getSample () { + const resultsMock = entities.map((row) => { + return { toJSON: sinon.stub().returns(row) }; + }); + const databaseMock = { + run: sinon.stub().returns(Promise.resolve([resultsMock])) + }; + const instanceMock = { + database: sinon.stub().returns(databaseMock) + }; + const spannerMock = { + instance: sinon.stub().returns(instanceMock) + }; + + const SpannerMock = sinon.stub().returns(spannerMock); + + return { + program: proxyquire(`../`, { + '@google-cloud/spanner': SpannerMock + }), + mocks: { + spanner: spannerMock, + database: databaseMock, + instance: instanceMock, + results: resultsMock, + res: { + status: sinon.stub().returnsThis(), + send: sinon.stub().returnsThis(), + end: sinon.stub().returnsThis(), + write: sinon.stub().returnsThis() + } + } + }; +} + +test(`get: Gets albums`, async (t) => { + const sample = getSample(); + const mocks = sample.mocks; + + await sample.program.get(mocks.req, mocks.res); + t.true(mocks.spanner.instance.called); + t.true(mocks.instance.database.called); + t.true(mocks.database.run.calledWith(query)); + t.true(mocks.results[0].toJSON.called); + t.true(mocks.res.write.calledWith(`SingerId: 1, AlbumId: 2, AlbumTitle: Total Junk\n`)); + t.true(mocks.res.end.called); +});