Skip to content

Commit 599e429

Browse files
Ace Nassrijmdobry
Ace Nassri
authored andcommitted
Add Spanner + Cloud Functions sample (#371)
1 parent d589d78 commit 599e429

File tree

3 files changed

+182
-0
lines changed

3 files changed

+182
-0
lines changed

functions/spanner/index.js

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright 2017, Google, Inc.
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
'use strict';
17+
18+
// [START spanner_functions_quickstart]
19+
// Imports the Google Cloud client library
20+
const Spanner = require('@google-cloud/spanner');
21+
22+
// Instantiates a client
23+
const spanner = Spanner();
24+
25+
// Your Cloud Spanner instance ID
26+
const instanceId = 'my-instance';
27+
28+
// Your Cloud Spanner database ID
29+
const databaseId = 'my-database';
30+
31+
/**
32+
* HTTP Cloud Function.
33+
*
34+
* @param {Object} req Cloud Function request context.
35+
* @param {Object} res Cloud Function response context.
36+
*/
37+
exports.get = (req, res) => {
38+
// Gets a reference to a Cloud Spanner instance and database
39+
const instance = spanner.instance(instanceId);
40+
const database = instance.database(databaseId);
41+
42+
// The query to execute
43+
const query = {
44+
sql: 'SELECT * FROM Albums'
45+
};
46+
47+
// Execute the query
48+
return database.run(query)
49+
.then((results) => {
50+
const rows = results[0].map((row) => row.toJSON());
51+
rows.forEach((row) => {
52+
res.write(`SingerId: ${row.SingerId.value}, AlbumId: ${row.AlbumId.value}, AlbumTitle: ${row.AlbumTitle}\n`);
53+
});
54+
res
55+
.status(200)
56+
.end();
57+
})
58+
.catch((err) => {
59+
res
60+
.status(500)
61+
.send(`Error querying Spanner: ${err}`)
62+
.end();
63+
});
64+
};
65+
// [END spanner_functions_quickstart]

functions/spanner/package.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "nodejs-docs-samples-functions-spanner",
3+
"version": "0.0.1",
4+
"private": true,
5+
"license": "Apache-2.0",
6+
"author": "Google Inc.",
7+
"repository": {
8+
"type": "git",
9+
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
10+
},
11+
"engines": {
12+
"node": ">=4.3.2"
13+
},
14+
"scripts": {
15+
"lint": "samples lint",
16+
"pretest": "npm run lint",
17+
"test": "ava -T 20s --verbose test/*.test.js"
18+
},
19+
"dependencies": {
20+
"@google-cloud/spanner": "0.4.2"
21+
},
22+
"devDependencies": {
23+
"@google-cloud/nodejs-repo-tools": "1.4.7",
24+
"ava": "0.19.1",
25+
"proxyquire": "1.7.11",
26+
"sinon": "2.1.0"
27+
},
28+
"cloud-repo-tools": {
29+
"requiresKeyFile": true,
30+
"requiresProjectId": true
31+
}
32+
}

functions/spanner/test/index.test.js

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Copyright 2017, Google, Inc.
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
'use strict';
17+
18+
const proxyquire = require(`proxyquire`).noCallThru();
19+
const sinon = require(`sinon`);
20+
const test = require(`ava`);
21+
22+
const entities = [
23+
{
24+
SingerId: { value: 1 },
25+
AlbumId: { value: 1 },
26+
AlbumTitle: 'Go, Go, Go'
27+
},
28+
{
29+
SingerId: { value: 1 },
30+
AlbumId: { value: 2 },
31+
AlbumTitle: 'Total Junk'
32+
}
33+
];
34+
35+
const query = {
36+
sql: 'SELECT * FROM Albums'
37+
};
38+
39+
function getSample () {
40+
const resultsMock = entities.map((row) => {
41+
return { toJSON: sinon.stub().returns(row) };
42+
});
43+
const databaseMock = {
44+
run: sinon.stub().returns(Promise.resolve([resultsMock]))
45+
};
46+
const instanceMock = {
47+
database: sinon.stub().returns(databaseMock)
48+
};
49+
const spannerMock = {
50+
instance: sinon.stub().returns(instanceMock)
51+
};
52+
53+
const SpannerMock = sinon.stub().returns(spannerMock);
54+
55+
return {
56+
program: proxyquire(`../`, {
57+
'@google-cloud/spanner': SpannerMock
58+
}),
59+
mocks: {
60+
spanner: spannerMock,
61+
database: databaseMock,
62+
instance: instanceMock,
63+
results: resultsMock,
64+
res: {
65+
status: sinon.stub().returnsThis(),
66+
send: sinon.stub().returnsThis(),
67+
end: sinon.stub().returnsThis(),
68+
write: sinon.stub().returnsThis()
69+
}
70+
}
71+
};
72+
}
73+
74+
test(`get: Gets albums`, async (t) => {
75+
const sample = getSample();
76+
const mocks = sample.mocks;
77+
78+
await sample.program.get(mocks.req, mocks.res);
79+
t.true(mocks.spanner.instance.called);
80+
t.true(mocks.instance.database.called);
81+
t.true(mocks.database.run.calledWith(query));
82+
t.true(mocks.results[0].toJSON.called);
83+
t.true(mocks.res.write.calledWith(`SingerId: 1, AlbumId: 2, AlbumTitle: Total Junk\n`));
84+
t.true(mocks.res.end.called);
85+
});

0 commit comments

Comments
 (0)