Skip to content

Commit 1450634

Browse files
author
Ace Nassri
authored
Update Cloud SQL sample (Node 8 + system tests) (GoogleCloudPlatform#1329)
* Update cloudsql sample (Node 8 + system tests) * Remove extra dependency * Address comments + make npm test work
1 parent 216006f commit 1450634

11 files changed

Lines changed: 158 additions & 274 deletions

File tree

.kokoro/build.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ export TWILIO_NUMBER="+15005550006" # public placeholder value
2424
export TWILIO_ACCOUNT_SID=$(cat $KOKORO_GFILE_DIR/secrets-twilio-sid.txt)
2525
export TWILIO_AUTH_TOKEN=$(cat $KOKORO_GFILE_DIR/secrets-twilio-auth-token.txt)
2626

27+
# Configure Cloud SQL variables
28+
export DB_NAME="kokoro_ci"
29+
export DB_USER="kokoro_ci"
30+
export DB_PASS=$(cat $KOKORO_GFILE_DIR/secrets-sql-password.txt)
31+
if [[ $SQL_CLIENT == 'pg' ]]; then
32+
export CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-pg-connection-name.txt)
33+
else
34+
export CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-mysql-connection-name.txt)
35+
fi
36+
2737
# Configure Sendgrid variables
2838
export SENDGRID_SENDER="test@google.com"
2939
export SENDGRID_API_KEY=$(cat $KOKORO_GFILE_DIR/secrets-sendgrid-api-key.txt)

.kokoro/cloudsql-mysql.cfg

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@ env_vars: {
1111
key: "TRAMPOLINE_BUILD_FILE"
1212
value: "github/nodejs-docs-samples/.kokoro/build.sh"
1313
}
14+
15+
# Specify which SQL client to use
16+
env_vars: {
17+
key: "SQL_CLIENT"
18+
value: "mysql"
19+
}

.kokoro/cloudsql-postgres.cfg

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Format: //devtools/kokoro/config/proto/build.proto
2+
3+
# Set the folder in which the tests are run
4+
env_vars: {
5+
key: "PROJECT"
6+
value: "cloud-sql/postgres/knex"
7+
}
8+
9+
# Tell the trampoline which build file to use.
10+
env_vars: {
11+
key: "TRAMPOLINE_BUILD_FILE"
12+
value: "github/nodejs-docs-samples/.kokoro/build.sh"
13+
}
14+
15+
# Specify which SQL client to use
16+
env_vars: {
17+
key: "SQL_CLIENT"
18+
value: "pg"
19+
}

