diff --git a/appengine/cloudsql/README.md b/appengine/cloudsql/README.md index 67f15cfeaf..d5e5ad6622 100644 --- a/appengine/cloudsql/README.md +++ b/appengine/cloudsql/README.md @@ -3,8 +3,11 @@ This sample demonstrates how to use [Google Cloud SQL][sql] (or any other SQL server) on [Google App Engine Flexible][flexible]. +This sample has instructions for both [MySQL][mysql] and [Postgres][postgres]. + ## Setup +### General steps Before you can run or deploy the sample, you will need to do the following: 1. In order for some of the commands below to work, you need to enable the @@ -26,6 +29,10 @@ SDK use the following command: where `[YOUR_INSTANCE_NAME]` is the name you chose in step 1 and `[YOUR_INSTANCE_ROOT_PASSWORD]` is a password of your choice. +1. Using the [Cloud SQL console][sql_console], select your Cloud SQL instance. +Then, create a [user][user] (using the button in the *Access Control* > *Users* tab) and a +[database][database] (using the button in the *Databases* tab). + 1. Create and download a [Service Account][service] for your project. You will use this service account to connect to your Cloud SQL instance locally. @@ -35,41 +42,47 @@ use this service account to connect to your Cloud SQL instance locally. machine: ./cloud_sql_proxy \ - -instances=[YOUR_INSTANCE_CONNECTION_NAME]=tcp:3306 \ + -instances=[YOUR_INSTANCE_CONNECTION_NAME]=tcp:[PORT] \ -credential_file=PATH_TO_YOUR_SERVICE_ACCOUNT_JSON_FILE where `[YOUR_INSTANCE_CONNECTION_NAME]` is the connection name of your instance on its Overview page in the Google Cloud Platform Console, or use - `[YOUR_PROJECT_ID]:[YOUR_REGION]:[YOUR_INSTANCE_NAME]`. + `[YOUR_PROJECT_ID]:[YOUR_REGION]:[YOUR_INSTANCE_NAME]`. If you're using + MySQL, `[PORT]` will be `3306`; for Postgres, it will be `5432`. -1. Use the MySQL command line tools (or a management tool of your choice) to -create a [new user][user] and [database][database] for your application: +1. In a separate terminal, set the `SQL_USER`, `SQL_PASSWORD`, and `SQL_DATABASE` environment +variables to their respective values. This allows your local app to connect to your Cloud SQL +instance through the proxy. - mysql -h 127.0.0.1 -P 3306 -u root -p - mysql> create database `YOUR_DATABASE`; - mysql> create user 'YOUR_USER'@'%' identified by 'PASSWORD'; - mysql> grant all on YOUR_DATABASE.* to 'YOUR_USER'@'%'; + export SQL_USER="..." + export SQL_PASSWORD="..." + export SQL_DATABASE="..." -1. Set the `MYSQL_USER`, `MYSQL_PASSWORD`, and `MYSQL_DATABASE` environment -variables (see below). This allows your local app to connect to your Cloud SQL -instance through the proxy. +### Choosing a SQL client +Choose which database connector to use via the `SQL_CLIENT` environment variable. + +To use MySQL, set it to `mysql`: + + export SQL_CLIENT="mysql" + +To use Postgres, set it to `pg`: + + export SQL_CLIENT="pg" -1. Update the values in in `app.yaml` with your instance configuration. +### Final setup steps +1. Update the values in `app.yaml` with your instance configuration. 1. Finally, run `createTables.js` to ensure that the database is properly configured and to create the tables needed for the sample. -## Running locally +### Running locally Refer to the [top-level README](../README.md) for instructions on running and deploying. It's recommended to follow the instructions above to run the Cloud SQL proxy. -You will need to set the following environment variables via your shell before -running the sample: +You will need to set the appropriate environment variables (as shown above) and +run the following commands via your shell to run the sample: - export MYSQL_USER="YOUR_USER" - export MYSQL_PASSWORD="YOUR_PASSWORD" - export MYSQL_DATABASE="YOUR_DATABASE" npm install npm start @@ -77,9 +90,12 @@ running the sample: [flexible]: https://cloud.google.com/appengine [gen]: https://cloud.google.com/sql/docs/create-instance [console]: https://console.developers.google.com +[sql_console]: https://console.developers.google.com/sql/instances/ [sdk]: https://cloud.google.com/sdk [service]: https://cloud.google.com/sql/docs/external#createServiceAccount [proxy]: https://cloud.google.com/sql/docs/external#install [start]: https://cloud.google.com/sql/docs/external#6_start_the_proxy [user]: https://cloud.google.com/sql/docs/create-user [database]: https://cloud.google.com/sql/docs/create-database +[mysql]: https://www.mysql.com/downloads/ +[postgres]: https://www.postgresql.org/download/ diff --git a/appengine/cloudsql/app.yaml b/appengine/cloudsql/app.yaml index 9c8f1d67c0..ac188039bb 100644 --- a/appengine/cloudsql/app.yaml +++ b/appengine/cloudsql/app.yaml @@ -17,11 +17,12 @@ env: flex # [START env] env_variables: - MYSQL_USER: YOUR_USER - MYSQL_PASSWORD: YOUR_PASSWORD - MYSQL_DATABASE: YOUR_DATABASE + SQL_USER: YOUR_USER + SQL_PASSWORD: YOUR_PASSWORD + SQL_DATABASE: YOUR_DATABASE # e.g. my-awesome-project:us-central1:my-cloud-sql-instance INSTANCE_CONNECTION_NAME: YOUR_INSTANCE_CONNECTION_NAME + SQL_CLIENT: YOUR_SQL_CLIENT # either 'pg' or 'mysql' (all lowercase) # [END env] # [START cloudsql_settings] diff --git a/appengine/cloudsql/createTables.js b/appengine/cloudsql/createTables.js index e9eba11ed3..819452df8e 100644 --- a/appengine/cloudsql/createTables.js +++ b/appengine/cloudsql/createTables.js @@ -13,71 +13,86 @@ * limitations under the License. */ +'use strict'; + +// Require process, so we can mock environment variables +const process = require('process'); + // [START createTables] // [START setup] -const mysql = require('mysql'); +const Knex = require('knex'); const prompt = require('prompt'); // [END setup] // [START createTable] -const SQL_STRING = `CREATE TABLE visits ( - id INT UNSIGNED NOT NULL AUTO_INCREMENT, - timestamp DATETIME NULL, - userIp VARCHAR(46) NULL, - PRIMARY KEY (id) -);`; - /** * Create the "visits" table. * - * @param {object} connection A mysql connection object. - * @param {function} callback The callback function. + * @param {object} knex A Knex client object. */ -function createTable (connection, callback) { - connection.query(SQL_STRING, callback); +function createTable (knex) { + return knex.schema.createTable('visits', (table) => { + table.increments(); + table.timestamp('timestamp'); + table.string('userIp'); + }) + .then(() => { + console.log(`Successfully created 'visits' table.`); + return knex; + }) + .catch((err) => { + console.error(`Failed to create 'visits' table:`, err); + return knex; + }); } // [END createTable] // [START getConnection] -const FIELDS = ['user', 'password', 'database']; - /** * Ask the user for connection configuration and create a new connection. - * - * @param {function} callback The callback function. */ -function getConnection (callback) { - prompt.start(); - prompt.get(FIELDS, (err, config) => { - if (err) { - callback(err); - return; - } - - const user = encodeURIComponent(config.user); - const password = encodeURIComponent(config.password); - const database = encodeURIComponent(config.database); +function getConnection () { + const FIELDS = ['user', 'password', 'database']; + return new Promise((resolve, reject) => { + prompt.start(); + prompt.get(FIELDS, (err, config) => { + if (err) { + return reject(err); + } - const uri = `mysql://${user}:${password}@127.0.0.1:3306/${database}`; - callback(null, mysql.createConnection(uri)); + // Connect to the database + return resolve(Knex({ + client: process.env.SQL_CLIENT, + connection: config + })); + }); }); } // [END getConnection] -// [START main] -getConnection((err, connection) => { - if (err) { - console.error(err); - return; - } - createTable(connection, (err, result) => { - connection.end(); - if (err) { - console.error(err); - return; - } - console.log(result); - }); -}); -// [END main] +exports.main = function () { + // [START main] + getConnection() + .then((knex) => { + return createTable(knex); + }) + .then((knex) => { + return knex.destroy(); + }) + .catch((err, knex) => { + console.error(`Failed to create database connection:`, err); + if (knex) { + knex.destroy(); + } + }); + // [END main] +}; // [END createTables] + +// Get type of SQL client to use +const sqlClient = process.env.SQL_CLIENT; +if (sqlClient === 'pg' || sqlClient === 'mysql') { + exports.main(); +} else { + throw new Error(`The SQL_CLIENT environment variable must be set to lowercase 'pg' or 'mysql'.`); +} diff --git a/appengine/cloudsql/package.json b/appengine/cloudsql/package.json index f61c9ce9b8..f42e2aea3b 100644 --- a/appengine/cloudsql/package.json +++ b/appengine/cloudsql/package.json @@ -24,7 +24,9 @@ }, "dependencies": { "express": "4.15.2", + "knex": "^0.13.0", "mysql": "2.13.0", + "pg": "^6.2.3", "prompt": "1.0.0" }, "devDependencies": { @@ -35,19 +37,18 @@ "test": { "app": { "requiredEnvVars": [ - "MYSQL_USER", - "MYSQL_PASSWORD", - "MYSQL_DATABASE", - "YOUR_INSTANCE_CONNECTION_NAME" + "SQL_CLIENT", + "SQL_USER", + "SQL_PASSWORD", + "SQL_DATABASE", + "INSTANCE_CONNECTION_NAME" ], "msg": "Last 10 visits:", - "args": ["server.js"] - }, - "deploy": { - "substitutions": "YOUR_USER=$MYSQL_USER,YOUR_PASSWORD=$MYSQL_PASSWORD,YOUR_DATABASE=$MYSQL_DATABASE,YOUR_INSTANCE_CONNECTION_NAME=$INSTANCE_CONNECTION_NAME" + "substitutions": "YOUR_SQL_CLIENT=$SQL_CLIENT,YOUR_USER=$SQL_USER,YOUR_PASSWORD=$SQL_PASSWORD,YOUR_DATABASE=$SQL_DATABASE,YOUR_INSTANCE_CONNECTION_NAME=$INSTANCE_CONNECTION_NAME", + "args": [ + "server.js" + ] } - }, - "requiresKeyFile": true, - "requiresProjectId": true + } } } diff --git a/appengine/cloudsql/server.js b/appengine/cloudsql/server.js index c221d008fb..1ae0eec385 100644 --- a/appengine/cloudsql/server.js +++ b/appengine/cloudsql/server.js @@ -15,69 +15,69 @@ 'use strict'; +// Require process, so we can mock environment variables +const process = require('process'); + // [START app] // [START setup] const express = require('express'); -const mysql = require('mysql'); +const Knex = require('knex'); const crypto = require('crypto'); const app = express(); app.enable('trust proxy'); // [END setup] +let knex; + +function connect () { + // [START connect] + const config = { + user: process.env.SQL_USER, + password: process.env.SQL_PASSWORD, + database: process.env.SQL_DATABASE + }; + + if (process.env.INSTANCE_CONNECTION_NAME && process.env.NODE_ENV === 'production') { + if (process.env.SQL_CLIENT === 'mysql') { + config.socketPath = `/cloudsql/${process.env.INSTANCE_CONNECTION_NAME}`; + } else if (process.env.SQL_CLIENT === 'pg') { + config.host = `/cloudsql/${process.env.INSTANCE_CONNECTION_NAME}`; + } + } -// [START connect] -const config = { - user: process.env.MYSQL_USER, - password: process.env.MYSQL_PASSWORD, - database: process.env.MYSQL_DATABASE -}; + // Connect to the database + const knex = Knex({ + client: process.env.SQL_CLIENT, + connection: config + }); + // [END connect] -if (process.env.INSTANCE_CONNECTION_NAME) { - config.socketPath = `/cloudsql/${process.env.INSTANCE_CONNECTION_NAME}`; + return knex; } -// Connect to the database -const connection = mysql.createConnection(config); -// [END connect] - // [START insertVisit] /** * Insert a visit record into the database. * * @param {object} visit The visit record to insert. - * @param {function} callback The callback function. */ -function insertVisit (visit, callback) { - connection.query('INSERT INTO `visits` SET ?', visit, (err) => { - if (err) { - callback(err); - return; - } - callback(); - }); +function insertVisit (visit) { + return knex('visits').insert(visit); } // [END insertVisit] // [START getVisits] -const SQL_STRING = `SELECT timestamp, userIp -FROM visits -ORDER BY timestamp DESC -LIMIT 10;`; - /** * Retrieve the latest 10 visit records from the database. - * - * @param {function} callback The callback function. */ -function getVisits (callback) { - connection.query(SQL_STRING, (err, results) => { - if (err) { - callback(err); - return; - } - - callback(null, results.map((visit) => `Time: ${visit.timestamp}, AddrHash: ${visit.userIp}`)); - }); +function getVisits () { + return knex.select('timestamp', 'userIp') + .from('visits') + .orderBy('timestamp', 'desc') + .limit(10) + .then((results) => { + return results.map((visit) => `Time: ${visit.timestamp}, AddrHash: ${visit.userIp}`); + }); } // [END getVisits] @@ -89,37 +89,38 @@ app.get('/', (req, res, next) => { userIp: crypto.createHash('sha256').update(req.ip).digest('hex').substr(0, 7) }; - insertVisit(visit, (err, results) => { - if (err) { - next(err); - return; - } - - // Query the last 10 visits from the database. - getVisits((err, visits) => { - if (err) { - next(err); - return; - } - + insertVisit(visit) + .then(() => { + // Query the last 10 visits from the database. + return getVisits(); + }) + .then((visits) => { res .status(200) .set('Content-Type', 'text/plain') .send(`Last 10 visits:\n${visits.join('\n')}`) .end(); + }) + .catch((err) => { + next(err); }); - }); }); -if (module === require.main) { - // [START listen] - const PORT = process.env.PORT || 8080; - app.listen(PORT, () => { - console.log(`App listening on port ${PORT}`); - console.log('Press Ctrl+C to quit.'); - }); - // [END listen] +// Get type of SQL client to use +const sqlClient = process.env.SQL_CLIENT; +if (sqlClient === 'pg' || sqlClient === 'mysql') { + knex = connect(); +} else { + throw new Error(`The SQL_CLIENT environment variable must be set to lowercase 'pg' or 'mysql'.`); } + +// [START listen] +const PORT = process.env.PORT || 8080; +app.listen(PORT, () => { + console.log(`App listening on port ${PORT}`); + console.log('Press Ctrl+C to quit.'); +}); +// [END listen] // [END app] module.exports = app; diff --git a/appengine/cloudsql/test/createTables.test.js b/appengine/cloudsql/test/createTables.test.js index 613acad23e..7002834cc1 100644 --- a/appengine/cloudsql/test/createTables.test.js +++ b/appengine/cloudsql/test/createTables.test.js @@ -23,99 +23,163 @@ const tools = require(`@google-cloud/nodejs-repo-tools`); const SAMPLE_PATH = path.join(__dirname, `../createTables.js`); -function getSample () { - const connectionMock = { - query: sinon.stub(), - end: sinon.stub() - }; - connectionMock.query.onFirstCall().yields(null, `created visits table!`); - const mysqlMock = { - createConnection: sinon.stub().returns(connectionMock) - }; - const configMock = { - user: `user`, - password: `password`, - database: `database` - }; +const exampleConfig = [ + `user`, + `password`, + `database` +]; + +function getSample (sqlClient) { + const configMock = exampleConfig; const promptMock = { start: sinon.stub(), get: sinon.stub().yields(null, configMock) }; + const tableMock = { + increments: sinon.stub(), + timestamp: sinon.stub(), + string: sinon.stub() + }; + const knexMock = { + schema: { + createTable: sinon.stub().returns(Promise.resolve(this)).yields(tableMock) + }, + destroy: sinon.stub().returns(Promise.resolve()) + }; - proxyquire(SAMPLE_PATH, { - mysql: mysqlMock, - prompt: promptMock - }); + const KnexMock = sinon.stub().returns(knexMock); + + const processMock = { + env: { + SQL_CLIENT: sqlClient + } + }; return { mocks: { - connection: connectionMock, - mysql: mysqlMock, + Knex: KnexMock, + knex: knexMock, config: configMock, - prompt: promptMock + prompt: promptMock, + process: processMock } }; } +function doProxiquire (sample) { + proxyquire(SAMPLE_PATH, { + knex: sample.mocks.Knex, + prompt: sample.mocks.prompt, + process: sample.mocks.process + }); +} + test.beforeEach(tools.stubConsole); test.afterEach.always(tools.restoreConsole); -test.cb.serial(`should record a visit`, (t) => { - const sample = getSample(); - const expectedResult = `created visits table!`; +test.cb.serial(`should create a table in MySQL`, (t) => { + const sample = getSample('mysql'); + const expectedResult = `Successfully created 'visits' table.`; + + proxyquire(SAMPLE_PATH, { + knex: sample.mocks.Knex, + prompt: sample.mocks.prompt, + process: sample.mocks.process + }); + + t.true(sample.mocks.prompt.start.calledOnce); + t.true(sample.mocks.prompt.get.calledOnce); + t.deepEqual(sample.mocks.prompt.get.firstCall.args[0], exampleConfig); + + setTimeout(() => { + t.true(sample.mocks.Knex.calledOnce); + t.deepEqual(sample.mocks.Knex.firstCall.args, [{ + client: 'mysql', + connection: exampleConfig + }]); + + t.true(sample.mocks.knex.schema.createTable.calledOnce); + t.is(sample.mocks.knex.schema.createTable.firstCall.args[0], 'visits'); + + t.true(console.log.calledWith(expectedResult)); + t.true(sample.mocks.knex.destroy.calledOnce); + t.end(); + }, 10); +}); + +test.cb.serial(`should create a table in Postgres`, (t) => { + const sample = getSample('pg'); + const expectedResult = `Successfully created 'visits' table.`; + + proxyquire(SAMPLE_PATH, { + knex: sample.mocks.Knex, + prompt: sample.mocks.prompt, + process: sample.mocks.process + }); t.true(sample.mocks.prompt.start.calledOnce); t.true(sample.mocks.prompt.get.calledOnce); - t.deepEqual(sample.mocks.prompt.get.firstCall.args[0], [ - `user`, - `password`, - `database` - ]); + t.deepEqual(sample.mocks.prompt.get.firstCall.args[0], exampleConfig); setTimeout(() => { - const uri = `mysql://${sample.mocks.config.user}:${sample.mocks.config.password}@127.0.0.1:3306/${sample.mocks.config.database}`; - t.deepEqual(sample.mocks.mysql.createConnection.firstCall.args, [uri]); + t.true(sample.mocks.Knex.calledOnce); + t.deepEqual(sample.mocks.Knex.firstCall.args, [{ + client: 'pg', + connection: exampleConfig + }]); + + t.true(sample.mocks.knex.schema.createTable.calledOnce); + t.is(sample.mocks.knex.schema.createTable.firstCall.args[0], 'visits'); + t.true(console.log.calledWith(expectedResult)); + t.true(sample.mocks.knex.destroy.calledOnce); t.end(); }, 10); }); test.cb.serial(`should handle prompt error`, (t) => { const error = new Error(`error`); - const sample = getSample(); + const sample = getSample('mysql'); + sample.mocks.prompt.get = sinon.stub().yields(error); proxyquire(SAMPLE_PATH, { - mysql: sample.mocks.mysql, - prompt: { - start: sinon.stub(), - get: sinon.stub().yields(error) - } + knex: sample.mocks.Knex, + prompt: sample.mocks.prompt, + process: sample.mocks.process }); setTimeout(() => { - t.true(console.error.calledWith(error)); + t.true(console.error.calledOnce); + t.true(console.error.calledWith(`Failed to create database connection:`, error)); + t.true(sample.mocks.Knex.notCalled); t.end(); }, 10); }); -test.cb.serial(`should handle insert error`, (t) => { +test.cb.serial(`should handle knex creation error`, (t) => { const error = new Error(`error`); - const sample = getSample(); - - const connectionMock = { - query: sinon.stub().yields(error), - end: sinon.stub() - }; + const sample = getSample('mysql'); + sample.mocks.knex.schema.createTable = sinon.stub().returns(Promise.reject(error)); proxyquire(SAMPLE_PATH, { - mysql: { - createConnection: sinon.stub().returns(connectionMock) - }, - prompt: sample.mocks.prompt + knex: sample.mocks.Knex, + prompt: sample.mocks.prompt, + process: sample.mocks.process }); setTimeout(() => { - t.true(console.error.calledWith(error)); + t.true(console.error.calledOnce); + t.true(console.error.calledWith(`Failed to create 'visits' table:`, error)); + t.true(sample.mocks.knex.destroy.calledOnce); t.end(); }, 10); }); + +test(`should validate SQL_CLIENT env var`, (t) => { + const expected = `The SQL_CLIENT environment variable must be set to lowercase 'pg' or 'mysql'.`; + t.throws(() => { doProxiquire(getSample(null)); }, expected); + t.throws(() => { doProxiquire(getSample('foo')); }, expected); + + t.notThrows(() => { doProxiquire(getSample('mysql')); }); + t.notThrows(() => { doProxiquire(getSample('pg')); }); +}); diff --git a/appengine/cloudsql/test/server.test.js b/appengine/cloudsql/test/server.test.js index 28d8dc881a..ff5553779c 100644 --- a/appengine/cloudsql/test/server.test.js +++ b/appengine/cloudsql/test/server.test.js @@ -25,7 +25,7 @@ const tools = require(`@google-cloud/nodejs-repo-tools`); const SAMPLE_PATH = path.join(__dirname, `../server.js`); -function getSample () { +function getSample (sqlClient) { const testApp = express(); sinon.stub(testApp, `listen`).yields(); const expressMock = sinon.stub().returns(testApp); @@ -35,27 +35,42 @@ function getSample () { userIp: `abcd` } ]; - const connectionMock = { - query: sinon.stub() - }; - connectionMock.query.onFirstCall().yields(); - connectionMock.query.onSecondCall().yields(null, resultsMock); - const mysqlMock = { - createConnection: sinon.stub().returns(connectionMock) + const knexMock = sinon.stub().returns({ + insert: sinon.stub().returns(Promise.resolve()) + }); + Object.assign(knexMock, { + select: sinon.stub().returnsThis(), + from: sinon.stub().returnsThis(), + orderBy: sinon.stub().returnsThis(), + limit: sinon.stub().returns(Promise.resolve(resultsMock)) + }); + + const KnexMock = sinon.stub().returns(knexMock); + + const processMock = { + env: { + SQL_CLIENT: sqlClient, + SQL_USER: 'user', + SQL_PASSWORD: 'password', + SQL_DATABASE: 'database' + } }; const app = proxyquire(SAMPLE_PATH, { - mysql: mysqlMock, - express: expressMock + knex: KnexMock, + express: expressMock, + process: processMock }); + return { app: app, mocks: { express: expressMock, results: resultsMock, - connection: connectionMock, - mysql: mysqlMock + knex: knexMock, + Knex: KnexMock, + process: processMock } }; } @@ -63,20 +78,47 @@ function getSample () { test.beforeEach(tools.stubConsole); test.afterEach.always(tools.restoreConsole); -test(`sets up sample`, (t) => { - const sample = getSample(); +test(`should set up sample in MySQL`, (t) => { + const sample = getSample('mysql'); t.true(sample.mocks.express.calledOnce); - t.true(sample.mocks.mysql.createConnection.calledOnce); - t.deepEqual(sample.mocks.mysql.createConnection.firstCall.args[0], { - user: process.env.MYSQL_USER, - password: process.env.MYSQL_PASSWORD, - database: process.env.MYSQL_DATABASE - }); + t.true(sample.mocks.Knex.calledOnce); + t.deepEqual(sample.mocks.Knex.firstCall.args, [{ + client: 'mysql', + connection: { + user: sample.mocks.process.env.SQL_USER, + password: sample.mocks.process.env.SQL_PASSWORD, + database: sample.mocks.process.env.SQL_DATABASE + } + }]); +}); + +test(`should set up sample in Postgres`, (t) => { + const sample = getSample('pg'); + + t.true(sample.mocks.express.calledOnce); + t.true(sample.mocks.Knex.calledOnce); + t.deepEqual(sample.mocks.Knex.firstCall.args, [{ + client: 'pg', + connection: { + user: sample.mocks.process.env.SQL_USER, + password: sample.mocks.process.env.SQL_PASSWORD, + database: sample.mocks.process.env.SQL_DATABASE + } + }]); +}); + +test(`should validate SQL_CLIENT env var`, (t) => { + const expected = `The SQL_CLIENT environment variable must be set to lowercase 'pg' or 'mysql'.`; + t.throws(() => { getSample(null); }, expected); + t.throws(() => { getSample('foo'); }, expected); + + t.notThrows(() => { getSample('mysql'); }); + t.notThrows(() => { getSample('pg'); }); }); -test.cb(`should record a visit`, (t) => { - const sample = getSample(); +test.cb(`should record a visit in mysql`, (t) => { + const sample = getSample('mysql'); const expectedResult = `Last 10 visits:\nTime: 1234, AddrHash: abcd`; request(sample.app) @@ -89,10 +131,10 @@ test.cb(`should record a visit`, (t) => { }); test.cb(`should handle insert error`, (t) => { - const sample = getSample(); + const sample = getSample('mysql'); const expectedResult = `insert_error`; - sample.mocks.connection.query.onFirstCall().yields(expectedResult); + sample.mocks.knex.limit.returns(Promise.reject(expectedResult)); request(sample.app) .get(`/`) @@ -104,10 +146,10 @@ test.cb(`should handle insert error`, (t) => { }); test.cb(`should handle read error`, (t) => { - const sample = getSample(); + const sample = getSample('mysql'); const expectedResult = `read_error`; - sample.mocks.connection.query.onSecondCall().yields(expectedResult); + sample.mocks.knex.limit.returns(Promise.reject(expectedResult)); request(sample.app) .get(`/`) diff --git a/appengine/cloudsql/yarn.lock b/appengine/cloudsql/yarn.lock index 9f5a3fc215..a1e1212390 100644 --- a/appengine/cloudsql/yarn.lock +++ b/appengine/cloudsql/yarn.lock @@ -37,21 +37,21 @@ ansi-styles "^2.2.1" esutils "^2.0.2" -"@google-cloud/nodejs-repo-tools@1.4.4": - version "1.4.4" - resolved "https://registry.yarnpkg.com/@google-cloud/nodejs-repo-tools/-/nodejs-repo-tools-1.4.4.tgz#699f8557a495c037bfd486ef9fbf7c6a28f37797" +"@google-cloud/nodejs-repo-tools@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@google-cloud/nodejs-repo-tools/-/nodejs-repo-tools-1.4.14.tgz#b778687bcd798ea172a01d2b92db43ad95a8b0ce" dependencies: ava "0.19.1" colors "1.1.2" - fs-extra "3.0.0" + fs-extra "3.0.1" got "6.7.1" - handlebars "4.0.6" + handlebars "4.0.8" lodash "4.17.4" proxyquire "1.7.11" - sinon "2.1.0" + sinon "2.2.0" string "3.3.3" supertest "3.0.0" - yargs "7.1.0" + yargs "8.0.1" abbrev@1: version "1.1.0" @@ -114,6 +114,10 @@ anymatch@^1.3.0: arrify "^1.0.0" micromatch "^2.1.5" +ap@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110" + aproba@^1.0.3: version "1.1.1" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab" @@ -558,7 +562,7 @@ babel-register@^6.24.1: mkdirp "^0.5.1" source-map-support "^0.4.2" -babel-runtime@^6.22.0: +babel-runtime@^6.11.6, babel-runtime@^6.22.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" dependencies: @@ -626,7 +630,7 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird@^3.0.0: +bluebird@^3.0.0, bluebird@^3.4.6: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" @@ -671,6 +675,10 @@ buffer-shims@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" +buffer-writer@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -711,11 +719,7 @@ camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - -camelcase@^4.0.0: +camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -860,6 +864,12 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" +commander@^2.2.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + common-path-prefix@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-1.0.0.tgz#cd52f6f0712e0baab97d6f9732874f22f47752c0" @@ -990,7 +1000,7 @@ debug@2.6.1, debug@^2.2.0: dependencies: ms "0.7.2" -debug@2.6.3, debug@^2.1.1: +debug@2.6.3, debug@^2.1.1, debug@^2.1.3: version "2.6.3" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" dependencies: @@ -1028,6 +1038,12 @@ destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" +detect-file@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-0.1.0.tgz#4935dedfd9488648e006b0129566e9386711ea63" + dependencies: + fs-exists-sync "^0.1.0" + detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" @@ -1161,6 +1177,12 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" +expand-tilde@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" + dependencies: + os-homedir "^1.0.1" + express@4.15.2: version "4.15.2" resolved "https://registry.yarnpkg.com/express/-/express-4.15.2.tgz#af107fc148504457f2dca9a6f2571d7129b97b35" @@ -1272,6 +1294,19 @@ find-up@^2.0.0: dependencies: locate-path "^2.0.0" +findup-sync@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" + dependencies: + detect-file "^0.1.0" + is-glob "^2.0.1" + micromatch "^2.3.7" + resolve-dir "^0.1.0" + +flagged-respawn@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5" + fn-name@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7" @@ -1316,9 +1351,13 @@ fresh@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" -fs-extra@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.0.tgz#244e0c4b0b8818f54040ec049d8a2bddc1202861" +fs-exists-sync@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" + +fs-extra@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" dependencies: graceful-fs "^4.1.2" jsonfile "^3.0.0" @@ -1365,6 +1404,14 @@ gauge@~2.7.1: strip-ansi "^3.0.1" wide-align "^1.1.0" +generic-pool@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.3.tgz#780c36f69dfad05a5a045dd37be7adca11a4f6ff" + +generic-pool@^2.4.2: + version "2.5.4" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.5.4.tgz#38c6188513e14030948ec6e5cf65523d9779299b" + get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" @@ -1418,6 +1465,22 @@ glob@^7.0.3, glob@^7.0.5: once "^1.3.0" path-is-absolute "^1.0.0" +global-modules@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" + dependencies: + global-prefix "^0.1.4" + is-windows "^0.2.0" + +global-prefix@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" + dependencies: + homedir-polyfill "^1.0.0" + ini "^1.3.4" + is-windows "^0.2.0" + which "^1.2.12" + globals@^9.0.0: version "9.17.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.17.0.tgz#0c0ca696d9b9bb694d2e5470bd37777caad50286" @@ -1452,9 +1515,13 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" -handlebars@4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +handlebars@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.8.tgz#22b875cd3f0e6cbea30314f144e82bc7a72ff420" dependencies: async "^1.4.0" optimist "^0.6.1" @@ -1519,6 +1586,12 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" +homedir-polyfill@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" + dependencies: + parse-passwd "^1.0.0" + hosted-git-info@^2.1.4: version "2.4.2" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67" @@ -1591,10 +1664,14 @@ inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" -ini@~1.3.0: +ini@^1.3.4, ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" +interpret@^0.6.5: + version "0.6.6" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" + invariant@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" @@ -1751,6 +1828,10 @@ is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" +is-windows@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -1910,6 +1991,29 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.0.2" +knex@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/knex/-/knex-0.13.0.tgz#08dd494f6bb64928934eec9dac34787a14ca5fa4" + dependencies: + babel-runtime "^6.11.6" + bluebird "^3.4.6" + chalk "^1.0.0" + commander "^2.2.0" + debug "^2.1.3" + generic-pool "^2.4.2" + inherits "~2.0.1" + interpret "^0.6.5" + liftoff "~2.2.0" + lodash "^4.6.0" + minimist "~1.1.0" + mkdirp "^0.5.0" + pg-connection-string "^0.1.3" + readable-stream "^1.1.12" + safe-buffer "^5.0.1" + tildify "~1.0.0" + uuid "^3.0.0" + v8flags "^2.0.2" + last-line-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/last-line-stream/-/last-line-stream-1.0.0.tgz#d1b64d69f86ff24af2d04883a2ceee14520a5600" @@ -1940,6 +2044,16 @@ leven@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" +liftoff@~2.2.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.2.5.tgz#998c2876cff484b103e4423b93d356da44734c91" + dependencies: + extend "^3.0.0" + findup-sync "^0.4.2" + flagged-respawn "^0.3.2" + rechoir "^0.6.2" + resolve "^1.1.7" + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -1998,7 +2112,7 @@ lodash.merge@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" -lodash@4.17.4, lodash@^4.2.0: +lodash@4.17.4, lodash@^4.2.0, lodash@^4.6.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -2064,6 +2178,12 @@ media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + meow@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -2087,7 +2207,7 @@ methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micromatch@^2.1.5, micromatch@^2.3.11: +micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -2137,6 +2257,10 @@ minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" +minimist@~1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8" + mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -2257,6 +2381,10 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" +object-assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -2306,15 +2434,17 @@ option-chain@^0.1.0: dependencies: object-assign "^4.0.1" -os-homedir@^1.0.0: +os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" +os-locale@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.0.0.tgz#15918ded510522b81ee7ae5a309d54f639fc39a4" dependencies: + execa "^0.5.0" lcid "^1.0.0" + mem "^1.1.0" os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: version "1.0.2" @@ -2365,6 +2495,10 @@ package-json@^4.0.0: registry-url "^3.0.3" semver "^5.1.0" +packet-reader@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27" + parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -2388,6 +2522,10 @@ parse-ms@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d" +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + parseurl@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" @@ -2442,6 +2580,45 @@ performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" +pg-connection-string@0.1.3, pg-connection-string@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" + +pg-pool@1.*: + version "1.7.1" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-1.7.1.tgz#421105cb7469979dcc48d6fc4fe3fe4659437437" + dependencies: + generic-pool "2.4.3" + object-assign "4.1.0" + +pg-types@1.*: + version "1.11.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.11.0.tgz#aae91a82d952b633bb88d006350a166daaf6ea90" + dependencies: + ap "~0.2.0" + postgres-array "~1.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.0" + postgres-interval "~1.0.0" + +pg@^6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/pg/-/pg-6.2.3.tgz#8988b7c69a1875a997d73b92036c42590b5f8024" + dependencies: + buffer-writer "1.0.1" + packet-reader "0.3.1" + pg-connection-string "0.1.3" + pg-pool "1.*" + pg-types "1.*" + pgpass "1.x" + semver "4.3.2" + +pgpass@1.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" + dependencies: + split "^1.0.0" + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -2497,6 +2674,24 @@ plur@^2.0.0: dependencies: irregular-plurals "^1.0.0" +postgres-array@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + +postgres-date@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" + +postgres-interval@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.0.2.tgz#7261438d862b412921c6fdb7617668424b73a6ed" + dependencies: + xtend "^4.0.0" + prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" @@ -2627,7 +2822,7 @@ read@1.0.x: dependencies: mute-stream "~0.0.4" -readable-stream@1.1.14: +readable-stream@1.1.14, readable-stream@^1.1.12: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" dependencies: @@ -2657,6 +2852,12 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -2778,11 +2979,18 @@ resolve-cwd@^1.0.0: dependencies: resolve-from "^2.0.0" +resolve-dir@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" + dependencies: + expand-tilde "^1.2.2" + global-modules "^0.2.3" + resolve-from@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" -resolve@~1.1.7: +resolve@^1.1.6, resolve@^1.1.7, resolve@~1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -2827,6 +3035,10 @@ semver-diff@^2.0.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" +semver@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" + send@0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/send/-/send-0.15.1.tgz#8a02354c26e6f5cca700065f5f0cdeba90ec7b5f" @@ -2870,9 +3082,9 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -sinon@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.1.0.tgz#e057a9d2bf1b32f5d6dd62628ca9ee3961b0cafb" +sinon@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.2.0.tgz#3b1b42ff5defcbf51a52a62aca6d61171b9fd262" dependencies: diff "^3.1.0" formatio "1.2.0" @@ -2937,6 +3149,12 @@ spdx-license-ids@^1.0.2: version "1.2.2" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" +split@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.0.tgz#c4395ce683abcd254bc28fe1dabb6e5c27dcffae" + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -2972,7 +3190,7 @@ stack-utils@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" -string-width@^1.0.1, string-width@^1.0.2: +string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" dependencies: @@ -3127,6 +3345,16 @@ through2@^2.0.0: readable-stream "^2.1.5" xtend "~4.0.1" +through@2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +tildify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.0.0.tgz#2a021db5e8fbde0a8f8b4df37adaa8fb1d39d7dd" + dependencies: + user-home "^1.0.0" + time-require@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/time-require/-/time-require-0.1.2.tgz#f9e12cb370fc2605e11404582ba54ef5ca2b2d98" @@ -3245,6 +3473,10 @@ url-parse-lax@^1.0.0: dependencies: prepend-http "^1.0.1" +user-home@^1.0.0, user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -3268,6 +3500,12 @@ uuid@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" +v8flags@^2.0.2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" + dependencies: + user-home "^1.1.1" + validate-npm-package-license@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" @@ -3285,11 +3523,11 @@ verror@1.3.6: dependencies: extsprintf "1.0.2" -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@^1.2.8, which@^1.2.9: +which@^1.2.12, which@^1.2.8, which@^1.2.9: version "1.2.14" resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" dependencies: @@ -3383,29 +3621,29 @@ yallist@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" -yargs-parser@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" +yargs-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" dependencies: - camelcase "^3.0.0" + camelcase "^4.1.0" -yargs@7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" +yargs@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.1.tgz#420ef75e840c1457a80adcca9bc6fa3849de51aa" dependencies: - camelcase "^3.0.0" + camelcase "^4.1.0" cliui "^3.2.0" decamelize "^1.1.1" get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" + os-locale "^2.0.0" + read-pkg-up "^2.0.0" require-directory "^2.1.1" require-main-filename "^1.0.1" set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" + string-width "^2.0.0" + which-module "^2.0.0" y18n "^3.2.1" - yargs-parser "^5.0.0" + yargs-parser "^7.0.0" yargs@~3.10.0: version "3.10.0" diff --git a/circle.yml b/circle.yml index 976241c45a..dbdb721ddb 100644 --- a/circle.yml +++ b/circle.yml @@ -84,8 +84,7 @@ deployment: owner: GoogleCloudPlatform commands: - node scripts/build "appengine/analytics" - # TODO: Add env vars to CI environment - # - node scripts/build "appengine/cloudsql" + - node scripts/build "appengine/cloudsql" -- --test-args "run unit-test" - node scripts/build "appengine/datastore" - node scripts/build "appengine/endpoints" - node scripts/build "appengine/errorreporting" @@ -119,4 +118,4 @@ deployment: # - node scripts/build "video" # TODO: Repo tools doesn't support Redis in build container yet. # When this is done, remove Vision from main system tests. - # - node scripts/build "vision" \ No newline at end of file + # - node scripts/build "vision"