123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- const fs = require("fs")
- , moment = require("moment")
- var Utils = require(__dirname + "/utils")
- , Migration = require(__dirname + "/migration")
- , DataTypes = require(__dirname + "/data-types")
- module.exports = (function() {
- var Migrator = function(sequelize, options) {
- this.sequelize = sequelize
- this.options = Utils._.extend({
- path: __dirname + '/../migrations',
- from: null,
- to: null,
- logging: console.log,
- filesFilter: /\.js$/
- }, options || {})
- if (this.options.logging === true) {
- console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
- this.options.logging = console.log
- }
- if (this.options.logging == console.log) {
- // using just console.log will break in node < 0.6
- this.options.logging = function(s) { console.log(s) }
- }
- }
- Object.defineProperty(Migrator.prototype, "queryInterface", {
- get: function() {
- return this.sequelize.getQueryInterface()
- }
- })
- Migrator.prototype.migrate = function(options) {
- var self = this
- options = Utils._.extend({
- method: 'up'
- }, options || {})
- return new Utils.CustomEventEmitter(function(emitter) {
- self.getUndoneMigrations(function(err, migrations) {
- if (err) {
- emitter.emit('error', err)
- } else {
- var chainer = new Utils.QueryChainer()
- , from = migrations[0]
- if (options.method === 'down') {
- migrations.reverse()
- }
- if (migrations.length === 0) {
- self.options.logging("There are no pending migrations.")
- } else {
- self.options.logging("Running migrations...")
- }
- migrations.forEach(function(migration) {
- var migrationTime
- chainer.add(migration, 'execute', [options], {
- before: function(migration) {
- if (self.options.logging !== false) {
- self.options.logging(migration.filename)
- }
- migrationTime = process.hrtime()
- },
- after: function(migration) {
- migrationTime = process.hrtime(migrationTime)
- migrationTime = Math.round( (migrationTime[0] * 1000) + (migrationTime[1] / 1000000));
- if (self.options.logging !== false) {
- self.options.logging('Completed in ' + migrationTime + 'ms')
- }
- },
- success: function(migration, callback) {
- if (options.method === 'down') {
- deleteUndoneMigration.call(self, from, migration, callback)
- } else {
- saveSuccessfulMigration.call(self, from, migration, callback)
- }
- }
- })
- })
- chainer
- .runSerially({ skipOnError: true })
- .success(function() { emitter.emit('success', null) })
- .error(function(err) { emitter.emit('error', err) })
- }
- })
- }).run()
- }
- Migrator.prototype.getUndoneMigrations = function(callback) {
- var self = this
- var filterFrom = function(migrations, from, callback, options) {
- var result = migrations.filter(function(migration) { return migration.isAfter(from, options) })
- callback && callback(null, result)
- }
- var filterTo = function(migrations, to, callback, options) {
- var result = migrations.filter(function(migration) { return migration.isBefore(to, options) })
- callback && callback(null, result)
- }
- var migrationFiles = fs.readdirSync(this.options.path).filter(function(file) {
- return self.options.filesFilter.test(file)
- })
- var migrations = migrationFiles.map(function(file) {
- return new Migration(self, self.options.path + '/' + file)
- })
- migrations = migrations.sort(function(a,b){
- return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0])
- })
- if (this.options.from) {
- filterFrom(migrations, this.options.from, function(err, migrations) {
- if (self.options.to) {
- filterTo(migrations, self.options.to, callback)
- } else {
- callback && callback(null, migrations)
- }
- })
- } else {
- getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) {
- if (lastMigrationId) {
- filterFrom(migrations, lastMigrationId, function(err, migrations) {
- if (self.options.to) {
- filterTo(migrations, self.options.to, callback)
- } else {
- callback && callback(null, migrations)
- }
- }, { withoutEqual: true })
- } else {
- if (self.options.to) {
- filterTo(migrations, self.options.to, callback)
- } else {
- callback && callback(null, migrations)
- }
- }
- }).error(function(err) {
- callback && callback(err, null)
- })
- }
- }
- Migrator.prototype.findOrCreateSequelizeMetaDAO = function(syncOptions) {
- var self = this
- return new Utils.CustomEventEmitter(function(emitter) {
- var storedDAO = self.sequelize.daoFactoryManager.getDAO('SequelizeMeta')
- , SequelizeMeta = storedDAO
- if (!storedDAO) {
- SequelizeMeta = self.sequelize.define('SequelizeMeta', {
- from: DataTypes.STRING,
- to: DataTypes.STRING
- }, {
- timestamps: false
- })
- }
- // force sync when model has newly created or if syncOptions are passed
- if (!storedDAO || syncOptions) {
- SequelizeMeta
- .sync(syncOptions || {})
- .success(function() { emitter.emit('success', SequelizeMeta) })
- .error(function(err) { emitter.emit('error', err) })
- } else {
- emitter.emit('success', SequelizeMeta)
- }
- }).run()
- }
- /**
- * Explicitly executes one or multiple migrations.
- *
- * @param filename {String|Array} Absolute filename(s) of the migrations script
- * @param options {Object} Can contain three functions, before, after and success, which are executed before
- * or after each migration respectively, with one parameter, the migration.
- */
- Migrator.prototype.exec = function(filename, options) {
- var self = this;
- return new Utils.CustomEventEmitter(function(emitter) {
- var chainer = new Utils.QueryChainer()
- var addMigration = function(filename) {
- self.options.logging('Adding migration script at ' + filename)
- var migration = new Migration(self, filename)
- chainer.add(migration, 'execute', [{ method: 'up' }], {
- before: function(migration) {
- if (options && Utils._.isFunction(options.before)) {
- options.before.call(self, migration);
- }
- },
- after: function(migration) {
- if (options && Utils._.isFunction(options.after)) {
- options.after.call(self, migration);
- }
- },
- success: function(migration, callback) {
- if (options && Utils._.isFunction(options.success)) {
- options.success.call(self, migration);
- }
- callback();
- }
- })
- }
- if (Utils._.isArray(filename)) {
- Utils._.each(filename, function(f) {
- addMigration(f);
- })
- } else {
- addMigration(filename);
- }
- chainer
- .runSerially({ skipOnError: true })
- .success(function() { emitter.emit('success', null) })
- .error(function(err) { emitter.emit('error', err) })
- }).run()
- }
- // private
- var getLastMigrationFromDatabase = Migrator.prototype.getLastMigrationFromDatabase = function() {
- var self = this
- return new Utils.CustomEventEmitter(function(emitter) {
- self
- .findOrCreateSequelizeMetaDAO()
- .success(function(SequelizeMeta) {
- SequelizeMeta
- .find({ order: 'id DESC' })
- .success(function(meta) {
- emitter.emit('success', meta ? meta : null)
- })
- .error(function(err) {
- emitter.emit('error', err)
- })
- })
- .error(function(err) {
- emitter.emit('error', err)
- })
- }).run()
- }
- var getLastMigrationIdFromDatabase = Migrator.prototype.getLastMigrationIdFromDatabase = function() {
- var self = this
- return new Utils.CustomEventEmitter(function(emitter) {
- getLastMigrationFromDatabase
- .call(self)
- .success(function(meta) {
- emitter.emit('success', meta ? meta.to : null)
- })
- .error(function(err) {
- emitter.emit('error', err)
- })
- }).run()
- }
- var getFormattedDateString = Migrator.prototype.getFormattedDateString = function(s) {
- var result = null
- try {
- result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
- } catch(e) {
- throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
- }
- return result
- }
- var stringToDate = Migrator.prototype.stringToDate = function(s) {
- return moment(getFormattedDateString(s), "YYYYMMDDHHmmss")
- }
- var saveSuccessfulMigration = Migrator.prototype.saveSuccessfulMigration = function(from, to, callback) {
- var self = this
- self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
- SequelizeMeta
- .create({ from: from.migrationId, to: to.migrationId })
- .success(callback)
- })
- }
- var deleteUndoneMigration = Migrator.prototype.deleteUndoneMigration = function(from, to, callback) {
- var self = this
- self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
- SequelizeMeta
- .find({ where: { from: from.migrationId.toString(), to: to.migrationId.toString() } })
- .success(function(meta) {
- meta.destroy().success(callback)
- })
- })
- }
- return Migrator
- })()
|