aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/initializers/migrator.ts
blob: d72c60638a2c58a23290a1308446d3abb7bc5e6c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { waterfall, eachSeries } from 'async'
import * as fs from 'fs'
import * as path from 'path'
import * as Sequelize from 'sequelize'

import { database as db } from './database'
import { LAST_MIGRATION_VERSION } from './constants'
import { logger } from '../helpers'
import { ApplicationInstance } from '../models'

function migrate (finalCallback: (err: Error) => void) {
  waterfall([

    function checkApplicationTableExists (callback) {
      db.sequelize.getQueryInterface().showAllTables().asCallback(function (err, tables) {
        if (err) return callback(err)

        // No tables, we don't need to migrate anything
        // The installer will do that
        if (tables.length === 0) return finalCallback(null)

        return callback(null)
      })
    },

    function loadMigrationVersion (callback) {
      db.Application.loadMigrationVersion(callback)
    },

    function createMigrationRowIfNotExists (actualVersion, callback) {
      if (actualVersion === null) {
        db.Application.create({
          migrationVersion: 0
        }, function (err) {
          return callback(err, 0)
        })
      }

      return callback(null, actualVersion)
    },

    function abortMigrationIfNotNeeded (actualVersion, callback) {
      // No need migrations
      if (actualVersion >= LAST_MIGRATION_VERSION) return finalCallback(null)

      return callback(null, actualVersion)
    },

    function getMigrations (actualVersion, callback) {
      // If there are a new migration scripts
      logger.info('Begin migrations.')

      getMigrationScripts(function (err, migrationScripts) {
        return callback(err, actualVersion, migrationScripts)
      })
    },

    function doMigrations (actualVersion, migrationScripts, callback) {
      eachSeries(migrationScripts, function (entity: any, callbackEach) {
        executeMigration(actualVersion, entity, callbackEach)
      }, function (err) {
        if (err) return callback(err)

        logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
        return callback(null)
      })
    }
  ], finalCallback)
}

// ---------------------------------------------------------------------------

export {
  migrate
}

// ---------------------------------------------------------------------------

type GetMigrationScriptsCallback = (err: Error, filesToMigrate?: { version: string, script: string }[]) => void
function getMigrationScripts (callback: GetMigrationScriptsCallback) {
  fs.readdir(path.join(__dirname, 'migrations'), function (err, files) {
    if (err) return callback(err)

    const filesToMigrate = []

    files.forEach(function (file) {
      // Filename is something like 'version-blabla.js'
      const version = file.split('-')[0]
      filesToMigrate.push({
        version,
        script: file
      })
    })

    return callback(err, filesToMigrate)
  })
}

function executeMigration (actualVersion: number, entity: { version: string, script: string }, callback: (err: Error) => void) {
  const versionScript = parseInt(entity.version, 10)

  // Do not execute old migration scripts
  if (versionScript <= actualVersion) return callback(null)

  // Load the migration module and run it
  const migrationScriptName = entity.script
  logger.info('Executing %s migration script.', migrationScriptName)

  const migrationScript = require(path.join(__dirname, 'migrations', migrationScriptName))

  db.sequelize.transaction().asCallback(function (err, t) {
    if (err) return callback(err)

    const options = {
      transaction: t,
      queryInterface: db.sequelize.getQueryInterface(),
      sequelize: db.sequelize,
      Sequelize: Sequelize
    }
    migrationScript.up(options, function (err) {
      if (err) {
        t.rollback()
        return callback(err)
      }

      // Update the new migration version
      db.Application.updateMigrationVersion(versionScript, t, function (err) {
        if (err) {
          t.rollback()
          return callback(err)
        }

        t.commit().asCallback(callback)
      })
    })
  })
}