diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..8bc16b011 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +**/node_modules/* + diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 000000000..599d506ab --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,19 @@ +--- +extends: + - 'eslint:recommended' + - 'plugin:node/recommended' + - prettier +env: + mocha: true +plugins: + - node + - prettier +rules: + prettier/prettier: error + block-scoped-var: error + eqeqeq: error + no-warning-comments: warn + no-console: off + node/no-missing-require: off + node/no-unpublished-require: off + diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..f6fac98b0 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +node_modules/* +samples/node_modules/* +src/**/doc/* diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..df6eac074 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +--- +bracketSpacing: false +printWidth: 80 +semi: true +singleQuote: true +tabWidth: 2 +trailingComma: es5 +useTabs: false diff --git a/1-hello-world/package.json b/1-hello-world/package.json index a390716f2..0f3ddaf88 100644 --- a/1-hello-world/package.json +++ b/1-hello-world/package.json @@ -22,34 +22,6 @@ } } }, - "semistandard": { - "globals": [ - "after", - "afterEach", - "before", - "beforeEach", - "describe", - "it" - ] - }, - "contributors": [ - "Ace Nassri ", - "Ahmet Alp Balkan ", - "Allen Day ", - "André Cipriani Bandarra ", - "Dominik Staskiewicz ", - "F. Hinkelmann ", - "Jason Dobry ", - "Jon Wayne Parrott ", - "Justin Beckwith ", - "Michael McDonald ", - "Sean McBreen ", - "Steren ", - "Steve Perry ", - "chenyumic ", - "renovate[bot] ", - "shollyman " - ], "scripts": { "start": "node app.js", "e2e": "repo-tools test deploy", diff --git a/2-structured-data/app.js b/2-structured-data/app.js index 976d3db4d..74b2ddaaf 100644 --- a/2-structured-data/app.js +++ b/2-structured-data/app.js @@ -39,7 +39,7 @@ app.use((req, res) => { }); // Basic error handler -app.use((err, req, res, next) => { +app.use((err, req, res) => { /* jshint unused:false */ console.error(err); // If our routes specified a specific response, then send that. Otherwise, diff --git a/2-structured-data/books/api.js b/2-structured-data/books/api.js index 98b65fc58..c81c35520 100644 --- a/2-structured-data/books/api.js +++ b/2-structured-data/books/api.js @@ -16,7 +16,7 @@ const express = require('express'); const bodyParser = require('body-parser'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -38,7 +38,7 @@ router.get('/', (req, res, next) => { } res.json({ items: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -94,7 +94,7 @@ router.put('/:book', (req, res, next) => { * Delete a book. */ router.delete('/:book', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; @@ -111,7 +111,7 @@ router.use((err, req, res, next) => { // responding to the request err.response = { message: err.message, - internalCode: err.code + internalCode: err.code, }; next(err); }); diff --git a/2-structured-data/books/crud.js b/2-structured-data/books/crud.js index 858e3f821..5b8d89281 100644 --- a/2-structured-data/books/crud.js +++ b/2-structured-data/books/crud.js @@ -16,14 +16,14 @@ const express = require('express'); const bodyParser = require('body-parser'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } const router = express.Router(); // Automatically parse request body as form data -router.use(bodyParser.urlencoded({ extended: false })); +router.use(bodyParser.urlencoded({extended: false})); // Set Content-Type for all responses for these routes router.use((req, res, next) => { @@ -44,7 +44,7 @@ router.get('/', (req, res, next) => { } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -58,7 +58,7 @@ router.get('/', (req, res, next) => { router.get('/add', (req, res) => { res.render('books/form.pug', { book: {}, - action: 'Add' + action: 'Add', }); }); // [END add_get] @@ -96,7 +96,7 @@ router.get('/:book/edit', (req, res, next) => { } res.render('books/form.pug', { book: entity, - action: 'Edit' + action: 'Edit', }); }); }); @@ -130,7 +130,7 @@ router.get('/:book', (req, res, next) => { return; } res.render('books/view.pug', { - book: entity + book: entity, }); }); }); @@ -141,7 +141,7 @@ router.get('/:book', (req, res, next) => { * Delete a book. */ router.get('/:book/delete', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; diff --git a/2-structured-data/books/model-cloudsql.js b/2-structured-data/books/model-cloudsql.js index 2f5c7a29e..be41ff3fe 100644 --- a/2-structured-data/books/model-cloudsql.js +++ b/2-structured-data/books/model-cloudsql.js @@ -20,20 +20,24 @@ const config = require('../config'); const options = { user: config.get('MYSQL_USER'), password: config.get('MYSQL_PASSWORD'), - database: 'bookshelf' + database: 'bookshelf', }; -if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') { +if ( + config.get('INSTANCE_CONNECTION_NAME') && + config.get('NODE_ENV') === 'production' +) { options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`; } const connection = mysql.createConnection(options); // [START list] -function list (limit, token, cb) { +function list(limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( - 'SELECT * FROM `books` LIMIT ? OFFSET ?', [limit, token], + 'SELECT * FROM `books` LIMIT ? OFFSET ?', + [limit, token], (err, results) => { if (err) { cb(err); @@ -47,7 +51,7 @@ function list (limit, token, cb) { // [END list] // [START create] -function create (data, cb) { +function create(data, cb) { connection.query('INSERT INTO `books` SET ?', data, (err, res) => { if (err) { cb(err); @@ -58,13 +62,15 @@ function create (data, cb) { } // [END create] -function read (id, cb) { +function read(id, cb) { connection.query( - 'SELECT * FROM `books` WHERE `id` = ?', id, (err, results) => { + 'SELECT * FROM `books` WHERE `id` = ?', + id, + (err, results) => { if (!err && !results.length) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -72,23 +78,23 @@ function read (id, cb) { return; } cb(null, results[0]); - }); + } + ); } // [START update] -function update (id, data, cb) { - connection.query( - 'UPDATE `books` SET ? WHERE `id` = ?', [data, id], (err) => { - if (err) { - cb(err); - return; - } - read(id, cb); - }); +function update(id, data, cb) { + connection.query('UPDATE `books` SET ? WHERE `id` = ?', [data, id], err => { + if (err) { + cb(err); + return; + } + read(id, cb); + }); } // [END update] -function _delete (id, cb) { +function _delete(id, cb) { connection.query('DELETE FROM `books` WHERE `id` = ?', id, cb); } @@ -98,7 +104,7 @@ module.exports = { create: create, read: read, update: update, - delete: _delete + delete: _delete, }; if (module === require.main) { @@ -107,7 +113,8 @@ if (module === require.main) { console.log( `Running this script directly will allow you to initialize your mysql database. - This script will not modify any existing tables.`); + This script will not modify any existing tables.` + ); prompt.get(['user', 'password'], (err, result) => { if (err) { @@ -117,10 +124,15 @@ if (module === require.main) { }); } -function createSchema (config) { - const connection = mysql.createConnection(extend({ - multipleStatements: true - }, config)); +function createSchema(config) { + const connection = mysql.createConnection( + extend( + { + multipleStatements: true, + }, + config + ) + ); connection.query( `CREATE DATABASE IF NOT EXISTS \`bookshelf\` @@ -137,7 +149,7 @@ function createSchema (config) { \`createdBy\` VARCHAR(255) NULL, \`createdById\` VARCHAR(255) NULL, PRIMARY KEY (\`id\`));`, - (err) => { + err => { if (err) { throw err; } diff --git a/2-structured-data/books/model-datastore.js b/2-structured-data/books/model-datastore.js index 8e8375448..90cf341ff 100644 --- a/2-structured-data/books/model-datastore.js +++ b/2-structured-data/books/model-datastore.js @@ -18,7 +18,7 @@ const config = require('../config'); // [START config] const ds = Datastore({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const kind = 'Book'; // [END config] @@ -39,7 +39,7 @@ const kind = 'Book'; // id: id, // property: value // } -function fromDatastore (obj) { +function fromDatastore(obj) { obj.id = obj[Datastore.KEY].id; return obj; } @@ -67,17 +67,17 @@ function fromDatastore (obj) { // excludeFromIndexes: true // } // ] -function toDatastore (obj, nonIndexed) { +function toDatastore(obj, nonIndexed) { nonIndexed = nonIndexed || []; const results = []; - Object.keys(obj).forEach((k) => { + Object.keys(obj).forEach(k => { if (obj[k] === undefined) { return; } results.push({ name: k, value: obj[k], - excludeFromIndexes: nonIndexed.indexOf(k) !== -1 + excludeFromIndexes: nonIndexed.indexOf(k) !== -1, }); }); return results; @@ -88,8 +88,9 @@ function toDatastore (obj, nonIndexed) { // return per page. The ``token`` argument allows requesting additional // pages. The callback is invoked with ``(err, books, nextPageToken)``. // [START list] -function list (limit, token, cb) { - const q = ds.createQuery([kind]) +function list(limit, token, cb) { + const q = ds + .createQuery([kind]) .limit(limit) .order('title') .start(token); @@ -99,7 +100,10 @@ function list (limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } @@ -109,7 +113,7 @@ function list (limit, token, cb) { // data is automatically translated into Datastore format. The book will be // queued for background processing. // [START update] -function update (id, data, cb) { +function update(id, data, cb) { let key; if (id) { key = ds.key([kind, parseInt(id, 10)]); @@ -119,30 +123,27 @@ function update (id, data, cb) { const entity = { key: key, - data: toDatastore(data, ['description']) + data: toDatastore(data, ['description']), }; - ds.save( - entity, - (err) => { - data.id = entity.key.id; - cb(err, err ? null : data); - } - ); + ds.save(entity, err => { + data.id = entity.key.id; + cb(err, err ? null : data); + }); } // [END update] -function create (data, cb) { +function create(data, cb) { update(null, data, cb); } -function read (id, cb) { +function read(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.get(key, (err, entity) => { if (!err && !entity) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -153,7 +154,7 @@ function read (id, cb) { }); } -function _delete (id, cb) { +function _delete(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.delete(key, cb); } @@ -164,6 +165,6 @@ module.exports = { read, update, delete: _delete, - list + list, }; // [END exports] diff --git a/2-structured-data/config.js b/2-structured-data/config.js index 0df822802..03c4b72f9 100644 --- a/2-structured-data/config.js +++ b/2-structured-data/config.js @@ -15,7 +15,7 @@ // Hierarchical node.js configuration with command-line arguments, environment // variables, and files. -const nconf = module.exports = require('nconf'); +const nconf = (module.exports = require('nconf')); const path = require('path'); nconf @@ -29,10 +29,10 @@ nconf 'MYSQL_USER', 'MYSQL_PASSWORD', 'NODE_ENV', - 'PORT' + 'PORT', ]) // 3. Config file - .file({ file: path.join(__dirname, 'config.json') }) + .file({file: path.join(__dirname, 'config.json')}) // 4. Defaults .defaults({ // dataBackend can be 'datastore' or 'cloudsql'. Be sure to @@ -47,7 +47,7 @@ nconf MYSQL_USER: '', MYSQL_PASSWORD: '', - PORT: 8080 + PORT: 8080, }); // Check for required settings @@ -61,8 +61,10 @@ if (nconf.get('DATA_BACKEND') === 'cloudsql') { } } -function checkConfig (setting) { +function checkConfig(setting) { if (!nconf.get(setting)) { - throw new Error(`You must set ${setting} as an environment variable or in config.json!`); + throw new Error( + `You must set ${setting} as an environment variable or in config.json!` + ); } } diff --git a/2-structured-data/package.json b/2-structured-data/package.json index 44b1b2676..049791238 100644 --- a/2-structured-data/package.json +++ b/2-structured-data/package.json @@ -22,34 +22,6 @@ } } }, - "semistandard": { - "globals": [ - "after", - "afterEach", - "before", - "beforeEach", - "describe", - "it" - ] - }, - "contributors": [ - "Ace Nassri ", - "Ahmet Alp Balkan ", - "Allen Day ", - "André Cipriani Bandarra ", - "Dominik Staskiewicz ", - "F. Hinkelmann ", - "Jason Dobry ", - "Jon Wayne Parrott ", - "Justin Beckwith ", - "Michael McDonald ", - "Sean McBreen ", - "Steren ", - "Steve Perry ", - "chenyumic ", - "renovate[bot] ", - "shollyman " - ], "scripts": { "start": "node app.js", "test": "repo-tools test app && ava -t 30s --tap test/*.test.js", diff --git a/2-structured-data/test/_api-tests.js b/2-structured-data/test/_api-tests.js index 233c0ffc9..428d6478b 100644 --- a/2-structured-data/test/_api-tests.js +++ b/2-structured-data/test/_api-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -26,12 +26,12 @@ module.exports = (DATA_BACKEND) => { appConfig.set(`DATA_BACKEND`, DATA_BACKEND); }); - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `beep` }) + .send({title: `beep`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `beep`); @@ -39,13 +39,13 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should list books`, (t) => { + test.serial.cb(`should list books`, t => { // Give Datastore time to become consistent setTimeout(() => { getRequest(testConfig) .get(`/api/books`) .expect(200) - .expect((response) => { + .expect(response => { t.true(Array.isArray(response.body.items)); t.true(response.body.items.length >= 1); }) @@ -53,11 +53,11 @@ module.exports = (DATA_BACKEND) => { }, 1000); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .delete(`/api/books/${id}/`) .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `OK`); }) .end(t.end); diff --git a/2-structured-data/test/_crud-tests.js b/2-structured-data/test/_crud-tests.js index f67b0a07a..20e64456c 100644 --- a/2-structured-data/test/_crud-tests.js +++ b/2-structured-data/test/_crud-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -27,12 +27,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -40,30 +40,30 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show a list of books`, (t) => { + test.serial.cb(`should show a list of books`, t => { // Give Datastore time to become consistent setTimeout(() => { const expected = /
/; getRequest(testConfig) .get(`/books`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }, 2000); }); - test.serial.cb(`should handle error`, (t) => { + test.serial.cb(`should handle error`, t => { getRequest(testConfig) .get(`/books`) - .query({ pageToken: `badrequest` }) + .query({pageToken: `badrequest`}) .expect(500) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -74,13 +74,13 @@ module.exports = (DATA_BACKEND) => { } }); - test.serial.cb(`should post to add book form`, (t) => { + test.serial.cb(`should post to add book form`, t => { const expected = /Redirecting to \/books\//; getRequest(testConfig) .post(`/books/add`) .send(`title=my%20book`) .expect(302) - .expect((response) => { + .expect(response => { const location = response.headers.location; const idPart = location.replace(`/books/`, ``); if (DATA_BACKEND !== `mongodb`) { @@ -93,19 +93,19 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show add book form`, (t) => { + test.serial.cb(`should show add book form`, t => { const expected = /Add book/; getRequest(testConfig) .get(`/books/add`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -117,12 +117,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb((t) => { + test.serial.cb(t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -130,47 +130,46 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should update a book`, (t) => { + test.serial.cb(`should update a book`, t => { const expected = new RegExp(`Redirecting to /books/${id}`); getRequest(testConfig) .post(`/books/${id}/edit`) .send(`title=my%20other%20book`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show edit book form`, (t) => { - const expected = - //; + test.serial.cb(`should show edit book form`, t => { + const expected = //; getRequest(testConfig) .get(`/books/${id}/edit`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show a book`, (t) => { + test.serial.cb(`should show a book`, t => { const expected = /

my other book <\/small><\/h4>/; getRequest(testConfig) .get(`/books/${id}`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { const expected = /Redirecting to \/books/; getRequest(testConfig) .get(`/books/${id}/delete`) .expect(302) - .expect((response) => { + .expect(response => { id = undefined; t.regex(response.text, expected); }) @@ -178,7 +177,7 @@ module.exports = (DATA_BACKEND) => { }); // clean up - test.always.after.cb((t) => { + test.always.after.cb(t => { appConfig.set(`DATA_BACKEND`, originalDataBackend); if (id) { diff --git a/2-structured-data/test/_test-config.js b/2-structured-data/test/_test-config.js index 8e5284860..228118e00 100644 --- a/2-structured-data/test/_test-config.js +++ b/2-structured-data/test/_test-config.js @@ -24,10 +24,10 @@ module.exports = { cmd: `app`, port: PORT, env: { - PORT: PORT + PORT: PORT, }, url: `http://localhost:${PORT}`, version: process.env.GAE_VERSION || TESTNAME, project: process.env.GCLOUD_PROJECT, - msg: `Bookshelf` + msg: `Bookshelf`, }; diff --git a/2-structured-data/test/app.test.js b/2-structured-data/test/app.test.js index 226a2b1ae..7e8a4c5ae 100644 --- a/2-structured-data/test/app.test.js +++ b/2-structured-data/test/app.test.js @@ -19,33 +19,34 @@ const sinon = require(`sinon`); const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); -test.cb(`should redirect / to /books`, (t) => { - utils.getRequest(testConfig) +test.cb(`should redirect / to /books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, /Redirecting to \/books/); }) .end(t.end); }); -test(`should check config`, (t) => { +test(`should check config`, t => { const nconfMock = { argv: sinon.stub().returnsThis(), env: sinon.stub().returnsThis(), file: sinon.stub().returnsThis(), defaults: sinon.stub().returnsThis(), - get: function (setting) { + get: function(setting) { return this[setting]; - } + }, }; - function getMsg (setting) { + function getMsg(setting) { return `You must set ${setting} as an environment variable or in config.json!`; } const testFunc = () => { - proxyquire(`../config`, { nconf: nconfMock }); + proxyquire(`../config`, {nconf: nconfMock}); }; nconfMock.DATA_BACKEND = `datastore`; diff --git a/2-structured-data/test/cloudsql.test.js b/2-structured-data/test/cloudsql.test.js index ff2ba5fd5..3ac8fbe5c 100644 --- a/2-structured-data/test/cloudsql.test.js +++ b/2-structured-data/test/cloudsql.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || + process.env.TEST_CLOUDSQL +) { require(`./_api-tests`)(`cloudsql`); require(`./_crud-tests`)(`cloudsql`); } else { - test(`Skipping Cloud SQL tests...`, (t) => t.pass()); + test(`Skipping Cloud SQL tests...`, t => t.pass()); } diff --git a/2-structured-data/test/datastore.test.js b/2-structured-data/test/datastore.test.js index a4faa673b..6df5120c1 100644 --- a/2-structured-data/test/datastore.test.js +++ b/2-structured-data/test/datastore.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `datastore` || process.env.TEST_DATASTORE) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `datastore` || + process.env.TEST_DATASTORE +) { require(`./_api-tests`)(`datastore`); require(`./_crud-tests`)(`datastore`); } else { - test(`Skipping Cloud Datastore tests...`, (t) => t.pass()); + test(`Skipping Cloud Datastore tests...`, t => t.pass()); } diff --git a/3-binary-data/app.js b/3-binary-data/app.js index 976d3db4d..74b2ddaaf 100644 --- a/3-binary-data/app.js +++ b/3-binary-data/app.js @@ -39,7 +39,7 @@ app.use((req, res) => { }); // Basic error handler -app.use((err, req, res, next) => { +app.use((err, req, res) => { /* jshint unused:false */ console.error(err); // If our routes specified a specific response, then send that. Otherwise, diff --git a/3-binary-data/books/api.js b/3-binary-data/books/api.js index 98b65fc58..c81c35520 100644 --- a/3-binary-data/books/api.js +++ b/3-binary-data/books/api.js @@ -16,7 +16,7 @@ const express = require('express'); const bodyParser = require('body-parser'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -38,7 +38,7 @@ router.get('/', (req, res, next) => { } res.json({ items: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -94,7 +94,7 @@ router.put('/:book', (req, res, next) => { * Delete a book. */ router.delete('/:book', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; @@ -111,7 +111,7 @@ router.use((err, req, res, next) => { // responding to the request err.response = { message: err.message, - internalCode: err.code + internalCode: err.code, }; next(err); }); diff --git a/3-binary-data/books/crud.js b/3-binary-data/books/crud.js index f05d129b6..c1c3623c9 100644 --- a/3-binary-data/books/crud.js +++ b/3-binary-data/books/crud.js @@ -17,14 +17,14 @@ const express = require('express'); const bodyParser = require('body-parser'); const images = require('../lib/images'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } const router = express.Router(); // Automatically parse request body as form data -router.use(bodyParser.urlencoded({ extended: false })); +router.use(bodyParser.urlencoded({extended: false})); // Set Content-Type for all responses for these routes router.use((req, res, next) => { @@ -45,7 +45,7 @@ router.get('/', (req, res, next) => { } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -58,7 +58,7 @@ router.get('/', (req, res, next) => { router.get('/add', (req, res) => { res.render('books/form.pug', { book: {}, - action: 'Add' + action: 'Add', }); }); @@ -106,7 +106,7 @@ router.get('/:book/edit', (req, res, next) => { } res.render('books/form.pug', { book: entity, - action: 'Edit' + action: 'Edit', }); }); }); @@ -151,7 +151,7 @@ router.get('/:book', (req, res, next) => { return; } res.render('books/view.pug', { - book: entity + book: entity, }); }); }); @@ -162,7 +162,7 @@ router.get('/:book', (req, res, next) => { * Delete a book. */ router.get('/:book/delete', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; diff --git a/3-binary-data/books/model-cloudsql.js b/3-binary-data/books/model-cloudsql.js index 2e4d0735b..0fe6729cf 100644 --- a/3-binary-data/books/model-cloudsql.js +++ b/3-binary-data/books/model-cloudsql.js @@ -20,19 +20,23 @@ const config = require('../config'); const options = { user: config.get('MYSQL_USER'), password: config.get('MYSQL_PASSWORD'), - database: 'bookshelf' + database: 'bookshelf', }; -if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') { +if ( + config.get('INSTANCE_CONNECTION_NAME') && + config.get('NODE_ENV') === 'production' +) { options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`; } const connection = mysql.createConnection(options); -function list (limit, token, cb) { +function list(limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( - 'SELECT * FROM `books` LIMIT ? OFFSET ?', [limit, token], + 'SELECT * FROM `books` LIMIT ? OFFSET ?', + [limit, token], (err, results) => { if (err) { cb(err); @@ -44,7 +48,7 @@ function list (limit, token, cb) { ); } -function create (data, cb) { +function create(data, cb) { connection.query('INSERT INTO `books` SET ?', data, (err, res) => { if (err) { cb(err); @@ -54,13 +58,15 @@ function create (data, cb) { }); } -function read (id, cb) { +function read(id, cb) { connection.query( - 'SELECT * FROM `books` WHERE `id` = ?', id, (err, results) => { + 'SELECT * FROM `books` WHERE `id` = ?', + id, + (err, results) => { if (!err && !results.length) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -68,21 +74,21 @@ function read (id, cb) { return; } cb(null, results[0]); - }); + } + ); } -function update (id, data, cb) { - connection.query( - 'UPDATE `books` SET ? WHERE `id` = ?', [data, id], (err) => { - if (err) { - cb(err); - return; - } - read(id, cb); - }); +function update(id, data, cb) { + connection.query('UPDATE `books` SET ? WHERE `id` = ?', [data, id], err => { + if (err) { + cb(err); + return; + } + read(id, cb); + }); } -function _delete (id, cb) { +function _delete(id, cb) { connection.query('DELETE FROM `books` WHERE `id` = ?', id, cb); } @@ -92,7 +98,7 @@ module.exports = { create: create, read: read, update: update, - delete: _delete + delete: _delete, }; if (module === require.main) { @@ -101,7 +107,8 @@ if (module === require.main) { console.log( `Running this script directly will allow you to initialize your mysql database. - This script will not modify any existing tables.`); + This script will not modify any existing tables.` + ); prompt.get(['user', 'password'], (err, result) => { if (err) { @@ -111,10 +118,15 @@ if (module === require.main) { }); } -function createSchema (config) { - const connection = mysql.createConnection(extend({ - multipleStatements: true - }, config)); +function createSchema(config) { + const connection = mysql.createConnection( + extend( + { + multipleStatements: true, + }, + config + ) + ); connection.query( `CREATE DATABASE IF NOT EXISTS \`bookshelf\` @@ -131,7 +143,7 @@ function createSchema (config) { \`createdBy\` VARCHAR(255) NULL, \`createdById\` VARCHAR(255) NULL, PRIMARY KEY (\`id\`));`, - (err) => { + err => { if (err) { throw err; } diff --git a/3-binary-data/books/model-datastore.js b/3-binary-data/books/model-datastore.js index 6efbab9f6..2a5ff7db9 100644 --- a/3-binary-data/books/model-datastore.js +++ b/3-binary-data/books/model-datastore.js @@ -17,7 +17,7 @@ const Datastore = require('@google-cloud/datastore'); const config = require('../config'); const ds = Datastore({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const kind = 'Book'; @@ -37,7 +37,7 @@ const kind = 'Book'; // id: id, // property: value // } -function fromDatastore (obj) { +function fromDatastore(obj) { obj.id = obj[Datastore.KEY].id; return obj; } @@ -65,17 +65,17 @@ function fromDatastore (obj) { // excludeFromIndexes: true // } // ] -function toDatastore (obj, nonIndexed) { +function toDatastore(obj, nonIndexed) { nonIndexed = nonIndexed || []; let results = []; - Object.keys(obj).forEach((k) => { + Object.keys(obj).forEach(k => { if (obj[k] === undefined) { return; } results.push({ name: k, value: obj[k], - excludeFromIndexes: nonIndexed.indexOf(k) !== -1 + excludeFromIndexes: nonIndexed.indexOf(k) !== -1, }); }); return results; @@ -85,8 +85,9 @@ function toDatastore (obj, nonIndexed) { // The ``limit`` argument determines the maximum amount of results to // return per page. The ``token`` argument allows requesting additional // pages. The callback is invoked with ``(err, books, nextPageToken)``. -function list (limit, token, cb) { - const q = ds.createQuery([kind]) +function list(limit, token, cb) { + const q = ds + .createQuery([kind]) .limit(limit) .order('title') .start(token); @@ -96,7 +97,10 @@ function list (limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } @@ -104,7 +108,7 @@ function list (limit, token, cb) { // Creates a new book or updates an existing book with new data. The provided // data is automatically translated into Datastore format. The book will be // queued for background processing. -function update (id, data, cb) { +function update(id, data, cb) { let key; if (id) { key = ds.key([kind, parseInt(id, 10)]); @@ -114,29 +118,26 @@ function update (id, data, cb) { const entity = { key: key, - data: toDatastore(data, ['description']) + data: toDatastore(data, ['description']), }; - ds.save( - entity, - (err) => { - data.id = entity.key.id; - cb(err, err ? null : data); - } - ); + ds.save(entity, err => { + data.id = entity.key.id; + cb(err, err ? null : data); + }); } -function create (data, cb) { +function create(data, cb) { update(null, data, cb); } -function read (id, cb) { +function read(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.get(key, (err, entity) => { if (!err && !entity) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -147,7 +148,7 @@ function read (id, cb) { }); } -function _delete (id, cb) { +function _delete(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.delete(key, cb); } @@ -157,5 +158,5 @@ module.exports = { read, update, delete: _delete, - list + list, }; diff --git a/3-binary-data/config.js b/3-binary-data/config.js index c6fa002d6..ac6493886 100644 --- a/3-binary-data/config.js +++ b/3-binary-data/config.js @@ -15,7 +15,7 @@ // Hierarchical node.js configuration with command-line arguments, environment // variables, and files. -const nconf = module.exports = require('nconf'); +const nconf = (module.exports = require('nconf')); const path = require('path'); nconf @@ -30,10 +30,10 @@ nconf 'MYSQL_USER', 'MYSQL_PASSWORD', 'NODE_ENV', - 'PORT' + 'PORT', ]) // 3. Config file - .file({ file: path.join(__dirname, 'config.json') }) + .file({file: path.join(__dirname, 'config.json')}) // 4. Defaults .defaults({ // Typically you will create a bucket with the same name as your project ID. @@ -51,7 +51,7 @@ nconf MYSQL_USER: '', MYSQL_PASSWORD: '', - PORT: 8080 + PORT: 8080, }); // Check for required settings @@ -66,8 +66,10 @@ if (nconf.get('DATA_BACKEND') === 'cloudsql') { } } -function checkConfig (setting) { +function checkConfig(setting) { if (!nconf.get(setting)) { - throw new Error(`You must set ${setting} as an environment variable or in config.json!`); + throw new Error( + `You must set ${setting} as an environment variable or in config.json!` + ); } } diff --git a/3-binary-data/lib/images.js b/3-binary-data/lib/images.js index 288f81b8c..84fdb0650 100644 --- a/3-binary-data/lib/images.js +++ b/3-binary-data/lib/images.js @@ -19,7 +19,7 @@ const config = require('../config'); const CLOUD_BUCKET = config.get('CLOUD_BUCKET'); const storage = Storage({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const bucket = storage.bucket(CLOUD_BUCKET); @@ -27,7 +27,7 @@ const bucket = storage.bucket(CLOUD_BUCKET); // object. // The object's ACL has to be set to public read. // [START public_url] -function getPublicUrl (filename) { +function getPublicUrl(filename) { return `https://storage.googleapis.com/${CLOUD_BUCKET}/${filename}`; } // [END public_url] @@ -37,7 +37,7 @@ function getPublicUrl (filename) { // * ``cloudStorageObject`` the object name in cloud storage. // * ``cloudStoragePublicUrl`` the public url to the object. // [START process] -function sendUploadToGCS (req, res, next) { +function sendUploadToGCS(req, res, next) { if (!req.file) { return next(); } @@ -47,12 +47,12 @@ function sendUploadToGCS (req, res, next) { const stream = file.createWriteStream({ metadata: { - contentType: req.file.mimetype + contentType: req.file.mimetype, }, - resumable: false + resumable: false, }); - stream.on('error', (err) => { + stream.on('error', err => { req.file.cloudStorageError = err; next(err); }); @@ -77,13 +77,13 @@ const Multer = require('multer'); const multer = Multer({ storage: Multer.MemoryStorage, limits: { - fileSize: 5 * 1024 * 1024 // no larger than 5mb - } + fileSize: 5 * 1024 * 1024, // no larger than 5mb + }, }); // [END multer] module.exports = { getPublicUrl, sendUploadToGCS, - multer + multer, }; diff --git a/3-binary-data/package.json b/3-binary-data/package.json index 78259b49e..4afccff0c 100644 --- a/3-binary-data/package.json +++ b/3-binary-data/package.json @@ -22,34 +22,6 @@ } } }, - "semistandard": { - "globals": [ - "after", - "afterEach", - "before", - "beforeEach", - "describe", - "it" - ] - }, - "contributors": [ - "Ace Nassri ", - "Ahmet Alp Balkan ", - "Allen Day ", - "André Cipriani Bandarra ", - "Dominik Staskiewicz ", - "F. Hinkelmann ", - "Jason Dobry ", - "Jon Wayne Parrott ", - "Justin Beckwith ", - "Michael McDonald ", - "Sean McBreen ", - "Steren ", - "Steve Perry ", - "chenyumic ", - "renovate[bot] ", - "shollyman " - ], "scripts": { "start": "node app.js", "test": "repo-tools test app && ava -t 30s --tap test/*.test.js", diff --git a/3-binary-data/test/_api-tests.js b/3-binary-data/test/_api-tests.js index e422353ca..31af46300 100644 --- a/3-binary-data/test/_api-tests.js +++ b/3-binary-data/test/_api-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -26,12 +26,12 @@ module.exports = (DATA_BACKEND) => { appConfig.set(`DATA_BACKEND`, DATA_BACKEND); }); - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `beep` }) + .send({title: `beep`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `beep`); @@ -39,13 +39,13 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should list books`, (t) => { + test.serial.cb(`should list books`, t => { // Give Datastore time to become consistent setTimeout(() => { getRequest(testConfig) .get(`/api/books`) .expect(200) - .expect((response) => { + .expect(response => { t.true(Array.isArray(response.body.items)); t.true(response.body.items.length >= 1); }) @@ -53,11 +53,11 @@ module.exports = (DATA_BACKEND) => { }, 1000); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .delete(`/api/books/${id}/`) // .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `OK`); }) .end(t.end); diff --git a/3-binary-data/test/_crud-tests.js b/3-binary-data/test/_crud-tests.js index f4fc19a29..6fad68fee 100644 --- a/3-binary-data/test/_crud-tests.js +++ b/3-binary-data/test/_crud-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -27,12 +27,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -40,30 +40,30 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show a list of books`, (t) => { + test.serial.cb(`should show a list of books`, t => { // Give Datastore time to become consistent setTimeout(() => { const expected = /
/; getRequest(testConfig) .get(`/books`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }, 2000); }); - test.serial.cb(`should handle error`, (t) => { + test.serial.cb(`should handle error`, t => { getRequest(testConfig) .get(`/books`) - .query({ pageToken: `badrequest` }) + .query({pageToken: `badrequest`}) .expect(500) .end(t.end); }); // delete the book - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -74,13 +74,13 @@ module.exports = (DATA_BACKEND) => { } }); - test.serial.cb(`should post to add book form`, (t) => { + test.serial.cb(`should post to add book form`, t => { const expected = /Redirecting to \/books\//; getRequest(testConfig) .post(`/books/add`) .send(`title=my%20book`) .expect(302) - .expect((response) => { + .expect(response => { const location = response.headers.location; const idPart = location.replace(`/books/`, ``); id = idPart; @@ -89,19 +89,19 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show add book form`, (t) => { + test.serial.cb(`should show add book form`, t => { const expected = /Add book/; getRequest(testConfig) .get(`/books/add`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -113,12 +113,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb((t) => { + test.serial.cb(t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -126,47 +126,46 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should update a book`, (t) => { + test.serial.cb(`should update a book`, t => { const expected = new RegExp(`Redirecting to /books/${id}`); getRequest(testConfig) .post(`/books/${id}/edit`) .send(`title=my%20other%20book`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show edit book form`, (t) => { - const expected = - //; + test.serial.cb(`should show edit book form`, t => { + const expected = //; getRequest(testConfig) .get(`/books/${id}/edit`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show a book`, (t) => { + test.serial.cb(`should show a book`, t => { const expected = /

my other book <\/small><\/h4>/; getRequest(testConfig) .get(`/books/${id}`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { const expected = /Redirecting to \/books/; getRequest(testConfig) .get(`/books/${id}/delete`) .expect(302) - .expect((response) => { + .expect(response => { id = undefined; t.regex(response.text, expected); }) @@ -174,7 +173,7 @@ module.exports = (DATA_BACKEND) => { }); // clean up - test.always.after.cb((t) => { + test.always.after.cb(t => { appConfig.set(`DATA_BACKEND`, originalDataBackend); if (id) { diff --git a/3-binary-data/test/_test-config.js b/3-binary-data/test/_test-config.js index 4f05f339e..b21555909 100644 --- a/3-binary-data/test/_test-config.js +++ b/3-binary-data/test/_test-config.js @@ -24,10 +24,10 @@ module.exports = { cmd: `app`, port: PORT, env: { - PORT: PORT + PORT: PORT, }, url: `http://localhost:${PORT}`, version: process.env.GAE_VERSION || TESTNAME, project: process.env.GCLOUD_PROJECT, - msg: `Bookshelf` + msg: `Bookshelf`, }; diff --git a/3-binary-data/test/app.test.js b/3-binary-data/test/app.test.js index f8e288424..cbd8a48d5 100644 --- a/3-binary-data/test/app.test.js +++ b/3-binary-data/test/app.test.js @@ -19,33 +19,34 @@ const sinon = require(`sinon`); const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); -test.cb(`should redirect / to /books`, (t) => { - utils.getRequest(testConfig) +test.cb(`should redirect / to /books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, /Redirecting to \/books/); }) .end(t.end); }); -test(`should check config`, (t) => { +test(`should check config`, t => { const nconfMock = { argv: sinon.stub().returnsThis(), env: sinon.stub().returnsThis(), file: sinon.stub().returnsThis(), defaults: sinon.stub().returnsThis(), - get: function (setting) { + get: function(setting) { return this[setting]; - } + }, }; - function getMsg (setting) { + function getMsg(setting) { return `You must set ${setting} as an environment variable or in config.json!`; } const testFunc = () => { - proxyquire(`../config`, { nconf: nconfMock }); + proxyquire(`../config`, {nconf: nconfMock}); }; nconfMock.DATA_BACKEND = `datastore`; diff --git a/3-binary-data/test/cloudsql.test.js b/3-binary-data/test/cloudsql.test.js index ff2ba5fd5..3ac8fbe5c 100644 --- a/3-binary-data/test/cloudsql.test.js +++ b/3-binary-data/test/cloudsql.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || + process.env.TEST_CLOUDSQL +) { require(`./_api-tests`)(`cloudsql`); require(`./_crud-tests`)(`cloudsql`); } else { - test(`Skipping Cloud SQL tests...`, (t) => t.pass()); + test(`Skipping Cloud SQL tests...`, t => t.pass()); } diff --git a/3-binary-data/test/datastore.test.js b/3-binary-data/test/datastore.test.js index a4faa673b..6df5120c1 100644 --- a/3-binary-data/test/datastore.test.js +++ b/3-binary-data/test/datastore.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `datastore` || process.env.TEST_DATASTORE) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `datastore` || + process.env.TEST_DATASTORE +) { require(`./_api-tests`)(`datastore`); require(`./_crud-tests`)(`datastore`); } else { - test(`Skipping Cloud Datastore tests...`, (t) => t.pass()); + test(`Skipping Cloud Datastore tests...`, t => t.pass()); } diff --git a/4-auth/app.js b/4-auth/app.js index 5b7506e30..0b7a64156 100644 --- a/4-auth/app.js +++ b/4-auth/app.js @@ -33,20 +33,21 @@ const sessionConfig = { resave: false, saveUninitialized: false, secret: config.get('SECRET'), - signed: true + signed: true, }; // In production use the Memcache instance to store session data, // otherwise fallback to the default MemoryStore in development. if (config.get('NODE_ENV') === 'production' && config.get('MEMCACHE_URL')) { - if (config.get('MEMCACHE_USERNAME') && (config.get('MEMCACHE_PASSWORD'))) { + if (config.get('MEMCACHE_USERNAME') && config.get('MEMCACHE_PASSWORD')) { sessionConfig.store = new MemcachedStore({ servers: [config.get('MEMCACHE_URL')], username: config.get('MEMCACHE_USERNAME'), - password: config.get('MEMCACHE_PASSWORD')}); + password: config.get('MEMCACHE_PASSWORD'), + }); } else { sessionConfig.store = new MemcachedStore({ - servers: [config.get('MEMCACHE_URL')] + servers: [config.get('MEMCACHE_URL')], }); } } @@ -74,7 +75,7 @@ app.use((req, res) => { }); // Basic error handler -app.use((err, req, res, next) => { +app.use((err, req, res) => { /* jshint unused:false */ console.error(err); // If our routes specified a specific response, then send that. Otherwise, diff --git a/4-auth/books/api.js b/4-auth/books/api.js index 98b65fc58..c81c35520 100644 --- a/4-auth/books/api.js +++ b/4-auth/books/api.js @@ -16,7 +16,7 @@ const express = require('express'); const bodyParser = require('body-parser'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -38,7 +38,7 @@ router.get('/', (req, res, next) => { } res.json({ items: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -94,7 +94,7 @@ router.put('/:book', (req, res, next) => { * Delete a book. */ router.delete('/:book', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; @@ -111,7 +111,7 @@ router.use((err, req, res, next) => { // responding to the request err.response = { message: err.message, - internalCode: err.code + internalCode: err.code, }; next(err); }); diff --git a/4-auth/books/crud.js b/4-auth/books/crud.js index 1090bab3d..7ba1c07ab 100644 --- a/4-auth/books/crud.js +++ b/4-auth/books/crud.js @@ -17,7 +17,7 @@ const express = require('express'); const images = require('../lib/images'); const oauth2 = require('../lib/oauth2'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -46,7 +46,7 @@ router.get('/', (req, res, next) => { } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -59,14 +59,14 @@ router.get('/mine', oauth2.required, (req, res, next) => { req.user.id, 10, req.query.pageToken, - (err, entities, cursor, apiResponse) => { + (err, entities, cursor) => { if (err) { next(err); return; } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); } ); @@ -81,7 +81,7 @@ router.get('/mine', oauth2.required, (req, res, next) => { router.get('/add', (req, res) => { res.render('books/form.pug', { book: {}, - action: 'Add' + action: 'Add', }); }); @@ -137,7 +137,7 @@ router.get('/:book/edit', (req, res, next) => { } res.render('books/form.pug', { book: entity, - action: 'Edit' + action: 'Edit', }); }); }); @@ -182,7 +182,7 @@ router.get('/:book', (req, res, next) => { return; } res.render('books/view.pug', { - book: entity + book: entity, }); }); }); @@ -193,7 +193,7 @@ router.get('/:book', (req, res, next) => { * Delete a book. */ router.get('/:book/delete', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; diff --git a/4-auth/books/model-cloudsql.js b/4-auth/books/model-cloudsql.js index aed30d772..ba7cdc8c9 100644 --- a/4-auth/books/model-cloudsql.js +++ b/4-auth/books/model-cloudsql.js @@ -20,19 +20,23 @@ const config = require('../config'); const options = { user: config.get('MYSQL_USER'), password: config.get('MYSQL_PASSWORD'), - database: 'bookshelf' + database: 'bookshelf', }; -if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') { +if ( + config.get('INSTANCE_CONNECTION_NAME') && + config.get('NODE_ENV') === 'production' +) { options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`; } const connection = mysql.createConnection(options); -function list (limit, token, cb) { +function list(limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( - 'SELECT * FROM `books` LIMIT ? OFFSET ?', [limit, token], + 'SELECT * FROM `books` LIMIT ? OFFSET ?', + [limit, token], (err, results) => { if (err) { cb(err); @@ -45,7 +49,7 @@ function list (limit, token, cb) { } // [START listby] -function listBy (userId, limit, token, cb) { +function listBy(userId, limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( 'SELECT * FROM `books` WHERE `createdById` = ? LIMIT ? OFFSET ?', @@ -57,11 +61,12 @@ function listBy (userId, limit, token, cb) { } const hasMore = results.length === limit ? token + results.length : false; cb(null, results, hasMore); - }); + } + ); } // [END listby] -function create (data, cb) { +function create(data, cb) { connection.query('INSERT INTO `books` SET ?', data, (err, res) => { if (err) { cb(err); @@ -71,13 +76,15 @@ function create (data, cb) { }); } -function read (id, cb) { +function read(id, cb) { connection.query( - 'SELECT * FROM `books` WHERE `id` = ?', id, (err, results) => { + 'SELECT * FROM `books` WHERE `id` = ?', + id, + (err, results) => { if (!err && !results.length) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -85,21 +92,21 @@ function read (id, cb) { return; } cb(null, results[0]); - }); + } + ); } -function update (id, data, cb) { - connection.query( - 'UPDATE `books` SET ? WHERE `id` = ?', [data, id], (err) => { - if (err) { - cb(err); - return; - } - read(id, cb); - }); +function update(id, data, cb) { + connection.query('UPDATE `books` SET ? WHERE `id` = ?', [data, id], err => { + if (err) { + cb(err); + return; + } + read(id, cb); + }); } -function _delete (id, cb) { +function _delete(id, cb) { connection.query('DELETE FROM `books` WHERE `id` = ?', id, cb); } @@ -110,7 +117,7 @@ module.exports = { create: create, read: read, update: update, - delete: _delete + delete: _delete, }; if (module === require.main) { @@ -119,7 +126,8 @@ if (module === require.main) { console.log( `Running this script directly will allow you to initialize your mysql - database.\n This script will not modify any existing tables.\n`); + database.\n This script will not modify any existing tables.\n` + ); prompt.get(['user', 'password'], (err, result) => { if (err) { @@ -129,10 +137,15 @@ if (module === require.main) { }); } -function createSchema (config) { - const connection = mysql.createConnection(extend({ - multipleStatements: true - }, config)); +function createSchema(config) { + const connection = mysql.createConnection( + extend( + { + multipleStatements: true, + }, + config + ) + ); connection.query( `CREATE DATABASE IF NOT EXISTS \`bookshelf\` @@ -149,7 +162,7 @@ function createSchema (config) { \`createdBy\` VARCHAR(255) NULL, \`createdById\` VARCHAR(255) NULL, PRIMARY KEY (\`id\`));`, - (err) => { + err => { if (err) { throw err; } diff --git a/4-auth/books/model-datastore.js b/4-auth/books/model-datastore.js index e24916630..56af38a12 100644 --- a/4-auth/books/model-datastore.js +++ b/4-auth/books/model-datastore.js @@ -17,7 +17,7 @@ const Datastore = require('@google-cloud/datastore'); const config = require('../config'); const ds = Datastore({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const kind = 'Book'; @@ -37,7 +37,7 @@ const kind = 'Book'; // id: id, // property: value // } -function fromDatastore (obj) { +function fromDatastore(obj) { obj.id = obj[Datastore.KEY].id; return obj; } @@ -65,17 +65,17 @@ function fromDatastore (obj) { // excludeFromIndexes: true // } // ] -function toDatastore (obj, nonIndexed) { +function toDatastore(obj, nonIndexed) { nonIndexed = nonIndexed || []; let results = []; - Object.keys(obj).forEach((k) => { + Object.keys(obj).forEach(k => { if (obj[k] === undefined) { return; } results.push({ name: k, value: obj[k], - excludeFromIndexes: nonIndexed.indexOf(k) !== -1 + excludeFromIndexes: nonIndexed.indexOf(k) !== -1, }); }); return results; @@ -85,8 +85,9 @@ function toDatastore (obj, nonIndexed) { // The ``limit`` argument determines the maximum amount of results to // return per page. The ``token`` argument allows requesting additional // pages. The callback is invoked with ``(err, books, nextPageToken)``. -function list (limit, token, cb) { - const q = ds.createQuery([kind]) +function list(limit, token, cb) { + const q = ds + .createQuery([kind]) .limit(limit) .order('title') .start(token); @@ -96,7 +97,10 @@ function list (limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } @@ -104,8 +108,9 @@ function list (limit, token, cb) { // Similar to ``list``, but only lists the books created by the specified // user. // [START listby] -function listBy (userId, limit, token, cb) { - const q = ds.createQuery([kind]) +function listBy(userId, limit, token, cb) { + const q = ds + .createQuery([kind]) .filter('createdById', '=', userId) .limit(limit) .start(token); @@ -115,7 +120,10 @@ function listBy (userId, limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } @@ -124,7 +132,7 @@ function listBy (userId, limit, token, cb) { // Creates a new book or updates an existing book with new data. The provided // data is automatically translated into Datastore format. The book will be // queued for background processing. -function update (id, data, cb) { +function update(id, data, cb) { let key; if (id) { key = ds.key([kind, parseInt(id, 10)]); @@ -134,25 +142,22 @@ function update (id, data, cb) { const entity = { key: key, - data: toDatastore(data, ['description']) + data: toDatastore(data, ['description']), }; - ds.save( - entity, - (err) => { - data.id = entity.key.id; - cb(err, err ? null : data); - } - ); + ds.save(entity, err => { + data.id = entity.key.id; + cb(err, err ? null : data); + }); } -function read (id, cb) { +function read(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.get(key, (err, entity) => { if (!err && !entity) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -163,7 +168,7 @@ function read (id, cb) { }); } -function _delete (id, cb) { +function _delete(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.delete(key, cb); } @@ -176,5 +181,5 @@ module.exports = { update: update, delete: _delete, list: list, - listBy: listBy + listBy: listBy, }; diff --git a/4-auth/config.js b/4-auth/config.js index 6602fcd69..7bdcb1bae 100644 --- a/4-auth/config.js +++ b/4-auth/config.js @@ -15,7 +15,7 @@ // Hierarchical node.js configuration with command-line arguments, environment // variables, and files. -const nconf = module.exports = require('nconf'); +const nconf = (module.exports = require('nconf')); const path = require('path'); nconf @@ -37,10 +37,10 @@ nconf 'OAUTH2_CLIENT_SECRET', 'OAUTH2_CALLBACK', 'PORT', - 'SECRET' + 'SECRET', ]) // 3. Config file - .file({ file: path.join(__dirname, 'config.json') }) + .file({file: path.join(__dirname, 'config.json')}) // 4. Defaults .defaults({ // Typically you will create a bucket with the same name as your project ID. @@ -65,7 +65,7 @@ nconf PORT: 8080, // Set this a secret string of your choosing - SECRET: 'keyboardcat' + SECRET: 'keyboardcat', }); // Check for required settings @@ -82,8 +82,10 @@ if (nconf.get('DATA_BACKEND') === 'cloudsql') { } } -function checkConfig (setting) { +function checkConfig(setting) { if (!nconf.get(setting)) { - throw new Error(`You must set ${setting} as an environment variable or in config.json!`); + throw new Error( + `You must set ${setting} as an environment variable or in config.json!` + ); } } diff --git a/4-auth/lib/images.js b/4-auth/lib/images.js index 174c32c65..c9456a887 100644 --- a/4-auth/lib/images.js +++ b/4-auth/lib/images.js @@ -19,14 +19,14 @@ const config = require('../config'); const CLOUD_BUCKET = config.get('CLOUD_BUCKET'); const storage = Storage({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const bucket = storage.bucket(CLOUD_BUCKET); // Returns the public, anonymously accessable URL to a given Cloud Storage // object. // The object's ACL has to be set to public read. -function getPublicUrl (filename) { +function getPublicUrl(filename) { return `https://storage.googleapis.com/${CLOUD_BUCKET}/${filename}`; } @@ -34,7 +34,7 @@ function getPublicUrl (filename) { // req.file is processed and will have two new properties: // * ``cloudStorageObject`` the object name in cloud storage. // * ``cloudStoragePublicUrl`` the public url to the object. -function sendUploadToGCS (req, res, next) { +function sendUploadToGCS(req, res, next) { if (!req.file) { return next(); } @@ -43,12 +43,12 @@ function sendUploadToGCS (req, res, next) { const file = bucket.file(gcsname); const stream = file.createWriteStream({ metadata: { - contentType: req.file.mimetype + contentType: req.file.mimetype, }, - resumable: false + resumable: false, }); - stream.on('error', (err) => { + stream.on('error', err => { req.file.cloudStorageError = err; next(err); }); @@ -71,12 +71,12 @@ const Multer = require('multer'); const multer = Multer({ storage: Multer.MemoryStorage, limits: { - fileSize: 5 * 1024 * 1024 // no larger than 5mb - } + fileSize: 5 * 1024 * 1024, // no larger than 5mb + }, }); module.exports = { getPublicUrl, sendUploadToGCS, - multer + multer, }; diff --git a/4-auth/lib/oauth2.js b/4-auth/lib/oauth2.js index fa63e7270..f4ac3ec84 100644 --- a/4-auth/lib/oauth2.js +++ b/4-auth/lib/oauth2.js @@ -20,7 +20,7 @@ const config = require('../config'); const passport = require('passport'); const GoogleStrategy = require('passport-google-oauth20').Strategy; -function extractProfile (profile) { +function extractProfile(profile) { let imageUrl = ''; if (profile.photos && profile.photos.length) { imageUrl = profile.photos[0].value; @@ -28,7 +28,7 @@ function extractProfile (profile) { return { id: profile.id, displayName: profile.displayName, - image: imageUrl + image: imageUrl, }; } @@ -39,16 +39,21 @@ function extractProfile (profile) { // along with the user's profile. The function must invoke `cb` with a user // object, which will be set at `req.user` in route handlers after // authentication. -passport.use(new GoogleStrategy({ - clientID: config.get('OAUTH2_CLIENT_ID'), - clientSecret: config.get('OAUTH2_CLIENT_SECRET'), - callbackURL: config.get('OAUTH2_CALLBACK'), - accessType: 'offline' -}, (accessToken, refreshToken, profile, cb) => { - // Extract the minimal profile information we need from the profile object - // provided by Google - cb(null, extractProfile(profile)); -})); +passport.use( + new GoogleStrategy( + { + clientID: config.get('OAUTH2_CLIENT_ID'), + clientSecret: config.get('OAUTH2_CLIENT_SECRET'), + callbackURL: config.get('OAUTH2_CALLBACK'), + accessType: 'offline', + }, + (accessToken, refreshToken, profile, cb) => { + // Extract the minimal profile information we need from the profile object + // provided by Google + cb(null, extractProfile(profile)); + } + ) +); passport.serializeUser((user, cb) => { cb(null, user); @@ -64,7 +69,7 @@ const router = express.Router(); // Middleware that requires the user to be logged in. If the user is not logged // in, it will redirect the user to authorize the application and then return // them to the original URL they requested. -function authRequired (req, res, next) { +function authRequired(req, res, next) { if (!req.user) { req.session.oauth2return = req.originalUrl; return res.redirect('/auth/login'); @@ -74,10 +79,14 @@ function authRequired (req, res, next) { // Middleware that exposes the user's profile as well as login/logout URLs to // any templates. These are available as `profile`, `login`, and `logout`. -function addTemplateVariables (req, res, next) { +function addTemplateVariables(req, res, next) { res.locals.profile = req.user; - res.locals.login = `/auth/login?return=${encodeURIComponent(req.originalUrl)}`; - res.locals.logout = `/auth/logout?return=${encodeURIComponent(req.originalUrl)}`; + res.locals.login = `/auth/login?return=${encodeURIComponent( + req.originalUrl + )}`; + res.locals.logout = `/auth/logout?return=${encodeURIComponent( + req.originalUrl + )}`; next(); } // [END middleware] @@ -102,7 +111,7 @@ router.get( }, // Start OAuth 2 flow using Passport.js - passport.authenticate('google', { scope: ['email', 'profile'] }) + passport.authenticate('google', {scope: ['email', 'profile']}) ); // [END authorize] @@ -135,5 +144,5 @@ module.exports = { extractProfile: extractProfile, router: router, required: authRequired, - template: addTemplateVariables + template: addTemplateVariables, }; diff --git a/4-auth/package.json b/4-auth/package.json index b3160efeb..9ac90e57a 100644 --- a/4-auth/package.json +++ b/4-auth/package.json @@ -22,34 +22,6 @@ } } }, - "semistandard": { - "globals": [ - "after", - "afterEach", - "before", - "beforeEach", - "describe", - "it" - ] - }, - "contributors": [ - "Ace Nassri ", - "Ahmet Alp Balkan ", - "Allen Day ", - "André Cipriani Bandarra ", - "Dominik Staskiewicz ", - "F. Hinkelmann ", - "Jason Dobry ", - "Jon Wayne Parrott ", - "Justin Beckwith ", - "Michael McDonald ", - "Sean McBreen ", - "Steren ", - "Steve Perry ", - "chenyumic ", - "renovate[bot] ", - "shollyman " - ], "scripts": { "start": "node app.js", "test": "repo-tools test app && ava -t 30s --tap test/*.test.js", @@ -78,6 +50,7 @@ "@google-cloud/nodejs-repo-tools": "^3.0.0", "ava": "0.25.0", "proxyquire": "2.1.0", - "sinon": "^7.0.0" + "sinon": "^7.0.0", + "supertest": "^3.3.0" } } diff --git a/4-auth/test/_api-tests.js b/4-auth/test/_api-tests.js index e422353ca..31af46300 100644 --- a/4-auth/test/_api-tests.js +++ b/4-auth/test/_api-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -26,12 +26,12 @@ module.exports = (DATA_BACKEND) => { appConfig.set(`DATA_BACKEND`, DATA_BACKEND); }); - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `beep` }) + .send({title: `beep`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `beep`); @@ -39,13 +39,13 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should list books`, (t) => { + test.serial.cb(`should list books`, t => { // Give Datastore time to become consistent setTimeout(() => { getRequest(testConfig) .get(`/api/books`) .expect(200) - .expect((response) => { + .expect(response => { t.true(Array.isArray(response.body.items)); t.true(response.body.items.length >= 1); }) @@ -53,11 +53,11 @@ module.exports = (DATA_BACKEND) => { }, 1000); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .delete(`/api/books/${id}/`) // .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `OK`); }) .end(t.end); diff --git a/4-auth/test/_crud-tests.js b/4-auth/test/_crud-tests.js index 53abe89e2..9be19cd3f 100644 --- a/4-auth/test/_crud-tests.js +++ b/4-auth/test/_crud-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -27,12 +27,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -40,30 +40,30 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show a list of books`, (t) => { + test.serial.cb(`should show a list of books`, t => { // Give Datastore time to become consistent setTimeout(() => { const expected = /
/; getRequest(testConfig) .get(`/books`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }, 2000); }); - test.serial.cb(`should handle error`, (t) => { + test.serial.cb(`should handle error`, t => { getRequest(testConfig) .get(`/books`) - .query({ pageToken: `badrequest` }) + .query({pageToken: `badrequest`}) .expect(500) .end(t.end); }); // delete the book - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -74,13 +74,13 @@ module.exports = (DATA_BACKEND) => { } }); - test.serial.cb(`should post to add book form`, (t) => { + test.serial.cb(`should post to add book form`, t => { const expected = /Redirecting to \/books\//; getRequest(testConfig) .post(`/books/add`) .field(`title`, `my book`) .expect(302) - .expect((response) => { + .expect(response => { const location = response.headers.location; const idPart = location.replace(`/books/`, ``); id = idPart; @@ -89,19 +89,19 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show add book form`, (t) => { + test.serial.cb(`should show add book form`, t => { const expected = /Add book/; getRequest(testConfig) .get(`/books/add`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -113,12 +113,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb((t) => { + test.serial.cb(t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -126,46 +126,46 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should update a book`, (t) => { + test.serial.cb(`should update a book`, t => { const expected = new RegExp(`Redirecting to /books/${id}`); getRequest(testConfig) .post(`/books/${id}/edit`) .field(`title`, `my other book`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show edit book form`, (t) => { + test.serial.cb(`should show edit book form`, t => { const expected = /"title" value="my other book"/; getRequest(testConfig) .get(`/books/${id}/edit`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show a book`, (t) => { + test.serial.cb(`should show a book`, t => { const expected = /

my other book <\/small><\/h4>/; getRequest(testConfig) .get(`/books/${id}`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { const expected = /Redirecting to \/books/; getRequest(testConfig) .get(`/books/${id}/delete`) .expect(302) - .expect((response) => { + .expect(response => { id = undefined; t.regex(response.text, expected); }) @@ -173,7 +173,7 @@ module.exports = (DATA_BACKEND) => { }); // clean up - test.always.after.cb((t) => { + test.always.after.cb(t => { appConfig.set(`DATA_BACKEND`, originalDataBackend); if (id) { diff --git a/4-auth/test/_test-config.js b/4-auth/test/_test-config.js index 4f5706a2d..2298626ac 100644 --- a/4-auth/test/_test-config.js +++ b/4-auth/test/_test-config.js @@ -24,10 +24,10 @@ module.exports = { cmd: `app`, port: PORT, env: { - PORT: PORT + PORT: PORT, }, url: `http://localhost:${PORT}`, version: process.env.GAE_VERSION || TESTNAME, project: process.env.GCLOUD_PROJECT, - msg: `Bookshelf` + msg: `Bookshelf`, }; diff --git a/4-auth/test/app.test.js b/4-auth/test/app.test.js index 0e03b050c..a33b28615 100644 --- a/4-auth/test/app.test.js +++ b/4-auth/test/app.test.js @@ -19,33 +19,34 @@ const sinon = require(`sinon`); const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); -test.cb(`should redirect / to /books`, (t) => { - utils.getRequest(testConfig) +test.cb(`should redirect / to /books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, /Redirecting to \/books/); }) .end(t.end); }); -test(`should check config`, (t) => { +test(`should check config`, t => { const nconfMock = { argv: sinon.stub().returnsThis(), env: sinon.stub().returnsThis(), file: sinon.stub().returnsThis(), defaults: sinon.stub().returnsThis(), - get: function (setting) { + get: function(setting) { return this[setting]; - } + }, }; - function getMsg (setting) { + function getMsg(setting) { return `You must set ${setting} as an environment variable or in config.json!`; } const testFunc = () => { - proxyquire(`../config`, { nconf: nconfMock }); + proxyquire(`../config`, {nconf: nconfMock}); }; nconfMock.DATA_BACKEND = `datastore`; diff --git a/4-auth/test/cloudsql.test.js b/4-auth/test/cloudsql.test.js index ff2ba5fd5..3ac8fbe5c 100644 --- a/4-auth/test/cloudsql.test.js +++ b/4-auth/test/cloudsql.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || + process.env.TEST_CLOUDSQL +) { require(`./_api-tests`)(`cloudsql`); require(`./_crud-tests`)(`cloudsql`); } else { - test(`Skipping Cloud SQL tests...`, (t) => t.pass()); + test(`Skipping Cloud SQL tests...`, t => t.pass()); } diff --git a/4-auth/test/datastore.test.js b/4-auth/test/datastore.test.js index a4faa673b..6df5120c1 100644 --- a/4-auth/test/datastore.test.js +++ b/4-auth/test/datastore.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `datastore` || process.env.TEST_DATASTORE) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `datastore` || + process.env.TEST_DATASTORE +) { require(`./_api-tests`)(`datastore`); require(`./_crud-tests`)(`datastore`); } else { - test(`Skipping Cloud Datastore tests...`, (t) => t.pass()); + test(`Skipping Cloud Datastore tests...`, t => t.pass()); } diff --git a/4-auth/test/oauth2.test.js b/4-auth/test/oauth2.test.js index a384a0f3e..40fdcd558 100644 --- a/4-auth/test/oauth2.test.js +++ b/4-auth/test/oauth2.test.js @@ -32,26 +32,26 @@ const getPassportMock = () => { authenticate: sinon.stub().returns((req, res, next) => { req.session.oauth2return = `/another/path`; next(); - }) + }), }; }; -test.cb(`should start authorization`, (t) => { +test.cb(`should start authorization`, t => { const passportMock = getPassportMock(); - passportMock.authenticate = sinon.stub().returns((req, res, next) => { + passportMock.authenticate = sinon.stub().returns((req, res) => { t.is(req.session.oauth2return, `/some/path`); res.redirect(`/auth/google/callback?code=foo`); }); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/login?return=%2Fsome%2Fpath`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/auth\/google\/callback\?code=foo/); t.true(passportMock.initialize.calledOnce); @@ -61,29 +61,28 @@ test.cb(`should start authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test.cb(`should finish authorization`, (t) => { +test.cb(`should finish authorization`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const app = proxyquire(`../app`, { passport: passportMock, - './lib/oauth2': oauth2 + './lib/oauth2': oauth2, }); request(app) .get(`/auth/google/callback?code=foo`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/another\/path/); t.true(passportMock.initialize.calledOnce); @@ -93,22 +92,21 @@ test.cb(`should finish authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); t.deepEqual( oauth2.extractProfile({ - photos: [{ value: `image.jpg` }], + photos: [{value: `image.jpg`}], id: 1, - displayName: `Joe Developer` + displayName: `Joe Developer`, }), { id: 1, displayName: `Joe Developer`, - image: `image.jpg` + image: `image.jpg`, } ); const serializeUser = passportMock.serializeUser.firstCall.args[0]; @@ -127,18 +125,18 @@ test.cb(`should finish authorization`, (t) => { .end(t.end); }); -test.cb(`should logout`, (t) => { +test.cb(`should logout`, t => { const passportMock = getPassportMock(); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/logout`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \//); t.true(passportMock.initialize.calledOnce); @@ -148,28 +146,27 @@ test.cb(`should logout`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test(`should require authentication`, (t) => { +test(`should require authentication`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: {}, - session: {} + session: {}, }; const res = { - redirect: sinon.stub() + redirect: sinon.stub(), }; const next = sinon.stub(); oauth2.required(req, res, next); @@ -183,30 +180,32 @@ test(`should require authentication`, (t) => { t.is(res.redirect.firstCall.args[0], `/auth/login`); }); -test(`should add template variables`, (t) => { +test(`should add template variables`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: { id: 1, displayName: `Joe Developer`, - image: `image.jpg` - } + image: `image.jpg`, + }, }; const res = { - locals: {} + locals: {}, }; const next = sinon.stub(); oauth2.template(req, res, next); t.true(next.calledOnce); t.is(res.locals.profile, req.user); t.is( - res.locals.login, `/auth/login?return=${encodeURIComponent(req.originalUrl)}` + res.locals.login, + `/auth/login?return=${encodeURIComponent(req.originalUrl)}` ); t.is( - res.locals.logout, `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` + res.locals.logout, + `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` ); }); diff --git a/5-logging/app.js b/5-logging/app.js index 0fb91fc8b..8ea8a6510 100644 --- a/5-logging/app.js +++ b/5-logging/app.js @@ -47,20 +47,21 @@ const sessionConfig = { resave: false, saveUninitialized: false, secret: config.get('SECRET'), - signed: true + signed: true, }; // In production use the Memcache instance to store session data, // otherwise fallback to the default MemoryStore in development. if (config.get('NODE_ENV') === 'production' && config.get('MEMCACHE_URL')) { - if (config.get('MEMCACHE_USERNAME') && (config.get('MEMCACHE_PASSWORD'))) { + if (config.get('MEMCACHE_USERNAME') && config.get('MEMCACHE_PASSWORD')) { sessionConfig.store = new MemcachedStore({ servers: [config.get('MEMCACHE_URL')], username: config.get('MEMCACHE_USERNAME'), - password: config.get('MEMCACHE_PASSWORD')}); + password: config.get('MEMCACHE_PASSWORD'), + }); } else { sessionConfig.store = new MemcachedStore({ - servers: [config.get('MEMCACHE_URL')] + servers: [config.get('MEMCACHE_URL')], }); } } @@ -93,7 +94,7 @@ app.use((req, res) => { }); // Basic error handler -app.use((err, req, res, next) => { +app.use((err, req, res) => { /* jshint unused:false */ // If our routes specified a specific response, then send that. Otherwise, // send a generic message so as not to leak anything. diff --git a/5-logging/books/api.js b/5-logging/books/api.js index 98b65fc58..c81c35520 100644 --- a/5-logging/books/api.js +++ b/5-logging/books/api.js @@ -16,7 +16,7 @@ const express = require('express'); const bodyParser = require('body-parser'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -38,7 +38,7 @@ router.get('/', (req, res, next) => { } res.json({ items: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -94,7 +94,7 @@ router.put('/:book', (req, res, next) => { * Delete a book. */ router.delete('/:book', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; @@ -111,7 +111,7 @@ router.use((err, req, res, next) => { // responding to the request err.response = { message: err.message, - internalCode: err.code + internalCode: err.code, }; next(err); }); diff --git a/5-logging/books/crud.js b/5-logging/books/crud.js index 896603b7b..06d902a96 100644 --- a/5-logging/books/crud.js +++ b/5-logging/books/crud.js @@ -17,7 +17,7 @@ const express = require('express'); const images = require('../lib/images'); const oauth2 = require('../lib/oauth2'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -46,7 +46,7 @@ router.get('/', (req, res, next) => { } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -58,14 +58,14 @@ router.get('/mine', oauth2.required, (req, res, next) => { req.user.id, 10, req.query.pageToken, - (err, entities, cursor, apiResponse) => { + (err, entities, cursor) => { if (err) { next(err); return; } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); } ); @@ -79,7 +79,7 @@ router.get('/mine', oauth2.required, (req, res, next) => { router.get('/add', (req, res) => { res.render('books/form.pug', { book: {}, - action: 'Add' + action: 'Add', }); }); @@ -135,7 +135,7 @@ router.get('/:book/edit', (req, res, next) => { } res.render('books/form.pug', { book: entity, - action: 'Edit' + action: 'Edit', }); }); }); @@ -180,7 +180,7 @@ router.get('/:book', (req, res, next) => { return; } res.render('books/view.pug', { - book: entity + book: entity, }); }); }); @@ -191,7 +191,7 @@ router.get('/:book', (req, res, next) => { * Delete a book. */ router.get('/:book/delete', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; diff --git a/5-logging/books/model-cloudsql.js b/5-logging/books/model-cloudsql.js index 1b9f30c25..b51bcd928 100644 --- a/5-logging/books/model-cloudsql.js +++ b/5-logging/books/model-cloudsql.js @@ -20,19 +20,23 @@ const config = require('../config'); const options = { user: config.get('MYSQL_USER'), password: config.get('MYSQL_PASSWORD'), - database: 'bookshelf' + database: 'bookshelf', }; -if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') { +if ( + config.get('INSTANCE_CONNECTION_NAME') && + config.get('NODE_ENV') === 'production' +) { options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`; } const connection = mysql.createConnection(options); -function list (limit, token, cb) { +function list(limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( - 'SELECT * FROM `books` LIMIT ? OFFSET ?', [limit, token], + 'SELECT * FROM `books` LIMIT ? OFFSET ?', + [limit, token], (err, results) => { if (err) { cb(err); @@ -44,7 +48,7 @@ function list (limit, token, cb) { ); } -function listBy (userId, limit, token, cb) { +function listBy(userId, limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( 'SELECT * FROM `books` WHERE `createdById` = ? LIMIT ? OFFSET ?', @@ -56,10 +60,11 @@ function listBy (userId, limit, token, cb) { } const hasMore = results.length === limit ? token + results.length : false; cb(null, results, hasMore); - }); + } + ); } -function create (data, cb) { +function create(data, cb) { connection.query('INSERT INTO `books` SET ?', data, (err, res) => { if (err) { cb(err); @@ -69,13 +74,15 @@ function create (data, cb) { }); } -function read (id, cb) { +function read(id, cb) { connection.query( - 'SELECT * FROM `books` WHERE `id` = ?', id, (err, results) => { + 'SELECT * FROM `books` WHERE `id` = ?', + id, + (err, results) => { if (!err && !results.length) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -83,21 +90,21 @@ function read (id, cb) { return; } cb(null, results[0]); - }); + } + ); } -function update (id, data, cb) { - connection.query( - 'UPDATE `books` SET ? WHERE `id` = ?', [data, id], (err) => { - if (err) { - cb(err); - return; - } - read(id, cb); - }); +function update(id, data, cb) { + connection.query('UPDATE `books` SET ? WHERE `id` = ?', [data, id], err => { + if (err) { + cb(err); + return; + } + read(id, cb); + }); } -function _delete (id, cb) { +function _delete(id, cb) { connection.query('DELETE FROM `books` WHERE `id` = ?', id, cb); } @@ -108,7 +115,7 @@ module.exports = { create: create, read: read, update: update, - delete: _delete + delete: _delete, }; if (module === require.main) { @@ -117,7 +124,8 @@ if (module === require.main) { console.log( `Running this script directly will allow you to initialize your mysql - database.\n This script will not modify any existing tables.\n`); + database.\n This script will not modify any existing tables.\n` + ); prompt.get(['user', 'password'], (err, result) => { if (err) { @@ -127,10 +135,15 @@ if (module === require.main) { }); } -function createSchema (config) { - const connection = mysql.createConnection(extend({ - multipleStatements: true - }, config)); +function createSchema(config) { + const connection = mysql.createConnection( + extend( + { + multipleStatements: true, + }, + config + ) + ); connection.query( `CREATE DATABASE IF NOT EXISTS \`bookshelf\` @@ -147,7 +160,7 @@ function createSchema (config) { \`createdBy\` VARCHAR(255) NULL, \`createdById\` VARCHAR(255) NULL, PRIMARY KEY (\`id\`));`, - (err) => { + err => { if (err) { throw err; } diff --git a/5-logging/books/model-datastore.js b/5-logging/books/model-datastore.js index 2f9470e74..e1ec141e9 100644 --- a/5-logging/books/model-datastore.js +++ b/5-logging/books/model-datastore.js @@ -17,7 +17,7 @@ const Datastore = require('@google-cloud/datastore'); const config = require('../config'); const ds = Datastore({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const kind = 'Book'; @@ -37,7 +37,7 @@ const kind = 'Book'; // id: id, // property: value // } -function fromDatastore (obj) { +function fromDatastore(obj) { obj.id = obj[Datastore.KEY].id; return obj; } @@ -65,17 +65,17 @@ function fromDatastore (obj) { // excludeFromIndexes: true // } // ] -function toDatastore (obj, nonIndexed) { +function toDatastore(obj, nonIndexed) { nonIndexed = nonIndexed || []; let results = []; - Object.keys(obj).forEach((k) => { + Object.keys(obj).forEach(k => { if (obj[k] === undefined) { return; } results.push({ name: k, value: obj[k], - excludeFromIndexes: nonIndexed.indexOf(k) !== -1 + excludeFromIndexes: nonIndexed.indexOf(k) !== -1, }); }); return results; @@ -85,8 +85,9 @@ function toDatastore (obj, nonIndexed) { // The ``limit`` argument determines the maximum amount of results to // return per page. The ``token`` argument allows requesting additional // pages. The callback is invoked with ``(err, books, nextPageToken)``. -function list (limit, token, cb) { - const q = ds.createQuery([kind]) +function list(limit, token, cb) { + const q = ds + .createQuery([kind]) .limit(limit) .order('title') .start(token); @@ -96,15 +97,19 @@ function list (limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } // Similar to ``list``, but only lists the books created by the specified // user. -function listBy (userId, limit, token, cb) { - const q = ds.createQuery([kind]) +function listBy(userId, limit, token, cb) { + const q = ds + .createQuery([kind]) .filter('createdById', '=', userId) .limit(limit) .start(token); @@ -114,7 +119,10 @@ function listBy (userId, limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } @@ -122,7 +130,7 @@ function listBy (userId, limit, token, cb) { // Creates a new book or updates an existing book with new data. The provided // data is automatically translated into Datastore format. The book will be // queued for background processing. -function update (id, data, cb) { +function update(id, data, cb) { let key; if (id) { key = ds.key([kind, parseInt(id, 10)]); @@ -132,25 +140,22 @@ function update (id, data, cb) { const entity = { key: key, - data: toDatastore(data, ['description']) + data: toDatastore(data, ['description']), }; - ds.save( - entity, - (err) => { - data.id = entity.key.id; - cb(err, err ? null : data); - } - ); + ds.save(entity, err => { + data.id = entity.key.id; + cb(err, err ? null : data); + }); } -function read (id, cb) { +function read(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.get(key, (err, entity) => { if (!err && !entity) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -161,7 +166,7 @@ function read (id, cb) { }); } -function _delete (id, cb) { +function _delete(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.delete(key, cb); } @@ -174,5 +179,5 @@ module.exports = { update: update, delete: _delete, list: list, - listBy: listBy + listBy: listBy, }; diff --git a/5-logging/config.js b/5-logging/config.js index 68b3c5d05..c82b9d5a7 100644 --- a/5-logging/config.js +++ b/5-logging/config.js @@ -15,7 +15,7 @@ // Hierarchical node.js configuration with command-line arguments, environment // variables, and files. -const nconf = module.exports = require('nconf'); +const nconf = (module.exports = require('nconf')); const path = require('path'); nconf @@ -37,10 +37,10 @@ nconf 'OAUTH2_CLIENT_SECRET', 'OAUTH2_CALLBACK', 'PORT', - 'SECRET' + 'SECRET', ]) // 3. Config file - .file({ file: path.join(__dirname, 'config.json') }) + .file({file: path.join(__dirname, 'config.json')}) // 4. Defaults .defaults({ // Typically you will create a bucket with the same name as your project ID. @@ -65,7 +65,7 @@ nconf PORT: 8080, // Set this a secret string of your choosing - SECRET: 'keyboardcat' + SECRET: 'keyboardcat', }); // Check for required settings @@ -82,8 +82,10 @@ if (nconf.get('DATA_BACKEND') === 'cloudsql') { } } -function checkConfig (setting) { +function checkConfig(setting) { if (!nconf.get(setting)) { - throw new Error(`You must set ${setting} as an environment variable or in config.json!`); + throw new Error( + `You must set ${setting} as an environment variable or in config.json!` + ); } } diff --git a/5-logging/lib/images.js b/5-logging/lib/images.js index 174c32c65..c9456a887 100644 --- a/5-logging/lib/images.js +++ b/5-logging/lib/images.js @@ -19,14 +19,14 @@ const config = require('../config'); const CLOUD_BUCKET = config.get('CLOUD_BUCKET'); const storage = Storage({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const bucket = storage.bucket(CLOUD_BUCKET); // Returns the public, anonymously accessable URL to a given Cloud Storage // object. // The object's ACL has to be set to public read. -function getPublicUrl (filename) { +function getPublicUrl(filename) { return `https://storage.googleapis.com/${CLOUD_BUCKET}/${filename}`; } @@ -34,7 +34,7 @@ function getPublicUrl (filename) { // req.file is processed and will have two new properties: // * ``cloudStorageObject`` the object name in cloud storage. // * ``cloudStoragePublicUrl`` the public url to the object. -function sendUploadToGCS (req, res, next) { +function sendUploadToGCS(req, res, next) { if (!req.file) { return next(); } @@ -43,12 +43,12 @@ function sendUploadToGCS (req, res, next) { const file = bucket.file(gcsname); const stream = file.createWriteStream({ metadata: { - contentType: req.file.mimetype + contentType: req.file.mimetype, }, - resumable: false + resumable: false, }); - stream.on('error', (err) => { + stream.on('error', err => { req.file.cloudStorageError = err; next(err); }); @@ -71,12 +71,12 @@ const Multer = require('multer'); const multer = Multer({ storage: Multer.MemoryStorage, limits: { - fileSize: 5 * 1024 * 1024 // no larger than 5mb - } + fileSize: 5 * 1024 * 1024, // no larger than 5mb + }, }); module.exports = { getPublicUrl, sendUploadToGCS, - multer + multer, }; diff --git a/5-logging/lib/logging.js b/5-logging/lib/logging.js index 62326369f..9a376123b 100644 --- a/5-logging/lib/logging.js +++ b/5-logging/lib/logging.js @@ -15,7 +15,8 @@ const winston = require('winston'); const expressWinston = require('express-winston'); -const StackdriverTransport = require('@google-cloud/logging-winston').LoggingWinston; +const StackdriverTransport = require('@google-cloud/logging-winston') + .LoggingWinston; const colorize = process.env.NODE_ENV !== 'production'; @@ -26,11 +27,11 @@ const requestLogger = expressWinston.logger({ new StackdriverTransport(), new winston.transports.Console({ json: false, - colorize: colorize - }) + colorize: colorize, + }), ], expressFormat: true, - meta: false + meta: false, }); // [END requests] @@ -41,9 +42,9 @@ const errorLogger = expressWinston.errorLogger({ new StackdriverTransport(), new winston.transports.Console({ json: true, - colorize: colorize - }) - ] + colorize: colorize, + }), + ], }); // [END errors] @@ -56,5 +57,5 @@ module.exports = { log: winston.log, verbose: winston.verbose, debug: winston.debug, - silly: winston.silly + silly: winston.silly, }; diff --git a/5-logging/lib/oauth2.js b/5-logging/lib/oauth2.js index fa63e7270..f4ac3ec84 100644 --- a/5-logging/lib/oauth2.js +++ b/5-logging/lib/oauth2.js @@ -20,7 +20,7 @@ const config = require('../config'); const passport = require('passport'); const GoogleStrategy = require('passport-google-oauth20').Strategy; -function extractProfile (profile) { +function extractProfile(profile) { let imageUrl = ''; if (profile.photos && profile.photos.length) { imageUrl = profile.photos[0].value; @@ -28,7 +28,7 @@ function extractProfile (profile) { return { id: profile.id, displayName: profile.displayName, - image: imageUrl + image: imageUrl, }; } @@ -39,16 +39,21 @@ function extractProfile (profile) { // along with the user's profile. The function must invoke `cb` with a user // object, which will be set at `req.user` in route handlers after // authentication. -passport.use(new GoogleStrategy({ - clientID: config.get('OAUTH2_CLIENT_ID'), - clientSecret: config.get('OAUTH2_CLIENT_SECRET'), - callbackURL: config.get('OAUTH2_CALLBACK'), - accessType: 'offline' -}, (accessToken, refreshToken, profile, cb) => { - // Extract the minimal profile information we need from the profile object - // provided by Google - cb(null, extractProfile(profile)); -})); +passport.use( + new GoogleStrategy( + { + clientID: config.get('OAUTH2_CLIENT_ID'), + clientSecret: config.get('OAUTH2_CLIENT_SECRET'), + callbackURL: config.get('OAUTH2_CALLBACK'), + accessType: 'offline', + }, + (accessToken, refreshToken, profile, cb) => { + // Extract the minimal profile information we need from the profile object + // provided by Google + cb(null, extractProfile(profile)); + } + ) +); passport.serializeUser((user, cb) => { cb(null, user); @@ -64,7 +69,7 @@ const router = express.Router(); // Middleware that requires the user to be logged in. If the user is not logged // in, it will redirect the user to authorize the application and then return // them to the original URL they requested. -function authRequired (req, res, next) { +function authRequired(req, res, next) { if (!req.user) { req.session.oauth2return = req.originalUrl; return res.redirect('/auth/login'); @@ -74,10 +79,14 @@ function authRequired (req, res, next) { // Middleware that exposes the user's profile as well as login/logout URLs to // any templates. These are available as `profile`, `login`, and `logout`. -function addTemplateVariables (req, res, next) { +function addTemplateVariables(req, res, next) { res.locals.profile = req.user; - res.locals.login = `/auth/login?return=${encodeURIComponent(req.originalUrl)}`; - res.locals.logout = `/auth/logout?return=${encodeURIComponent(req.originalUrl)}`; + res.locals.login = `/auth/login?return=${encodeURIComponent( + req.originalUrl + )}`; + res.locals.logout = `/auth/logout?return=${encodeURIComponent( + req.originalUrl + )}`; next(); } // [END middleware] @@ -102,7 +111,7 @@ router.get( }, // Start OAuth 2 flow using Passport.js - passport.authenticate('google', { scope: ['email', 'profile'] }) + passport.authenticate('google', {scope: ['email', 'profile']}) ); // [END authorize] @@ -135,5 +144,5 @@ module.exports = { extractProfile: extractProfile, router: router, required: authRequired, - template: addTemplateVariables + template: addTemplateVariables, }; diff --git a/5-logging/package.json b/5-logging/package.json index 1fd1b9094..efc721e1d 100644 --- a/5-logging/package.json +++ b/5-logging/package.json @@ -22,34 +22,6 @@ } } }, - "semistandard": { - "globals": [ - "after", - "afterEach", - "before", - "beforeEach", - "describe", - "it" - ] - }, - "contributors": [ - "Ace Nassri ", - "Ahmet Alp Balkan ", - "Allen Day ", - "André Cipriani Bandarra ", - "Dominik Staskiewicz ", - "F. Hinkelmann ", - "Jason Dobry ", - "Jon Wayne Parrott ", - "Justin Beckwith ", - "Michael McDonald ", - "Sean McBreen ", - "Steren ", - "Steve Perry ", - "chenyumic ", - "renovate[bot] ", - "shollyman " - ], "scripts": { "start": "node app.js", "test": "repo-tools test app && ava -t 30s --tap test/*.test.js", diff --git a/5-logging/test/_api-tests.js b/5-logging/test/_api-tests.js index e422353ca..31af46300 100644 --- a/5-logging/test/_api-tests.js +++ b/5-logging/test/_api-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -26,12 +26,12 @@ module.exports = (DATA_BACKEND) => { appConfig.set(`DATA_BACKEND`, DATA_BACKEND); }); - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `beep` }) + .send({title: `beep`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `beep`); @@ -39,13 +39,13 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should list books`, (t) => { + test.serial.cb(`should list books`, t => { // Give Datastore time to become consistent setTimeout(() => { getRequest(testConfig) .get(`/api/books`) .expect(200) - .expect((response) => { + .expect(response => { t.true(Array.isArray(response.body.items)); t.true(response.body.items.length >= 1); }) @@ -53,11 +53,11 @@ module.exports = (DATA_BACKEND) => { }, 1000); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .delete(`/api/books/${id}/`) // .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `OK`); }) .end(t.end); diff --git a/5-logging/test/_crud-tests.js b/5-logging/test/_crud-tests.js index ae72bc273..a94c5d9ab 100644 --- a/5-logging/test/_crud-tests.js +++ b/5-logging/test/_crud-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -27,12 +27,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -40,30 +40,30 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show a list of books`, (t) => { + test.serial.cb(`should show a list of books`, t => { // Give Datastore time to become consistent setTimeout(() => { const expected = /
/; getRequest(testConfig) .get(`/books`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }, 2000); }); - test.serial.cb(`should handle error`, (t) => { + test.serial.cb(`should handle error`, t => { getRequest(testConfig) .get(`/books`) - .query({ pageToken: `badrequest` }) + .query({pageToken: `badrequest`}) .expect(500) .end(t.end); }); // delete the book - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -74,13 +74,13 @@ module.exports = (DATA_BACKEND) => { } }); - test.serial.cb(`should post to add book form`, (t) => { + test.serial.cb(`should post to add book form`, t => { const expected = /Redirecting to \/books\//; getRequest(testConfig) .post(`/books/add`) .field(`title`, `my book`) .expect(302) - .expect((response) => { + .expect(response => { const location = response.headers.location; const idPart = location.replace(`/books/`, ``); id = idPart; @@ -89,19 +89,19 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show add book form`, (t) => { + test.serial.cb(`should show add book form`, t => { const expected = /Add book/; getRequest(testConfig) .get(`/books/add`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -113,12 +113,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb((t) => { + test.serial.cb(t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -126,47 +126,46 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should update a book`, (t) => { + test.serial.cb(`should update a book`, t => { const expected = new RegExp(`Redirecting to /books/${id}`); getRequest(testConfig) .post(`/books/${id}/edit`) .field(`title`, `my other book`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show edit book form`, (t) => { - const expected = - //; + test.serial.cb(`should show edit book form`, t => { + const expected = //; getRequest(testConfig) .get(`/books/${id}/edit`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show a book`, (t) => { + test.serial.cb(`should show a book`, t => { const expected = /

my other book <\/small><\/h4>/; getRequest(testConfig) .get(`/books/${id}`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { const expected = /Redirecting to \/books/; getRequest(testConfig) .get(`/books/${id}/delete`) .expect(302) - .expect((response) => { + .expect(response => { id = undefined; t.regex(response.text, expected); }) @@ -174,7 +173,7 @@ module.exports = (DATA_BACKEND) => { }); // clean up - test.always.after.cb((t) => { + test.always.after.cb(t => { appConfig.set(`DATA_BACKEND`, originalDataBackend); if (id) { diff --git a/5-logging/test/_test-config.js b/5-logging/test/_test-config.js index 7cc2e0710..c5335181b 100644 --- a/5-logging/test/_test-config.js +++ b/5-logging/test/_test-config.js @@ -24,10 +24,10 @@ module.exports = { cmd: `app`, port: PORT, env: { - PORT: PORT + PORT: PORT, }, url: `http://localhost:${PORT}`, version: process.env.GAE_VERSION || TESTNAME, project: process.env.GCLOUD_PROJECT, - msg: `Bookshelf` + msg: `Bookshelf`, }; diff --git a/5-logging/test/app.test.js b/5-logging/test/app.test.js index 0e03b050c..a33b28615 100644 --- a/5-logging/test/app.test.js +++ b/5-logging/test/app.test.js @@ -19,33 +19,34 @@ const sinon = require(`sinon`); const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); -test.cb(`should redirect / to /books`, (t) => { - utils.getRequest(testConfig) +test.cb(`should redirect / to /books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, /Redirecting to \/books/); }) .end(t.end); }); -test(`should check config`, (t) => { +test(`should check config`, t => { const nconfMock = { argv: sinon.stub().returnsThis(), env: sinon.stub().returnsThis(), file: sinon.stub().returnsThis(), defaults: sinon.stub().returnsThis(), - get: function (setting) { + get: function(setting) { return this[setting]; - } + }, }; - function getMsg (setting) { + function getMsg(setting) { return `You must set ${setting} as an environment variable or in config.json!`; } const testFunc = () => { - proxyquire(`../config`, { nconf: nconfMock }); + proxyquire(`../config`, {nconf: nconfMock}); }; nconfMock.DATA_BACKEND = `datastore`; diff --git a/5-logging/test/cloudsql.test.js b/5-logging/test/cloudsql.test.js index ff2ba5fd5..3ac8fbe5c 100644 --- a/5-logging/test/cloudsql.test.js +++ b/5-logging/test/cloudsql.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || + process.env.TEST_CLOUDSQL +) { require(`./_api-tests`)(`cloudsql`); require(`./_crud-tests`)(`cloudsql`); } else { - test(`Skipping Cloud SQL tests...`, (t) => t.pass()); + test(`Skipping Cloud SQL tests...`, t => t.pass()); } diff --git a/5-logging/test/datastore.test.js b/5-logging/test/datastore.test.js index a4faa673b..6df5120c1 100644 --- a/5-logging/test/datastore.test.js +++ b/5-logging/test/datastore.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `datastore` || process.env.TEST_DATASTORE) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `datastore` || + process.env.TEST_DATASTORE +) { require(`./_api-tests`)(`datastore`); require(`./_crud-tests`)(`datastore`); } else { - test(`Skipping Cloud Datastore tests...`, (t) => t.pass()); + test(`Skipping Cloud Datastore tests...`, t => t.pass()); } diff --git a/5-logging/test/oauth2.test.js b/5-logging/test/oauth2.test.js index a384a0f3e..40fdcd558 100644 --- a/5-logging/test/oauth2.test.js +++ b/5-logging/test/oauth2.test.js @@ -32,26 +32,26 @@ const getPassportMock = () => { authenticate: sinon.stub().returns((req, res, next) => { req.session.oauth2return = `/another/path`; next(); - }) + }), }; }; -test.cb(`should start authorization`, (t) => { +test.cb(`should start authorization`, t => { const passportMock = getPassportMock(); - passportMock.authenticate = sinon.stub().returns((req, res, next) => { + passportMock.authenticate = sinon.stub().returns((req, res) => { t.is(req.session.oauth2return, `/some/path`); res.redirect(`/auth/google/callback?code=foo`); }); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/login?return=%2Fsome%2Fpath`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/auth\/google\/callback\?code=foo/); t.true(passportMock.initialize.calledOnce); @@ -61,29 +61,28 @@ test.cb(`should start authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test.cb(`should finish authorization`, (t) => { +test.cb(`should finish authorization`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const app = proxyquire(`../app`, { passport: passportMock, - './lib/oauth2': oauth2 + './lib/oauth2': oauth2, }); request(app) .get(`/auth/google/callback?code=foo`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/another\/path/); t.true(passportMock.initialize.calledOnce); @@ -93,22 +92,21 @@ test.cb(`should finish authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); t.deepEqual( oauth2.extractProfile({ - photos: [{ value: `image.jpg` }], + photos: [{value: `image.jpg`}], id: 1, - displayName: `Joe Developer` + displayName: `Joe Developer`, }), { id: 1, displayName: `Joe Developer`, - image: `image.jpg` + image: `image.jpg`, } ); const serializeUser = passportMock.serializeUser.firstCall.args[0]; @@ -127,18 +125,18 @@ test.cb(`should finish authorization`, (t) => { .end(t.end); }); -test.cb(`should logout`, (t) => { +test.cb(`should logout`, t => { const passportMock = getPassportMock(); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/logout`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \//); t.true(passportMock.initialize.calledOnce); @@ -148,28 +146,27 @@ test.cb(`should logout`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test(`should require authentication`, (t) => { +test(`should require authentication`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: {}, - session: {} + session: {}, }; const res = { - redirect: sinon.stub() + redirect: sinon.stub(), }; const next = sinon.stub(); oauth2.required(req, res, next); @@ -183,30 +180,32 @@ test(`should require authentication`, (t) => { t.is(res.redirect.firstCall.args[0], `/auth/login`); }); -test(`should add template variables`, (t) => { +test(`should add template variables`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: { id: 1, displayName: `Joe Developer`, - image: `image.jpg` - } + image: `image.jpg`, + }, }; const res = { - locals: {} + locals: {}, }; const next = sinon.stub(); oauth2.template(req, res, next); t.true(next.calledOnce); t.is(res.locals.profile, req.user); t.is( - res.locals.login, `/auth/login?return=${encodeURIComponent(req.originalUrl)}` + res.locals.login, + `/auth/login?return=${encodeURIComponent(req.originalUrl)}` ); t.is( - res.locals.logout, `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` + res.locals.logout, + `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` ); }); diff --git a/6-pubsub/app.js b/6-pubsub/app.js index 4d0e894bd..176892ef0 100644 --- a/6-pubsub/app.js +++ b/6-pubsub/app.js @@ -43,20 +43,21 @@ const sessionConfig = { resave: false, saveUninitialized: false, secret: config.get('SECRET'), - signed: true + signed: true, }; // In production use the Memcache instance to store session data, // otherwise fallback to the default MemoryStore in development. if (config.get('NODE_ENV') === 'production' && config.get('MEMCACHE_URL')) { - if (config.get('MEMCACHE_USERNAME') && (config.get('MEMCACHE_PASSWORD'))) { + if (config.get('MEMCACHE_USERNAME') && config.get('MEMCACHE_PASSWORD')) { sessionConfig.store = new MemcachedStore({ servers: [config.get('MEMCACHE_URL')], username: config.get('MEMCACHE_USERNAME'), - password: config.get('MEMCACHE_PASSWORD')}); + password: config.get('MEMCACHE_PASSWORD'), + }); } else { sessionConfig.store = new MemcachedStore({ - servers: [config.get('MEMCACHE_URL')] + servers: [config.get('MEMCACHE_URL')], }); } } @@ -88,7 +89,7 @@ app.use((req, res) => { }); // Basic error handler -app.use((err, req, res, next) => { +app.use((err, req, res) => { /* jshint unused:false */ // If our routes specified a specific response, then send that. Otherwise, // send a generic message so as not to leak anything. diff --git a/6-pubsub/books/api.js b/6-pubsub/books/api.js index 9e9691fe5..750a4b37b 100644 --- a/6-pubsub/books/api.js +++ b/6-pubsub/books/api.js @@ -16,7 +16,7 @@ const express = require('express'); const bodyParser = require('body-parser'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -38,7 +38,7 @@ router.get('/', (req, res, next) => { } res.json({ items: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -94,7 +94,7 @@ router.put('/:book', (req, res, next) => { * Delete a book. */ router.delete('/:book', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; @@ -111,7 +111,7 @@ router.use((err, req, res, next) => { // responding to the request err.response = { message: err.message, - internalCode: err.code + internalCode: err.code, }; next(err); }); diff --git a/6-pubsub/books/crud.js b/6-pubsub/books/crud.js index 214207e18..5d04ea808 100644 --- a/6-pubsub/books/crud.js +++ b/6-pubsub/books/crud.js @@ -17,7 +17,7 @@ const express = require('express'); const images = require('../lib/images'); const oauth2 = require('../lib/oauth2'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -46,7 +46,7 @@ router.get('/', (req, res, next) => { } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -58,14 +58,14 @@ router.get('/mine', oauth2.required, (req, res, next) => { req.user.id, 10, req.query.pageToken, - (err, entities, cursor, apiResponse) => { + (err, entities, cursor) => { if (err) { next(err); return; } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); } ); @@ -79,7 +79,7 @@ router.get('/mine', oauth2.required, (req, res, next) => { router.get('/add', (req, res) => { res.render('books/form.pug', { book: {}, - action: 'Add' + action: 'Add', }); }); @@ -135,7 +135,7 @@ router.get('/:book/edit', (req, res, next) => { } res.render('books/form.pug', { book: entity, - action: 'Edit' + action: 'Edit', }); }); }); @@ -180,7 +180,7 @@ router.get('/:book', (req, res, next) => { return; } res.render('books/view.pug', { - book: entity + book: entity, }); }); }); @@ -191,7 +191,7 @@ router.get('/:book', (req, res, next) => { * Delete a book. */ router.get('/:book/delete', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; diff --git a/6-pubsub/books/model-cloudsql.js b/6-pubsub/books/model-cloudsql.js index 89058e9c8..f6a436fcc 100644 --- a/6-pubsub/books/model-cloudsql.js +++ b/6-pubsub/books/model-cloudsql.js @@ -21,19 +21,23 @@ const background = require('../lib/background'); const options = { user: config.get('MYSQL_USER'), password: config.get('MYSQL_PASSWORD'), - database: 'bookshelf' + database: 'bookshelf', }; -if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') { +if ( + config.get('INSTANCE_CONNECTION_NAME') && + config.get('NODE_ENV') === 'production' +) { options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`; } const connection = mysql.createConnection(options); -function list (limit, token, cb) { +function list(limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( - 'SELECT * FROM `books` LIMIT ? OFFSET ?', [limit, token], + 'SELECT * FROM `books` LIMIT ? OFFSET ?', + [limit, token], (err, results) => { if (err) { cb(err); @@ -45,7 +49,7 @@ function list (limit, token, cb) { ); } -function listBy (userId, limit, token, cb) { +function listBy(userId, limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( 'SELECT * FROM `books` WHERE `createdById` = ? LIMIT ? OFFSET ?', @@ -57,11 +61,12 @@ function listBy (userId, limit, token, cb) { } const hasMore = results.length === limit ? token + results.length : false; cb(null, results, hasMore); - }); + } + ); } // [START create] -function create (data, queueBook, cb) { +function create(data, queueBook, cb) { connection.query('INSERT INTO `books` SET ?', data, (err, res) => { if (err) { cb(err); @@ -75,13 +80,15 @@ function create (data, queueBook, cb) { } // [END create] -function read (id, cb) { +function read(id, cb) { connection.query( - 'SELECT * FROM `books` WHERE `id` = ?', id, (err, results) => { + 'SELECT * FROM `books` WHERE `id` = ?', + id, + (err, results) => { if (!err && !results.length) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -89,26 +96,26 @@ function read (id, cb) { return; } cb(null, results[0]); - }); + } + ); } // [START update] -function update (id, data, queueBook, cb) { - connection.query( - 'UPDATE `books` SET ? WHERE `id` = ?', [data, id], (err) => { - if (err) { - cb(err); - return; - } - if (queueBook) { - background.queueBook(id); - } - read(id, cb); - }); +function update(id, data, queueBook, cb) { + connection.query('UPDATE `books` SET ? WHERE `id` = ?', [data, id], err => { + if (err) { + cb(err); + return; + } + if (queueBook) { + background.queueBook(id); + } + read(id, cb); + }); } // [END update] -function _delete (id, cb) { +function _delete(id, cb) { connection.query('DELETE FROM `books` WHERE `id` = ?', id, cb); } @@ -119,7 +126,7 @@ module.exports = { create: create, read: read, update: update, - delete: _delete + delete: _delete, }; if (module === require.main) { @@ -128,7 +135,8 @@ if (module === require.main) { console.log( `Running this script directly will allow you to initialize your mysql - database.\n This script will not modify any existing tables.\n`); + database.\n This script will not modify any existing tables.\n` + ); prompt.get(['user', 'password'], (err, result) => { if (err) { @@ -138,10 +146,15 @@ if (module === require.main) { }); } -function createSchema (config) { - const connection = mysql.createConnection(extend({ - multipleStatements: true - }, config)); +function createSchema(config) { + const connection = mysql.createConnection( + extend( + { + multipleStatements: true, + }, + config + ) + ); connection.query( `CREATE DATABASE IF NOT EXISTS \`bookshelf\` @@ -158,7 +171,7 @@ function createSchema (config) { \`createdBy\` VARCHAR(255) NULL, \`createdById\` VARCHAR(255) NULL, PRIMARY KEY (\`id\`));`, - (err) => { + err => { if (err) { throw err; } diff --git a/6-pubsub/books/model-datastore.js b/6-pubsub/books/model-datastore.js index 306b93264..0bc12735d 100644 --- a/6-pubsub/books/model-datastore.js +++ b/6-pubsub/books/model-datastore.js @@ -18,7 +18,7 @@ const config = require('../config'); const background = require('../lib/background'); const ds = Datastore({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const kind = 'Book'; @@ -38,7 +38,7 @@ const kind = 'Book'; // id: id, // property: value // } -function fromDatastore (obj) { +function fromDatastore(obj) { obj.id = obj[Datastore.KEY].id; return obj; } @@ -66,17 +66,17 @@ function fromDatastore (obj) { // excludeFromIndexes: true // } // ] -function toDatastore (obj, nonIndexed) { +function toDatastore(obj, nonIndexed) { nonIndexed = nonIndexed || []; let results = []; - Object.keys(obj).forEach((k) => { + Object.keys(obj).forEach(k => { if (obj[k] === undefined) { return; } results.push({ name: k, value: obj[k], - excludeFromIndexes: nonIndexed.indexOf(k) !== -1 + excludeFromIndexes: nonIndexed.indexOf(k) !== -1, }); }); return results; @@ -86,8 +86,9 @@ function toDatastore (obj, nonIndexed) { // The ``limit`` argument determines the maximum amount of results to // return per page. The ``token`` argument allows requesting additional // pages. The callback is invoked with ``(err, books, nextPageToken)``. -function list (limit, token, cb) { - const q = ds.createQuery([kind]) +function list(limit, token, cb) { + const q = ds + .createQuery([kind]) .limit(limit) .order('title') .start(token); @@ -97,15 +98,19 @@ function list (limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } // Similar to ``list``, but only lists the books created by the specified // user. -function listBy (userId, limit, token, cb) { - const q = ds.createQuery([kind]) +function listBy(userId, limit, token, cb) { + const q = ds + .createQuery([kind]) .filter('createdById', '=', userId) .limit(limit) .start(token); @@ -115,7 +120,10 @@ function listBy (userId, limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } @@ -124,7 +132,7 @@ function listBy (userId, limit, token, cb) { // data is automatically translated into Datastore format. The book will be // queued for background processing. // [START update] -function update (id, data, queueBook, cb) { +function update(id, data, queueBook, cb) { let key; if (id) { key = ds.key([kind, parseInt(id, 10)]); @@ -134,33 +142,30 @@ function update (id, data, queueBook, cb) { const entity = { key: key, - data: toDatastore(data, ['description']) + data: toDatastore(data, ['description']), }; - ds.save( - entity, - (err) => { - if (err) { - cb(err); - return; - } - data.id = entity.key.id; - if (queueBook) { - background.queueBook(data.id); - } - cb(null, data); + ds.save(entity, err => { + if (err) { + cb(err); + return; } - ); + data.id = entity.key.id; + if (queueBook) { + background.queueBook(data.id); + } + cb(null, data); + }); } // [END update] -function read (id, cb) { +function read(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.get(key, (err, entity) => { if (!err && !entity) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -171,7 +176,7 @@ function read (id, cb) { }); } -function _delete (id, cb) { +function _delete(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.delete(key, cb); } @@ -184,5 +189,5 @@ module.exports = { update: update, delete: _delete, list: list, - listBy: listBy + listBy: listBy, }; diff --git a/6-pubsub/config.js b/6-pubsub/config.js index 4e0aa8168..d37bf7cbe 100644 --- a/6-pubsub/config.js +++ b/6-pubsub/config.js @@ -15,7 +15,7 @@ // Hierarchical node.js configuration with command-line arguments, environment // variables, and files. -const nconf = module.exports = require('nconf'); +const nconf = (module.exports = require('nconf')); const path = require('path'); nconf @@ -38,10 +38,10 @@ nconf 'OAUTH2_CALLBACK', 'PORT', 'SECRET', - 'TOPIC_NAME' + 'TOPIC_NAME', ]) // 3. Config file - .file({ file: path.join(__dirname, 'config.json') }) + .file({file: path.join(__dirname, 'config.json')}) // 4. Defaults .defaults({ // Typically you will create a bucket with the same name as your project ID. @@ -71,7 +71,7 @@ nconf // Set this a secret string of your choosing SECRET: 'keyboardcat', - TOPIC_NAME: 'book-process-queue' + TOPIC_NAME: 'book-process-queue', }); // Check for required settings @@ -88,8 +88,10 @@ if (nconf.get('DATA_BACKEND') === 'cloudsql') { } } -function checkConfig (setting) { +function checkConfig(setting) { if (!nconf.get(setting)) { - throw new Error(`You must set ${setting} as an environment variable or in config.json!`); + throw new Error( + `You must set ${setting} as an environment variable or in config.json!` + ); } } diff --git a/6-pubsub/lib/background.js b/6-pubsub/lib/background.js index 28c8d2630..65106e0be 100644 --- a/6-pubsub/lib/background.js +++ b/6-pubsub/lib/background.js @@ -20,7 +20,7 @@ const logging = require('./logging'); const topicName = config.get('TOPIC_NAME'); const pubsub = new Pubsub({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); // This configuration will automatically create the topic if @@ -29,7 +29,7 @@ const pubsub = new Pubsub({ // publishing anything to it as topics without subscribers // will essentially drop any messages. // [START topic] -function getTopic (cb) { +function getTopic(cb) { pubsub.createTopic(topicName, (err, topic) => { // topic already exists. if (err && err.code === 6) { @@ -43,7 +43,7 @@ function getTopic (cb) { // Adds a book to the queue to be processed by the worker. // [START queue] -function queueBook (bookId) { +function queueBook(bookId) { getTopic((err, topic) => { if (err) { logging.error('Error occurred while getting pubsub topic', err); @@ -52,11 +52,11 @@ function queueBook (bookId) { const data = { action: 'processBook', - bookId: bookId + bookId: bookId, }; const publisher = topic.publisher(); - publisher.publish(Buffer.from(JSON.stringify(data)), (err) => { + publisher.publish(Buffer.from(JSON.stringify(data)), err => { if (err) { logging.error('Error occurred while queuing background task', err); } else { @@ -68,5 +68,5 @@ function queueBook (bookId) { // [END queue] module.exports = { - queueBook + queueBook, }; diff --git a/6-pubsub/lib/images.js b/6-pubsub/lib/images.js index 159ebac81..b2afceb0b 100644 --- a/6-pubsub/lib/images.js +++ b/6-pubsub/lib/images.js @@ -21,32 +21,34 @@ const logging = require('./logging'); const CLOUD_BUCKET = config.get('CLOUD_BUCKET'); const storage = Storage({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const bucket = storage.bucket(CLOUD_BUCKET); // Downloads a given image (by URL) and then uploads it to // Google Cloud Storage. Provides the publicly accessable URL to the callback. // [START download_and_upload] -function downloadAndUploadImage (sourceUrl, destFileName, cb) { +function downloadAndUploadImage(sourceUrl, destFileName, cb) { const file = bucket.file(destFileName); request .get(sourceUrl) - .on('error', (err) => { + .on('error', err => { logging.warn(`Could not fetch image ${sourceUrl}`, err); cb(err); }) - .pipe(file.createWriteStream({ - resumable: false - })) + .pipe( + file.createWriteStream({ + resumable: false, + }) + ) .on('finish', () => { logging.info(`Uploaded image ${destFileName}`); file.makePublic(() => { cb(null, getPublicUrl(destFileName)); }); }) - .on('error', (err) => { + .on('error', err => { logging.error('Could not upload image', err); cb(err); }); @@ -56,7 +58,7 @@ function downloadAndUploadImage (sourceUrl, destFileName, cb) { // Returns the public, anonymously accessable URL to a given Cloud Storage // object. // The object's ACL has to be set to public read. -function getPublicUrl (filename) { +function getPublicUrl(filename) { return `https://storage.googleapis.com/${CLOUD_BUCKET}/${filename}`; } @@ -64,7 +66,7 @@ function getPublicUrl (filename) { // req.file is processed and will have two new properties: // * ``cloudStorageObject`` the object name in cloud storage. // * ``cloudStoragePublicUrl`` the public url to the object. -function sendUploadToGCS (req, res, next) { +function sendUploadToGCS(req, res, next) { if (!req.file) { return next(); } @@ -73,12 +75,12 @@ function sendUploadToGCS (req, res, next) { const file = bucket.file(gcsname); const stream = file.createWriteStream({ metadata: { - contentType: req.file.mimetype + contentType: req.file.mimetype, }, - resumable: false + resumable: false, }); - stream.on('error', (err) => { + stream.on('error', err => { req.file.cloudStorageError = err; next(err); }); @@ -101,13 +103,13 @@ const Multer = require('multer'); const multer = Multer({ storage: Multer.MemoryStorage, limits: { - fileSize: 5 * 1024 * 1024 // no larger than 5mb - } + fileSize: 5 * 1024 * 1024, // no larger than 5mb + }, }); module.exports = { downloadAndUploadImage, getPublicUrl, sendUploadToGCS, - multer + multer, }; diff --git a/6-pubsub/lib/logging.js b/6-pubsub/lib/logging.js index 94ddbc7ed..886af99dd 100644 --- a/6-pubsub/lib/logging.js +++ b/6-pubsub/lib/logging.js @@ -15,7 +15,8 @@ const winston = require('winston'); const expressWinston = require('express-winston'); -const StackdriverTransport = require('@google-cloud/logging-winston').LoggingWinston; +const StackdriverTransport = require('@google-cloud/logging-winston') + .LoggingWinston; const colorize = process.env.NODE_ENV !== 'production'; @@ -25,11 +26,11 @@ const requestLogger = expressWinston.logger({ new StackdriverTransport(), new winston.transports.Console({ json: false, - colorize: colorize - }) + colorize: colorize, + }), ], expressFormat: true, - meta: false + meta: false, }); // Logger to capture any top-level errors and output json diagnostic info. @@ -38,9 +39,9 @@ const errorLogger = expressWinston.errorLogger({ new StackdriverTransport(), new winston.transports.Console({ json: true, - colorize: colorize - }) - ] + colorize: colorize, + }), + ], }); module.exports = { @@ -52,5 +53,5 @@ module.exports = { log: winston.log, verbose: winston.verbose, debug: winston.debug, - silly: winston.silly + silly: winston.silly, }; diff --git a/6-pubsub/lib/oauth2.js b/6-pubsub/lib/oauth2.js index fa63e7270..f4ac3ec84 100644 --- a/6-pubsub/lib/oauth2.js +++ b/6-pubsub/lib/oauth2.js @@ -20,7 +20,7 @@ const config = require('../config'); const passport = require('passport'); const GoogleStrategy = require('passport-google-oauth20').Strategy; -function extractProfile (profile) { +function extractProfile(profile) { let imageUrl = ''; if (profile.photos && profile.photos.length) { imageUrl = profile.photos[0].value; @@ -28,7 +28,7 @@ function extractProfile (profile) { return { id: profile.id, displayName: profile.displayName, - image: imageUrl + image: imageUrl, }; } @@ -39,16 +39,21 @@ function extractProfile (profile) { // along with the user's profile. The function must invoke `cb` with a user // object, which will be set at `req.user` in route handlers after // authentication. -passport.use(new GoogleStrategy({ - clientID: config.get('OAUTH2_CLIENT_ID'), - clientSecret: config.get('OAUTH2_CLIENT_SECRET'), - callbackURL: config.get('OAUTH2_CALLBACK'), - accessType: 'offline' -}, (accessToken, refreshToken, profile, cb) => { - // Extract the minimal profile information we need from the profile object - // provided by Google - cb(null, extractProfile(profile)); -})); +passport.use( + new GoogleStrategy( + { + clientID: config.get('OAUTH2_CLIENT_ID'), + clientSecret: config.get('OAUTH2_CLIENT_SECRET'), + callbackURL: config.get('OAUTH2_CALLBACK'), + accessType: 'offline', + }, + (accessToken, refreshToken, profile, cb) => { + // Extract the minimal profile information we need from the profile object + // provided by Google + cb(null, extractProfile(profile)); + } + ) +); passport.serializeUser((user, cb) => { cb(null, user); @@ -64,7 +69,7 @@ const router = express.Router(); // Middleware that requires the user to be logged in. If the user is not logged // in, it will redirect the user to authorize the application and then return // them to the original URL they requested. -function authRequired (req, res, next) { +function authRequired(req, res, next) { if (!req.user) { req.session.oauth2return = req.originalUrl; return res.redirect('/auth/login'); @@ -74,10 +79,14 @@ function authRequired (req, res, next) { // Middleware that exposes the user's profile as well as login/logout URLs to // any templates. These are available as `profile`, `login`, and `logout`. -function addTemplateVariables (req, res, next) { +function addTemplateVariables(req, res, next) { res.locals.profile = req.user; - res.locals.login = `/auth/login?return=${encodeURIComponent(req.originalUrl)}`; - res.locals.logout = `/auth/logout?return=${encodeURIComponent(req.originalUrl)}`; + res.locals.login = `/auth/login?return=${encodeURIComponent( + req.originalUrl + )}`; + res.locals.logout = `/auth/logout?return=${encodeURIComponent( + req.originalUrl + )}`; next(); } // [END middleware] @@ -102,7 +111,7 @@ router.get( }, // Start OAuth 2 flow using Passport.js - passport.authenticate('google', { scope: ['email', 'profile'] }) + passport.authenticate('google', {scope: ['email', 'profile']}) ); // [END authorize] @@ -135,5 +144,5 @@ module.exports = { extractProfile: extractProfile, router: router, required: authRequired, - template: addTemplateVariables + template: addTemplateVariables, }; diff --git a/6-pubsub/package.json b/6-pubsub/package.json index 3c041f70c..aea7f9842 100644 --- a/6-pubsub/package.json +++ b/6-pubsub/package.json @@ -22,34 +22,6 @@ } } }, - "semistandard": { - "globals": [ - "after", - "afterEach", - "before", - "beforeEach", - "describe", - "it" - ] - }, - "contributors": [ - "Ace Nassri ", - "Ahmet Alp Balkan ", - "Allen Day ", - "André Cipriani Bandarra ", - "Dominik Staskiewicz ", - "F. Hinkelmann ", - "Jason Dobry ", - "Jon Wayne Parrott ", - "Justin Beckwith ", - "Michael McDonald ", - "Sean McBreen ", - "Steren ", - "Steve Perry ", - "chenyumic ", - "renovate[bot] ", - "shollyman " - ], "scripts": { "start": "node ${SCRIPT:-app.js}", "test": "repo-tools test app && ava -t 30s --tap test/*.test.js", diff --git a/6-pubsub/test/_api-tests.js b/6-pubsub/test/_api-tests.js index e422353ca..31af46300 100644 --- a/6-pubsub/test/_api-tests.js +++ b/6-pubsub/test/_api-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -26,12 +26,12 @@ module.exports = (DATA_BACKEND) => { appConfig.set(`DATA_BACKEND`, DATA_BACKEND); }); - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `beep` }) + .send({title: `beep`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `beep`); @@ -39,13 +39,13 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should list books`, (t) => { + test.serial.cb(`should list books`, t => { // Give Datastore time to become consistent setTimeout(() => { getRequest(testConfig) .get(`/api/books`) .expect(200) - .expect((response) => { + .expect(response => { t.true(Array.isArray(response.body.items)); t.true(response.body.items.length >= 1); }) @@ -53,11 +53,11 @@ module.exports = (DATA_BACKEND) => { }, 1000); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .delete(`/api/books/${id}/`) // .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `OK`); }) .end(t.end); diff --git a/6-pubsub/test/_crud-tests.js b/6-pubsub/test/_crud-tests.js index 01d76dc2d..179ba1072 100644 --- a/6-pubsub/test/_crud-tests.js +++ b/6-pubsub/test/_crud-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -27,12 +27,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -40,30 +40,30 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show a list of books`, (t) => { + test.serial.cb(`should show a list of books`, t => { // Give Datastore time to become consistent setTimeout(() => { const expected = /
/; getRequest(testConfig) .get(`/books`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }, 2000); }); - test.serial.cb(`should handle error`, (t) => { + test.serial.cb(`should handle error`, t => { getRequest(testConfig) .get(`/books`) - .query({ pageToken: `badrequest` }) + .query({pageToken: `badrequest`}) .expect(500) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -74,13 +74,13 @@ module.exports = (DATA_BACKEND) => { } }); - test.serial.cb(`should post to add book form`, (t) => { + test.serial.cb(`should post to add book form`, t => { const expected = /Redirecting to \/books\//; getRequest(testConfig) .post(`/books/add`) .field(`title`, `my book`) .expect(302) - .expect((response) => { + .expect(response => { const location = response.headers.location; const idPart = location.replace(`/books/`, ``); id = idPart; @@ -89,19 +89,19 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show add book form`, (t) => { + test.serial.cb(`should show add book form`, t => { const expected = /Add book/; getRequest(testConfig) .get(`/books/add`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -113,12 +113,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -126,46 +126,46 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should update a book`, (t) => { + test.serial.cb(`should update a book`, t => { const expected = new RegExp(`Redirecting to /books/${id}`); getRequest(testConfig) .post(`/books/${id}/edit`) .field(`title`, `my other book`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show edit book form`, (t) => { + test.serial.cb(`should show edit book form`, t => { const expected = /"title" value="my other book"/; getRequest(testConfig) .get(`/books/${id}/edit`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show a book`, (t) => { + test.serial.cb(`should show a book`, t => { const expected = /

my other book <\/small><\/h4>/; getRequest(testConfig) .get(`/books/${id}`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { const expected = /Redirecting to \/books/; getRequest(testConfig) .get(`/books/${id}/delete`) .expect(302) - .expect((response) => { + .expect(response => { id = undefined; t.regex(response.text, expected); }) @@ -173,7 +173,7 @@ module.exports = (DATA_BACKEND) => { }); // clean up - test.always.after.cb((t) => { + test.always.after.cb(t => { appConfig.set(`DATA_BACKEND`, originalDataBackend); if (id) { diff --git a/6-pubsub/test/_test-config.js b/6-pubsub/test/_test-config.js index bcf639e47..ed401ef46 100644 --- a/6-pubsub/test/_test-config.js +++ b/6-pubsub/test/_test-config.js @@ -29,6 +29,6 @@ module.exports = { project: process.env.GCLOUD_PROJECT, env: { PORT: PORT, - TOPIC_NAME: `book-process-queue-${TESTNAME}` - } + TOPIC_NAME: `book-process-queue-${TESTNAME}`, + }, }; diff --git a/6-pubsub/test/_test-config.worker.js b/6-pubsub/test/_test-config.worker.js index 2756835fe..90670f96f 100644 --- a/6-pubsub/test/_test-config.worker.js +++ b/6-pubsub/test/_test-config.worker.js @@ -28,10 +28,10 @@ module.exports = { msg: `This worker has processed`, port: PORT, env: { - TOPIC_NAME: `book-process-queue-${TESTNAME}` + TOPIC_NAME: `book-process-queue-${TESTNAME}`, }, version: VERSION, - project: PROJECT_ID + project: PROJECT_ID, }; if (process.env.E2E_TESTS) { diff --git a/6-pubsub/test/app.test.js b/6-pubsub/test/app.test.js index 0e03b050c..a33b28615 100644 --- a/6-pubsub/test/app.test.js +++ b/6-pubsub/test/app.test.js @@ -19,33 +19,34 @@ const sinon = require(`sinon`); const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); -test.cb(`should redirect / to /books`, (t) => { - utils.getRequest(testConfig) +test.cb(`should redirect / to /books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, /Redirecting to \/books/); }) .end(t.end); }); -test(`should check config`, (t) => { +test(`should check config`, t => { const nconfMock = { argv: sinon.stub().returnsThis(), env: sinon.stub().returnsThis(), file: sinon.stub().returnsThis(), defaults: sinon.stub().returnsThis(), - get: function (setting) { + get: function(setting) { return this[setting]; - } + }, }; - function getMsg (setting) { + function getMsg(setting) { return `You must set ${setting} as an environment variable or in config.json!`; } const testFunc = () => { - proxyquire(`../config`, { nconf: nconfMock }); + proxyquire(`../config`, {nconf: nconfMock}); }; nconfMock.DATA_BACKEND = `datastore`; diff --git a/6-pubsub/test/background.test.js b/6-pubsub/test/background.test.js index 86b3f29ca..ccb0446b7 100644 --- a/6-pubsub/test/background.test.js +++ b/6-pubsub/test/background.test.js @@ -20,49 +20,46 @@ const test = require(`ava`); let background; const mocks = {}; -test.beforeEach((t) => { +test.beforeEach(t => { // Mock dependencies used by background.js mocks.config = { GCLOUD_PROJECT: process.env.GCLOUD_PROJECT, SUBSCRIPTION_NAME: `shared-worker-subscription`, - TOPIC_NAME: `book-process-queue` + TOPIC_NAME: `book-process-queue`, }; - mocks.config.get = function (key) { + mocks.config.get = function(key) { return this[key]; }; mocks.subscription = { - on: sinon.stub() + on: sinon.stub(), }; mocks.publisher = { - publish: sinon.stub().callsArgWith(1, null) + publish: sinon.stub().callsArgWith(1, null), }; mocks.topic = { createSubscription: sinon.stub().callsArgWith(1, null, mocks.subscription), - publisher: sinon.stub().returns(mocks.publisher) + publisher: sinon.stub().returns(mocks.publisher), }; mocks.pubsub = { createTopic: sinon.stub().callsArgWith(1, null, mocks.topic), - topic: sinon.stub().returns(mocks.topic) + topic: sinon.stub().returns(mocks.topic), }; mocks.Pubsub = sinon.stub().returns(mocks.pubsub); mocks.logging = { info: sinon.stub(), - error: sinon.stub() + error: sinon.stub(), }; // Load background.js with provided mocks background = proxyquire(`../lib/background`, { '@google-cloud/pubsub': mocks.Pubsub, '../config': mocks.config, - './logging': mocks.logging + './logging': mocks.logging, }); - t.true( - mocks.Pubsub.calledOnce, - `Pubsub() should have been called once` - ); + t.true(mocks.Pubsub.calledOnce, `Pubsub() should have been called once`); }); -test.serial(`should queue a book and log message`, (t) => { +test.serial(`should queue a book and log message`, t => { // Setup const testBookId = 1; @@ -94,10 +91,12 @@ test.serial(`should queue a book and log message`, (t) => { ); t.deepEqual( mocks.publisher.publish.firstCall.args[0], - Buffer.from(JSON.stringify({ - action: `processBook`, - bookId: testBookId - })), + Buffer.from( + JSON.stringify({ + action: `processBook`, + bookId: testBookId, + }) + ), `publisher.publish() should have been called with the right arguments` ); t.true( @@ -111,11 +110,11 @@ test.serial(`should queue a book and log message`, (t) => { ); }); -test.serial(`should queue a book and log message even if topic exists`, (t) => { +test.serial(`should queue a book and log message even if topic exists`, t => { // Setup const testBookId = 1; mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, { - code: 6 + code: 6, }); // Run target functionality @@ -150,10 +149,12 @@ test.serial(`should queue a book and log message even if topic exists`, (t) => { ); t.deepEqual( mocks.publisher.publish.firstCall.args[0], - Buffer.from(JSON.stringify({ - action: `processBook`, - bookId: testBookId - })), + Buffer.from( + JSON.stringify({ + action: `processBook`, + bookId: testBookId, + }) + ), `publisher.publish() should have been called with the right arguments` ); t.true( @@ -167,7 +168,7 @@ test.serial(`should queue a book and log message even if topic exists`, (t) => { ); }); -test.serial(`should log error if cannot get topic`, (t) => { +test.serial(`should log error if cannot get topic`, t => { // Setup const testBookId = 1; const testErrorMsg = `test error`; @@ -212,7 +213,7 @@ test.serial(`should log error if cannot get topic`, (t) => { ); }); -test.serial(`should log error if cannot publish message`, (t) => { +test.serial(`should log error if cannot publish message`, t => { // Setup const testBookId = 1; const testErrorMsg = `test error`; diff --git a/6-pubsub/test/cloudsql.test.js b/6-pubsub/test/cloudsql.test.js index ff2ba5fd5..3ac8fbe5c 100644 --- a/6-pubsub/test/cloudsql.test.js +++ b/6-pubsub/test/cloudsql.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || + process.env.TEST_CLOUDSQL +) { require(`./_api-tests`)(`cloudsql`); require(`./_crud-tests`)(`cloudsql`); } else { - test(`Skipping Cloud SQL tests...`, (t) => t.pass()); + test(`Skipping Cloud SQL tests...`, t => t.pass()); } diff --git a/6-pubsub/test/datastore.test.js b/6-pubsub/test/datastore.test.js index a4faa673b..6df5120c1 100644 --- a/6-pubsub/test/datastore.test.js +++ b/6-pubsub/test/datastore.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `datastore` || process.env.TEST_DATASTORE) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `datastore` || + process.env.TEST_DATASTORE +) { require(`./_api-tests`)(`datastore`); require(`./_crud-tests`)(`datastore`); } else { - test(`Skipping Cloud Datastore tests...`, (t) => t.pass()); + test(`Skipping Cloud Datastore tests...`, t => t.pass()); } diff --git a/6-pubsub/test/oauth2.test.js b/6-pubsub/test/oauth2.test.js index a384a0f3e..40fdcd558 100644 --- a/6-pubsub/test/oauth2.test.js +++ b/6-pubsub/test/oauth2.test.js @@ -32,26 +32,26 @@ const getPassportMock = () => { authenticate: sinon.stub().returns((req, res, next) => { req.session.oauth2return = `/another/path`; next(); - }) + }), }; }; -test.cb(`should start authorization`, (t) => { +test.cb(`should start authorization`, t => { const passportMock = getPassportMock(); - passportMock.authenticate = sinon.stub().returns((req, res, next) => { + passportMock.authenticate = sinon.stub().returns((req, res) => { t.is(req.session.oauth2return, `/some/path`); res.redirect(`/auth/google/callback?code=foo`); }); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/login?return=%2Fsome%2Fpath`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/auth\/google\/callback\?code=foo/); t.true(passportMock.initialize.calledOnce); @@ -61,29 +61,28 @@ test.cb(`should start authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test.cb(`should finish authorization`, (t) => { +test.cb(`should finish authorization`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const app = proxyquire(`../app`, { passport: passportMock, - './lib/oauth2': oauth2 + './lib/oauth2': oauth2, }); request(app) .get(`/auth/google/callback?code=foo`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/another\/path/); t.true(passportMock.initialize.calledOnce); @@ -93,22 +92,21 @@ test.cb(`should finish authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); t.deepEqual( oauth2.extractProfile({ - photos: [{ value: `image.jpg` }], + photos: [{value: `image.jpg`}], id: 1, - displayName: `Joe Developer` + displayName: `Joe Developer`, }), { id: 1, displayName: `Joe Developer`, - image: `image.jpg` + image: `image.jpg`, } ); const serializeUser = passportMock.serializeUser.firstCall.args[0]; @@ -127,18 +125,18 @@ test.cb(`should finish authorization`, (t) => { .end(t.end); }); -test.cb(`should logout`, (t) => { +test.cb(`should logout`, t => { const passportMock = getPassportMock(); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/logout`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \//); t.true(passportMock.initialize.calledOnce); @@ -148,28 +146,27 @@ test.cb(`should logout`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test(`should require authentication`, (t) => { +test(`should require authentication`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: {}, - session: {} + session: {}, }; const res = { - redirect: sinon.stub() + redirect: sinon.stub(), }; const next = sinon.stub(); oauth2.required(req, res, next); @@ -183,30 +180,32 @@ test(`should require authentication`, (t) => { t.is(res.redirect.firstCall.args[0], `/auth/login`); }); -test(`should add template variables`, (t) => { +test(`should add template variables`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: { id: 1, displayName: `Joe Developer`, - image: `image.jpg` - } + image: `image.jpg`, + }, }; const res = { - locals: {} + locals: {}, }; const next = sinon.stub(); oauth2.template(req, res, next); t.true(next.calledOnce); t.is(res.locals.profile, req.user); t.is( - res.locals.login, `/auth/login?return=${encodeURIComponent(req.originalUrl)}` + res.locals.login, + `/auth/login?return=${encodeURIComponent(req.originalUrl)}` ); t.is( - res.locals.logout, `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` + res.locals.logout, + `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` ); }); diff --git a/6-pubsub/test/worker.test.js b/6-pubsub/test/worker.test.js index 6b8179f93..6d6e47e26 100644 --- a/6-pubsub/test/worker.test.js +++ b/6-pubsub/test/worker.test.js @@ -20,42 +20,44 @@ const sinon = require(`sinon`); const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); -test.serial.cb(`should return number of processed books`, (t) => { - utils.getRequest(testConfig) +test.serial.cb(`should return number of processed books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, /This worker has processed/); }) .end(t.end); }); -test.serial.cb(`should do a health check`, (t) => { - utils.getRequest(testConfig) +test.serial.cb(`should do a health check`, t => { + utils + .getRequest(testConfig) .get(`/_ah/health`) .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `ok`); }) .end(t.end); }); -test.serial.cb(`should process a book`, (t) => { +test.serial.cb(`should process a book`, t => { const appConfig = require(`../config`); const loggingStub = { error: sinon.stub(), info: sinon.stub(), - warn: sinon.stub() + warn: sinon.stub(), }; const stubs = { './lib/logging': loggingStub, '@google-cloud/trace-agent': { start: sinon.stub(), - '@noCallThru': true + '@noCallThru': true, }, '@google-cloud/debug-agent': { - '@noCallThru': true - } + '@noCallThru': true, + }, }; stubs[`./books/model-${appConfig.get('DATA_BACKEND')}`] = { read: (bookId, cb) => { @@ -63,12 +65,12 @@ test.serial.cb(`should process a book`, (t) => { }, update: (bookId, book, queueBook, cb) => { cb(); - } + }, }; const worker = proxyquire(path.join(__dirname, `../worker`), stubs).mocks; const processBook = worker.processBook; - processBook(1, (err, bookId) => { + processBook(1, err => { if (err) { return t.end(err); } diff --git a/6-pubsub/worker.js b/6-pubsub/worker.js index 31b78c7ea..374b8225a 100644 --- a/6-pubsub/worker.js +++ b/6-pubsub/worker.js @@ -57,8 +57,9 @@ app.post('/endpoint', jsonParser, (req, res) => { return res.sendStatus(400); } - const dataUtf8encoded = Buffer.from(req.body.message.data, 'base64') - .toString('utf8'); + const dataUtf8encoded = Buffer.from(req.body.message.data, 'base64').toString( + 'utf8' + ); var content; try { content = JSON.parse(dataUtf8encoded); @@ -90,32 +91,35 @@ if (module === require.main) { // Processes a book by reading its existing data, attempting to find // more information, and updating the database with the new information. // [START process] -function processBook (bookId, callback) { - waterfall([ - // Load the current data - (cb) => { - model.read(bookId, cb); - }, - // Find the information from Google - findBookInfo, - // Save the updated data - (updated, cb) => { - model.update(updated.id, updated, false, cb); - } - ], (err) => { - if (err) { - logging.error('Error occurred', err); +function processBook(bookId, callback) { + waterfall( + [ + // Load the current data + cb => { + model.read(bookId, cb); + }, + // Find the information from Google + findBookInfo, + // Save the updated data + (updated, cb) => { + model.update(updated.id, updated, false, cb); + }, + ], + err => { + if (err) { + logging.error('Error occurred', err); + if (callback) { + callback(err); + } + return; + } + logging.info(`Updated book ${bookId}`); + bookCount += 1; if (callback) { - callback(err); + callback(); } - return; } - logging.info(`Updated book ${bookId}`); - bookCount += 1; - if (callback) { - callback(); - } - }); + ); } // [END process] @@ -123,7 +127,7 @@ function processBook (bookId, callback) { // the book's data. Also uploads a cover image to Cloud Storage // if available. // [START find] -function findBookInfo (book, cb) { +function findBookInfo(book, cb) { queryBooksApi(book.title, (err, r) => { if (!err && !r.items) { err = 'Not found'; @@ -152,13 +156,12 @@ function findBookInfo (book, cb) { top.volumeInfo.imageLinks.smallThumbnail; const imageName = `${book.id}.jpg`; - images.downloadAndUploadImage( - imageUrl, imageName, (err, publicUrl) => { - if (!err) { - book.imageUrl = publicUrl; - } - cb(null, book); - }); + images.downloadAndUploadImage(imageUrl, imageName, (err, publicUrl) => { + if (!err) { + book.imageUrl = publicUrl; + } + cb(null, book); + }); }); } // [END find] @@ -166,9 +169,11 @@ function findBookInfo (book, cb) { // Calls out to the Google Books API to get additional // information about a given book. // [START query] -function queryBooksApi (query, cb) { +function queryBooksApi(query, cb) { request( - `https://www.googleapis.com/books/v1/volumes?country=US&q=${encodeURIComponent(query)}`, + `https://www.googleapis.com/books/v1/volumes?country=US&q=${encodeURIComponent( + query + )}`, (err, resp, body) => { if (err || resp.statusCode !== 200) { console.log(`Error: ${err}`); @@ -186,7 +191,7 @@ function queryBooksApi (query, cb) { app.mocks = { processBook: processBook, findBookInfo: findBookInfo, - queryBooksApi: queryBooksApi + queryBooksApi: queryBooksApi, }; // Proxyquire requires this *exact* line, hence the "app.mocks" above diff --git a/7-gce/app.js b/7-gce/app.js index e2f7c0669..9f7a96ba2 100644 --- a/7-gce/app.js +++ b/7-gce/app.js @@ -43,14 +43,14 @@ const sessionConfig = { resave: false, saveUninitialized: false, secret: config.get('SECRET'), - signed: true + signed: true, }; // In production use the App Engine Memcache instance to store session data, // otherwise fallback to the default MemoryStore in development. if (config.get('NODE_ENV') === 'production' && config.get('MEMCACHE_URL')) { sessionConfig.store = new MemcachedStore({ - hosts: [config.get('MEMCACHE_URL')] + hosts: [config.get('MEMCACHE_URL')], }); } @@ -87,7 +87,7 @@ app.use((req, res) => { }); // Basic error handler -app.use((err, req, res, next) => { +app.use((err, req, res) => { /* jshint unused:false */ // If our routes specified a specific response, then send that. Otherwise, // send a generic message so as not to leak anything. diff --git a/7-gce/books/api.js b/7-gce/books/api.js index 9e9691fe5..750a4b37b 100644 --- a/7-gce/books/api.js +++ b/7-gce/books/api.js @@ -16,7 +16,7 @@ const express = require('express'); const bodyParser = require('body-parser'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -38,7 +38,7 @@ router.get('/', (req, res, next) => { } res.json({ items: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -94,7 +94,7 @@ router.put('/:book', (req, res, next) => { * Delete a book. */ router.delete('/:book', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; @@ -111,7 +111,7 @@ router.use((err, req, res, next) => { // responding to the request err.response = { message: err.message, - internalCode: err.code + internalCode: err.code, }; next(err); }); diff --git a/7-gce/books/crud.js b/7-gce/books/crud.js index 214207e18..5d04ea808 100644 --- a/7-gce/books/crud.js +++ b/7-gce/books/crud.js @@ -17,7 +17,7 @@ const express = require('express'); const images = require('../lib/images'); const oauth2 = require('../lib/oauth2'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -46,7 +46,7 @@ router.get('/', (req, res, next) => { } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -58,14 +58,14 @@ router.get('/mine', oauth2.required, (req, res, next) => { req.user.id, 10, req.query.pageToken, - (err, entities, cursor, apiResponse) => { + (err, entities, cursor) => { if (err) { next(err); return; } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); } ); @@ -79,7 +79,7 @@ router.get('/mine', oauth2.required, (req, res, next) => { router.get('/add', (req, res) => { res.render('books/form.pug', { book: {}, - action: 'Add' + action: 'Add', }); }); @@ -135,7 +135,7 @@ router.get('/:book/edit', (req, res, next) => { } res.render('books/form.pug', { book: entity, - action: 'Edit' + action: 'Edit', }); }); }); @@ -180,7 +180,7 @@ router.get('/:book', (req, res, next) => { return; } res.render('books/view.pug', { - book: entity + book: entity, }); }); }); @@ -191,7 +191,7 @@ router.get('/:book', (req, res, next) => { * Delete a book. */ router.get('/:book/delete', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; diff --git a/7-gce/books/model-cloudsql.js b/7-gce/books/model-cloudsql.js index 18d9d705a..2761b3c61 100644 --- a/7-gce/books/model-cloudsql.js +++ b/7-gce/books/model-cloudsql.js @@ -21,19 +21,23 @@ const background = require('../lib/background'); const options = { user: config.get('MYSQL_USER'), password: config.get('MYSQL_PASSWORD'), - database: 'bookshelf' + database: 'bookshelf', }; -if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') { +if ( + config.get('INSTANCE_CONNECTION_NAME') && + config.get('NODE_ENV') === 'production' +) { options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`; } const connection = mysql.createConnection(options); -function list (limit, token, cb) { +function list(limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( - 'SELECT * FROM `books` LIMIT ? OFFSET ?', [limit, token], + 'SELECT * FROM `books` LIMIT ? OFFSET ?', + [limit, token], (err, results) => { if (err) { cb(err); @@ -45,7 +49,7 @@ function list (limit, token, cb) { ); } -function listBy (userId, limit, token, cb) { +function listBy(userId, limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( 'SELECT * FROM `books` WHERE `createdById` = ? LIMIT ? OFFSET ?', @@ -57,10 +61,11 @@ function listBy (userId, limit, token, cb) { } const hasMore = results.length === limit ? token + results.length : false; cb(null, results, hasMore); - }); + } + ); } -function create (data, queueBook, cb) { +function create(data, queueBook, cb) { connection.query('INSERT INTO `books` SET ?', data, (err, res) => { if (err) { cb(err); @@ -73,13 +78,15 @@ function create (data, queueBook, cb) { }); } -function read (id, cb) { +function read(id, cb) { connection.query( - 'SELECT * FROM `books` WHERE `id` = ?', id, (err, results) => { + 'SELECT * FROM `books` WHERE `id` = ?', + id, + (err, results) => { if (!err && !results.length) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -87,24 +94,24 @@ function read (id, cb) { return; } cb(null, results[0]); - }); + } + ); } -function update (id, data, queueBook, cb) { - connection.query( - 'UPDATE `books` SET ? WHERE `id` = ?', [data, id], (err) => { - if (err) { - cb(err); - return; - } - if (queueBook) { - background.queueBook(id); - } - read(id, cb); - }); +function update(id, data, queueBook, cb) { + connection.query('UPDATE `books` SET ? WHERE `id` = ?', [data, id], err => { + if (err) { + cb(err); + return; + } + if (queueBook) { + background.queueBook(id); + } + read(id, cb); + }); } -function _delete (id, cb) { +function _delete(id, cb) { connection.query('DELETE FROM `books` WHERE `id` = ?', id, cb); } @@ -115,7 +122,7 @@ module.exports = { create: create, read: read, update: update, - delete: _delete + delete: _delete, }; if (module === require.main) { @@ -124,7 +131,8 @@ if (module === require.main) { console.log( `Running this script directly will allow you to initialize your mysql - database.\n This script will not modify any existing tables.\n`); + database.\n This script will not modify any existing tables.\n` + ); prompt.get(['user', 'password'], (err, result) => { if (err) { @@ -134,10 +142,15 @@ if (module === require.main) { }); } -function createSchema (config) { - const connection = mysql.createConnection(extend({ - multipleStatements: true - }, config)); +function createSchema(config) { + const connection = mysql.createConnection( + extend( + { + multipleStatements: true, + }, + config + ) + ); connection.query( `CREATE DATABASE IF NOT EXISTS \`bookshelf\` @@ -154,7 +167,7 @@ function createSchema (config) { \`createdBy\` VARCHAR(255) NULL, \`createdById\` VARCHAR(255) NULL, PRIMARY KEY (\`id\`));`, - (err) => { + err => { if (err) { throw err; } diff --git a/7-gce/books/model-datastore.js b/7-gce/books/model-datastore.js index 0693f9671..f191dc0db 100644 --- a/7-gce/books/model-datastore.js +++ b/7-gce/books/model-datastore.js @@ -18,7 +18,7 @@ const config = require('../config'); const background = require('../lib/background'); const ds = Datastore({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const kind = 'Book'; @@ -38,7 +38,7 @@ const kind = 'Book'; // id: id, // property: value // } -function fromDatastore (obj) { +function fromDatastore(obj) { obj.id = obj[Datastore.KEY].id; return obj; } @@ -66,17 +66,17 @@ function fromDatastore (obj) { // excludeFromIndexes: true // } // ] -function toDatastore (obj, nonIndexed) { +function toDatastore(obj, nonIndexed) { nonIndexed = nonIndexed || []; let results = []; - Object.keys(obj).forEach((k) => { + Object.keys(obj).forEach(k => { if (obj[k] === undefined) { return; } results.push({ name: k, value: obj[k], - excludeFromIndexes: nonIndexed.indexOf(k) !== -1 + excludeFromIndexes: nonIndexed.indexOf(k) !== -1, }); }); return results; @@ -86,8 +86,9 @@ function toDatastore (obj, nonIndexed) { // The ``limit`` argument determines the maximum amount of results to // return per page. The ``token`` argument allows requesting additional // pages. The callback is invoked with ``(err, books, nextPageToken)``. -function list (limit, token, cb) { - const q = ds.createQuery([kind]) +function list(limit, token, cb) { + const q = ds + .createQuery([kind]) .limit(limit) .order('title') .start(token); @@ -97,15 +98,19 @@ function list (limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } // Similar to ``list``, but only lists the books created by the specified // user. -function listBy (userId, limit, token, cb) { - const q = ds.createQuery([kind]) +function listBy(userId, limit, token, cb) { + const q = ds + .createQuery([kind]) .filter('createdById', '=', userId) .limit(limit) .start(token); @@ -115,7 +120,10 @@ function listBy (userId, limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } @@ -123,7 +131,7 @@ function listBy (userId, limit, token, cb) { // Creates a new book or updates an existing book with new data. The provided // data is automatically translated into Datastore format. The book will be // queued for background processing. -function update (id, data, queueBook, cb) { +function update(id, data, queueBook, cb) { let key; if (id) { key = ds.key([kind, parseInt(id, 10)]); @@ -133,32 +141,29 @@ function update (id, data, queueBook, cb) { const entity = { key: key, - data: toDatastore(data, ['description']) + data: toDatastore(data, ['description']), }; - ds.save( - entity, - (err) => { - if (err) { - cb(err); - return; - } - data.id = entity.key.id; - if (queueBook) { - background.queueBook(data.id); - } - cb(null, data); + ds.save(entity, err => { + if (err) { + cb(err); + return; } - ); + data.id = entity.key.id; + if (queueBook) { + background.queueBook(data.id); + } + cb(null, data); + }); } -function read (id, cb) { +function read(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.get(key, (err, entity) => { if (!err && !entity) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -169,7 +174,7 @@ function read (id, cb) { }); } -function _delete (id, cb) { +function _delete(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.delete(key, cb); } @@ -182,5 +187,5 @@ module.exports = { update: update, delete: _delete, list: list, - listBy: listBy + listBy: listBy, }; diff --git a/7-gce/books/model-mongodb.js b/7-gce/books/model-mongodb.js index 7e9accdb6..2061ac5b8 100644 --- a/7-gce/books/model-mongodb.js +++ b/7-gce/books/model-mongodb.js @@ -20,7 +20,7 @@ const background = require('../lib/background'); let collection; -function fromMongo (item) { +function fromMongo(item) { if (Array.isArray(item) && item.length) { item = item[0]; } @@ -29,30 +29,33 @@ function fromMongo (item) { return item; } -function toMongo (item) { +function toMongo(item) { delete item.id; return item; } -function getCollection (cb) { +function getCollection(cb) { if (collection) { setImmediate(() => { cb(null, collection); }); return; } - MongoClient.connect(config.get('MONGO_URL'), (err, client) => { - if (err) { - cb(err); - return; + MongoClient.connect( + config.get('MONGO_URL'), + (err, client) => { + if (err) { + cb(err); + return; + } + const db = client.db(config.get('MONGO_DB_NAME')); + collection = db.collection(config.get('MONGO_COLLECTION')); + cb(null, collection); } - const db = client.db(config.get('MONGO_DB_NAME')); - collection = db.collection(config.get('MONGO_COLLECTION')); - cb(null, collection); - }); + ); } -function list (limit, token, cb) { +function list(limit, token, cb) { token = token ? parseInt(token, 10) : 0; if (isNaN(token)) { cb(new Error('invalid token')); @@ -63,7 +66,8 @@ function list (limit, token, cb) { cb(err); return; } - collection.find({}) + collection + .find({}) .skip(token) .limit(limit) .toArray((err, results) => { @@ -78,7 +82,7 @@ function list (limit, token, cb) { }); } -function listBy (userId, limit, token, cb) { +function listBy(userId, limit, token, cb) { token = token ? parseInt(token, 10) : 0; if (isNaN(token)) { cb(new Error('invalid token')); @@ -89,7 +93,8 @@ function listBy (userId, limit, token, cb) { cb(err); return; } - collection.find({ createdById: userId }) + collection + .find({createdById: userId}) .skip(token) .limit(limit) .toArray((err, results) => { @@ -104,7 +109,7 @@ function listBy (userId, limit, token, cb) { }); } -function create (data, queueBook, cb) { +function create(data, queueBook, cb) { getCollection((err, collection) => { if (err) { cb(err); @@ -124,42 +129,45 @@ function create (data, queueBook, cb) { }); } -function read (id, cb) { +function read(id, cb) { getCollection((err, collection) => { if (err) { cb(err); return; } - collection.findOne({ - _id: new ObjectID(id) - }, (err, result) => { - if (!err && !result) { - err = { - code: 404, - message: 'Not found' - }; - return; - } - if (err) { - cb(err); - return; + collection.findOne( + { + _id: new ObjectID(id), + }, + (err, result) => { + if (!err && !result) { + err = { + code: 404, + message: 'Not found', + }; + return; + } + if (err) { + cb(err); + return; + } + cb(null, fromMongo(result)); } - cb(null, fromMongo(result)); - }); + ); }); } -function update (id, data, queueBook, cb) { +function update(id, data, queueBook, cb) { getCollection((err, collection) => { if (err) { cb(err); return; } collection.update( - { _id: new ObjectID(id) }, - { '$set': toMongo(data) }, - { w: 1 }, - (err) => { + {_id: new ObjectID(id)}, + {$set: toMongo(data)}, + {w: 1}, + err => { if (err) { cb(err); return; @@ -173,15 +181,18 @@ function update (id, data, queueBook, cb) { }); } -function _delete (id, cb) { +function _delete(id, cb) { getCollection((err, collection) => { if (err) { cb(err); return; } - collection.remove({ - _id: new ObjectID(id) - }, cb); + collection.remove( + { + _id: new ObjectID(id), + }, + cb + ); }); } @@ -191,5 +202,5 @@ module.exports = { update: update, delete: _delete, list: list, - listBy: listBy + listBy: listBy, }; diff --git a/7-gce/config.js b/7-gce/config.js index 6fcc654f6..3f46a309f 100644 --- a/7-gce/config.js +++ b/7-gce/config.js @@ -15,7 +15,7 @@ // Hierarchical node.js configuration with command-line arguments, environment // variables, and files. -const nconf = module.exports = require('nconf'); +const nconf = (module.exports = require('nconf')); const path = require('path'); nconf @@ -40,10 +40,10 @@ nconf 'PORT', 'SECRET', 'SUBSCRIPTION_NAME', - 'TOPIC_NAME' + 'TOPIC_NAME', ]) // 3. Config file - .file({ file: path.join(__dirname, 'config.json') }) + .file({file: path.join(__dirname, 'config.json')}) // 4. Defaults .defaults({ // Typically you will create a bucket with the same name as your project ID. @@ -79,7 +79,7 @@ nconf SECRET: 'keyboardcat', SUBSCRIPTION_NAME: 'shared-worker-subscription', - TOPIC_NAME: 'book-process-queue' + TOPIC_NAME: 'book-process-queue', }); // Check for required settings @@ -100,8 +100,10 @@ if (nconf.get('DATA_BACKEND') === 'cloudsql') { checkConfig('MONGO_DB_NAME'); } -function checkConfig (setting) { +function checkConfig(setting) { if (!nconf.get(setting)) { - throw new Error(`You must set ${setting} as an environment variable or in config.json!`); + throw new Error( + `You must set ${setting} as an environment variable or in config.json!` + ); } } diff --git a/7-gce/lib/background.js b/7-gce/lib/background.js index ff3f55ee6..84a8fc1c9 100644 --- a/7-gce/lib/background.js +++ b/7-gce/lib/background.js @@ -21,7 +21,7 @@ const topicName = config.get('TOPIC_NAME'); const subscriptionName = config.get('SUBSCRIPTION_NAME'); const pubsub = new Pubsub({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); // This configuration will automatically create the topic if @@ -29,7 +29,7 @@ const pubsub = new Pubsub({ // that a least one subscription exists on the topic before // publishing anything to it as topics without subscribers // will essentially drop any messages. -function getTopic (cb) { +function getTopic(cb) { pubsub.createTopic(topicName, (err, topic) => { // topic already exists. if (err && err.code === 6) { @@ -44,16 +44,16 @@ function getTopic (cb) { // When more than one worker is running they will all share the same // subscription, which means that pub/sub will evenly distribute messages // to each worker. -function subscribe (cb) { +function subscribe(cb) { let subscription; // Event handlers - function handleMessage (message) { + function handleMessage(message) { const data = JSON.parse(message.data); message.ack(); cb(null, data); } - function handleError (err) { + function handleError(err) { logging.error(err); } @@ -75,7 +75,9 @@ function subscribe (cb) { subscription.on('message', handleMessage); subscription.on('error', handleError); - logging.info(`Listening to ${topicName} with subscription ${subscriptionName}`); + logging.info( + `Listening to ${topicName} with subscription ${subscriptionName}` + ); }); }); @@ -91,7 +93,7 @@ function subscribe (cb) { } // Adds a book to the queue to be processed by the worker. -function queueBook (bookId) { +function queueBook(bookId) { getTopic((err, topic) => { if (err) { logging.error('Error occurred while getting pubsub topic', err); @@ -100,11 +102,11 @@ function queueBook (bookId) { const data = { action: 'processBook', - bookId: bookId + bookId: bookId, }; const publisher = topic.publisher(); - publisher.publish(Buffer.from(JSON.stringify(data)), (err) => { + publisher.publish(Buffer.from(JSON.stringify(data)), err => { if (err) { logging.error('Error occurred while queuing background task', err); } else { @@ -116,5 +118,5 @@ function queueBook (bookId) { module.exports = { subscribe, - queueBook + queueBook, }; diff --git a/7-gce/lib/images.js b/7-gce/lib/images.js index 1073c3b90..45ef1cc02 100644 --- a/7-gce/lib/images.js +++ b/7-gce/lib/images.js @@ -21,18 +21,18 @@ const logging = require('./logging'); const CLOUD_BUCKET = config.get('CLOUD_BUCKET'); const storage = Storage({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const bucket = storage.bucket(CLOUD_BUCKET); // Downloads a given image (by URL) and then uploads it to // Google Cloud Storage. Provides the publicly accessable URL to the callback. -function downloadAndUploadImage (sourceUrl, destFileName, cb) { +function downloadAndUploadImage(sourceUrl, destFileName, cb) { const file = bucket.file(destFileName); request .get(sourceUrl) - .on('error', (err) => { + .on('error', err => { logging.warn(`Could not fetch image ${sourceUrl}`, err); cb(err); }) @@ -43,7 +43,7 @@ function downloadAndUploadImage (sourceUrl, destFileName, cb) { cb(null, getPublicUrl(destFileName)); }); }) - .on('error', (err) => { + .on('error', err => { logging.error('Could not upload image', err); cb(err); }); @@ -52,7 +52,7 @@ function downloadAndUploadImage (sourceUrl, destFileName, cb) { // Returns the public, anonymously accessable URL to a given Cloud Storage // object. // The object's ACL has to be set to public read. -function getPublicUrl (filename) { +function getPublicUrl(filename) { return `https://storage.googleapis.com/${CLOUD_BUCKET}/${filename}`; } @@ -60,7 +60,7 @@ function getPublicUrl (filename) { // req.file is processed and will have two new properties: // * ``cloudStorageObject`` the object name in cloud storage. // * ``cloudStoragePublicUrl`` the public url to the object. -function sendUploadToGCS (req, res, next) { +function sendUploadToGCS(req, res, next) { if (!req.file) { return next(); } @@ -69,11 +69,11 @@ function sendUploadToGCS (req, res, next) { const file = bucket.file(gcsname); const stream = file.createWriteStream({ metadata: { - contentType: req.file.mimetype - } + contentType: req.file.mimetype, + }, }); - stream.on('error', (err) => { + stream.on('error', err => { req.file.cloudStorageError = err; next(err); }); @@ -96,13 +96,13 @@ const Multer = require('multer'); const multer = Multer({ storage: Multer.MemoryStorage, limits: { - fileSize: 5 * 1024 * 1024 // no larger than 5mb - } + fileSize: 5 * 1024 * 1024, // no larger than 5mb + }, }); module.exports = { downloadAndUploadImage, getPublicUrl, sendUploadToGCS, - multer + multer, }; diff --git a/7-gce/lib/logging.js b/7-gce/lib/logging.js index 94ddbc7ed..886af99dd 100644 --- a/7-gce/lib/logging.js +++ b/7-gce/lib/logging.js @@ -15,7 +15,8 @@ const winston = require('winston'); const expressWinston = require('express-winston'); -const StackdriverTransport = require('@google-cloud/logging-winston').LoggingWinston; +const StackdriverTransport = require('@google-cloud/logging-winston') + .LoggingWinston; const colorize = process.env.NODE_ENV !== 'production'; @@ -25,11 +26,11 @@ const requestLogger = expressWinston.logger({ new StackdriverTransport(), new winston.transports.Console({ json: false, - colorize: colorize - }) + colorize: colorize, + }), ], expressFormat: true, - meta: false + meta: false, }); // Logger to capture any top-level errors and output json diagnostic info. @@ -38,9 +39,9 @@ const errorLogger = expressWinston.errorLogger({ new StackdriverTransport(), new winston.transports.Console({ json: true, - colorize: colorize - }) - ] + colorize: colorize, + }), + ], }); module.exports = { @@ -52,5 +53,5 @@ module.exports = { log: winston.log, verbose: winston.verbose, debug: winston.debug, - silly: winston.silly + silly: winston.silly, }; diff --git a/7-gce/lib/oauth2.js b/7-gce/lib/oauth2.js index fa63e7270..f4ac3ec84 100644 --- a/7-gce/lib/oauth2.js +++ b/7-gce/lib/oauth2.js @@ -20,7 +20,7 @@ const config = require('../config'); const passport = require('passport'); const GoogleStrategy = require('passport-google-oauth20').Strategy; -function extractProfile (profile) { +function extractProfile(profile) { let imageUrl = ''; if (profile.photos && profile.photos.length) { imageUrl = profile.photos[0].value; @@ -28,7 +28,7 @@ function extractProfile (profile) { return { id: profile.id, displayName: profile.displayName, - image: imageUrl + image: imageUrl, }; } @@ -39,16 +39,21 @@ function extractProfile (profile) { // along with the user's profile. The function must invoke `cb` with a user // object, which will be set at `req.user` in route handlers after // authentication. -passport.use(new GoogleStrategy({ - clientID: config.get('OAUTH2_CLIENT_ID'), - clientSecret: config.get('OAUTH2_CLIENT_SECRET'), - callbackURL: config.get('OAUTH2_CALLBACK'), - accessType: 'offline' -}, (accessToken, refreshToken, profile, cb) => { - // Extract the minimal profile information we need from the profile object - // provided by Google - cb(null, extractProfile(profile)); -})); +passport.use( + new GoogleStrategy( + { + clientID: config.get('OAUTH2_CLIENT_ID'), + clientSecret: config.get('OAUTH2_CLIENT_SECRET'), + callbackURL: config.get('OAUTH2_CALLBACK'), + accessType: 'offline', + }, + (accessToken, refreshToken, profile, cb) => { + // Extract the minimal profile information we need from the profile object + // provided by Google + cb(null, extractProfile(profile)); + } + ) +); passport.serializeUser((user, cb) => { cb(null, user); @@ -64,7 +69,7 @@ const router = express.Router(); // Middleware that requires the user to be logged in. If the user is not logged // in, it will redirect the user to authorize the application and then return // them to the original URL they requested. -function authRequired (req, res, next) { +function authRequired(req, res, next) { if (!req.user) { req.session.oauth2return = req.originalUrl; return res.redirect('/auth/login'); @@ -74,10 +79,14 @@ function authRequired (req, res, next) { // Middleware that exposes the user's profile as well as login/logout URLs to // any templates. These are available as `profile`, `login`, and `logout`. -function addTemplateVariables (req, res, next) { +function addTemplateVariables(req, res, next) { res.locals.profile = req.user; - res.locals.login = `/auth/login?return=${encodeURIComponent(req.originalUrl)}`; - res.locals.logout = `/auth/logout?return=${encodeURIComponent(req.originalUrl)}`; + res.locals.login = `/auth/login?return=${encodeURIComponent( + req.originalUrl + )}`; + res.locals.logout = `/auth/logout?return=${encodeURIComponent( + req.originalUrl + )}`; next(); } // [END middleware] @@ -102,7 +111,7 @@ router.get( }, // Start OAuth 2 flow using Passport.js - passport.authenticate('google', { scope: ['email', 'profile'] }) + passport.authenticate('google', {scope: ['email', 'profile']}) ); // [END authorize] @@ -135,5 +144,5 @@ module.exports = { extractProfile: extractProfile, router: router, required: authRequired, - template: addTemplateVariables + template: addTemplateVariables, }; diff --git a/7-gce/package.json b/7-gce/package.json index 2fca70a66..9346fbf45 100644 --- a/7-gce/package.json +++ b/7-gce/package.json @@ -12,24 +12,6 @@ "init-cloudsql": "node books/model-cloudsql.js" }, "author": "Google Inc.", - "contributors": [ - { - "name": "Jon Wayne Parrott", - "email": "jonwayne@google.com" - }, - { - "name": "Jonathan Simon", - "email": "jbsimon@google.com" - }, - { - "name": "Jason Dobry", - "email": "jdobry@google.com" - }, - { - "name": "Ace Nassri", - "email": "anassri@google.com" - } - ], "cloud-repo-tools": { "requiresKeyFile": true, "requiresProjectId": true, @@ -43,16 +25,6 @@ } }, "license": "Apache-2.0", - "semistandard": { - "globals": [ - "after", - "afterEach", - "before", - "beforeEach", - "describe", - "it" - ] - }, "dependencies": { "@google-cloud/datastore": "1.4.2", "@google-cloud/debug-agent": "3.0.0", diff --git a/7-gce/test/_api-tests.js b/7-gce/test/_api-tests.js index e422353ca..31af46300 100644 --- a/7-gce/test/_api-tests.js +++ b/7-gce/test/_api-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -26,12 +26,12 @@ module.exports = (DATA_BACKEND) => { appConfig.set(`DATA_BACKEND`, DATA_BACKEND); }); - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `beep` }) + .send({title: `beep`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `beep`); @@ -39,13 +39,13 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should list books`, (t) => { + test.serial.cb(`should list books`, t => { // Give Datastore time to become consistent setTimeout(() => { getRequest(testConfig) .get(`/api/books`) .expect(200) - .expect((response) => { + .expect(response => { t.true(Array.isArray(response.body.items)); t.true(response.body.items.length >= 1); }) @@ -53,11 +53,11 @@ module.exports = (DATA_BACKEND) => { }, 1000); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .delete(`/api/books/${id}/`) // .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `OK`); }) .end(t.end); diff --git a/7-gce/test/_crud-tests.js b/7-gce/test/_crud-tests.js index 7c0293b84..f1265ca92 100644 --- a/7-gce/test/_crud-tests.js +++ b/7-gce/test/_crud-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -27,12 +27,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -40,30 +40,30 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show a list of books`, (t) => { + test.serial.cb(`should show a list of books`, t => { // Give Datastore time to become consistent setTimeout(() => { const expected = /
/; getRequest(testConfig) .get(`/books`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }, 2000); }); - test.serial.cb(`should handle error`, (t) => { + test.serial.cb(`should handle error`, t => { getRequest(testConfig) .get(`/books`) - .query({ pageToken: `badrequest` }) + .query({pageToken: `badrequest`}) .expect(500) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -74,13 +74,13 @@ module.exports = (DATA_BACKEND) => { } }); - test.serial.cb(`should post to add book form`, (t) => { + test.serial.cb(`should post to add book form`, t => { const expected = /Redirecting to \/books\//; getRequest(testConfig) .post(`/books/add`) .field(`title`, `my book`) .expect(302) - .expect((response) => { + .expect(response => { const location = response.headers.location; const idPart = location.replace(`/books/`, ``); if (DATA_BACKEND !== `mongodb`) { @@ -93,19 +93,19 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show add book form`, (t) => { + test.serial.cb(`should show add book form`, t => { const expected = /Add book/; getRequest(testConfig) .get(`/books/add`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -117,12 +117,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -130,46 +130,46 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should update a book`, (t) => { + test.serial.cb(`should update a book`, t => { const expected = new RegExp(`Redirecting to /books/${id}`); getRequest(testConfig) .post(`/books/${id}/edit`) .field(`title`, `my other book`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show edit book form`, (t) => { + test.serial.cb(`should show edit book form`, t => { const expected = /"title" value="my other book"/; getRequest(testConfig) .get(`/books/${id}/edit`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show a book`, (t) => { + test.serial.cb(`should show a book`, t => { const expected = /

my other book <\/small><\/h4>/; getRequest(testConfig) .get(`/books/${id}`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { const expected = /Redirecting to \/books/; getRequest(testConfig) .get(`/books/${id}/delete`) .expect(302) - .expect((response) => { + .expect(response => { id = undefined; t.regex(response.text, expected); }) @@ -177,7 +177,7 @@ module.exports = (DATA_BACKEND) => { }); // clean up - test.always.after.cb((t) => { + test.always.after.cb(t => { appConfig.set(`DATA_BACKEND`, originalDataBackend); if (id) { diff --git a/7-gce/test/_test-config.js b/7-gce/test/_test-config.js index e9422563c..d12e60d8a 100644 --- a/7-gce/test/_test-config.js +++ b/7-gce/test/_test-config.js @@ -30,6 +30,6 @@ module.exports = { env: { PORT: PORT, SUBSCRIPTION_NAME: `shared-worker-subscription-${TESTNAME}`, - TOPIC_NAME: `book-process-queue-${TESTNAME}` - } + TOPIC_NAME: `book-process-queue-${TESTNAME}`, + }, }; diff --git a/7-gce/test/_test-config.worker.js b/7-gce/test/_test-config.worker.js index 3bc90bb70..712a256a3 100644 --- a/7-gce/test/_test-config.worker.js +++ b/7-gce/test/_test-config.worker.js @@ -29,10 +29,10 @@ module.exports = { port: PORT, env: { SUBSCRIPTION_NAME: `shared-worker-subscription-${TESTNAME}`, - TOPIC_NAME: `book-process-queue-${TESTNAME}` + TOPIC_NAME: `book-process-queue-${TESTNAME}`, }, version: VERSION, - project: PROJECT_ID + project: PROJECT_ID, }; if (process.env.E2E_TESTS) { diff --git a/7-gce/test/app.test.js b/7-gce/test/app.test.js index 06ae909c7..120d80f40 100644 --- a/7-gce/test/app.test.js +++ b/7-gce/test/app.test.js @@ -19,33 +19,34 @@ const sinon = require(`sinon`); const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); -test.cb(`should redirect / to /books`, (t) => { - utils.getRequest(testConfig) +test.cb(`should redirect / to /books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, /Redirecting to \/books/); }) .end(t.end); }); -test(`should check config`, (t) => { +test(`should check config`, t => { const nconfMock = { argv: sinon.stub().returnsThis(), env: sinon.stub().returnsThis(), file: sinon.stub().returnsThis(), defaults: sinon.stub().returnsThis(), - get: function (setting) { + get: function(setting) { return this[setting]; - } + }, }; - function getMsg (setting) { + function getMsg(setting) { return `You must set ${setting} as an environment variable or in config.json!`; } const testFunc = () => { - proxyquire(`../config`, { nconf: nconfMock }); + proxyquire(`../config`, {nconf: nconfMock}); }; nconfMock.DATA_BACKEND = `datastore`; diff --git a/7-gce/test/background.test.js b/7-gce/test/background.test.js index 40f2df205..a0eb9433e 100644 --- a/7-gce/test/background.test.js +++ b/7-gce/test/background.test.js @@ -20,51 +20,48 @@ const test = require(`ava`); let background; const mocks = {}; -test.beforeEach((t) => { +test.beforeEach(t => { // Mock dependencies used by background.js mocks.config = { GCLOUD_PROJECT: process.env.GCLOUD_PROJECT, SUBSCRIPTION_NAME: `shared-worker-subscription`, - TOPIC_NAME: `book-process-queue` + TOPIC_NAME: `book-process-queue`, }; - mocks.config.get = function (key) { + mocks.config.get = function(key) { return this[key]; }; mocks.subscription = { - on: sinon.stub() + on: sinon.stub(), }; mocks.publisher = { - publish: sinon.stub().callsArg(1, null) + publish: sinon.stub().callsArg(1, null), }; mocks.topic = { createSubscription: sinon.stub().callsArgWith(1, null, mocks.subscription), - publisher: sinon.stub().returns(mocks.publisher) + publisher: sinon.stub().returns(mocks.publisher), }; mocks.pubsub = { createTopic: sinon.stub().callsArgWith(1, null, mocks.topic), - topic: sinon.stub().returns(mocks.topic) + topic: sinon.stub().returns(mocks.topic), }; mocks.Pubsub = sinon.stub().returns(mocks.pubsub); mocks.logging = { info: sinon.stub(), - error: sinon.stub() + error: sinon.stub(), }; // Load background.js with provided mocks background = proxyquire(`../lib/background`, { '@google-cloud/pubsub': mocks.Pubsub, '../config': mocks.config, - './logging': mocks.logging + './logging': mocks.logging, }); - t.true( - mocks.Pubsub.calledOnce, - `Pubsub() should have been called once` - ); + t.true(mocks.Pubsub.calledOnce, `Pubsub() should have been called once`); }); -test.serial.cb(`should subscribe and log message`, (t) => { +test.serial.cb(`should subscribe and log message`, t => { // Setup - const testMessage = { test: `foo` }; + const testMessage = {test: `foo`}; // Run target functionality background.subscribe((err, message) => { @@ -123,18 +120,18 @@ test.serial.cb(`should subscribe and log message`, (t) => { setTimeout(() => { mocks.subscription.on.firstCall.args[1]({ data: JSON.stringify(testMessage), - ack: sinon.stub() + ack: sinon.stub(), }); }, 10); }); -test.serial.cb(`should return topic error, if any`, (t) => { +test.serial.cb(`should return topic error, if any`, t => { // Setup const testErrorMsg = `test error`; mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg); // Run target functionality - background.subscribe((data) => { + background.subscribe(data => { // Assertions t.true( mocks.pubsub.createTopic.calledOnce, @@ -165,13 +162,13 @@ test.serial.cb(`should return topic error, if any`, (t) => { }); }); -test.serial.cb(`should return subscription error, if any`, (t) => { +test.serial.cb(`should return subscription error, if any`, t => { // Setup const testErrorMsg = `test error`; mocks.topic.createSubscription = sinon.stub().callsArgWith(1, testErrorMsg); // Run target functionality - background.subscribe((data) => { + background.subscribe(data => { // Assertions t.true( mocks.pubsub.createTopic.calledOnce, @@ -211,7 +208,7 @@ test.serial.cb(`should return subscription error, if any`, (t) => { }); }); -test.serial(`should queue a book and log message`, (t) => { +test.serial(`should queue a book and log message`, t => { // Setup const testBookId = 1; @@ -243,10 +240,12 @@ test.serial(`should queue a book and log message`, (t) => { ); t.deepEqual( mocks.publisher.publish.firstCall.args[0], - Buffer.from(JSON.stringify({ - action: `processBook`, - bookId: testBookId - })), + Buffer.from( + JSON.stringify({ + action: `processBook`, + bookId: testBookId, + }) + ), `publisher.publish() should have been called with the right arguments` ); t.true( @@ -260,11 +259,11 @@ test.serial(`should queue a book and log message`, (t) => { ); }); -test.serial(`should queue a book and log message even if topic exists`, (t) => { +test.serial(`should queue a book and log message even if topic exists`, t => { // Setup const testBookId = 1; mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, { - code: 6 + code: 6, }); // Run target functionality @@ -299,10 +298,12 @@ test.serial(`should queue a book and log message even if topic exists`, (t) => { ); t.deepEqual( mocks.publisher.publish.firstCall.args[0], - Buffer.from(JSON.stringify({ - action: `processBook`, - bookId: testBookId - })), + Buffer.from( + JSON.stringify({ + action: `processBook`, + bookId: testBookId, + }) + ), `publisher.publish() should have been called with the right arguments` ); t.true( @@ -316,7 +317,7 @@ test.serial(`should queue a book and log message even if topic exists`, (t) => { ); }); -test.serial(`should log error if cannot get topic`, (t) => { +test.serial(`should log error if cannot get topic`, t => { // Setup const testBookId = 1; const testErrorMsg = `test error`; @@ -356,7 +357,7 @@ test.serial(`should log error if cannot get topic`, (t) => { ); }); -test.serial(`should log error if cannot publish message`, (t) => { +test.serial(`should log error if cannot publish message`, t => { // Setup const testBookId = 1; const testErrorMsg = `test error`; diff --git a/7-gce/test/cloudsql.test.js b/7-gce/test/cloudsql.test.js index ff2ba5fd5..3ac8fbe5c 100644 --- a/7-gce/test/cloudsql.test.js +++ b/7-gce/test/cloudsql.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || + process.env.TEST_CLOUDSQL +) { require(`./_api-tests`)(`cloudsql`); require(`./_crud-tests`)(`cloudsql`); } else { - test(`Skipping Cloud SQL tests...`, (t) => t.pass()); + test(`Skipping Cloud SQL tests...`, t => t.pass()); } diff --git a/7-gce/test/datastore.test.js b/7-gce/test/datastore.test.js index a4faa673b..6df5120c1 100644 --- a/7-gce/test/datastore.test.js +++ b/7-gce/test/datastore.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `datastore` || process.env.TEST_DATASTORE) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `datastore` || + process.env.TEST_DATASTORE +) { require(`./_api-tests`)(`datastore`); require(`./_crud-tests`)(`datastore`); } else { - test(`Skipping Cloud Datastore tests...`, (t) => t.pass()); + test(`Skipping Cloud Datastore tests...`, t => t.pass()); } diff --git a/7-gce/test/mongodb.test.js b/7-gce/test/mongodb.test.js index c7b49311b..3a9bbf47d 100644 --- a/7-gce/test/mongodb.test.js +++ b/7-gce/test/mongodb.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `mongodb` || process.env.TEST_MONGODB) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `mongodb` || + process.env.TEST_MONGODB +) { require(`./_api-tests`)(`mongodb`); require(`./_crud-tests`)(`mongodb`); } else { - test(`Skipping MongoDB tests...`, (t) => t.pass()); + test(`Skipping MongoDB tests...`, t => t.pass()); } diff --git a/7-gce/test/oauth2.test.js b/7-gce/test/oauth2.test.js index a384a0f3e..40fdcd558 100644 --- a/7-gce/test/oauth2.test.js +++ b/7-gce/test/oauth2.test.js @@ -32,26 +32,26 @@ const getPassportMock = () => { authenticate: sinon.stub().returns((req, res, next) => { req.session.oauth2return = `/another/path`; next(); - }) + }), }; }; -test.cb(`should start authorization`, (t) => { +test.cb(`should start authorization`, t => { const passportMock = getPassportMock(); - passportMock.authenticate = sinon.stub().returns((req, res, next) => { + passportMock.authenticate = sinon.stub().returns((req, res) => { t.is(req.session.oauth2return, `/some/path`); res.redirect(`/auth/google/callback?code=foo`); }); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/login?return=%2Fsome%2Fpath`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/auth\/google\/callback\?code=foo/); t.true(passportMock.initialize.calledOnce); @@ -61,29 +61,28 @@ test.cb(`should start authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test.cb(`should finish authorization`, (t) => { +test.cb(`should finish authorization`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const app = proxyquire(`../app`, { passport: passportMock, - './lib/oauth2': oauth2 + './lib/oauth2': oauth2, }); request(app) .get(`/auth/google/callback?code=foo`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/another\/path/); t.true(passportMock.initialize.calledOnce); @@ -93,22 +92,21 @@ test.cb(`should finish authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); t.deepEqual( oauth2.extractProfile({ - photos: [{ value: `image.jpg` }], + photos: [{value: `image.jpg`}], id: 1, - displayName: `Joe Developer` + displayName: `Joe Developer`, }), { id: 1, displayName: `Joe Developer`, - image: `image.jpg` + image: `image.jpg`, } ); const serializeUser = passportMock.serializeUser.firstCall.args[0]; @@ -127,18 +125,18 @@ test.cb(`should finish authorization`, (t) => { .end(t.end); }); -test.cb(`should logout`, (t) => { +test.cb(`should logout`, t => { const passportMock = getPassportMock(); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/logout`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \//); t.true(passportMock.initialize.calledOnce); @@ -148,28 +146,27 @@ test.cb(`should logout`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test(`should require authentication`, (t) => { +test(`should require authentication`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: {}, - session: {} + session: {}, }; const res = { - redirect: sinon.stub() + redirect: sinon.stub(), }; const next = sinon.stub(); oauth2.required(req, res, next); @@ -183,30 +180,32 @@ test(`should require authentication`, (t) => { t.is(res.redirect.firstCall.args[0], `/auth/login`); }); -test(`should add template variables`, (t) => { +test(`should add template variables`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: { id: 1, displayName: `Joe Developer`, - image: `image.jpg` - } + image: `image.jpg`, + }, }; const res = { - locals: {} + locals: {}, }; const next = sinon.stub(); oauth2.template(req, res, next); t.true(next.calledOnce); t.is(res.locals.profile, req.user); t.is( - res.locals.login, `/auth/login?return=${encodeURIComponent(req.originalUrl)}` + res.locals.login, + `/auth/login?return=${encodeURIComponent(req.originalUrl)}` ); t.is( - res.locals.logout, `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` + res.locals.logout, + `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` ); }); diff --git a/7-gce/test/worker.test.js b/7-gce/test/worker.test.js index 6b8179f93..6d6e47e26 100644 --- a/7-gce/test/worker.test.js +++ b/7-gce/test/worker.test.js @@ -20,42 +20,44 @@ const sinon = require(`sinon`); const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); -test.serial.cb(`should return number of processed books`, (t) => { - utils.getRequest(testConfig) +test.serial.cb(`should return number of processed books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, /This worker has processed/); }) .end(t.end); }); -test.serial.cb(`should do a health check`, (t) => { - utils.getRequest(testConfig) +test.serial.cb(`should do a health check`, t => { + utils + .getRequest(testConfig) .get(`/_ah/health`) .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `ok`); }) .end(t.end); }); -test.serial.cb(`should process a book`, (t) => { +test.serial.cb(`should process a book`, t => { const appConfig = require(`../config`); const loggingStub = { error: sinon.stub(), info: sinon.stub(), - warn: sinon.stub() + warn: sinon.stub(), }; const stubs = { './lib/logging': loggingStub, '@google-cloud/trace-agent': { start: sinon.stub(), - '@noCallThru': true + '@noCallThru': true, }, '@google-cloud/debug-agent': { - '@noCallThru': true - } + '@noCallThru': true, + }, }; stubs[`./books/model-${appConfig.get('DATA_BACKEND')}`] = { read: (bookId, cb) => { @@ -63,12 +65,12 @@ test.serial.cb(`should process a book`, (t) => { }, update: (bookId, book, queueBook, cb) => { cb(); - } + }, }; const worker = proxyquire(path.join(__dirname, `../worker`), stubs).mocks; const processBook = worker.processBook; - processBook(1, (err, bookId) => { + processBook(1, err => { if (err) { return t.end(err); } diff --git a/7-gce/worker.js b/7-gce/worker.js index e513a9912..995992043 100644 --- a/7-gce/worker.js +++ b/7-gce/worker.js @@ -49,7 +49,7 @@ app.get('/', (req, res) => { app.use(logging.errorLogger); -function subscribe () { +function subscribe() { // Subscribe to Cloud Pub/Sub and receive messages to process books. // The subscription will continue to listen for messages until the process // is killed. @@ -77,38 +77,41 @@ if (module === require.main) { // Processes a book by reading its existing data, attempting to find // more information, and updating the database with the new information. -function processBook (bookId, callback) { - waterfall([ - // Load the current data - (cb) => { - model.read(bookId, cb); - }, - // Find the information from Google - findBookInfo, - // Save the updated data - (updated, cb) => { - model.update(updated.id, updated, false, cb); - } - ], (err) => { - if (err) { - logging.error('Error occurred', err); +function processBook(bookId, callback) { + waterfall( + [ + // Load the current data + cb => { + model.read(bookId, cb); + }, + // Find the information from Google + findBookInfo, + // Save the updated data + (updated, cb) => { + model.update(updated.id, updated, false, cb); + }, + ], + err => { + if (err) { + logging.error('Error occurred', err); + if (callback) { + callback(err); + } + return; + } + logging.info(`Updated book ${bookId}`); + bookCount += 1; if (callback) { - callback(err); + callback(); } - return; } - logging.info(`Updated book ${bookId}`); - bookCount += 1; - if (callback) { - callback(); - } - }); + ); } // Tries to find additional information about a book and updates // the book's data. Also uploads a cover image to Cloud Storage // if available. -function findBookInfo (book, cb) { +function findBookInfo(book, cb) { queryBooksApi(book.title, (err, r) => { if (!err && !r.items) { err = 'Not found'; @@ -137,21 +140,22 @@ function findBookInfo (book, cb) { top.volumeInfo.imageLinks.smallThumbnail; const imageName = `${book.id}.jpg`; - images.downloadAndUploadImage( - imageUrl, imageName, (err, publicUrl) => { - if (!err) { - book.imageUrl = publicUrl; - } - cb(null, book); - }); + images.downloadAndUploadImage(imageUrl, imageName, (err, publicUrl) => { + if (!err) { + book.imageUrl = publicUrl; + } + cb(null, book); + }); }); } // Calls out to the Google Books API to get additional // information about a given book. -function queryBooksApi (query, cb) { +function queryBooksApi(query, cb) { request( - `https://www.googleapis.com/books/v1/volumes?q=${encodeURIComponent(query)}`, + `https://www.googleapis.com/books/v1/volumes?q=${encodeURIComponent( + query + )}`, (err, resp, body) => { if (err || resp.statusCode !== 200) { cb(err || `Response returned ${resp.statusCode}`); @@ -165,7 +169,7 @@ function queryBooksApi (query, cb) { app.mocks = { processBook: processBook, findBookInfo: findBookInfo, - queryBooksApi: queryBooksApi + queryBooksApi: queryBooksApi, }; // Proxyquire requires this *exact* line, hence the "app.mocks" above diff --git a/optional-kubernetes-engine/app.js b/optional-kubernetes-engine/app.js index c0a257fac..27d1c1aae 100644 --- a/optional-kubernetes-engine/app.js +++ b/optional-kubernetes-engine/app.js @@ -43,14 +43,14 @@ const sessionConfig = { resave: false, saveUninitialized: false, secret: config.get('SECRET'), - signed: true + signed: true, }; // In production use the App Engine Memcache instance to store session data, // otherwise fallback to the default MemoryStore in development. if (config.get('NODE_ENV') === 'production' && config.get('MEMCACHE_URL')) { sessionConfig.store = new MemcachedStore({ - hosts: [config.get('MEMCACHE_URL')] + hosts: [config.get('MEMCACHE_URL')], }); } @@ -87,7 +87,7 @@ app.use((req, res) => { }); // Basic error handler -app.use((err, req, res, next) => { +app.use((err, req, res) => { /* jshint unused:false */ // If our routes specified a specific response, then send that. Otherwise, // send a generic message so as not to leak anything. diff --git a/optional-kubernetes-engine/books/api.js b/optional-kubernetes-engine/books/api.js index 5d11f7fb9..108c996c5 100644 --- a/optional-kubernetes-engine/books/api.js +++ b/optional-kubernetes-engine/books/api.js @@ -16,7 +16,7 @@ const express = require('express'); const bodyParser = require('body-parser'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -38,7 +38,7 @@ router.get('/', (req, res, next) => { } res.json({ items: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -94,7 +94,7 @@ router.put('/:book', (req, res, next) => { * Delete a book. */ router.delete('/:book', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; @@ -111,7 +111,7 @@ router.use((err, req, res, next) => { // responding to the request err.response = { message: err.message, - internalCode: err.code + internalCode: err.code, }; next(err); }); diff --git a/optional-kubernetes-engine/books/crud.js b/optional-kubernetes-engine/books/crud.js index b33af34cf..8cafa66b5 100644 --- a/optional-kubernetes-engine/books/crud.js +++ b/optional-kubernetes-engine/books/crud.js @@ -17,7 +17,7 @@ const express = require('express'); const images = require('../lib/images'); const oauth2 = require('../lib/oauth2'); -function getModel () { +function getModel() { return require(`./model-${require('../config').get('DATA_BACKEND')}`); } @@ -46,7 +46,7 @@ router.get('/', (req, res, next) => { } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); }); }); @@ -58,14 +58,14 @@ router.get('/mine', oauth2.required, (req, res, next) => { req.user.id, 10, req.query.pageToken, - (err, entities, cursor, apiResponse) => { + (err, entities, cursor) => { if (err) { next(err); return; } res.render('books/list.pug', { books: entities, - nextPageToken: cursor + nextPageToken: cursor, }); } ); @@ -79,7 +79,7 @@ router.get('/mine', oauth2.required, (req, res, next) => { router.get('/add', (req, res) => { res.render('books/form.pug', { book: {}, - action: 'Add' + action: 'Add', }); }); @@ -135,7 +135,7 @@ router.get('/:book/edit', (req, res, next) => { } res.render('books/form.pug', { book: entity, - action: 'Edit' + action: 'Edit', }); }); }); @@ -180,7 +180,7 @@ router.get('/:book', (req, res, next) => { return; } res.render('books/view.pug', { - book: entity + book: entity, }); }); }); @@ -191,7 +191,7 @@ router.get('/:book', (req, res, next) => { * Delete a book. */ router.get('/:book/delete', (req, res, next) => { - getModel().delete(req.params.book, (err) => { + getModel().delete(req.params.book, err => { if (err) { next(err); return; diff --git a/optional-kubernetes-engine/books/model-cloudsql.js b/optional-kubernetes-engine/books/model-cloudsql.js index 897c22a03..ca0a5f009 100644 --- a/optional-kubernetes-engine/books/model-cloudsql.js +++ b/optional-kubernetes-engine/books/model-cloudsql.js @@ -21,19 +21,23 @@ const background = require('../lib/background'); const options = { user: config.get('MYSQL_USER'), password: config.get('MYSQL_PASSWORD'), - database: 'bookshelf' + database: 'bookshelf', }; -if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') { +if ( + config.get('INSTANCE_CONNECTION_NAME') && + config.get('NODE_ENV') === 'production' +) { options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`; } const connection = mysql.createConnection(options); -function list (limit, token, cb) { +function list(limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( - 'SELECT * FROM `books` LIMIT ? OFFSET ?', [limit, token], + 'SELECT * FROM `books` LIMIT ? OFFSET ?', + [limit, token], (err, results) => { if (err) { cb(err); @@ -45,7 +49,7 @@ function list (limit, token, cb) { ); } -function listBy (userId, limit, token, cb) { +function listBy(userId, limit, token, cb) { token = token ? parseInt(token, 10) : 0; connection.query( 'SELECT * FROM `books` WHERE `createdById` = ? LIMIT ? OFFSET ?', @@ -57,10 +61,11 @@ function listBy (userId, limit, token, cb) { } const hasMore = results.length === limit ? token + results.length : false; cb(null, results, hasMore); - }); + } + ); } -function create (data, queueBook, cb) { +function create(data, queueBook, cb) { connection.query('INSERT INTO `books` SET ?', data, (err, res) => { if (err) { cb(err); @@ -73,13 +78,15 @@ function create (data, queueBook, cb) { }); } -function read (id, cb) { +function read(id, cb) { connection.query( - 'SELECT * FROM `books` WHERE `id` = ?', id, (err, results) => { + 'SELECT * FROM `books` WHERE `id` = ?', + id, + (err, results) => { if (!err && !results.length) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -87,24 +94,24 @@ function read (id, cb) { return; } cb(null, results[0]); - }); + } + ); } -function update (id, data, queueBook, cb) { - connection.query( - 'UPDATE `books` SET ? WHERE `id` = ?', [data, id], (err) => { - if (err) { - cb(err); - return; - } - if (queueBook) { - background.queueBook(id); - } - read(id, cb); - }); +function update(id, data, queueBook, cb) { + connection.query('UPDATE `books` SET ? WHERE `id` = ?', [data, id], err => { + if (err) { + cb(err); + return; + } + if (queueBook) { + background.queueBook(id); + } + read(id, cb); + }); } -function _delete (id, cb) { +function _delete(id, cb) { connection.query('DELETE FROM `books` WHERE `id` = ?', id, cb); } @@ -115,7 +122,7 @@ module.exports = { create: create, read: read, update: update, - delete: _delete + delete: _delete, }; if (module === require.main) { @@ -124,7 +131,8 @@ if (module === require.main) { console.log( `Running this script directly will allow you to initialize your mysql - database.\n This script will not modify any existing tables.\n`); + database.\n This script will not modify any existing tables.\n` + ); prompt.get(['user', 'password'], (err, result) => { if (err) { @@ -134,10 +142,15 @@ if (module === require.main) { }); } -function createSchema (config) { - const connection = mysql.createConnection(extend({ - multipleStatements: true - }, config)); +function createSchema(config) { + const connection = mysql.createConnection( + extend( + { + multipleStatements: true, + }, + config + ) + ); connection.query( `CREATE DATABASE IF NOT EXISTS \`bookshelf\` @@ -154,7 +167,7 @@ function createSchema (config) { \`createdBy\` VARCHAR(255) NULL, \`createdById\` VARCHAR(255) NULL, PRIMARY KEY (\`id\`));`, - (err) => { + err => { if (err) { throw err; } diff --git a/optional-kubernetes-engine/books/model-datastore.js b/optional-kubernetes-engine/books/model-datastore.js index 4ba2a3c59..51782c005 100644 --- a/optional-kubernetes-engine/books/model-datastore.js +++ b/optional-kubernetes-engine/books/model-datastore.js @@ -18,7 +18,7 @@ const config = require('../config'); const background = require('../lib/background'); const ds = Datastore({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const kind = 'Book'; @@ -38,7 +38,7 @@ const kind = 'Book'; // id: id, // property: value // } -function fromDatastore (obj) { +function fromDatastore(obj) { obj.id = obj[Datastore.KEY].id; return obj; } @@ -66,17 +66,17 @@ function fromDatastore (obj) { // excludeFromIndexes: true // } // ] -function toDatastore (obj, nonIndexed) { +function toDatastore(obj, nonIndexed) { nonIndexed = nonIndexed || []; let results = []; - Object.keys(obj).forEach((k) => { + Object.keys(obj).forEach(k => { if (obj[k] === undefined) { return; } results.push({ name: k, value: obj[k], - excludeFromIndexes: nonIndexed.indexOf(k) !== -1 + excludeFromIndexes: nonIndexed.indexOf(k) !== -1, }); }); return results; @@ -86,8 +86,9 @@ function toDatastore (obj, nonIndexed) { // The ``limit`` argument determines the maximum amount of results to // return per page. The ``token`` argument allows requesting additional // pages. The callback is invoked with ``(err, books, nextPageToken)``. -function list (limit, token, cb) { - const q = ds.createQuery([kind]) +function list(limit, token, cb) { + const q = ds + .createQuery([kind]) .limit(limit) .order('title') .start(token); @@ -97,15 +98,19 @@ function list (limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } // Similar to ``list``, but only lists the books created by the specified // user. -function listBy (userId, limit, token, cb) { - const q = ds.createQuery([kind]) +function listBy(userId, limit, token, cb) { + const q = ds + .createQuery([kind]) .filter('createdById', '=', userId) .limit(limit) .start(token); @@ -115,7 +120,10 @@ function listBy (userId, limit, token, cb) { cb(err); return; } - const hasMore = nextQuery.moreResults !== Datastore.NO_MORE_RESULTS ? nextQuery.endCursor : false; + const hasMore = + nextQuery.moreResults !== Datastore.NO_MORE_RESULTS + ? nextQuery.endCursor + : false; cb(null, entities.map(fromDatastore), hasMore); }); } @@ -123,7 +131,7 @@ function listBy (userId, limit, token, cb) { // Creates a new book or updates an existing book with new data. The provided // data is automatically translated into Datastore format. The book will be // queued for background processing. -function update (id, data, queueBook, cb) { +function update(id, data, queueBook, cb) { let key; if (id) { key = ds.key([kind, parseInt(id, 10)]); @@ -133,32 +141,29 @@ function update (id, data, queueBook, cb) { const entity = { key: key, - data: toDatastore(data, ['description']) + data: toDatastore(data, ['description']), }; - ds.save( - entity, - (err) => { - if (err) { - cb(err); - return; - } - data.id = entity.key.id; - if (queueBook) { - background.queueBook(data.id); - } - cb(null, data); + ds.save(entity, err => { + if (err) { + cb(err); + return; } - ); + data.id = entity.key.id; + if (queueBook) { + background.queueBook(data.id); + } + cb(null, data); + }); } -function read (id, cb) { +function read(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.get(key, (err, entity) => { if (!err && !entity) { err = { code: 404, - message: 'Not found' + message: 'Not found', }; } if (err) { @@ -169,7 +174,7 @@ function read (id, cb) { }); } -function _delete (id, cb) { +function _delete(id, cb) { const key = ds.key([kind, parseInt(id, 10)]); ds.delete(key, cb); } @@ -182,5 +187,5 @@ module.exports = { update: update, delete: _delete, list: list, - listBy: listBy + listBy: listBy, }; diff --git a/optional-kubernetes-engine/books/model-mongodb.js b/optional-kubernetes-engine/books/model-mongodb.js index 5892c483c..c993c3233 100644 --- a/optional-kubernetes-engine/books/model-mongodb.js +++ b/optional-kubernetes-engine/books/model-mongodb.js @@ -20,7 +20,7 @@ const background = require('../lib/background'); let collection; -function fromMongo (item) { +function fromMongo(item) { if (Array.isArray(item) && item.length) { item = item[0]; } @@ -29,30 +29,33 @@ function fromMongo (item) { return item; } -function toMongo (item) { +function toMongo(item) { delete item.id; return item; } -function getCollection (cb) { +function getCollection(cb) { if (collection) { setImmediate(() => { cb(null, collection); }); return; } - MongoClient.connect(config.get('MONGO_URL'), (err, client) => { - if (err) { - cb(err); - return; + MongoClient.connect( + config.get('MONGO_URL'), + (err, client) => { + if (err) { + cb(err); + return; + } + const db = client.db(config.get('MONGO_DB_NAME')); + collection = db.collection(config.get('MONGO_COLLECTION')); + cb(null, collection); } - const db = client.db(config.get('MONGO_DB_NAME')); - collection = db.collection(config.get('MONGO_COLLECTION')); - cb(null, collection); - }); + ); } -function list (limit, token, cb) { +function list(limit, token, cb) { token = token ? parseInt(token, 10) : 0; if (isNaN(token)) { cb(new Error('invalid token')); @@ -63,7 +66,8 @@ function list (limit, token, cb) { cb(err); return; } - collection.find({}) + collection + .find({}) .skip(token) .limit(limit) .toArray((err, results) => { @@ -78,7 +82,7 @@ function list (limit, token, cb) { }); } -function listBy (userId, limit, token, cb) { +function listBy(userId, limit, token, cb) { token = token ? parseInt(token, 10) : 0; if (isNaN(token)) { cb(new Error('invalid token')); @@ -89,7 +93,8 @@ function listBy (userId, limit, token, cb) { cb(err); return; } - collection.find({ createdById: userId }) + collection + .find({createdById: userId}) .skip(token) .limit(limit) .toArray((err, results) => { @@ -104,7 +109,7 @@ function listBy (userId, limit, token, cb) { }); } -function create (data, queueBook, cb) { +function create(data, queueBook, cb) { getCollection((err, collection) => { if (err) { cb(err); @@ -124,42 +129,45 @@ function create (data, queueBook, cb) { }); } -function read (id, cb) { +function read(id, cb) { getCollection((err, collection) => { if (err) { cb(err); return; } - collection.findOne({ - _id: new ObjectID(id) - }, (err, result) => { - if (!err && !result) { - err = { - code: 404, - message: 'Not found' - }; - return; - } - if (err) { - cb(err); - return; + collection.findOne( + { + _id: new ObjectID(id), + }, + (err, result) => { + if (!err && !result) { + err = { + code: 404, + message: 'Not found', + }; + return; + } + if (err) { + cb(err); + return; + } + cb(null, fromMongo(result)); } - cb(null, fromMongo(result)); - }); + ); }); } -function update (id, data, queueBook, cb) { +function update(id, data, queueBook, cb) { getCollection((err, collection) => { if (err) { cb(err); return; } collection.update( - { _id: new ObjectID(id) }, - { '$set': toMongo(data) }, - { w: 1 }, - (err) => { + {_id: new ObjectID(id)}, + {$set: toMongo(data)}, + {w: 1}, + err => { if (err) { cb(err); return; @@ -173,15 +181,18 @@ function update (id, data, queueBook, cb) { }); } -function _delete (id, cb) { +function _delete(id, cb) { getCollection((err, collection) => { if (err) { cb(err); return; } - collection.remove({ - _id: new ObjectID(id) - }, cb); + collection.remove( + { + _id: new ObjectID(id), + }, + cb + ); }); } @@ -191,5 +202,5 @@ module.exports = { update: update, delete: _delete, list: list, - listBy: listBy + listBy: listBy, }; diff --git a/optional-kubernetes-engine/config.js b/optional-kubernetes-engine/config.js index f80de6106..82943d0d2 100644 --- a/optional-kubernetes-engine/config.js +++ b/optional-kubernetes-engine/config.js @@ -15,7 +15,7 @@ // Hierarchical node.js configuration with command-line arguments, environment // variables, and files. -const nconf = module.exports = require('nconf'); +const nconf = (module.exports = require('nconf')); const path = require('path'); nconf @@ -40,10 +40,10 @@ nconf 'SECRET', 'SUBSCRIPTION_NAME', 'INSTANCE_CONNECTION_NAME', - 'TOPIC_NAME' + 'TOPIC_NAME', ]) // 3. Config file - .file({ file: path.join(__dirname, 'config.json') }) + .file({file: path.join(__dirname, 'config.json')}) // 4. Defaults .defaults({ // Typically you will create a bucket with the same name as your project ID. @@ -79,7 +79,7 @@ nconf SECRET: 'keyboardcat', SUBSCRIPTION_NAME: 'shared-worker-subscription', - TOPIC_NAME: 'book-process-queue' + TOPIC_NAME: 'book-process-queue', }); // Check for required settings @@ -97,8 +97,10 @@ if (nconf.get('DATA_BACKEND') === 'cloudsql') { checkConfig('MONGO_DB_NAME'); } -function checkConfig (setting) { +function checkConfig(setting) { if (!nconf.get(setting)) { - throw new Error(`You must set ${setting} as an environment variable or in config.json!`); + throw new Error( + `You must set ${setting} as an environment variable or in config.json!` + ); } } diff --git a/optional-kubernetes-engine/lib/background.js b/optional-kubernetes-engine/lib/background.js index f734ccd61..6eec2a361 100644 --- a/optional-kubernetes-engine/lib/background.js +++ b/optional-kubernetes-engine/lib/background.js @@ -21,7 +21,7 @@ const topicName = config.get('TOPIC_NAME'); const subscriptionName = config.get('SUBSCRIPTION_NAME'); const pubsub = new Pubsub({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); // This configuration will automatically create the topic if @@ -29,7 +29,7 @@ const pubsub = new Pubsub({ // that a least one subscription exists on the topic before // publishing anything to it as topics without subscribers // will essentially drop any messages. -function getTopic (cb) { +function getTopic(cb) { pubsub.createTopic(topicName, (err, topic) => { // topic already exists. if (err && err.code === 6) { @@ -44,16 +44,16 @@ function getTopic (cb) { // When more than one worker is running they will all share the same // subscription, which means that pub/sub will evenly distribute messages // to each worker. -function subscribe (cb) { +function subscribe(cb) { let subscription; // Event handlers - function handleMessage (message) { + function handleMessage(message) { const data = JSON.parse(message.data); message.ack(); cb(null, data); } - function handleError (err) { + function handleError(err) { logging.error(err); } @@ -75,7 +75,9 @@ function subscribe (cb) { subscription.on('message', handleMessage); subscription.on('error', handleError); - logging.info(`Listening to ${topicName} with subscription ${subscriptionName}`); + logging.info( + `Listening to ${topicName} with subscription ${subscriptionName}` + ); }); }); @@ -91,7 +93,7 @@ function subscribe (cb) { } // Adds a book to the queue to be processed by the worker. -function queueBook (bookId) { +function queueBook(bookId) { getTopic((err, topic) => { if (err) { logging.error('Error occurred while getting pubsub topic', err); @@ -100,11 +102,11 @@ function queueBook (bookId) { const data = { action: 'processBook', - bookId: bookId + bookId: bookId, }; const publisher = topic.publisher(); - publisher.publish(Buffer.from(JSON.stringify(data)), (err) => { + publisher.publish(Buffer.from(JSON.stringify(data)), err => { if (err) { logging.error('Error occurred while queuing background task', err); } else { @@ -116,5 +118,5 @@ function queueBook (bookId) { module.exports = { subscribe, - queueBook + queueBook, }; diff --git a/optional-kubernetes-engine/lib/images.js b/optional-kubernetes-engine/lib/images.js index 63f2045ac..fc2973538 100644 --- a/optional-kubernetes-engine/lib/images.js +++ b/optional-kubernetes-engine/lib/images.js @@ -21,18 +21,18 @@ const logging = require('./logging'); const CLOUD_BUCKET = config.get('CLOUD_BUCKET'); const storage = Storage({ - projectId: config.get('GCLOUD_PROJECT') + projectId: config.get('GCLOUD_PROJECT'), }); const bucket = storage.bucket(CLOUD_BUCKET); // Downloads a given image (by URL) and then uploads it to // Google Cloud Storage. Provides the publicly accessable URL to the callback. -function downloadAndUploadImage (sourceUrl, destFileName, cb) { +function downloadAndUploadImage(sourceUrl, destFileName, cb) { const file = bucket.file(destFileName); request .get(sourceUrl) - .on('error', (err) => { + .on('error', err => { logging.warn(`Could not fetch image ${sourceUrl}`, err); cb(err); }) @@ -43,7 +43,7 @@ function downloadAndUploadImage (sourceUrl, destFileName, cb) { cb(null, getPublicUrl(destFileName)); }); }) - .on('error', (err) => { + .on('error', err => { logging.error('Could not upload image', err); cb(err); }); @@ -52,7 +52,7 @@ function downloadAndUploadImage (sourceUrl, destFileName, cb) { // Returns the public, anonymously accessable URL to a given Cloud Storage // object. // The object's ACL has to be set to public read. -function getPublicUrl (filename) { +function getPublicUrl(filename) { return `https://storage.googleapis.com/${CLOUD_BUCKET}/${filename}`; } @@ -60,7 +60,7 @@ function getPublicUrl (filename) { // req.file is processed and will have two new properties: // * ``cloudStorageObject`` the object name in cloud storage. // * ``cloudStoragePublicUrl`` the public url to the object. -function sendUploadToGCS (req, res, next) { +function sendUploadToGCS(req, res, next) { if (!req.file) { return next(); } @@ -69,11 +69,11 @@ function sendUploadToGCS (req, res, next) { const file = bucket.file(gcsname); const stream = file.createWriteStream({ metadata: { - contentType: req.file.mimetype - } + contentType: req.file.mimetype, + }, }); - stream.on('error', (err) => { + stream.on('error', err => { req.file.cloudStorageError = err; next(err); }); @@ -96,13 +96,13 @@ const Multer = require('multer'); const multer = Multer({ storage: Multer.MemoryStorage, limits: { - fileSize: 5 * 1024 * 1024 // no larger than 5mb - } + fileSize: 5 * 1024 * 1024, // no larger than 5mb + }, }); module.exports = { downloadAndUploadImage, getPublicUrl, sendUploadToGCS, - multer + multer, }; diff --git a/optional-kubernetes-engine/lib/logging.js b/optional-kubernetes-engine/lib/logging.js index 13a48b589..8fd939f1c 100644 --- a/optional-kubernetes-engine/lib/logging.js +++ b/optional-kubernetes-engine/lib/logging.js @@ -15,7 +15,8 @@ const winston = require('winston'); const expressWinston = require('express-winston'); -const StackdriverTransport = require('@google-cloud/logging-winston').LoggingWinston; +const StackdriverTransport = require('@google-cloud/logging-winston') + .LoggingWinston; const colorize = process.env.NODE_ENV !== 'production'; @@ -25,11 +26,11 @@ const requestLogger = expressWinston.logger({ new StackdriverTransport(), new winston.transports.Console({ json: false, - colorize: colorize - }) + colorize: colorize, + }), ], expressFormat: true, - meta: false + meta: false, }); // Logger to capture any top-level errors and output json diagnostic info. @@ -38,9 +39,9 @@ const errorLogger = expressWinston.errorLogger({ new StackdriverTransport(), new winston.transports.Console({ json: true, - colorize: colorize - }) - ] + colorize: colorize, + }), + ], }); module.exports = { @@ -52,5 +53,5 @@ module.exports = { log: winston.log, verbose: winston.verbose, debug: winston.debug, - silly: winston.silly + silly: winston.silly, }; diff --git a/optional-kubernetes-engine/lib/oauth2.js b/optional-kubernetes-engine/lib/oauth2.js index c6593420d..37c1d4fd9 100644 --- a/optional-kubernetes-engine/lib/oauth2.js +++ b/optional-kubernetes-engine/lib/oauth2.js @@ -20,7 +20,7 @@ const config = require('../config'); const passport = require('passport'); const GoogleStrategy = require('passport-google-oauth20').Strategy; -function extractProfile (profile) { +function extractProfile(profile) { let imageUrl = ''; if (profile.photos && profile.photos.length) { imageUrl = profile.photos[0].value; @@ -28,7 +28,7 @@ function extractProfile (profile) { return { id: profile.id, displayName: profile.displayName, - image: imageUrl + image: imageUrl, }; } @@ -39,16 +39,21 @@ function extractProfile (profile) { // along with the user's profile. The function must invoke `cb` with a user // object, which will be set at `req.user` in route handlers after // authentication. -passport.use(new GoogleStrategy({ - clientID: config.get('OAUTH2_CLIENT_ID'), - clientSecret: config.get('OAUTH2_CLIENT_SECRET'), - callbackURL: config.get('OAUTH2_CALLBACK'), - accessType: 'offline' -}, (accessToken, refreshToken, profile, cb) => { - // Extract the minimal profile information we need from the profile object - // provided by Google - cb(null, extractProfile(profile)); -})); +passport.use( + new GoogleStrategy( + { + clientID: config.get('OAUTH2_CLIENT_ID'), + clientSecret: config.get('OAUTH2_CLIENT_SECRET'), + callbackURL: config.get('OAUTH2_CALLBACK'), + accessType: 'offline', + }, + (accessToken, refreshToken, profile, cb) => { + // Extract the minimal profile information we need from the profile object + // provided by Google + cb(null, extractProfile(profile)); + } + ) +); passport.serializeUser((user, cb) => { cb(null, user); @@ -64,7 +69,7 @@ const router = express.Router(); // Middleware that requires the user to be logged in. If the user is not logged // in, it will redirect the user to authorize the application and then return // them to the original URL they requested. -function authRequired (req, res, next) { +function authRequired(req, res, next) { if (!req.user) { req.session.oauth2return = req.originalUrl; return res.redirect('/auth/login'); @@ -74,10 +79,14 @@ function authRequired (req, res, next) { // Middleware that exposes the user's profile as well as login/logout URLs to // any templates. These are available as `profile`, `login`, and `logout`. -function addTemplateVariables (req, res, next) { +function addTemplateVariables(req, res, next) { res.locals.profile = req.user; - res.locals.login = `/auth/login?return=${encodeURIComponent(req.originalUrl)}`; - res.locals.logout = `/auth/logout?return=${encodeURIComponent(req.originalUrl)}`; + res.locals.login = `/auth/login?return=${encodeURIComponent( + req.originalUrl + )}`; + res.locals.logout = `/auth/logout?return=${encodeURIComponent( + req.originalUrl + )}`; next(); } // [END middleware] @@ -102,7 +111,7 @@ router.get( }, // Start OAuth 2 flow using Passport.js - passport.authenticate('google', { scope: ['email', 'profile'] }) + passport.authenticate('google', {scope: ['email', 'profile']}) ); // [END authorize] @@ -135,5 +144,5 @@ module.exports = { extractProfile: extractProfile, router: router, required: authRequired, - template: addTemplateVariables + template: addTemplateVariables, }; diff --git a/optional-kubernetes-engine/package.json b/optional-kubernetes-engine/package.json index c4e853edf..8f8b47b02 100644 --- a/optional-kubernetes-engine/package.json +++ b/optional-kubernetes-engine/package.json @@ -22,34 +22,6 @@ } } }, - "semistandard": { - "globals": [ - "after", - "afterEach", - "before", - "beforeEach", - "describe", - "it" - ] - }, - "contributors": [ - "Ace Nassri ", - "Ahmet Alp Balkan ", - "Allen Day ", - "André Cipriani Bandarra ", - "Dominik Staskiewicz ", - "F. Hinkelmann ", - "Jason Dobry ", - "Jon Wayne Parrott ", - "Justin Beckwith ", - "Michael McDonald ", - "Sean McBreen ", - "Steren ", - "Steve Perry ", - "chenyumic ", - "renovate[bot] ", - "shollyman " - ], "scripts": { "start": "node ${SCRIPT:-app.js}", "test": "repo-tools test app && ava -t 30s --tap test/*.test.js", diff --git a/optional-kubernetes-engine/test/_api-tests.js b/optional-kubernetes-engine/test/_api-tests.js index 8cd72283f..667ea1a92 100644 --- a/optional-kubernetes-engine/test/_api-tests.js +++ b/optional-kubernetes-engine/test/_api-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -26,12 +26,12 @@ module.exports = (DATA_BACKEND) => { appConfig.set(`DATA_BACKEND`, DATA_BACKEND); }); - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `beep` }) + .send({title: `beep`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `beep`); @@ -39,13 +39,13 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should list books`, (t) => { + test.serial.cb(`should list books`, t => { // Give Datastore time to become consistent setTimeout(() => { getRequest(testConfig) .get(`/api/books`) .expect(200) - .expect((response) => { + .expect(response => { t.true(Array.isArray(response.body.items)); t.true(response.body.items.length >= 1); }) @@ -53,11 +53,11 @@ module.exports = (DATA_BACKEND) => { }, 1000); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .delete(`/api/books/${id}/`) // .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `OK`); }) .end(t.end); diff --git a/optional-kubernetes-engine/test/_crud-tests.js b/optional-kubernetes-engine/test/_crud-tests.js index 7d1b151d9..3ec443ebf 100644 --- a/optional-kubernetes-engine/test/_crud-tests.js +++ b/optional-kubernetes-engine/test/_crud-tests.js @@ -16,7 +16,7 @@ const getRequest = require(`@google-cloud/nodejs-repo-tools`).getRequest; const test = require(`ava`); -module.exports = (DATA_BACKEND) => { +module.exports = DATA_BACKEND => { let originalDataBackend, id, testConfig, appConfig; test.before(() => { @@ -27,12 +27,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should create a book`, (t) => { + test.serial.cb(`should create a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -40,30 +40,30 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show a list of books`, (t) => { + test.serial.cb(`should show a list of books`, t => { // Give Datastore time to become consistent setTimeout(() => { const expected = /
/; getRequest(testConfig) .get(`/books`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }, 2000); }); - test.serial.cb(`should handle error`, (t) => { + test.serial.cb(`should handle error`, t => { getRequest(testConfig) .get(`/books`) - .query({ pageToken: `badrequest` }) + .query({pageToken: `badrequest`}) .expect(500) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -74,13 +74,13 @@ module.exports = (DATA_BACKEND) => { } }); - test.serial.cb(`should post to add book form`, (t) => { + test.serial.cb(`should post to add book form`, t => { const expected = /Redirecting to \/books\//; getRequest(testConfig) .post(`/books/add`) .field(`title`, `my book`) .expect(302) - .expect((response) => { + .expect(response => { const location = response.headers.location; const idPart = location.replace(`/books/`, ``); if (DATA_BACKEND !== `mongodb`) { @@ -93,19 +93,19 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should show add book form`, (t) => { + test.serial.cb(`should show add book form`, t => { const expected = /Add book/; getRequest(testConfig) .get(`/books/add`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); // delete the book - test.serial.cb((t) => { + test.serial.cb(t => { if (id) { getRequest(testConfig) .delete(`/api/books/${id}`) @@ -117,12 +117,12 @@ module.exports = (DATA_BACKEND) => { }); // setup a book - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { getRequest(testConfig) .post(`/api/books`) - .send({ title: `my book` }) + .send({title: `my book`}) .expect(200) - .expect((response) => { + .expect(response => { id = response.body.id; t.truthy(response.body.id); t.is(response.body.title, `my book`); @@ -130,47 +130,46 @@ module.exports = (DATA_BACKEND) => { .end(t.end); }); - test.serial.cb(`should update a book`, (t) => { + test.serial.cb(`should update a book`, t => { const expected = new RegExp(`Redirecting to /books/${id}`); getRequest(testConfig) .post(`/books/${id}/edit`) .field(`title`, `my other book`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show edit book form`, (t) => { - const expected = - //; + test.serial.cb(`should show edit book form`, t => { + const expected = //; getRequest(testConfig) .get(`/books/${id}/edit`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should show a book`, (t) => { + test.serial.cb(`should show a book`, t => { const expected = /

my other book <\/small><\/h4>/; getRequest(testConfig) .get(`/books/${id}`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, expected); }) .end(t.end); }); - test.serial.cb(`should delete a book`, (t) => { + test.serial.cb(`should delete a book`, t => { const expected = /Redirecting to \/books/; getRequest(testConfig) .get(`/books/${id}/delete`) .expect(302) - .expect((response) => { + .expect(response => { id = undefined; t.regex(response.text, expected); }) @@ -178,7 +177,7 @@ module.exports = (DATA_BACKEND) => { }); // clean up - test.always.after.cb((t) => { + test.always.after.cb(t => { appConfig.set(`DATA_BACKEND`, originalDataBackend); if (id) { diff --git a/optional-kubernetes-engine/test/_test-config.js b/optional-kubernetes-engine/test/_test-config.js index 4b0a4b073..32c70d0fe 100644 --- a/optional-kubernetes-engine/test/_test-config.js +++ b/optional-kubernetes-engine/test/_test-config.js @@ -30,6 +30,6 @@ module.exports = { env: { PORT: PORT, SUBSCRIPTION_NAME: `shared-worker-subscription-${TESTNAME}`, - TOPIC_NAME: `book-process-queue-${TESTNAME}` - } + TOPIC_NAME: `book-process-queue-${TESTNAME}`, + }, }; diff --git a/optional-kubernetes-engine/test/_test-config.worker.js b/optional-kubernetes-engine/test/_test-config.worker.js index f143d77ee..95e493e45 100644 --- a/optional-kubernetes-engine/test/_test-config.worker.js +++ b/optional-kubernetes-engine/test/_test-config.worker.js @@ -27,6 +27,6 @@ module.exports = { port: port, env: { SUBSCRIPTION_NAME: `shared-worker-subscription-${test}`, - TOPIC_NAME: `book-process-queue-${test}` - } + TOPIC_NAME: `book-process-queue-${test}`, + }, }; diff --git a/optional-kubernetes-engine/test/app.test.js b/optional-kubernetes-engine/test/app.test.js index 065c9a660..ef9dc9d40 100644 --- a/optional-kubernetes-engine/test/app.test.js +++ b/optional-kubernetes-engine/test/app.test.js @@ -19,33 +19,34 @@ const sinon = require(`sinon`); const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); -test.cb(`should redirect / to /books`, (t) => { - utils.getRequest(testConfig) +test.cb(`should redirect / to /books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(302) - .expect((response) => { + .expect(response => { t.regex(response.text, /Redirecting to \/books/); }) .end(t.end); }); -test(`should check config`, (t) => { +test(`should check config`, t => { const nconfMock = { argv: sinon.stub().returnsThis(), env: sinon.stub().returnsThis(), file: sinon.stub().returnsThis(), defaults: sinon.stub().returnsThis(), - get: function (setting) { + get: function(setting) { return this[setting]; - } + }, }; - function getMsg (setting) { + function getMsg(setting) { return `You must set ${setting} as an environment variable or in config.json!`; } const testFunc = () => { - proxyquire(`../config`, { nconf: nconfMock }); + proxyquire(`../config`, {nconf: nconfMock}); }; nconfMock.DATA_BACKEND = `datastore`; diff --git a/optional-kubernetes-engine/test/background.test.js b/optional-kubernetes-engine/test/background.test.js index a90948814..f835d3090 100644 --- a/optional-kubernetes-engine/test/background.test.js +++ b/optional-kubernetes-engine/test/background.test.js @@ -20,51 +20,48 @@ const test = require(`ava`); let background; const mocks = {}; -test.beforeEach((t) => { +test.beforeEach(t => { // Mock dependencies used by background.js mocks.config = { GCLOUD_PROJECT: process.env.GCLOUD_PROJECT, SUBSCRIPTION_NAME: `shared-worker-subscription`, - TOPIC_NAME: `book-process-queue` + TOPIC_NAME: `book-process-queue`, }; - mocks.config.get = function (key) { + mocks.config.get = function(key) { return this[key]; }; mocks.subscription = { - on: sinon.stub() + on: sinon.stub(), }; mocks.publisher = { - publish: sinon.stub().callsArgWith(1, null) + publish: sinon.stub().callsArgWith(1, null), }; mocks.topic = { createSubscription: sinon.stub().callsArgWith(1, null, mocks.subscription), - publisher: sinon.stub().returns(mocks.publisher) + publisher: sinon.stub().returns(mocks.publisher), }; mocks.pubsub = { createTopic: sinon.stub().callsArgWith(1, null, mocks.topic), - topic: sinon.stub().returns(mocks.topic) + topic: sinon.stub().returns(mocks.topic), }; mocks.Pubsub = sinon.stub().returns(mocks.pubsub); mocks.logging = { info: sinon.stub(), - error: sinon.stub() + error: sinon.stub(), }; // Load background.js with provided mocks background = proxyquire(`../lib/background`, { '@google-cloud/pubsub': mocks.Pubsub, '../config': mocks.config, - './logging': mocks.logging + './logging': mocks.logging, }); - t.true( - mocks.Pubsub.calledOnce, - `Pubsub() should have been called once` - ); + t.true(mocks.Pubsub.calledOnce, `Pubsub() should have been called once`); }); -test.serial.cb(`should subscribe and log message`, (t) => { +test.serial.cb(`should subscribe and log message`, t => { // Setup - const testMessage = { test: `foo` }; + const testMessage = {test: `foo`}; // Run target functionality background.subscribe((err, message) => { @@ -123,18 +120,18 @@ test.serial.cb(`should subscribe and log message`, (t) => { setTimeout(() => { mocks.subscription.on.firstCall.args[1]({ data: JSON.stringify(testMessage), - ack: sinon.stub() + ack: sinon.stub(), }); }, 10); }); -test.serial.cb(`should return topic error, if any`, (t) => { +test.serial.cb(`should return topic error, if any`, t => { // Setup const testErrorMsg = `test error`; mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg); // Run target functionality - background.subscribe((data) => { + background.subscribe(data => { // Assertions t.true( mocks.pubsub.createTopic.calledOnce, @@ -165,13 +162,13 @@ test.serial.cb(`should return topic error, if any`, (t) => { }); }); -test.serial.cb(`should return subscription error, if any`, (t) => { +test.serial.cb(`should return subscription error, if any`, t => { // Setup const testErrorMsg = `test error`; mocks.topic.createSubscription = sinon.stub().callsArgWith(1, testErrorMsg); // Run target functionality - background.subscribe((data) => { + background.subscribe(data => { // Assertions t.true( mocks.pubsub.createTopic.calledOnce, @@ -211,7 +208,7 @@ test.serial.cb(`should return subscription error, if any`, (t) => { }); }); -test.serial(`should queue a book and log message`, (t) => { +test.serial(`should queue a book and log message`, t => { // Setup const testBookId = 1; @@ -243,10 +240,12 @@ test.serial(`should queue a book and log message`, (t) => { ); t.deepEqual( mocks.publisher.publish.firstCall.args[0], - Buffer.from(JSON.stringify({ - action: `processBook`, - bookId: testBookId - })), + Buffer.from( + JSON.stringify({ + action: `processBook`, + bookId: testBookId, + }) + ), `publisher.publish() should have been called with the right arguments` ); t.true( @@ -260,11 +259,11 @@ test.serial(`should queue a book and log message`, (t) => { ); }); -test.serial(`should queue a book and log message even if topic exists`, (t) => { +test.serial(`should queue a book and log message even if topic exists`, t => { // Setup const testBookId = 1; mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, { - code: 6 + code: 6, }); // Run target functionality @@ -299,10 +298,12 @@ test.serial(`should queue a book and log message even if topic exists`, (t) => { ); t.deepEqual( mocks.publisher.publish.firstCall.args[0], - Buffer.from(JSON.stringify({ - action: `processBook`, - bookId: testBookId - })), + Buffer.from( + JSON.stringify({ + action: `processBook`, + bookId: testBookId, + }) + ), `publisher.publish() should have been called with the right arguments` ); t.true( @@ -316,7 +317,7 @@ test.serial(`should queue a book and log message even if topic exists`, (t) => { ); }); -test.serial(`should log error if cannot get topic`, (t) => { +test.serial(`should log error if cannot get topic`, t => { // Setup const testBookId = 1; const testErrorMsg = `test error`; @@ -361,7 +362,7 @@ test.serial(`should log error if cannot get topic`, (t) => { ); }); -test.serial(`should log error if cannot publish message`, (t) => { +test.serial(`should log error if cannot publish message`, t => { // Setup const testBookId = 1; const testErrorMsg = `test error`; diff --git a/optional-kubernetes-engine/test/cloudsql.test.js b/optional-kubernetes-engine/test/cloudsql.test.js index 4e6ca3f5b..76fe26ea0 100644 --- a/optional-kubernetes-engine/test/cloudsql.test.js +++ b/optional-kubernetes-engine/test/cloudsql.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || + process.env.TEST_CLOUDSQL +) { require(`./_api-tests`)(`cloudsql`); require(`./_crud-tests`)(`cloudsql`); } else { - test(`Skipping Cloud SQL tests...`, (t) => t.pass()); + test(`Skipping Cloud SQL tests...`, t => t.pass()); } diff --git a/optional-kubernetes-engine/test/datastore.test.js b/optional-kubernetes-engine/test/datastore.test.js index 331966401..b99d115e9 100644 --- a/optional-kubernetes-engine/test/datastore.test.js +++ b/optional-kubernetes-engine/test/datastore.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `datastore` || process.env.TEST_DATASTORE) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `datastore` || + process.env.TEST_DATASTORE +) { require(`./_api-tests`)(`datastore`); require(`./_crud-tests`)(`datastore`); } else { - test(`Skipping Cloud Datastore tests...`, (t) => t.pass()); + test(`Skipping Cloud Datastore tests...`, t => t.pass()); } diff --git a/optional-kubernetes-engine/test/mongodb.test.js b/optional-kubernetes-engine/test/mongodb.test.js index 626cb79de..11d342d3f 100644 --- a/optional-kubernetes-engine/test/mongodb.test.js +++ b/optional-kubernetes-engine/test/mongodb.test.js @@ -15,9 +15,12 @@ const test = require(`ava`); -if (require(`../config`).get(`DATA_BACKEND`) === `mongodb` || process.env.TEST_MONGODB) { +if ( + require(`../config`).get(`DATA_BACKEND`) === `mongodb` || + process.env.TEST_MONGODB +) { require(`./_api-tests`)(`mongodb`); require(`./_crud-tests`)(`mongodb`); } else { - test(`Skipping MongoDB tests...`, (t) => t.pass()); + test(`Skipping MongoDB tests...`, t => t.pass()); } diff --git a/optional-kubernetes-engine/test/oauth2.test.js b/optional-kubernetes-engine/test/oauth2.test.js index 2e08088b4..f711332c0 100644 --- a/optional-kubernetes-engine/test/oauth2.test.js +++ b/optional-kubernetes-engine/test/oauth2.test.js @@ -32,26 +32,26 @@ const getPassportMock = () => { authenticate: sinon.stub().returns((req, res, next) => { req.session.oauth2return = `/another/path`; next(); - }) + }), }; }; -test.cb(`should start authorization`, (t) => { +test.cb(`should start authorization`, t => { const passportMock = getPassportMock(); - passportMock.authenticate = sinon.stub().returns((req, res, next) => { + passportMock.authenticate = sinon.stub().returns((req, res) => { t.is(req.session.oauth2return, `/some/path`); res.redirect(`/auth/google/callback?code=foo`); }); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/login?return=%2Fsome%2Fpath`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/auth\/google\/callback\?code=foo/); t.true(passportMock.initialize.calledOnce); @@ -61,29 +61,28 @@ test.cb(`should start authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test.cb(`should finish authorization`, (t) => { +test.cb(`should finish authorization`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const app = proxyquire(`../app`, { passport: passportMock, - './lib/oauth2': oauth2 + './lib/oauth2': oauth2, }); request(app) .get(`/auth/google/callback?code=foo`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \/another\/path/); t.true(passportMock.initialize.calledOnce); @@ -93,22 +92,21 @@ test.cb(`should finish authorization`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); t.deepEqual( oauth2.extractProfile({ - photos: [{ value: `image.jpg` }], + photos: [{value: `image.jpg`}], id: 1, - displayName: `Joe Developer` + displayName: `Joe Developer`, }), { id: 1, displayName: `Joe Developer`, - image: `image.jpg` + image: `image.jpg`, } ); const serializeUser = passportMock.serializeUser.firstCall.args[0]; @@ -127,18 +125,18 @@ test.cb(`should finish authorization`, (t) => { .end(t.end); }); -test.cb(`should logout`, (t) => { +test.cb(`should logout`, t => { const passportMock = getPassportMock(); const app = proxyquire(`../app`, { passport: passportMock, './lib/oauth2': proxyquire(`../lib/oauth2`, { - passport: passportMock - }) + passport: passportMock, + }), }); request(app) .get(`/auth/logout`) .expect(302) - .expect((response) => { + .expect(response => { const text = response.text; t.regex(text, /Redirecting to \//); t.true(passportMock.initialize.calledOnce); @@ -148,28 +146,27 @@ test.cb(`should logout`, (t) => { t.true(passportMock.deserializeUser.calledOnce); t.true(passportMock.authenticate.calledTwice); t.is(passportMock.authenticate.firstCall.args[0], `google`); - t.deepEqual( - passportMock.authenticate.firstCall.args[1], - { scope: [`email`, `profile`] } - ); + t.deepEqual(passportMock.authenticate.firstCall.args[1], { + scope: [`email`, `profile`], + }); t.is(passportMock.authenticate.secondCall.args[0], `google`); t.is(passportMock.authenticate.secondCall.args[1], undefined); }) .end(t.end); }); -test(`should require authentication`, (t) => { +test(`should require authentication`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: {}, - session: {} + session: {}, }; const res = { - redirect: sinon.stub() + redirect: sinon.stub(), }; const next = sinon.stub(); oauth2.required(req, res, next); @@ -183,30 +180,32 @@ test(`should require authentication`, (t) => { t.is(res.redirect.firstCall.args[0], `/auth/login`); }); -test(`should add template variables`, (t) => { +test(`should add template variables`, t => { const passportMock = getPassportMock(); const oauth2 = proxyquire(`../lib/oauth2`, { - passport: passportMock + passport: passportMock, }); const req = { originalUrl: `/some/path`, user: { id: 1, displayName: `Joe Developer`, - image: `image.jpg` - } + image: `image.jpg`, + }, }; const res = { - locals: {} + locals: {}, }; const next = sinon.stub(); oauth2.template(req, res, next); t.true(next.calledOnce); t.is(res.locals.profile, req.user); t.is( - res.locals.login, `/auth/login?return=${encodeURIComponent(req.originalUrl)}` + res.locals.login, + `/auth/login?return=${encodeURIComponent(req.originalUrl)}` ); t.is( - res.locals.logout, `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` + res.locals.logout, + `/auth/logout?return=${encodeURIComponent(req.originalUrl)}` ); }); diff --git a/optional-kubernetes-engine/test/worker.test.js b/optional-kubernetes-engine/test/worker.test.js index c2c20ae28..934bd3d76 100644 --- a/optional-kubernetes-engine/test/worker.test.js +++ b/optional-kubernetes-engine/test/worker.test.js @@ -21,43 +21,45 @@ const test = require(`ava`); const utils = require(`@google-cloud/nodejs-repo-tools`); if (!process.env.SKIP_WORKER_HTTP_TESTS) { - test.serial.cb(`should return number of processed books`, (t) => { - utils.getRequest(testConfig) + test.serial.cb(`should return number of processed books`, t => { + utils + .getRequest(testConfig) .get(`/`) .expect(200) - .expect((response) => { + .expect(response => { t.regex(response.text, /This worker has processed/); }) .end(t.end); }); - test.serial.cb(`should do a health check`, (t) => { - utils.getRequest(testConfig) + test.serial.cb(`should do a health check`, t => { + utils + .getRequest(testConfig) .get(`/_ah/health`) .expect(200) - .expect((response) => { + .expect(response => { t.is(response.text, `ok`); }) .end(t.end); }); } -test.serial.cb(`should process a book`, (t) => { +test.serial.cb(`should process a book`, t => { const appConfig = require(`../config`); const loggingStub = { error: sinon.stub(), info: sinon.stub(), - warn: sinon.stub() + warn: sinon.stub(), }; const stubs = { './lib/logging': loggingStub, '@google-cloud/trace-agent': { start: sinon.stub(), - '@noCallThru': true + '@noCallThru': true, }, '@google-cloud/debug-agent': { - '@noCallThru': true - } + '@noCallThru': true, + }, }; stubs[`./books/model-${appConfig.get('DATA_BACKEND')}`] = { read: (bookId, cb) => { @@ -65,12 +67,12 @@ test.serial.cb(`should process a book`, (t) => { }, update: (bookId, book, queueBook, cb) => { cb(); - } + }, }; const worker = proxyquire(path.join(__dirname, `../worker`), stubs).mocks; const processBook = worker.processBook; - processBook(1, (err, bookId) => { + processBook(1, err => { if (err) { return t.end(err); } diff --git a/optional-kubernetes-engine/worker.js b/optional-kubernetes-engine/worker.js index c043b07a5..18a9aaf4c 100644 --- a/optional-kubernetes-engine/worker.js +++ b/optional-kubernetes-engine/worker.js @@ -49,7 +49,7 @@ app.get('/', (req, res) => { app.use(logging.errorLogger); -function subscribe () { +function subscribe() { // Subscribe to Cloud Pub/Sub and receive messages to process books. // The subscription will continue to listen for messages until the process // is killed. @@ -77,38 +77,41 @@ if (module === require.main) { // Processes a book by reading its existing data, attempting to find // more information, and updating the database with the new information. -function processBook (bookId, callback) { - waterfall([ - // Load the current data - (cb) => { - model.read(bookId, cb); - }, - // Find the information from Google - findBookInfo, - // Save the updated data - (updated, cb) => { - model.update(updated.id, updated, false, cb); - } - ], (err) => { - if (err) { - logging.error(`Error occurred`, err); +function processBook(bookId, callback) { + waterfall( + [ + // Load the current data + cb => { + model.read(bookId, cb); + }, + // Find the information from Google + findBookInfo, + // Save the updated data + (updated, cb) => { + model.update(updated.id, updated, false, cb); + }, + ], + err => { + if (err) { + logging.error(`Error occurred`, err); + if (callback) { + callback(err); + } + return; + } + logging.info(`Updated book ${bookId}`); + bookCount += 1; if (callback) { - callback(err); + callback(); } - return; } - logging.info(`Updated book ${bookId}`); - bookCount += 1; - if (callback) { - callback(); - } - }); + ); } // Tries to find additional information about a book and updates // the book's data. Also uploads a cover image to Cloud Storage // if available. -function findBookInfo (book, cb) { +function findBookInfo(book, cb) { queryBooksApi(book.title, (err, r) => { if (!err && !r.items) { err = 'Not found'; @@ -136,21 +139,22 @@ function findBookInfo (book, cb) { top.volumeInfo.imageLinks.smallThumbnail; const imageName = `${book.id}.jpg`; - images.downloadAndUploadImage( - imageUrl, imageName, (err, publicUrl) => { - if (!err) { - book.imageUrl = publicUrl; - } - cb(null, book); - }); + images.downloadAndUploadImage(imageUrl, imageName, (err, publicUrl) => { + if (!err) { + book.imageUrl = publicUrl; + } + cb(null, book); + }); }); } // Calls out to the Google Books API to get additional // information about a given book. -function queryBooksApi (query, cb) { +function queryBooksApi(query, cb) { request( - `https://www.googleapis.com/books/v1/volumes?q=${encodeURIComponent(query)}`, + `https://www.googleapis.com/books/v1/volumes?q=${encodeURIComponent( + query + )}`, (err, resp, body) => { if (err || resp.statusCode !== 200) { cb(err || `Response returned ${resp.statusCode}`); @@ -164,7 +168,7 @@ function queryBooksApi (query, cb) { app.mocks = { processBook: processBook, findBookInfo: findBookInfo, - queryBooksApi: queryBooksApi + queryBooksApi: queryBooksApi, }; // Proxyquire requires this *exact* line, hence the "app.mocks" above diff --git a/package.json b/package.json index 642908756..7e5a2141e 100644 --- a/package.json +++ b/package.json @@ -10,36 +10,8 @@ "repository": "https://github.com/GoogleCloudPlatform/nodejs-getting-started", "main": "src/index.js", "private": true, - "semistandard": { - "globals": [ - "after", - "afterEach", - "before", - "beforeEach", - "describe", - "it" - ] - }, - "contributors": [ - "Ace Nassri ", - "Ahmet Alp Balkan ", - "Allen Day ", - "André Cipriani Bandarra ", - "Dominik Staskiewicz ", - "F. Hinkelmann ", - "Jason Dobry ", - "Jon Wayne Parrott ", - "Justin Beckwith ", - "Michael McDonald ", - "Sean McBreen ", - "Steren ", - "Steve Perry ", - "chenyumic ", - "renovate[bot] ", - "shollyman " - ], "scripts": { - "lint": "semistandard \"**/*.js\"", + "lint": "eslint '**/*.js'", "ava": "ava -s -t 30s --tap \"**/!(datastore).test.js\"", "test": "npm run lint && npm run ava", "cover": "nyc --cache npm test; nyc report --reporter=html", @@ -75,8 +47,12 @@ "devDependencies": { "@google-cloud/nodejs-repo-tools": "^3.0.0", "ava": "^0.25.0", + "eslint": "^5.9.0", + "eslint-config-prettier": "^3.1.0", + "eslint-plugin-node": "^8.0.0", + "eslint-plugin-prettier": "^3.0.0", + "prettier": "^1.15.1", "proxyquire": "^2.0.0", - "semistandard": "^12.0.1", "sinon": "^7.0.0", "supertest": "^3.0.0" } diff --git a/test/e2e.js b/test/e2e.js index 89e589342..feccc664e 100644 --- a/test/e2e.js +++ b/test/e2e.js @@ -28,14 +28,14 @@ const steps = [ // Worker steps require(`../6-pubsub/test/config.worker`), - require(`../7-gce/test/config.worker`) + require(`../7-gce/test/config.worker`), ]; -function tryToFinish (numTests, steps, done) { +function tryToFinish(numTests, steps, done) { let doneCount = 0; let errCount = 0; let err = ``; - steps.forEach((config) => { + steps.forEach(config => { if (config.done) { doneCount++; } @@ -52,7 +52,7 @@ function tryToFinish (numTests, steps, done) { if (doneCount === numTests) { console.log(`All tests complete!`); } else { - console.log(`${(numTests - doneCount)} deployments remaining...`); + console.log(`${numTests - doneCount} deployments remaining...`); } if (errCount) { @@ -62,24 +62,33 @@ function tryToFinish (numTests, steps, done) { } } -before((done) => { +before(done => { // Delete existing versions - async.each(steps, (config, cb) => { - console.log(`Deletion queued for version ${config.test}...`); - utils.deleteVersion(config.test, config.cwd, () => { - console.log(`Deleted version ${config.test}!`); - cb(); - }); - }, done); + async.each( + steps, + (config, cb) => { + console.log(`Deletion queued for version ${config.test}...`); + utils.deleteVersion(config.test, config.cwd, () => { + console.log(`Deleted version ${config.test}!`); + cb(); + }); + }, + done + ); }); -it(`should deploy all steps`, (done) => { +it(`should deploy all steps`, done => { let numTests = steps.length; - async.eachLimit(steps, 5, (config, cb) => { - utils.testDeploy(config, (err) => { - config.err = err; - config.done = true; - tryToFinish(numTests, steps, cb); - }); - }, done); + async.eachLimit( + steps, + 5, + (config, cb) => { + utils.testDeploy(config, err => { + config.err = err; + config.done = true; + tryToFinish(numTests, steps, cb); + }); + }, + done + ); });