diff --git a/api.js b/api.js index ccf3e973..da5e50b9 100644 --- a/api.js +++ b/api.js @@ -73,14 +73,20 @@ function dbmigrate (plugins, isModule, options, callback) { // delayed loading of bluebird Promise = require('bluebird'); this.internals.migrationOptions = { - dbmigrate: this.internals.dbm, + dbmigrate: { + version: this.internals.dbm.version, + dataType: this.internals.dbm.dataType + }, + dryRun: this.internals.dryRun, + cwd: this.internals.cwd, + noTransactions: this.internals.notransactions, + verbose: this.internals.verbose, + type: this.internals.dbm.dataType, + log: log, ignoreOnInit: this.internals.argv['ignore-on-init'], Promise: Promise }; - this.internals.seederOptions = { - dbmigrate: this.internals.dbm, - Promise: Promise - }; + this.internals.safeOptions = this.internals.migrationOptions; } dbmigrate.prototype = { @@ -101,10 +107,10 @@ dbmigrate.prototype = { }, /** - * Registers and initializes hooks. - * - * @returns Promise - */ + * Registers and initializes hooks. + * + * @returns Promise + */ registerAPIHook: function (callback) { var plugins = this.internals.plugins; var self = this; @@ -309,8 +315,8 @@ dbmigrate.prototype = { }, /** - * Transition migrations to the latest defined protocol. - */ + * Transition migrations to the latest defined protocol. + */ transition: function () { load('transition')(this.internals); }, diff --git a/connect.js b/connect.js index 590aadc9..897389db 100644 --- a/connect.js +++ b/connect.js @@ -1,3 +1,7 @@ +/** + * This file is going to disappear. + * Only still here for backwards compatibility. + * */ var recursive = require('final-fs').readdirRecursive; var fs = require('fs'); var driver = require('./lib/driver'); @@ -6,8 +10,9 @@ var log = require('db-migrate-shared').log; exports.connect = function (config, PassedClass, callback) { var internals = {}; - + var prefix = 'migration'; if (config.config) { + prefix = config.prefix || prefix; internals = config.internals; config = config.config; } @@ -19,15 +24,16 @@ exports.connect = function (config, PassedClass, callback) { } if (internals.migrationMode) { - var dirPath = path.resolve(config['migrations-dir'] || 'migrations'); - + var dirPath = path.resolve( + internals.argv['migrations-dir'] || 'migrations' + ); if (internals.migrationMode !== 'all') { var switched = false; var newConf; try { newConf = require(path.resolve( - config['migrations-dir'] || 'migrations', + internals.argv['migrations-dir'] || 'migrations', internals.migrationMode ) + '/config.json'); log.info( @@ -48,9 +54,10 @@ exports.connect = function (config, PassedClass, callback) { null, new PassedClass( db, - config['migrations-dir'], + internals.argv['migrations-dir'], internals.mode !== 'static', - internals + internals, + prefix ) ); }); @@ -60,9 +67,10 @@ exports.connect = function (config, PassedClass, callback) { null, new PassedClass( db, - config['migrations-dir'], + internals.argv['migrations-dir'], internals.mode !== 'static', - internals + internals, + prefix ) ); } @@ -70,7 +78,7 @@ exports.connect = function (config, PassedClass, callback) { recursive( dirPath, false, - config['migrations-dir'] || 'migrations' + internals.argv['migrations-dir'] || 'migrations' ).then(function (files) { var oldClose = db.close; @@ -89,6 +97,7 @@ exports.connect = function (config, PassedClass, callback) { PassedClass, db, oldClose, + prefix, cb ); }; @@ -101,9 +110,10 @@ exports.connect = function (config, PassedClass, callback) { null, new PassedClass( db, - config['migrations-dir'], + internals.argv['migrations-dir'], internals.mode !== 'static', - internals + internals, + prefix ) ); } @@ -128,6 +138,7 @@ function migrationFiles ( PassedClass, db, close, + prefix, cb ) { var file; @@ -156,8 +167,8 @@ function migrationFiles ( db.switchDatabase(switched ? newConf : config.database, function () { internals.matching = file.substr( - file.indexOf(config['migrations-dir'] || 'migrations') + - (config['migrations-dir'] || 'migrations').length + + file.indexOf(internals.argv['migrations-dir'] || 'migrations') + + (internals.argv['migrations-dir'] || 'migrations').length + 1 ); @@ -170,9 +181,10 @@ function migrationFiles ( null, new PassedClass( db, - config['migrations-dir'], + internals.argv['migrations-dir'], internals.mode !== 'static', - internals + internals, + prefix ) ); @@ -181,14 +193,3 @@ function migrationFiles ( } }); } - -exports.createMigration = function (migration, callback) { - migration.write(function (err) { - if (err) { - callback(err); - return; - } - - callback(null, migration); - }); -}; diff --git a/lib/commands/check.js b/lib/commands/check.js index 63891ff6..d4c1f6b0 100644 --- a/lib/commands/check.js +++ b/lib/commands/check.js @@ -4,24 +4,33 @@ var assert = require('./helper/assert.js'); var migrationHook = require('./helper/migration-hook.js'); module.exports = function (internals, config, callback) { - migrationHook(internals) - .then(function () { - var Migrator = require('../migrator.js'); - var index = require('../../connect'); + migrationHook(internals).then(function () { + var Migrator = require('../walker.js'); + var index = require('../../connect'); - if (!internals.argv.count) { - internals.argv.count = Number.MAX_VALUE; - } - index.connect({ + if (!internals.argv.count) { + internals.argv.count = Number.MAX_VALUE; + } + index.connect( + { config: config.getCurrent().settings, - internals: internals - }, Migrator, function (err, migrator) { + internals: internals, + prefix: 'migration' + }, + Migrator, + function (err, migrator) { if (!assert(err, callback)) return; if (internals.locTitle) { - migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'], - internals.locTitle); - } else { migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); } + migrator.migrationsDir = path.resolve( + internals.argv['migrations-dir'], + internals.locTitle + ); + } else { + migrator.migrationsDir = path.resolve( + internals.argv['migrations-dir'] + ); + } internals.migrationsDir = migrator.migrationsDir; @@ -29,9 +38,12 @@ module.exports = function (internals, config, callback) { if (!assert(err, callback)) return; log.verbose('migration table created'); - migrator.check(internals.argv, internals.onComplete.bind(this, - migrator, internals, callback)); + migrator.check( + internals.argv, + internals.onComplete.bind(this, migrator, internals, callback) + ); }); - }); - }); + } + ); + }); }; diff --git a/lib/commands/create-migration.js b/lib/commands/create-migration.js index 92adc3fb..3d0e0006 100644 --- a/lib/commands/create-migration.js +++ b/lib/commands/create-migration.js @@ -1,11 +1,11 @@ 'use strict'; -var _assert = require('./helper/assert'); -var log = require('db-migrate-shared').log; -var mkdirp = require('mkdirp'); -var fs = require('fs'); -var optimist = require('optimist'); -var util = require('util'); +const _assert = require('./helper/assert'); +const log = require('db-migrate-shared').log; +const mkdirp = require('mkdirp'); +const fs = require('fs'); +const optimist = require('optimist'); +const util = require('util'); function createMigrationDir (dir, callback) { fs.stat(dir, function (err) { @@ -18,7 +18,8 @@ function createMigrationDir (dir, callback) { } function executeCreateMigration (internals, config, callback) { - var migrationsDir = internals.argv['migrations-dir']; + let migrationsDir = internals.argv['migrations-dir']; + let folder, path; internals.runTimestamp = new Date(); @@ -27,8 +28,6 @@ function executeCreateMigration (internals, config, callback) { internals.argv['migrations-dir'] + '/' + internals.migrationMode; } - var folder, path; - if (internals.argv._.length === 0) { log.error("'migrationName' is required."); if (!internals.isModule) { @@ -43,8 +42,7 @@ function executeCreateMigration (internals, config, callback) { } createMigrationDir(migrationsDir, function (err) { - var index = require('../../connect'); - var Migration = require('../migration.js'); + const Migration = require('../template.js'); if (err) { log.error('Failed to create migration directory at ', migrationsDir, err); @@ -64,12 +62,12 @@ function executeCreateMigration (internals, config, callback) { if (folder.length > 1) { path += '/'; - for (var i = 0; i < folder.length - 1; ++i) { + for (let i = 0; i < folder.length - 1; ++i) { path += folder[i] + '/'; } } - var templateType = Migration.TemplateType.DEFAULT_JS; + let templateType = Migration.TemplateType.DEFAULT_JS; if ( shouldCreateSqlFiles(internals, config) && shouldCreateCoffeeFile(internals, config) @@ -85,16 +83,16 @@ function executeCreateMigration (internals, config, callback) { } else if (shouldCreateCoffeeFile(internals, config)) { templateType = Migration.TemplateType.DEFAULT_COFFEE; } - var migration = new Migration( + const migration = new Migration( internals.argv.title + (shouldCreateCoffeeFile(internals, config) ? '.coffee' : '.js'), path, internals.runTimestamp, templateType ); - index.createMigration(migration, function (err, migration) { + migration.write(function (err) { if (_assert(err, callback)) { - log.info(util.format('Created migration at %s', migration.path)); + log.info(util.format('Created migration at %s', migration.file.path)); if (shouldCreateSqlFiles(internals, config)) { createSqlFiles(internals, config, callback); } else { @@ -120,17 +118,16 @@ function shouldCreateCoffeeFile (internals, config) { } function createSqlFiles (internals, config, callback) { - var migrationsDir = internals.argv['migrations-dir']; + let migrationsDir = internals.argv['migrations-dir']; if (internals.migrationMode && internals.migrationMode !== 'all') { migrationsDir = internals.argv['migrations-dir'] + '/' + internals.migrationMode; } - var sqlDir = migrationsDir + '/sqls'; + const sqlDir = migrationsDir + '/sqls'; createMigrationDir(sqlDir, function (err) { - var index = require('../../connect'); - var Migration = require('../migration.js'); + const Migration = require('../template.js'); if (err) { log.error('Failed to create migration directory at ', sqlDir, err); @@ -142,31 +139,34 @@ function createSqlFiles (internals, config, callback) { } } - var templateTypeDefaultSQL = Migration.TemplateType.DEFAULT_SQL; - var migrationUpSQL = new Migration( + let templateTypeDefaultSQL = Migration.TemplateType.DEFAULT_SQL; + const migrationUpSQL = new Migration( internals.argv.title + '-up.sql', sqlDir, internals.runTimestamp, templateTypeDefaultSQL ); - index.createMigration(migrationUpSQL, function (err, migration) { + migrationUpSQL.write(function (err) { if (_assert(err, callback)) { log.info( - util.format('Created migration up sql file at %s', migration.path) + util.format( + 'Created migration up sql file at %s', + migrationUpSQL.file.path + ) ); - var migrationDownSQL = new Migration( + const migrationDownSQL = new Migration( internals.argv.title + '-down.sql', sqlDir, internals.runTimestamp, templateTypeDefaultSQL ); - index.createMigration(migrationDownSQL, function (err, migration) { + migrationDownSQL.write(function (err) { if (_assert(err, callback)) { log.info( util.format( 'Created migration down sql file at %s', - migration.path + migrationDownSQL.file.path ) ); if (typeof callback === 'function') callback(); diff --git a/lib/commands/down.js b/lib/commands/down.js index 4454f1ed..ad05bcfe 100644 --- a/lib/commands/down.js +++ b/lib/commands/down.js @@ -4,29 +4,35 @@ var assert = require('./helper/assert.js'); var migrationHook = require('./helper/migration-hook.js'); module.exports = function (internals, config, callback) { - migrationHook(internals) - .then(function () { - var Migrator = require('../migrator.js'); - var index = require('../../connect'); + migrationHook(internals).then(function () { + var Migrator = require('../walker.js'); + var index = require('../../connect'); - if (!internals.argv.count) { - log.info('Defaulting to running 1 down migration.'); - internals.argv.count = 1; - } + if (!internals.argv.count) { + log.info('Defaulting to running 1 down migration.'); + internals.argv.count = 1; + } - index.connect({ + index.connect( + { config: config.getCurrent().settings, - internals: internals - }, Migrator, function (err, migrator) { + internals: internals, + prefix: 'migration' + }, + Migrator, + function (err, migrator) { if (!assert(err)) return; migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); migrator.driver.createMigrationsTable(function (err) { if (!assert(err)) return; - migrator.down(internals.argv, internals.onComplete.bind(this, - migrator, internals, callback)); + migrator.down( + internals.argv, + internals.onComplete.bind(this, migrator, internals, callback) + ); }); - }); - }); + } + ); + }); }; diff --git a/lib/commands/helper/migration-hook.js b/lib/commands/helper/migration-hook.js index cb69a90e..8662fa5f 100644 --- a/lib/commands/helper/migration-hook.js +++ b/lib/commands/helper/migration-hook.js @@ -1,5 +1,4 @@ - module.exports = function (internals) { - var Migration = require('../../migration.js'); + var Migration = require('../../file.js'); return Migration.registerHook(internals.plugins, internals); }; diff --git a/lib/commands/run.js b/lib/commands/run.js index 40bffeb5..bfdfe819 100644 --- a/lib/commands/run.js +++ b/lib/commands/run.js @@ -42,7 +42,6 @@ function run (internals, config) { break; case 'up': case 'down': - case 'check': case 'reset': if (action === 'reset') internals.argv.count = Number.MAX_VALUE; @@ -63,15 +62,22 @@ function run (internals, config) { if (action === 'up') { var executeUp = load('up'); executeUp(internals, config); - } else if (action === 'down') { + } else { var executeDown = load('down'); executeDown(internals, config); - } else { - var executeCheck = load('check'); - executeCheck(internals, config); } break; + case 'check': + var executeCheck = load('check'); + + if (folder[1]) { + internals.matching = folder[1]; + internals.migrationMode = folder[1]; + } + + executeCheck(internals, config); + break; case 'db': if (folder.length < 1) { log.info('Please enter a valid command, i.e. db:create|db:drop'); diff --git a/lib/commands/seed.js b/lib/commands/seed.js index 72b18a66..3a295c37 100644 --- a/lib/commands/seed.js +++ b/lib/commands/seed.js @@ -6,7 +6,7 @@ var _assert = require('./helper/assert'); function executeSeed (internals, config, callback) { var index = require('../../connect'); - var Seeder = require('../seeder.js'); + var Seeder = require('../walker.js'); if (internals.argv._.length > 0) { internals.argv.destination = internals.argv._.shift().toString(); @@ -15,7 +15,8 @@ function executeSeed (internals, config, callback) { index.connect( { config: config.getCurrent().settings, - internals: internals + internals: internals, + prefix: 'seed' }, Seeder, function (err, seeder) { diff --git a/lib/commands/sync.js b/lib/commands/sync.js index ed1d3fc0..1674cb22 100644 --- a/lib/commands/sync.js +++ b/lib/commands/sync.js @@ -4,24 +4,33 @@ var assert = require('./helper/assert.js'); var migrationHook = require('./helper/migration-hook.js'); module.exports = function (internals, config, callback) { - migrationHook(internals) - .then(function () { - var Migrator = require('../migrator.js'); - var index = require('../../connect'); + migrationHook(internals).then(function () { + var Migrator = require('../walker.js'); + var index = require('../../connect'); - if (!internals.argv.count) { - internals.argv.count = Number.MAX_VALUE; - } - index.connect({ + if (!internals.argv.count) { + internals.argv.count = Number.MAX_VALUE; + } + index.connect( + { config: config.getCurrent().settings, - internals: internals - }, Migrator, function (err, migrator) { + internals: internals, + prefix: 'migration' + }, + Migrator, + function (err, migrator) { if (!assert(err)) return; if (internals.locTitle) { - migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'], - internals.locTitle); - } else { migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); } + migrator.migrationsDir = path.resolve( + internals.argv['migrations-dir'], + internals.locTitle + ); + } else { + migrator.migrationsDir = path.resolve( + internals.argv['migrations-dir'] + ); + } internals.migrationsDir = migrator.migrationsDir; @@ -29,9 +38,12 @@ module.exports = function (internals, config, callback) { if (!assert(err)) return; log.verbose('migration table created'); - migrator.sync(internals.argv, internals.onComplete.bind(this, - migrator, internals, callback)); + migrator.sync( + internals.argv, + internals.onComplete.bind(this, migrator, internals, callback) + ); }); - }); - }); + } + ); + }); }; diff --git a/lib/commands/undo-seed.js b/lib/commands/undo-seed.js index 6f2cd09f..7c07acc6 100644 --- a/lib/commands/undo-seed.js +++ b/lib/commands/undo-seed.js @@ -7,7 +7,7 @@ var _assert = require('./helper/assert'); function executeUndoSeed (internals, config, callback) { var index = require('./connect'); - var Seeder = require('./lib/seeder.js'); + var Seeder = require('./lib/walker.js'); if (!internals.argv.count) { log.info('Defaulting to running 1 down seed.'); @@ -21,7 +21,8 @@ function executeUndoSeed (internals, config, callback) { index.connect( { config: config.getCurrent().settings, - internals: internals + internals: internals, + prefix: 'seed' }, Seeder, function (err, seeder) { diff --git a/lib/commands/up.js b/lib/commands/up.js index fccd9348..77f4ebc4 100644 --- a/lib/commands/up.js +++ b/lib/commands/up.js @@ -4,24 +4,33 @@ var assert = require('./helper/assert.js'); var migrationHook = require('./helper/migration-hook.js'); module.exports = function (internals, config, callback) { - migrationHook(internals) - .then(function () { - var Migrator = require('../migrator.js'); - var index = require('../../connect'); + migrationHook(internals).then(function () { + var Migrator = require('../walker.js'); + var index = require('../../connect'); - if (!internals.argv.count) { - internals.argv.count = Number.MAX_VALUE; - } - index.connect({ + if (!internals.argv.count) { + internals.argv.count = Number.MAX_VALUE; + } + index.connect( + { config: config.getCurrent().settings, - internals: internals - }, Migrator, function (err, migrator) { + internals: internals, + prefix: 'migration' + }, + Migrator, + function (err, migrator) { if (!assert(err, callback)) return; if (internals.locTitle) { - migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'], - internals.locTitle); - } else { migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); } + migrator.migrationsDir = path.resolve( + internals.argv['migrations-dir'], + internals.locTitle + ); + } else { + migrator.migrationsDir = path.resolve( + internals.argv['migrations-dir'] + ); + } internals.migrationsDir = migrator.migrationsDir; @@ -29,9 +38,12 @@ module.exports = function (internals, config, callback) { if (!assert(err, callback)) return; log.verbose('migration table created'); - migrator.up(internals.argv, internals.onComplete.bind(this, - migrator, internals, callback)); + migrator.up( + internals.argv, + internals.onComplete.bind(this, migrator, internals, callback) + ); }); - }); - }); + } + ); + }); }; diff --git a/lib/executors/versioned/v1.js b/lib/executors/versioned/v1.js new file mode 100644 index 00000000..fc1e42f4 --- /dev/null +++ b/lib/executors/versioned/v1.js @@ -0,0 +1,48 @@ +'use strict'; + +const Promise = require('bluebird'); +const maybePromised = require('../../temputils.js').maybePromised; + +const execUnit = { + up: function (context, driver, file) { + return context.driver + .startMigration() + .then(() => { + const _file = file.get(); + + if (typeof _file.setup === 'function') { + _file.setup(context.internals.safeOptions, context.seedLink); + } + + return maybePromised(file, _file.up, [context.driver]); + }) + .then(() => { + return Promise.promisify(context.writeMigrationRecord.bind(context))( + file + ); + }) + .then(context.driver.endMigration.bind(context.driver)); + }, + + down: function (context, driver, file) { + return driver + .startMigration() + .then(() => { + const _file = file.get(); + + if (typeof _file.setup === 'function') { + _file.setup(context.internals.safeOptions, context.seedLink); + } + + return maybePromised(file, _file.down, [context.driver]); + }) + .then(() => { + return Promise.promisify(context.deleteMigrationRecord.bind(context))( + file + ); + }) + .then(context.driver.endMigration.bind(context.driver)); + } +}; + +module.exports = execUnit; diff --git a/lib/file.js b/lib/file.js new file mode 100644 index 00000000..04b1186b --- /dev/null +++ b/lib/file.js @@ -0,0 +1,192 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const log = require('db-migrate-shared').log; +const inflection = require('inflection'); +const Promise = require('bluebird'); +const lpad = require('db-migrate-shared').util.lpad; + +Promise.promisifyAll(fs); + +const LOADER = { + seeder: 'allLoadedSeedsAsync', + migration: 'allLoadedMigrationsAsync' +}; + +function formatPath (dir, name) { + return path.join(dir, name); +} + +function formatName (title, date) { + return formatDate(date) + '-' + formatTitle(title); +} + +function formatDate (date) { + return [ + date.getUTCFullYear(), + lpad(date.getUTCMonth() + 1, '0', 2), + lpad(date.getUTCDate(), '0', 2), + lpad(date.getUTCHours(), '0', 2), + lpad(date.getUTCMinutes(), '0', 2), + lpad(date.getUTCSeconds(), '0', 2) + ].join(''); +} + +function formatTitle (title) { + return inflection.dasherize(title); +} + +function parseDate (name) { + let date = new Date(); + const match = name.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})-[^.]+/); + date.setUTCFullYear(match[1]); + date.setUTCDate(match[3]); + date.setUTCMonth(match[2] - 1); + date.setUTCHours(match[4]); + date.setUTCMinutes(match[5]); + date.setUTCSeconds(match[6]); + return date; +} + +function parseTitle (name) { + const match = name.match(/\d{14}-([^.]+)/); + return inflection.humanize(match[1], true); +} + +const filesRegEx = /\.js$/; + +const File = function () { + if (arguments.length >= 3) { + this.title = arguments[0]; + this.date = arguments[2]; + this.name = this.formatName(this.title, this.date); + this.path = this.formatPath(arguments[1], this.name); + this.internals = arguments[4]; + } else if (arguments.length === 2) { + this.path = arguments[0]; + this.name = this.parseName(this.path); + this.date = this.parseDate(this.name); + this.title = this.parseTitle(this.name); + this.internals = arguments[1]; + } + + if (this.internals) this.internals = this.internals.safeOptions; +}; + +File.prototype = { + parseName: function (path) { + const match = path.match(/(\d{14}-[^.]+)(?:\.*?)?/); + return match[1]; + }, + + get: function () { + return this._required || (this._required = require(this.path)); + }, + + write: function (data) { + return fs.writeFileAsync(this.path, data); + }, + + parseTitle: parseTitle, + parseDate: parseDate, + formatTitle: formatTitle, + formatPath: formatPath, + formatName: formatName +}; + +File.registerHook = function (Plugin, internals) { + const plugin = [].concat(Plugin.hook('file:hook:require') || []).concat( + Plugin.hook('migrator:migration:hook:require') || [] // backwards compatible + ); + + internals.parser = internals.parser || { + filesRegEx: filesRegEx, + extensions: 'js' + }; + + if (!plugin) { + return Promise.resolve(null); + } + + return Promise.resolve(plugin) + .map(function (plugin) { + // Backwards compatibility and notice for everyone so they + // can bug the plugin maintainer + if (plugin['migrator:migration:hook:require']) { + log.warn( + `The plugin '${plugin.name}' is outdated! The hook` + + `migrator:migration:hook:require was deprecated!` + ); + log.warn( + `Report the maintainer of '${plugin.name}' to patch the ` + + `hook instead with file:hook:require.` + ); + + if (plugin.maintainer && plugin.maintainer.repository) { + log.verbose( + `The repo of the ${plugin.name} plugin is ${ + plugin.maintainer.repository + }!` + ); + } + return plugin['migrator:migration:hook:require'](); + } + + return plugin['file:hook:require'](); + }) + .each(function (parser) { + if (parser && parser.extensions) { + internals.parser.extensions = + internals.parser.extensions + '|' + parser.extensions; + } + }) + .then(function () { + internals.parser.filesRegEx = new RegExp( + '\\.(' + internals.parser.extensions + ')$' + ); + + return internals.parser; + }); +}; + +File.loadFromFileystem = function (dir, prefix, internals) { + log.verbose(`[${prefix}] loading from dir`, dir); + + return fs + .readdirAsync(dir) + .filter(function (files) { + return internals.parser.filesRegEx.test(files); + }) + .then(function (files) { + return files.sort(); + }) + .map(function (file) { + return new File(path.join(dir, file), internals); + }); +}; + +File.loadFromDatabase = function (dir, prefix, driver, internals) { + log.verbose(`[${prefix}] loading from database`); + return driver[LOADER[prefix]]() + .catch(function (err) { + if (internals.dryRun) { + return []; + } else { + return Promise.reject(err); + } + }) + .filter(function (result) { + return ( + result.name.substr(0, result.name.lastIndexOf('/')) === + internals.matching + ); + }) + .map(function (result) { + return new File(path.join(dir, result.name), internals); + }); +}; + +Promise.promisifyAll(fs); + +module.exports = File; diff --git a/lib/migrator.js b/lib/migrator.js deleted file mode 100644 index 02a62909..00000000 --- a/lib/migrator.js +++ /dev/null @@ -1,320 +0,0 @@ -var dbmUtil = require('db-migrate-shared').util; -var Migration = require('./migration'); -var log = require('db-migrate-shared').log; -var Promise = require('bluebird'); -var MigratorInterface = require('./interface/migratorInterface.js'); - -function SeedLink (driver, internals) { - this.seeder = require('./seeder.js')( - driver, - internals.argv['vcseeder-dir'], - true, - internals - ); - this.internals = internals; - this.links = []; -} -SeedLink.prototype = { - seed: function (partialName) { - var reset = !this.internals.notransactions; - - this.internals.notransactions = true; - - return new Promise(function (resolve, reject) { - this.seeder.up(partialName, function (err) { - if (reset) { - this.internals.notransactions = false; - } - - if (err) { - reject(err); - } else { - resolve(err); - } - }); - }); - }, - - link: function (partialName) { - this.links.push(partialName); - }, - - process: function () { - this.clear(); - }, - - clear: function () { - this.links = []; - } -}; - -var Migrator = function (driver, migrationsDir, empty, intern) { - this.driver = dbmUtil.reduceToInterface(driver, MigratorInterface); - this._driver = driver; - this.migrationsDir = migrationsDir; - this.internals = intern; - - if (intern.linked === false) { - this.seedLink = new SeedLink(driver, intern); - intern.linked = true; - } - - this.internals.migrationOptions.relation = require('./relation'); -}; - -Migrator.prototype = { - createMigrationsTable: function (callback) { - this._driver.createMigrationsTable(callback); - }, - - writeMigrationRecord: function (migration, callback) { - function onComplete (err) { - if (err) { - log.error(migration.name, err); - } else { - log.info('Processed migration', migration.name); - } - callback(err); - } - this._driver.addMigrationRecord( - this.internals.matching + '/' + migration.name, - onComplete - ); - }, - - deleteMigrationRecord: function (migration, callback) { - function onComplete (err) { - if (err) { - log.error(migration.name, err); - } else { - log.info('Processed migration', migration.name); - } - callback(err); - } - this._driver.deleteMigration( - this.internals.matching + '/' + migration.name, - function (err) { - if (!this.internals.matching) { - this._driver.deleteMigration(migration.name, onComplete); - } else { - onComplete.apply(err); - } - }.bind(this) - ); - }, - - up: function (funcOrOpts, callback) { - if (dbmUtil.isFunction(funcOrOpts)) { - return funcOrOpts(this.driver, callback); - } else { - this.upToBy(funcOrOpts.destination, funcOrOpts.count, callback); - } - }, - - down: function (funcOrOpts, callback) { - if (dbmUtil.isFunction(funcOrOpts)) { - return funcOrOpts(this.driver, callback); - } else { - this.downToBy(funcOrOpts.destination, funcOrOpts.count, callback); - } - }, - - check: function (funcOrOpts, callback) { - var self = this; - Migration.loadFromFilesystem(self.migrationsDir, self.internals, function ( - err, - allMigrations - ) { - if (err) { - callback(err); - return; - } - - Migration.loadFromDatabase( - self.migrationsDir, - self._driver, - self.internals, - function (err, completedMigrations) { - if (err) { - callback(err); - return; - } - - // Requires pr to export filterCompleted from db-migrate-shared - var toRun = dbmUtil.filterCompleted( - allMigrations, - completedMigrations - ); - - log.info('Migrations to run:', toRun.map(migration => migration.name)); - callback(null, toRun); - } - ); - }); - }, - - sync: function (funcOrOpts, callback) { - var self = this; - - Migration.loadFromDatabase( - self.migrationsDir, - self._driver, - self.internals, - function (err, completedMigrations) { - if (err) { - callback(err); - return; - } - - var mode = dbmUtil.syncMode( - completedMigrations, - funcOrOpts.destination - ); - if (mode === 1) { - log.info('Syncing upwards.'); - self.up(funcOrOpts, callback); - } else { - log.info('Syncing downwards.'); - self.down(funcOrOpts, callback); - } - } - ); - }, - - upToBy: function (partialName, count, callback) { - var self = this; - Migration.loadFromFilesystem(self.migrationsDir, self.internals, function ( - err, - allMigrations - ) { - if (err) { - callback(err); - return; - } - - Migration.loadFromDatabase( - self.migrationsDir, - self._driver, - self.internals, - function (err, completedMigrations) { - if (err) { - callback(err); - return; - } - var toRun = dbmUtil.filterUp( - allMigrations, - completedMigrations, - partialName, - count - ); - - if (toRun.length === 0) { - log.info('No migrations to run'); - callback(null); - return; - } - - if (self.internals.check) { - var toRunNames = toRun.map(migration => migration.name); - log.info('Migrations to run:', toRunNames); - callback(null, toRunNames); - return; - } - - return Promise.resolve(toRun) - .each(function (migration) { - log.verbose('preparing to run up migration:', migration.name); - - return self.driver - .startMigration() - .then(function () { - var setup = migration.setup(); - - if (typeof setup === 'function') { - setup(self.internals.migrationOptions, self.seedLink); - } - - return self.up(migration.up.bind(migration)); - }) - .then(function () { - if (self.seedLink && self.seedLink.links.length) { - log.info('Calling linked seeds'); - - return self.seedLink.process(); - } - }) - .then(function () { - return Promise.promisify( - self.writeMigrationRecord.bind(self) - )(migration); - }) - .then(self.driver.endMigration.bind(self.driver)); - }) - .nodeify(callback); - } - ); - }); - }, - - downToBy: function (partialName, count, callback) { - var self = this; - Migration.loadFromDatabase( - self.migrationsDir, - self._driver, - self.internals, - function (err, completedMigrations) { - if (err) { - return callback(err); - } - - var toRun = dbmUtil.filterDown(completedMigrations, partialName, count); - - if (toRun.length === 0) { - log.info('No migrations to run'); - callback(null); - return; - } - - if (self.internals.check) { - var toRunNames = toRun.map(migration => migration.name); - log.info('Migrations to run:', toRunNames); - callback(null, toRunNames); - return; - } - - return Promise.resolve(toRun) - .each(function (migration) { - log.verbose('preparing to run down migration:', migration.name); - - return self.driver - .startMigration() - .then(function () { - var setup = migration.setup(); - - if (typeof setup === 'function') { - setup(self.internals.migrationOptions, self.seedLink); - } - - return self.down(migration.down.bind(migration)); - }) - .then(function () { - if (self.seedLink && self.seedLink.links.length) { - log.info('Calling linked seeds'); - - return self.seedLink.process(); - } - }) - .then(function () { - return Promise.promisify( - self.deleteMigrationRecord.bind(self) - )(migration); - }) - .then(self.driver.endMigration.bind(self.driver)); - }) - .nodeify(callback); - } - ); - } -}; - -module.exports = Migrator; diff --git a/lib/seed.js b/lib/seed.js deleted file mode 100644 index 5bdbf6fc..00000000 --- a/lib/seed.js +++ /dev/null @@ -1,139 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var log = require('db-migrate-shared').log; -var Skeleton = require('./skeleton'); - -var Seed = Skeleton.extend({ - init: function () { - if (arguments.length >= 3) { - this.title = arguments[0]; - this.date = arguments[2]; - this.name = this.formatName(this.title, this.date); - this.path = this.formatPath(arguments[1], this.name); - this.templateType = arguments[3]; - this.internals = arguments[4]; - } else if (arguments.length === 2) { - this.path = arguments[0]; - this.name = this.parseName(this.path); - this.date = this.parseDate(this.name); - this.title = this.parseTitle(this.name); - this.internals = arguments[1]; - } - - this._super(this.internals); - }, - - up: function (db, isStatic) { - if (isStatic) { - var seed = require(this.path); - var cbExecuted = false; - - return new Promise( - function (resolve, reject) { - var r = function (err) { - if (cbExecuted === false) { - cbExecuted = true; - - if (err) { - log.error('Error while truncating static seed.'); - reject(err); - } else resolve(); - } - }; - - Promise.resolve(seed.truncate.apply(this, [db, r])) - .then(function (Promise) { - if (Promise !== undefined && cbExecuted === false) { - cbExecuted = true; - resolve(); - } - }) - .catch(function (err) { - if (cbExecuted === false) { - cbExecuted = true; - reject(err); - } - }); - }.bind(this) - ).then(function () { - var seedExecuted = false; - - return new Promise(function (resolve, reject) { - var r = function (err) { - if (seedExecuted === false) { - seedExecuted = true; - - if (err) { - log.error('Error while seeding static seed.'); - reject(err); - } else resolve(); - } - }; - - Promise.resolve(seed.seed.apply(this, [db, r])) - .then(function (Promise) { - if (Promise !== undefined && seedExecuted === false) { - seedExecuted = true; - resolve(); - } - }) - .catch(function (err) { - if (seedExecuted === false) { - seedExecuted = true; - reject(err); - } - }); - }); - }); - } else { - return this._up(db); - } - }, - - down: function (db) { - return this._down(db); - } -}); - -Seed.loadFromFilesystem = function (dir, internals, callback) { - log.verbose('loading seeds from dir', dir); - fs.readdir(dir, function (err, files) { - if (err) { - callback(err); - return; - } - files = files.filter(function (file) { - return internals.parser.filesRegEx.test(file); - }); - var seeds = files.sort().map(function (file) { - return new Seed(path.join(dir, file), internals); - }); - callback(null, seeds); - }); -}; - -Seed.loadFromDatabase = function (dir, driver, internals, callback) { - log.verbose('loading seeds from database'); - driver.allLoadedSeeds(function (err, dbResults) { - if (err && !internals.dryRun) { - callback(err); - return; - } else if (err && internals.dryRun) { - dbResults = []; - } - var seeds = dbResults - .filter(function (result) { - return ( - result.name.substr(0, result.name.lastIndexOf('/')) === - internals.matching - ); - }) - .map(function (result) { - return new Seed(path.join(dir, result.name), internals); - }); - - callback(null, seeds); - }); -}; - -module.exports = Seed; diff --git a/lib/seeder.js b/lib/seeder.js deleted file mode 100644 index 072fc7ff..00000000 --- a/lib/seeder.js +++ /dev/null @@ -1,367 +0,0 @@ -var Seed = require('./seed'); -var log = require('db-migrate-shared').log; -var dbmUtil = require('db-migrate-shared').util; -var Promise = require('bluebird'); -var SeederInterface = require('./interface/seederInterface.js'); - -var internals = {}; - -function MigrationLink (driver, internals) { - this.migrator = require('./migrator.js')( - driver, - internals.migrationsDir, - null, - internals - ); - this.links = []; -} - -MigrationLink.prototype = { - link: function (partialName) { - this.links.push(partialName); - }, - - migrate: function (partialName) { - var reset = !internals.notransactions; - - internals.notransactions = true; - - return new Promise(function (resolve, reject) { - this.migrator.up(partialName, function (err) { - if (reset) { - internals.notransactions = false; - } - - if (err) { - reject(err); - } else { - resolve(err); - } - }); - }); - }, - - process: function () { - var reset = !internals.notransactions; - - internals.notransactions = true; - - return new Promise( - function (resolve, reject) { - var keys = Object.keys(this.links); - var i = 0; - - var migrate = function (i) { - if (i < keys.length) { - if (reset) { - internals.notransactions = false; - } - - resolve(); - this.clear(); - } - - this.migrator.up( - { - destination: this.links[keys[i]] - }, - function (err) { - if (err) { - if (reset) { - internals.notransactions = false; - } - - reject(err); - } else { - migrate(++i); - } - } - ); - }.bind(this); - - migrate(i); - }.bind(this) - ); - }, - - clear: function () { - this.links = []; - } -}; - -var Seeder = function (driver, seedsDir, versionControlled, intern) { - SeederInterface.extending = intern.interfaces.SeederInterface; - this.driver = dbmUtil.reduceToInterface(driver, SeederInterface); - this._driver = driver; - this.seedDir = seedsDir; - this.isVC = versionControlled; - - if (intern.linked === false) { - intern.linked = true; - this.migrationLink = new MigrationLink(driver, intern); - } - - internals = intern; - this.internals = intern; -}; - -Seeder.prototype = { - createSeedsTable: function (callback) { - this._driver.createSeedsTable(callback); - }, - - seed: function (argv, callback) { - if (this.isVC) { - this.up(argv, callback); - } else { - this._staticSeed(argv.destination, callback); - } - }, - - up: function (funcOrOpts, callback) { - if (dbmUtil.isFunction(funcOrOpts)) { - return funcOrOpts(this.driver, false, callback); - } else { - this.upToBy(funcOrOpts.destination, funcOrOpts.count, callback); - } - }, - - down: function (funcOrOpts, callback) { - if (dbmUtil.isFunction(funcOrOpts)) { - return funcOrOpts(this.driver, callback); - } else { - this.downToBy(funcOrOpts.count, callback); - } - }, - - /** - * Statically call two methods from a static seeder. - * - * First: cleanSeeds - * Second: seed - * - * It's highly recommended to not use version controlled seeders at the same - * time as statics. While the cleanSeeds most of the time, the user executes - * truncates or deletes on his database. A VC-Seeder can't detect this - * and thus the state keeps the same, even if all changes of the VC-Seeder - * are gone. - * - * Nevertheless, there is a possiblity to use static seeders together with - * VC-Seeder, if you keep everything organized well at least. - * - * If a single seed is linked with it's tables and databases which it got - * applied to, the state table of the seeds will automatically cleaned up. - * - */ - _staticSeed: function (partialName, callback) { - var self = this; - - return Seed.loadFromFilesystem(self.seedDir, self.internals, function ( - err, - allSeeds - ) { - if (err) { - callback(err); - return; - } - - var toRun = dbmUtil.filterUp(allSeeds, [], partialName); - - if (toRun.length === 0) { - log.info('No seeds to run'); - callback(null); - return; - } - - return Promise.resolve(toRun) - .each(function (seeder) { - log.verbose('preparing to run up seeder:', seeder.name); - - var setup = seeder.setup(); - if (typeof setup === 'function') { - setup(self.internals.seederOptions); - } - - return self._driver - .startMigration() - .catch(callback) - .then(function () { - return seeder.up(self.driver, true); - }); - }) - .then(self._driver.endMigration.bind(self.driver)) - .then(function () { - callback(); - }) - .catch(function (e) { - throw e; - }); - }); - }, - - writeSeedRecord: function (seed, callback) { - function onComplete (err) { - if (err) { - log.error(seed.name, err); - } else { - log.info('Processed seed', seed.name); - } - callback(err); - } - this._driver.addSeedRecord( - this.internals.matching + '/' + seed.name, - onComplete - ); - }, - - deleteSeedRecord: function (seed, callback) { - function onComplete (err) { - if (err) { - log.error(seed.name, err); - } else { - log.info('Processed seed', seed.name); - } - callback(err); - } - this._driver.deleteSeed( - this.internals.matching + '/' + seed.name, - function (err) { - if (!this.internals.matching) { - this._driver.deleteSeed(seed.name, onComplete); - } else { - onComplete.apply(err); - } - }.bind(this) - ); - }, - - upToBy: function (partialName, count, callback) { - var self = this; - - return Seed.loadFromFilesystem(self.seedDir, self.internals, function ( - err, - allMigrations - ) { - if (err) { - callback(err); - return; - } - - return Seed.loadFromDatabase( - self.seedDir, - self._driver, - self.internals, - function (err, completedSeeds) { - if (err) { - callback(err); - return; - } - var toRun = dbmUtil.filterUp( - allMigrations, - completedSeeds, - partialName, - count - ); - - if (toRun.length === 0) { - log.info('No seeds to run'); - callback(null); - return; - } - - return Promise.resolve(toRun) - .each(function (seeder) { - log.verbose('preparing to run up seeder:', seeder.name); - - return self._driver - .startMigration() - .then(function () { - var setup = seeder.setup(); - if (typeof setup === 'function') { - setup(self.internals.seederOptions, self.migrationLink); - } - - return self.up(seeder.up.bind(seeder)); - }) - .then(function () { - if (self.seedLink && self.seedLink.links.length) { - log.info('Calling linked migrations'); - - return self.seedLink.process(self.migrationLink); - } - }) - .then(function () { - return Promise.promisify( - self.writeSeedRecord.bind(self) - )(seeder); - }) - .then(self._driver.endMigration.bind(self.driver)) - .catch(function (e) { - throw e; - }); - }) - .then(function () { - callback(); - }); - } - ); - }); - }, - - downToBy: function (count, callback) { - var self = this; - Seed.loadFromDatabase(self.seedDir, self._driver, self.internals, function ( - err, - completedSeeds - ) { - if (err) { - return callback(err); - } - - var toRun = dbmUtil.filterDown(completedSeeds, count); - - if (toRun.length === 0) { - log.info('No migrations to run'); - callback(null); - return; - } - - return Promise.resolve(toRun) - .each(function (seeder) { - log.verbose('preparing to run down seeder:', seeder.name); - - return self._driver - .startMigration() - .then(function () { - var setup = seeder.setup(); - - if (typeof setup === 'function') { - setup(self.internals.seederOptions, self.migrationLink); - } - - return self.down(seeder.down.bind(seeder)); - }) - .then(function () { - if (self.seedLink && self.seedLink.links.length) { - log.info('Calling linked migrations'); - - return self.seedLink.process(); - } - }) - .then(function () { - return Promise.promisify( - self.deleteSeedRecord.bind(self) - )(seeder); - }) - .then(self._driver.endMigration.bind(self.driver)) - .catch(function (e) { - throw e; - }); - }) - .then(function () { - callback(); - }); - }); - } -}; -module.exports = Seeder; diff --git a/lib/skeleton.js b/lib/skeleton.js deleted file mode 100644 index 0adca10b..00000000 --- a/lib/skeleton.js +++ /dev/null @@ -1,173 +0,0 @@ -var path = require('path'); -var inflection = require('inflection'); -var Promise = require('bluebird'); -var lpad = require('db-migrate-shared').util.lpad; -var Class = require('./class'); - -function isPromise (probe) { - return ( - probe instanceof Promise || - (probe && - probe.then && - probe.constructor && - probe.constructor.name === 'Promise') - ); -} - -function formatPath (dir, name) { - return path.join(dir, name); -} - -function formatName (title, date) { - return formatDate(date) + '-' + formatTitle(title); -} - -function formatDate (date) { - return [ - date.getUTCFullYear(), - lpad(date.getUTCMonth() + 1, '0', 2), - lpad(date.getUTCDate(), '0', 2), - lpad(date.getUTCHours(), '0', 2), - lpad(date.getUTCMinutes(), '0', 2), - lpad(date.getUTCSeconds(), '0', 2) - ].join(''); -} - -function formatTitle (title) { - return inflection.dasherize(title); -} - -function parseDate (name) { - var date = new Date(); - var match = name.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})-[^.]+/); - date.setUTCFullYear(match[1]); - date.setUTCDate(match[3]); - date.setUTCMonth(match[2] - 1); - date.setUTCHours(match[4]); - date.setUTCMinutes(match[5]); - date.setUTCSeconds(match[6]); - return date; -} - -function parseTitle (name) { - var match = name.match(/\d{14}-([^.]+)/); - var dashed = match[1]; - return inflection.humanize(dashed, true); -} - -var Skeleton = Class.extend({ - init: function (intern) { - this.internals = intern; - }, - - _up: function () { - var params = arguments; - - var cbExecuted = false; - - return new Promise( - function (resolve, reject) { - var migration; - var r = function (err) { - if (cbExecuted === false) { - cbExecuted = true; - - if (err) { - reject(err); - } else { - resolve(); - } - } - }; - - params[params.length++] = r; - - migration = require(this.path).up.apply(this, params); - - if (migration === null) migration = Promise.resolve(); - if (isPromise(migration)) { - migration - .then(function () { - if (cbExecuted === false) { - cbExecuted = true; - resolve(); - } - }) - .catch(function (err) { - if (cbExecuted === false) { - cbExecuted = true; - reject(err); - } - }); - } - }.bind(this) - ); - }, - - _down: function () { - var params = arguments; - var cbExecuted = false; - - return new Promise( - function (resolve, reject) { - var migration; - var r = function (err) { - if (cbExecuted === false) { - cbExecuted = true; - - if (err) { - reject(err); - } else { - resolve(); - } - } - }; - - params[params.length++] = r; - migration = require(this.path).down.apply(this, params); - - if (migration === null) migration = Promise.resolve(); - if (isPromise(migration)) { - migration - .then(function () { - if (cbExecuted === false) { - cbExecuted = true; - resolve(); - } - }) - .catch(function (err) { - if (cbExecuted === false) { - cbExecuted = true; - reject(err); - } - }); - } - }.bind(this) - ); - }, - - up: function (db) { - return this._up(db); - }, - - down: function (db) { - return this._down(db); - }, - - setup: function () { - return require(this.path).setup; - }, - - parseName: function (path) { - var match = path.match(/(\d{14}-[^.]+)(?:\.*?)?/); - return match[1]; - }, - - parseTitle: parseTitle, - parseDate: parseDate, - formatTitle: formatTitle, - formatPath: formatPath, - formatName: formatName -}); - -module.exports = Skeleton; diff --git a/lib/migration.js b/lib/template.js similarity index 60% rename from lib/migration.js rename to lib/template.js index d94ae5e2..b0629664 100644 --- a/lib/migration.js +++ b/lib/template.js @@ -1,63 +1,17 @@ -var fs = require('fs'); -var path = require('path'); -var log = require('db-migrate-shared').log; -var Skeleton = require('./skeleton'); -var Promise = require('bluebird'); +'use strict'; -var filesRegEx = /\.js$/; +const File = require('./file.js'); +const dbmUtil = require('db-migrate-shared').util; -var Migration = Skeleton.extend({ - init: function () { - if (arguments.length >= 3) { - this.title = arguments[0]; - this.date = arguments[2]; - this.name = this.formatName(this.title, this.date); - this.path = this.formatPath(arguments[1], this.name); - this.templateType = arguments[3]; - this.internals = arguments[4]; - } else if (arguments.length === 2) { - this.path = arguments[0]; - this.name = this.parseName(this.path); - this.date = this.parseDate(this.name); - this.title = this.parseTitle(this.name); - this.internals = arguments[1]; - } - - this._super(this.internals); - } -}); - -Migration.registerHook = function (Plugin, internals) { - var plugin = Plugin.hook('migrator:migration:hook:require'); - internals.parser = internals.parser || { - filesRegEx: filesRegEx, - extensions: 'js' - }; - - if (!plugin) { - return Promise.resolve(null); - } - - return Promise.resolve(plugin) - .map(function (plugin) { - return plugin['migrator:migration:hook:require'](); - }) - .each(function (parser) { - if (parser && parser.extensions) { - internals.parser.extensions = - internals.parser.extensions + '|' + parser.extensions; - } - }) - .then(function () { - internals.parser.filesRegEx = new RegExp( - '\\.(' + internals.parser.extensions + ')$' - ); - - return internals.parser; - }); +const Template = function () { + this.templateType = arguments[3]; + // this is a tweak for node 4 this can be translated + // to ...arguments instead in upcoming node versions + const Comp = File.bind.apply(File, [null].concat(dbmUtil.toArray(arguments))); + this.file = new Comp(); }; -Migration.prototype.defaultCoffeeTemplate = function () { +Template.prototype.defaultCoffeeTemplate = function () { return [ "'use strict';", '', @@ -80,7 +34,7 @@ Migration.prototype.defaultCoffeeTemplate = function () { ].join('\n'); }; -Migration.prototype.defaultJsTemplate = function () { +Template.prototype.defaultJsTemplate = function () { return [ "'use strict';", '', @@ -113,11 +67,11 @@ Migration.prototype.defaultJsTemplate = function () { ].join('\n'); }; -Migration.prototype.defaultSqlTemplate = function () { +Template.prototype.defaultSqlTemplate = function () { return '/* Replace with your SQL commands */'; }; -Migration.prototype.sqlFileLoaderTemplate = function () { +Template.prototype.sqlFileLoaderTemplate = function () { return [ "'use strict';", '', @@ -141,7 +95,7 @@ Migration.prototype.sqlFileLoaderTemplate = function () { '', 'exports.up = function(db) {', " var filePath = path.join(__dirname, 'sqls', '" + - this.name.replace('.js', '') + + this.file.name.replace('.js', '') + "-up.sql');", ' return new Promise( function( resolve, reject ) {', " fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){", @@ -158,7 +112,7 @@ Migration.prototype.sqlFileLoaderTemplate = function () { '', 'exports.down = function(db) {', " var filePath = path.join(__dirname, 'sqls', '" + - this.name.replace('.js', '') + + this.file.name.replace('.js', '') + "-down.sql');", ' return new Promise( function( resolve, reject ) {', " fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){", @@ -180,7 +134,7 @@ Migration.prototype.sqlFileLoaderTemplate = function () { ].join('\n'); }; -Migration.prototype.sqlFileLoaderIgnoreOnInitTemplate = function () { +Template.prototype.sqlFileLoaderIgnoreOnInitTemplate = function () { return [ "'use strict';", '', @@ -206,7 +160,7 @@ Migration.prototype.sqlFileLoaderIgnoreOnInitTemplate = function () { '', 'exports.up = function(db, callback) {', " var filePath = path.join(__dirname + '/sqls/" + - this.name.replace('.js', '') + + this.file.name.replace('.js', '') + "-up.sql');", ' if (!ignoreOnInit) {', ' return new Promise( function( resolve, reject ) {', @@ -229,7 +183,7 @@ Migration.prototype.sqlFileLoaderIgnoreOnInitTemplate = function () { '', 'exports.down = function(db, callback) {', " var filePath = path.join(__dirname + '/sqls/" + - this.name.replace('.js', '') + + this.file.name.replace('.js', '') + "-down.sql');", ' return new Promise( function( resolve, reject ) {', " fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){", @@ -251,7 +205,7 @@ Migration.prototype.sqlFileLoaderIgnoreOnInitTemplate = function () { ].join('\n'); }; -Migration.prototype.coffeeSqlFileLoaderTemplate = function () { +Template.prototype.coffeeSqlFileLoaderTemplate = function () { return [ "'use strict';", '', @@ -273,7 +227,7 @@ Migration.prototype.coffeeSqlFileLoaderTemplate = function () { '', 'exports.up = (db, callback) ->', ' filePath = path.join "#{__dirname}/sqls/' + - this.name.replace('.coffee', '') + + this.file.name.replace('.coffee', '') + '-up.sql"', " fs.readFile filePath, {encoding: 'utf-8'}, (err,data) ->", ' return callback err if err', @@ -282,7 +236,7 @@ Migration.prototype.coffeeSqlFileLoaderTemplate = function () { '', 'exports.down = (db, callback) ->', ' filePath = path.join "#{__dirname}/sqls/' + - this.name.replace('.coffee', '') + + this.file.name.replace('.coffee', '') + '-down.sql"', " fs.readFile filePath, {encoding: 'utf-8'}, (err,data) ->", @@ -293,7 +247,7 @@ Migration.prototype.coffeeSqlFileLoaderTemplate = function () { ].join('\n'); }; -Migration.TemplateType = { +Template.TemplateType = { DEFAULT_JS: 0, DEFAULT_SQL: 1, SQL_FILE_LOADER: 2, @@ -302,72 +256,27 @@ Migration.TemplateType = { SQL_FILE_LOADER_IGNORE_ON_INIT: 5 }; -Migration.prototype.getTemplate = function () { +Template.prototype.getTemplate = function () { switch (this.templateType) { - case Migration.TemplateType.DEFAULT_SQL: + case Template.TemplateType.DEFAULT_SQL: return this.defaultSqlTemplate(); - case Migration.TemplateType.SQL_FILE_LOADER: + case Template.TemplateType.SQL_FILE_LOADER: return this.sqlFileLoaderTemplate(); - case Migration.TemplateType.DEFAULT_COFFEE: + case Template.TemplateType.DEFAULT_COFFEE: return this.defaultCoffeeTemplate(); - case Migration.TemplateType.COFFEE_SQL_FILE_LOADER: + case Template.TemplateType.COFFEE_SQL_FILE_LOADER: return this.coffeeSqlFileLoaderTemplate(); - case Migration.TemplateType.SQL_FILE_LOADER_IGNORE_ON_INIT: + case Template.TemplateType.SQL_FILE_LOADER_IGNORE_ON_INIT: return this.sqlFileLoaderIgnoreOnInitTemplate(); - case Migration.TemplateType.DEFAULT_JS: + case Template.TemplateType.DEFAULT_JS: /* falls through */ default: return this.defaultJsTemplate(); } }; -Migration.prototype.write = function (callback) { - fs.writeFile(this.path, this.getTemplate(), callback); -}; - -Migration.loadFromFilesystem = function (dir, internals, callback) { - log.verbose('loading migrations from dir', dir); - fs.readdir(dir, function (err, files) { - if (err) { - callback(err); - return; - } - files = files.filter(function (file) { - return internals.parser.filesRegEx.test(file); - }); - var migrations = files.sort().map(function (file) { - return new Migration(path.join(dir, file), internals); - }); - callback(null, migrations); - }); -}; - -Migration.loadFromDatabase = function (dir, driver, internals, callback) { - if (internals.ignoreCompleted) { - callback(null, []); - } else { - log.verbose('loading migrations from database'); - driver.allLoadedMigrations(function (err, dbResults) { - if (err && !internals.dryRun) { - callback(err); - return; - } else if (err && internals.dryRun) { - dbResults = []; - } - var migrations = dbResults - .filter(function (result) { - return ( - result.name.substr(0, result.name.lastIndexOf('/')) === - internals.matching - ); - }) - .map(function (result) { - return new Migration(path.join(dir, result.name), internals); - }); - - callback(null, migrations); - }); - } +Template.prototype.write = function (callback) { + return this.file.write(this.getTemplate()).nodeify(callback); }; -module.exports = Migration; +module.exports = Template; diff --git a/lib/temputils.js b/lib/temputils.js new file mode 100644 index 00000000..4d3013e6 --- /dev/null +++ b/lib/temputils.js @@ -0,0 +1,54 @@ +'use strict'; + +function isPromise (probe) { + return ( + probe instanceof Promise || + (probe && + probe.then && + probe.constructor && + probe.constructor.name === 'Promise') + ); +} + +function maybePromised (context, action, params) { + let cbExecuted = false; + + return new Promise((resolve, reject) => { + const r = err => { + if (cbExecuted === false) { + cbExecuted = true; + + if (err) { + reject(err); + } else { + resolve(); + } + } + }; + + params[params.length++] = r; + + if (typeof action === 'function') action = action.apply(context, params); + if (action === null) action = Promise.resolve(); + + if (isPromise(action)) { + action + .then(() => { + if (cbExecuted === false) { + cbExecuted = true; + resolve(); + } + }) + .catch(err => { + if (cbExecuted === false) { + cbExecuted = true; + reject(err); + } + }); + } + }); +} + +module.exports = { + maybePromised: maybePromised +}; diff --git a/lib/transitions/transitioner.js b/lib/transitions/transitioner.js index ea19caa1..d26c861d 100644 --- a/lib/transitions/transitioner.js +++ b/lib/transitions/transitioner.js @@ -1,4 +1,4 @@ -var Migration = require('../migration.js'); +var Migration = require('../file.js'); var tryRequire = require('./try-require.js'); var updateVersion = require('./update-version.js'); var ask = require('./ask.js'); diff --git a/lib/walker.js b/lib/walker.js new file mode 100644 index 00000000..603ec278 --- /dev/null +++ b/lib/walker.js @@ -0,0 +1,221 @@ +'use strict'; + +const dbmUtil = require('db-migrate-shared').util; +const log = require('db-migrate-shared').log; +const Promise = require('bluebird'); +const File = require('./file.js'); + +// Not sure what will happen to this yet +function SeedLink (driver, internals) { + this.seeder = require('./seeder.js')( + driver, + internals.argv['vcseeder-dir'], + true, + internals + ); + this.internals = internals; + this.links = []; +} + +const INTERFACES = { + migration: require('./interface/migratorInterface.js'), + seed: require('./interface/seederInterface.js'), + 'static-seed': require('./interface/seederInterface.js') +}; + +const Walker = function (driver, directory, mode, intern, prefix) { + this.driver = dbmUtil.reduceToInterface(driver, INTERFACES[prefix]); + this._driver = driver; + Promise.promisifyAll(this._driver); + this.directory = directory; + this.internals = intern; + this.mode = mode; + + if (!this.mode) this.prefix = `static-${prefix}`; + else this.prefix = prefix; + + this.title = `[${prefix}] `; + + // keep it until we decide how we do the cross linking + if (intern.linked === false) { + this.seedLink = new SeedLink(driver, intern); + intern.linked = true; + } +}; + +Walker.prototype = { + createMigrationsTable: function (callback) { + this._driver.createMigrationsTable(callback); + }, + + writeMigrationRecord: function (migration, callback) { + const onComplete = err => { + if (err) { + log.error(this.title + migration.name, err); + } else { + log.info(this.title + 'Processed', migration.name); + } + callback(err); + }; + this._driver.addMigrationRecord( + this.internals.matching + '/' + migration.name, + onComplete + ); + }, + + deleteMigrationRecord: function (migration, callback) { + const onComplete = err => { + if (err) { + log.error(this.title + migration.name, err); + } else { + log.info(this.title + 'Processed', migration.name); + } + callback(err); + }; + this._driver.deleteMigration( + this.internals.matching + '/' + migration.name, + function (err) { + if (!this.internals.matching) { + this._driver.deleteMigration(migration.name, onComplete); + } else { + onComplete.apply(err); + } + }.bind(this) + ); + }, + + sync: function (options, callback) { + return File.loadFromDatabase( + this.directory, + this.prefix, + this._driver, + this.internals + ) + .then(completedFiles => { + const mode = dbmUtil.syncMode(completedFiles, options.destination); + if (mode === 1) { + log.info(this.title + 'Syncing upwards.'); + return this.up(options); + } else { + log.info(this.title + 'Syncing downwards.'); + return this.down(options); + } + }) + .nodeify(callback); + }, + + up: function (options, callback) { + const partialName = options.destination; + const count = options.count; + const files = Promise.all([ + File.loadFromFileystem(this.directory, this.prefix, this.internals), + File.loadFromDatabase( + this.directory, + this.prefix, + this._driver, + this.internals + ) + ]).spread((allFiles, completedFiles) => { + const toRun = dbmUtil.filterUp( + allFiles, + completedFiles, + partialName, + count + ); + + if (toRun.length === 0) { + log.info(this.title + 'Nothing to run'); + } + + if (this.internals.check) { + const toRunNames = toRun.map(migration => migration.name); + log.info(this.title + 'run:', toRunNames); + return toRunNames; + } + + return toRun; + }); + + if (this.internals.check) { + return files.nodeify(callback); + } + + return files + .each(file => { + log.verbose(this.title + 'preparing to run up:', file.name); + const _meta = file.get()._meta || {}; + const version = _meta.version || 1; + return require(`./executors/versioned/v${version}`).up( + this, + this.driver, + file + ); + }) + .nodeify(callback); + }, + + down: function (options, callback) { + const partialName = options.destination; + const count = options.count; + + const files = File.loadFromDatabase( + this.directory, + this.prefix, + this._driver, + this.internals + ).then(completedFiles => { + const toRun = dbmUtil.filterDown(completedFiles, partialName, count); + + if (toRun.length === 0) { + log.info(this.title + 'Nothing to run'); + } + + if (this.internals.check) { + const toRunNames = toRun.map(migration => migration.name); + log.info(this.title + 'run:', toRunNames); + return toRunNames; + } + + return toRun; + }); + + if (this.internals.check) { + return files.nodeify(callback); + } + + return files + .each(file => { + log.verbose(this.title + 'preparing to run down:', file.name); + const _meta = file.get()._meta || {}; + const version = _meta.version || 1; + return require(`./executors/versioned/v${version}`).down( + this, + this.driver, + file + ); + }) + .nodeify(callback); + }, + + check: function (options, callback) { + return Promise.all([ + File.loadFromDatabase( + this.directory, + this.prefix, + this._driver, + this.internals + ), + File.loadFromFileystem(this.directory, this.prefix, this.internals) + ]) + .spread((completedFiles, allFiles) => { + // Requires pr to export filterCompleted from db-migrate-shared + const toRun = dbmUtil.filterCompleted(allFiles, completedFiles); + + log.info('Files to run:', toRun.map(migration => migration.name)); + return toRun; + }) + .nodeify(callback); + } +}; + +module.exports = Walker; diff --git a/test/integration/create_test.js b/test/integration/create_test.js index dd18106c..11b63a45 100644 --- a/test/integration/create_test.js +++ b/test/integration/create_test.js @@ -1,28 +1,30 @@ -var Code = require('code'); -var Lab = require('lab'); -var lab = (exports.lab = Lab.script()); -var fs = require('fs'); -var path = require('path'); -var cp = require('child_process'); -var dbmUtil = require('db-migrate-shared').util; +'use strict'; -var rmdir = require('rimraf'); +const Code = require('code'); +const Lab = require('lab'); +const lab = (exports.lab = Lab.script()); +const fs = require('fs'); +const path = require('path'); +const cp = require('child_process'); +const dbmUtil = require('db-migrate-shared').util; + +const rmdir = require('rimraf'); function wipeMigrations (callback) { - var dir = path.join(__dirname, 'migrations'); + const dir = path.join(__dirname, 'migrations'); rmdir(dir, callback); } function dbMigrate () { - var args = dbmUtil.toArray(arguments); - var dbm = path.join(__dirname, '..', '..', 'bin', 'db-migrate'); + const args = dbmUtil.toArray(arguments); + const dbm = path.join(__dirname, '..', '..', 'bin', 'db-migrate'); args.unshift(dbm); return cp.spawn('node', args, { cwd: __dirname }); } lab.experiment('create', function () { lab.experiment('without a migration directory', function () { - var exitCode; + let exitCode; lab.before(function (done) { wipeMigrations(function (err) { @@ -40,15 +42,15 @@ lab.experiment('create', function () { }); lab.test('will create a new migration directory', function (done) { - var stats = fs.statSync(path.join(__dirname, 'migrations')); + const stats = fs.statSync(path.join(__dirname, 'migrations')); Code.expect(stats.isDirectory()).to.be.true(); done(); }); lab.test('will create a new migration', function (done) { - var files = fs.readdirSync(path.join(__dirname, 'migrations')); + const files = fs.readdirSync(path.join(__dirname, 'migrations')); Code.expect(files.length).to.equal(1); - var file = files[0]; + const file = files[0]; Code.expect(file).to.match(/first-migration\.js$/); done(); }); @@ -57,10 +59,10 @@ lab.experiment('create', function () { lab.experiment( 'with sql-file option set to true from config file', function () { - var exitCode; + let exitCode; lab.before(function (done) { - var configOption = path.join( + const configOption = path.join( '--config=', __dirname, 'database_with_sql_file.json' @@ -84,27 +86,29 @@ lab.experiment('create', function () { }); lab.test('will create a new migration', function (done) { - var files = fs.readdirSync(path.join(__dirname, 'migrations')); + const files = fs.readdirSync(path.join(__dirname, 'migrations')); - for (var i = 0; i < files.length; i++) { - var file = files[i]; - var stats = fs.statSync(path.join(__dirname, 'migrations', file)); - if (stats.isFile()) { Code.expect(file).to.match(/second-migration\.js$/); } + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const stats = fs.statSync(path.join(__dirname, 'migrations', file)); + if (stats.isFile()) { + Code.expect(file).to.match(/second-migration\.js$/); + } } done(); }); lab.test('will create a new migration/sqls directory', function (done) { - var stats = fs.statSync(path.join(__dirname, 'migrations/sqls')); + const stats = fs.statSync(path.join(__dirname, 'migrations/sqls')); Code.expect(stats.isDirectory()).to.be.true(); done(); }); lab.test('will create a new migration sql up file', function (done) { - var files = fs.readdirSync(path.join(__dirname, 'migrations/sqls')); + const files = fs.readdirSync(path.join(__dirname, 'migrations/sqls')); Code.expect(files.length).to.equal(2); - var file = files[1]; + const file = files[1]; Code.expect(file).to.match(/second-migration-up\.sql$/); done(); }); @@ -114,10 +118,10 @@ lab.experiment('create', function () { lab.experiment( 'with sql-file option set to true as a command parameter', function () { - var exitCode; + let exitCode; lab.before(function (done) { - var configOption = path.join('--sql-file'); + const configOption = path.join('--sql-file'); wipeMigrations(function (err) { Code.expect(err).to.not.exist(); dbMigrate('create', 'third migration', configOption).on( @@ -136,26 +140,28 @@ lab.experiment('create', function () { }); lab.test('will create a new migration', function (done) { - var files = fs.readdirSync(path.join(__dirname, 'migrations')); + const files = fs.readdirSync(path.join(__dirname, 'migrations')); - for (var i = 0; i < files.length; i++) { - var file = files[i]; - var stats = fs.statSync(path.join(__dirname, 'migrations', file)); - if (stats.isFile()) { Code.expect(file).to.match(/third-migration\.js$/); } + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const stats = fs.statSync(path.join(__dirname, 'migrations', file)); + if (stats.isFile()) { + Code.expect(file).to.match(/third-migration\.js$/); + } } done(); }); lab.test('will create a new migration/sqls directory', function (done) { - var stats = fs.statSync(path.join(__dirname, 'migrations/sqls')); + const stats = fs.statSync(path.join(__dirname, 'migrations/sqls')); Code.expect(stats.isDirectory()).to.be.true(); done(); }); lab.test('will create a new migration sql up file', function (done) { - var files = fs.readdirSync(path.join(__dirname, 'migrations/sqls')); + const files = fs.readdirSync(path.join(__dirname, 'migrations/sqls')); Code.expect(files.length).to.equal(2); - var file = files[1]; + const file = files[1]; Code.expect(file).to.match(/third-migration-up\.sql$/); done(); }); @@ -165,10 +171,10 @@ lab.experiment('create', function () { lab.experiment( 'with coffee-file option set to true from config file', function () { - var exitCode; + let exitCode; lab.before(function (done) { - var configOption = path.join( + const configOption = path.join( '--config=', __dirname, 'database_with_coffee_file.json' @@ -192,11 +198,11 @@ lab.experiment('create', function () { }); lab.test('will create a new coffeescript migration', function (done) { - var files = fs.readdirSync(path.join(__dirname, 'migrations')); + const files = fs.readdirSync(path.join(__dirname, 'migrations')); - for (var i = 0; i < files.length; i++) { - var file = files[i]; - var stats = fs.statSync(path.join(__dirname, 'migrations', file)); + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const stats = fs.statSync(path.join(__dirname, 'migrations', file)); if (stats.isFile()) { Code.expect(file).to.match(/fourth-migration\.coffee$/); } @@ -210,10 +216,10 @@ lab.experiment('create', function () { lab.experiment( 'with coffee-file option set to true as a command parameter', function () { - var exitCode; + let exitCode; lab.before(function (done) { - var configOption = path.join('--coffee-file'); + const configOption = path.join('--coffee-file'); wipeMigrations(function (err) { Code.expect(err).to.not.exist(); dbMigrate('create', 'fifth migration', configOption).on( @@ -232,11 +238,11 @@ lab.experiment('create', function () { }); lab.test('will create a new coffeescript migration', function (done) { - var files = fs.readdirSync(path.join(__dirname, 'migrations')); + const files = fs.readdirSync(path.join(__dirname, 'migrations')); - for (var i = 0; i < files.length; i++) { - var file = files[i]; - var stats = fs.statSync(path.join(__dirname, 'migrations', file)); + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const stats = fs.statSync(path.join(__dirname, 'migrations', file)); if (stats.isFile()) { Code.expect(file).to.match(/fifth-migration\.coffee$/); } @@ -249,20 +255,20 @@ lab.experiment('create', function () { lab.experiment( 'with sql-file and a bad migration, causes an exit', function () { - var exitCode; + let exitCode; lab.before(function (done) { - var configOption = path.join('--sql-file'); + const configOption = path.join('--sql-file'); wipeMigrations(function (err) { Code.expect(err).to.not.exist(); dbMigrate('create', 'sixth migration', configOption).on( 'exit', function () { - var files = fs.readdirSync(path.join(__dirname, 'migrations')); + const files = fs.readdirSync(path.join(__dirname, 'migrations')); - for (var i = 0; i < files.length; i++) { - var file = files[i]; - var stats = fs.statSync( + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const stats = fs.statSync( path.join(__dirname, 'migrations', file) ); @@ -288,12 +294,14 @@ lab.experiment('create', function () { }); lab.test('did create the new migration', function (done) { - var files = fs.readdirSync(path.join(__dirname, 'migrations')); + const files = fs.readdirSync(path.join(__dirname, 'migrations')); - for (var i = 0; i < files.length; i++) { - var file = files[i]; - var stats = fs.statSync(path.join(__dirname, 'migrations', file)); - if (stats.isFile()) { Code.expect(file).to.match(/sixth-migration\.js$/); } + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const stats = fs.statSync(path.join(__dirname, 'migrations', file)); + if (stats.isFile()) { + Code.expect(file).to.match(/sixth-migration\.js$/); + } } done(); diff --git a/test/migration_test.js b/test/migration_test.js index daac3a68..8b51d0dc 100644 --- a/test/migration_test.js +++ b/test/migration_test.js @@ -1,17 +1,20 @@ -var Code = require('code'); -var Lab = require('lab'); -var proxyquire = require('proxyquire').noPreserveCache(); -var lab = (exports.lab = Lab.script()); -var Migration = require('../lib/migration.js'); - -var date = createDateForTest(); -var dateString = '20140220143050'; -var dirName = '/directory/name/'; -var fileNameNoExtension = 'filename'; -var fileName = 'filename.js'; -var templateType = Migration.TemplateType.SQL_FILE_LOADER; - -var internals = {}; +'use strict'; + +const Code = require('code'); +const Lab = require('lab'); +const proxyquire = require('proxyquire').noPreserveCache(); +const lab = (exports.lab = Lab.script()); +const Migration = require('../lib/file.js'); +const Template = require('../lib/template.js'); + +const date = createDateForTest(); +const dateString = '20140220143050'; +const dirName = '/directory/name/'; +const fileNameNoExtension = 'filename'; +const fileName = 'filename.js'; +const templateType = Template.TemplateType.SQL_FILE_LOADER; + +let internals = {}; internals.migrationTable = 'migrations'; lab.experiment('migration', function () { @@ -32,7 +35,7 @@ lab.experiment('migration', function () { function asModule () { lab.test('should create migration', function (done) { - var dbmigrate = stubApiInstance(true, {}, {}); + const dbmigrate = stubApiInstance(true, {}, {}); dbmigrate.setConfigParam('_', []); dbmigrate.create('migrationName').then(done); @@ -44,7 +47,7 @@ function newMigrationObject () { 'with 2 parameters as the complete filepath', function () { - var migration = new Migration( + const migration = new Migration( dirName + dateString + '-' + fileName, internals ); @@ -91,7 +94,7 @@ function newMigrationObject () { ); lab.experiment('with 3 parameters', function () { - var migration = new Migration(fileName, dirName, date); + const migration = new Migration(fileName, dirName, date); lab.test('should have title set', function (done) { Code.expect(migration.title).to.equal(fileName); @@ -122,7 +125,7 @@ function newMigrationObject () { }); lab.experiment('with 5 parameters', function () { - var migration = new Migration( + const migration = new Template( fileName, dirName, date, @@ -131,22 +134,22 @@ function newMigrationObject () { ); lab.test('should have title set', function (done) { - Code.expect(migration.title).to.equal(fileName); + Code.expect(migration.file.title).to.equal(fileName); done(); }); lab.test('should have date set', function (done) { - Code.expect(migration.date).to.equal(date); + Code.expect(migration.file.date).to.equal(date); done(); }); lab.test('should have name set', function (done) { - Code.expect(migration.name).to.equal(dateString + '-' + fileName); + Code.expect(migration.file.name).to.equal(dateString + '-' + fileName); done(); }); lab.test('should have path set', function (done) { - Code.expect(migration.path).to.equal( + Code.expect(migration.file.path).to.equal( dirName + dateString + '-' + fileName ); done(); @@ -164,13 +167,13 @@ function getTemplate () { 'when template type is not set', function () { - var migration = new Migration(fileName, dirName, date, internals); + const migration = new Template(fileName, dirName, date, internals); lab.test( 'should return default javascript template', function (done) { - var actual = migration.getTemplate(); + const actual = migration.getTemplate(); Code.expect(actual).to.equal(migration.defaultJsTemplate()); done(); } @@ -180,11 +183,11 @@ function getTemplate () { lab.experiment('when template type is set', function () { lab.experiment('as sql file loader', function () { - var migration = new Migration( + const migration = new Template( fileName, dirName, date, - Migration.TemplateType.SQL_FILE_LOADER, + Template.TemplateType.SQL_FILE_LOADER, internals ); @@ -192,7 +195,7 @@ function getTemplate () { 'should return sql file loader template', function (done) { - var actual = migration.getTemplate(); + const actual = migration.getTemplate(); Code.expect(actual).to.equal(migration.sqlFileLoaderTemplate()); done(); } @@ -200,11 +203,11 @@ function getTemplate () { }); lab.experiment('as default sql', function () { - var migration = new Migration( + const migration = new Template( fileName, dirName, date, - Migration.TemplateType.DEFAULT_SQL, + Template.TemplateType.DEFAULT_SQL, internals ); @@ -212,7 +215,7 @@ function getTemplate () { 'should return default sql template', function (done) { - var actual = migration.getTemplate(); + const actual = migration.getTemplate(); Code.expect(actual).to.equal(migration.defaultSqlTemplate()); done(); } @@ -220,11 +223,11 @@ function getTemplate () { }); lab.experiment('as default coffee', function () { - var migration = new Migration( + const migration = new Template( fileName, dirName, date, - Migration.TemplateType.DEFAULT_COFFEE, + Template.TemplateType.DEFAULT_COFFEE, internals ); @@ -232,7 +235,7 @@ function getTemplate () { 'should return default coffee template', function (done) { - var actual = migration.getTemplate(); + const actual = migration.getTemplate(); Code.expect(actual).to.equal(migration.defaultCoffeeTemplate()); done(); } @@ -240,11 +243,11 @@ function getTemplate () { }); lab.experiment('as coffee sql loader', function () { - var migration = new Migration( + const migration = new Template( fileName, dirName, date, - Migration.TemplateType.COFFEE_SQL_FILE_LOADER, + Template.TemplateType.COFFEE_SQL_FILE_LOADER, internals ); @@ -252,7 +255,7 @@ function getTemplate () { 'should return default coffee template', function (done) { - var actual = migration.getTemplate(); + const actual = migration.getTemplate(); Code.expect(actual).to.equal(migration.coffeeSqlFileLoaderTemplate()); done(); } @@ -260,11 +263,11 @@ function getTemplate () { }); lab.experiment('as default javascript', function () { - var migration = new Migration( + const migration = new Template( fileName, dirName, date, - Migration.TemplateType.DEFAULT_JS, + Template.TemplateType.DEFAULT_JS, internals ); @@ -272,7 +275,7 @@ function getTemplate () { 'should return default sql template', function (done) { - var actual = migration.getTemplate(); + const actual = migration.getTemplate(); Code.expect(actual).to.equal(migration.defaultJsTemplate()); done(); } @@ -284,8 +287,8 @@ function getTemplate () { function stubApiInstance (isModule, stubs, options, callback) { delete require.cache[require.resolve('../api.js')]; delete require.cache[require.resolve('optimist')]; - var Mod = proxyquire('../api.js', stubs); - var plugins = {}; + const Mod = proxyquire('../api.js', stubs); + const plugins = {}; options = options || {}; options = Object.assign(options, { @@ -297,7 +300,7 @@ function stubApiInstance (isModule, stubs, options, callback) { } function createDateForTest () { - var date = new Date(); + const date = new Date(); date.setUTCFullYear(2014); date.setUTCDate('20'); date.setUTCMonth('01'); diff --git a/test/migrator_test.js b/test/migrator_test.js index 3ac29fc7..f3493fc1 100644 --- a/test/migrator_test.js +++ b/test/migrator_test.js @@ -1,24 +1,25 @@ -var Code = require('code'); -var Lab = require('lab'); -var proxyquire = require('proxyquire').noPreserveCache(); -var lab = (exports.lab = Lab.script()); +const Promise = require('bluebird'); +const Code = require('code'); +const Lab = require('lab'); +const proxyquire = require('proxyquire').noPreserveCache(); +const lab = (exports.lab = Lab.script()); lab.experiment('migrators', function () { lab.experiment('check', function () { lab.test('should return the migrations to be run', function (done) { - var completedMigration = { + const completedMigration = { name: '20180330020329-thisMigrationIsCompleted' }; - var uncompletedMigration = { + const uncompletedMigration = { name: '20180330020330-thisMigrationIsNotCompleted' }; - var Migrator = proxyquire('../lib/migrator.js', { - './migration': { - loadFromFilesystem: (migrationsDir, internals, cb) => { - return cb(null, [completedMigration, uncompletedMigration]); + const Migrator = proxyquire('../lib/walker.js', { + './file.js': { + loadFromFileystem: (migrationsDir, prefix, internals) => { + return Promise.resolve([uncompletedMigration]); }, - loadFromDatabase: (migrationsDir, driver, internals, cb) => { - return cb(null, [completedMigration]); + loadFromDatabase: (migrationsDir, prefix, driver, internals) => { + return Promise.resolve([completedMigration]); } } });