From 6827ff2f1fd723145c9e05d13523d7a68c4fa2a7 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 7 Jun 2017 10:27:42 -0600 Subject: [PATCH] Added support for version enforcing --- migrations/1496771633_tags.js | 5 +++ package.json | 5 +++ services/cache.js | 2 +- services/karma.js | 2 +- services/migration.js | 74 +++++++++++++++++++++++++++++------ services/mongoose.js | 1 + services/passport.js | 2 +- services/redis.js | 2 +- 8 files changed, 76 insertions(+), 17 deletions(-) diff --git a/migrations/1496771633_tags.js b/migrations/1496771633_tags.js index 9b2aeedf0..be3fa2577 100644 --- a/migrations/1496771633_tags.js +++ b/migrations/1496771633_tags.js @@ -17,6 +17,11 @@ module.exports = { }} ]); + // If no comments were found, nothing needes to be done! + if (comments.length <= 0) { + return; + } + // Create a new batch operation. let batch = CommentModel.collection.initializeUnorderedBulkOp(); diff --git a/package.json b/package.json index 450764c90..137249884 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,11 @@ "embed-start": "NODE_ENV=development yarn build && ./bin/cli serve --jobs", "heroku-postbuild": "./bin/cli plugins reconcile && yarn build" }, + "talk": { + "migration": { + "minVersion": 1496771633 + } + }, "config": { "pre-git": { "commit-msg": [], diff --git a/services/cache.js b/services/cache.js index 92da6df0a..25642e1d0 100644 --- a/services/cache.js +++ b/services/cache.js @@ -1,5 +1,5 @@ const redis = require('./redis'); -const debug = require('debug')('talk:cache'); +const debug = require('debug')('talk:services:cache'); const crypto = require('crypto'); const cache = module.exports = { diff --git a/services/karma.js b/services/karma.js index ea932c86a..c76bcebb3 100644 --- a/services/karma.js +++ b/services/karma.js @@ -1,4 +1,4 @@ -const debug = require('debug')('talk:trust'); +const debug = require('debug')('talk:services:karma'); const UserModel = require('../models/user'); /** diff --git a/services/migration.js b/services/migration.js index bc2274de4..11eec3247 100644 --- a/services/migration.js +++ b/services/migration.js @@ -2,10 +2,11 @@ const MigrationModel = require('../models/migration'); const fs = require('fs'); const path = require('path'); const Joi = require('joi'); +const debug = require('debug')('talk:services:migration'); const sc = require('snake-case'); +const {talk: {migration: {minVersion}}} = require('../package.json'); -const migrationTemplate = ` -module.exports = { +const migrationTemplate = `module.exports = { async up() { } @@ -13,12 +14,19 @@ module.exports = { `; -module.exports = class MigrationService { +class MigrationService { + + /** + * Creates a new migration file. + * + * @param {String} name name of the migration + */ static async create(name) { if (!name || typeof name !== 'string' || name.length === 0) { throw new Error('name must be defined'); } + // Create a new Migration based on the current time. let version = Math.round(Date.now() / 1000, 0); let filename = path.join(__dirname, '..', 'migrations', `${version}_${sc(name)}.js`); fs.writeFileSync(filename, migrationTemplate, 'utf8'); @@ -26,6 +34,9 @@ module.exports = class MigrationService { console.log(`Created migration ${version} in ${filename}`); } + /** + * Returns a list of all pending migrations. + */ static async listPending() { // Get all the migration files. @@ -85,27 +96,48 @@ module.exports = class MigrationService { return migrations; } + /** + * Runs an list of migrations. + * + * @param {Array} migrations a list of migrations returned by `listPending` + */ static async run(migrations) { if (migrations.length === 0) { console.log('No migrations to run!'); return; } - for (let {version, migration} of migrations) { - console.log(`Starting migration ${version}.js`); - await migration.up(); - console.log(`Finished migration ${version}.js`); + for (let {filename, version, migration} of migrations) { + try { + console.log(`Starting migration ${filename}`); + await migration.up(); + console.log(`Finished migration ${filename}`); + } catch (e) { + console.error(`Migration ${filename} failed`); + throw e; + } - console.log(`Recording migration ${version}.js`); + try { + console.log(`Recording migration ${filename}`); - // Record that the migration was finished. - let m = new MigrationModel({version}); - await m.save(); + // Record that the migration was finished. + let m = new MigrationModel({version}); + await m.save(); - console.log(`Finished recording migration ${version}.js`); + console.log(`Finished recording migration ${filename}`); + } catch (e) { + console.error(`Migration ${filename} could not be recorded`); + throw e; + } } + + console.log(`Database now at migration version ${migrations[migrations.length - 1].version}`); } + /** + * Returns the latest migration version number that has been applied to the + * database, null if none were found. + */ static async latestVersion() { // Load the latest migration details from the database. @@ -138,5 +170,21 @@ module.exports = class MigrationService { if (!latestVersion || latestVersion < requiredVersion) { throw new Error(`A database migration is required, version required ${requiredVersion}, found ${latestVersion}. Please run \`./bin/cli migration run\``); } + + return latestVersion; } -}; +} + +// Verify that the minimum migration version is met. +MigrationService + .verify(minVersion) + .then((latestVersion) => { + debug(`minimum migration version ${minVersion} was met with version ${latestVersion}`); + }) + .catch((e) => { + + // Throw the error in the catch block. + throw e; + }); + +module.exports = MigrationService; diff --git a/services/mongoose.js b/services/mongoose.js index acda2f564..c697e38ef 100644 --- a/services/mongoose.js +++ b/services/mongoose.js @@ -59,3 +59,4 @@ require('../models/asset'); require('../models/comment'); require('../models/setting'); require('../models/user'); +require('./migration'); diff --git a/services/passport.js b/services/passport.js index c64bb2334..826297ae5 100644 --- a/services/passport.js +++ b/services/passport.js @@ -7,7 +7,7 @@ const JWT = require('jsonwebtoken'); const LocalStrategy = require('passport-local').Strategy; const errors = require('../errors'); const uuid = require('uuid'); -const debug = require('debug')('talk:passport'); +const debug = require('debug')('talk:services:passport'); const {createClient} = require('./redis'); // Create a redis client to use for authentication. diff --git a/services/redis.js b/services/redis.js index c049b2d00..c6506eb3c 100644 --- a/services/redis.js +++ b/services/redis.js @@ -1,5 +1,5 @@ const redis = require('redis'); -const debug = require('debug')('talk:redis'); +const debug = require('debug')('talk:services:redis'); const { REDIS_URL } = require('../config');