cloud-sql/mysql/mysql/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"scripts": {
1616
"system-test": "mocha test/*.test.js --timeout=60000 --exit",
17-
"all-test": "npm run system-test"
17+
"test": "npm run system-test"
1818
},
1919
"dependencies": {
2020
"@google-cloud/logging-winston": "^0.11.0",

cloud-sql/mysql/mysql/server.js

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -99,34 +99,31 @@ app.on('listening', async () => {
9999
// Serve the index page, showing vote tallies.
100100
app.get('/', async (req, res) => {
101101
// Get the 5 most recent votes.
102-
const recentResultPromise = pool
103-
.query(
104-
'SELECT candidate, time_cast FROM votes ORDER BY time_cast DESC LIMIT 5'
105-
)
106-
.then(rows => {
107-
return rows;
108-
});
102+
const recentVotesQuery = pool.query(
103+
'SELECT candidate, time_cast FROM votes ORDER BY time_cast DESC LIMIT 5'
104+
);
109105

106+
// Get votes
110107
const stmt = 'SELECT COUNT(vote_id) as count FROM votes WHERE candidate=?';
111-
// Get the total number of "TABS" votes.
112-
const tabsResultPromise = pool.query(stmt, ['TABS']).then(rows => {
113-
return rows[0].count;
114-
});
115-
// Get the total number of "SPACES" votes.
116-
const spacesResultPromise = pool.query(stmt, ['SPACES']).then(rows => {
117-
return rows[0].count;
118-
});
108+
const tabsQuery = pool.query(stmt, ['TABS']);
109+
const spacesQuery = pool.query(stmt, ['SPACES']);
110+
111+
// Run queries concurrently, and wait for them to complete
112+
// This is faster than await-ing each query object as it is created
113+
const recentVotes = await recentVotesQuery;
114+
const [tabsVotes] = await tabsQuery;
115+
const [spacesVotes] = await spacesQuery;
119116

120117
res.render('index.pug', {
121-
recentVotes: await recentResultPromise,
122-
tabCount: await tabsResultPromise,
123-
spaceCount: await spacesResultPromise,
118+
recentVotes,
119+
tabCount: tabsVotes.count,
120+
spaceCount: spacesVotes.count,
124121
});
125122
});
126123

127124
// Handle incoming vote requests and inserting them into the database.
128125
app.post('/', async (req, res) => {
129-
const team = req.body.team;
126+
const {team} = req.body;
130127
const timestamp = new Date();
131128

132129
if (!team || (team !== 'TABS' && team !== 'SPACES')) {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"requiresKeyFile": true,
3+
"requiresProjectId": true,
4+
"test": {
5+
"app": {
6+
"requiredEnvVars": [
7+
"DB_USER",
8+
"DB_PASS",
9+
"DB_NAME",
10+
"CLOUD_SQL_INSTANCE_NAME"
11+
],
12+
"args": [
13+
"server.js"
14+
]
15+
},
16+
"build": {
17+
"requiredEnvVars": [
18+
"DB_USER",
19+
"DB_PASS",
20+
"DB_NAME",
21+
"CLOUD_SQL_INSTANCE_NAME"
22+
]
23+
}
24+
}
25+
}

cloud-sql/postgres/knex/createTable.js

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,35 @@
1616
'use strict';
1717

1818
const Knex = require('knex');
19-
const prompt = require('prompt');
20-
21-
const FIELDS = ['user', 'password', 'database'];
22-
23-
prompt.start();
24-
25-
// Prompt the user for connection details
26-
prompt.get(FIELDS, (err, config) => {
27-
if (err) {
28-
console.error(err);
29-
return;
30-
}
3119

20+
const createTable = async config => {
3221
// Connect to the database
22+
config.host = `/cloudsql/${config.connectionName}`;
3323
const knex = Knex({client: 'pg', connection: config});
3424

3525
// Create the "votes" table
36-
knex.schema
37-
.createTable('votes', table => {
26+
try {
27+
await knex.schema.createTable('votes', table => {
3828
table.bigIncrements('vote_id').notNull();
3929
table.timestamp('time_cast').notNull();
4030
table.specificType('candidate', 'CHAR(6) NOT NULL');
41-
})
42-
.then(() => {
43-
console.log(`Successfully created 'votes' table.`);
44-
return knex.destroy();
45-
})
46-
.catch(err => {
47-
console.error(`Failed to create 'votes' table:`, err);
48-
if (knex) {
49-
knex.destroy();
50-
}
5131
});
52-
});
32+
33+
console.log(`Successfully created 'votes' table.`);
34+
return knex.destroy();
35+
} catch (err) {
36+
console.error(`Failed to create 'votes' table:`, err);
37+
if (knex) {
38+
knex.destroy();
39+
}
40+
}
41+
};
42+
43+
require('yargs')
44+
.command(
45+
'* <user> <password> <database> <connectionName>',
46+
'Create a "votes" table',
47+
{},
48+
createTable
49+
)
50+
.help().argv;

cloud-sql/postgres/knex/package.json

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,44 +21,18 @@
2121
"test": "repo-tools test run --cmd npm -- run all-test"
2222
},
2323
"dependencies": {
24+
"@google-cloud/logging-winston": "^0.11.0",
2425
"body-parser": "^1.18.3",
2526
"express": "^4.16.2",
2627
"knex": "^0.17.0",
27-
"prompt": "^1.0.0",
28+
"pg": "^7.11.0",
2829
"pug": "^2.0.3",
29-
"@google-cloud/logging-winston": "^0.11.0",
30-
"winston": "^3.1.0"
30+
"winston": "^3.1.0",
31+
"yargs": "^13.2.4"
3132
},
3233
"devDependencies": {
3334
"@google-cloud/nodejs-repo-tools": "3.3.0",
3435
"mocha": "^6.0.0",
35-
"proxyquire": "^2.1.0",
36-
"supertest": "^4.0.0",
37-
"sinon": "^7.1.1"
38-
},
39-
"cloud-repo-tools": {
40-
"requiresKeyFile": true,
41-
"requiresProjectId": true,
42-
"test": {
43-
"app": {
44-
"requiredEnvVars": [
45-
"DB_USER",
46-
"DB_PASS",
47-
"DB_NAME",
48-
"CLOUD_SQL_INSTANCE_NAME"
49-
],
50-
"args": [
51-
"server.js"
52-
]
53-
},
54-
"build": {
55-
"requiredEnvVars": [
56-
"DB_USER",
57-
"DB_PASS",
58-
"DB_NAME",
59-
"CLOUD_SQL_INSTANCE_NAME"
60-
]
61-
}
62-
}
36+
"supertest": "^4.0.0"
6337
}
6438
}

cloud-sql/postgres/knex/server.js

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -188,17 +188,18 @@ app.get('/', (req, res) => {
188188
})();
189189
});
190190

191-
app.post('/', (req, res) => {
191+
app.post('/', async (req, res) => {
192192
// [START cloud_sql_example_statement]
193193
// Get the team from the request and record the time of the vote.
194-
const team = req.body.team;
194+
const {team} = req.body;
195195
const timestamp = new Date();
196196

197197
if (!team || (team !== 'TABS' && team !== 'SPACES')) {
198198
res
199199
.status(400)
200200
.send('Invalid team specified.')
201201
.end();
202+
return;
202203
}
203204

204205
// Create a vote record to be stored in the database.
@@ -208,28 +209,26 @@ app.post('/', (req, res) => {
208209
};
209210

210211
// Save the data to the database.
211-
insertVote(knex, vote)
212-
// [END cloud_sql_example_statement]
213-
.catch(err => {
214-
logger.error('Error while attempting to submit vote. Error:' + err);
215-
let msg = 'Unable to successfully cast vote!';
216-
msg += 'Please check the application logs for more details.';
217-
res
218-
.status(500)
219-
.send(msg)
220-
.end();
221-
});
222-
const msg = 'Successfully voted for ' + team + ' at ' + timestamp;
212+
try {
213+
await insertVote(knex, vote);
214+
} catch (err) {
215+
logger.error('Error while attempting to submit vote:' + err);
216+
res
217+
.status(500)
218+
.send('Unable to cast vote; see logs for more details.')
219+
.end();
220+
return;
221+
}
223222
res
224223
.status(200)
225-
.send(msg)
224+
.send(`Successfully voted for ${team} at ${timestamp}`)
226225
.end();
227226
});
228227

229228
const PORT = process.env.PORT || 8080;
230-
app.listen(PORT, () => {
229+
const server = app.listen(PORT, () => {
231230
console.log(`App listening on port ${PORT}`);
232231
console.log('Press Ctrl+C to quit.');
233232
});
234233

235-
module.exports = app;
234+
module.exports = server;

0 commit comments

Comments
 (0)