import { Umzug , SequelizeStorage } from "umzug" ; import { Sequelize , Op } from "sequelize" ; import * as path from "path" ; export namespace Migrations { export type SequelizeConfig = { [ key ] : any ; } ; export type MigrationLogger = ( message : string , severity : string , data ? : any ) => void ; export async function migrate ( sequelizeConfig , sequelizeInstance , logger , logLevel ) { logger ( "running sequelize migrations" , "debug" ) ; const umzugs = await importMigrationsFromDirectory ( sequelizeConfig , sequelizeInstance , logger , logLevel ) ; await upAll ( umzugs ) ; } export async function upAll ( umzugs ) { for ( const umzug of umzugs ) await umzug . up ( ) ; } export async function downAll ( umzugs ) { for ( const umzug of umzugs ) await umzug . down ( { to : 0 } ) ; } export async function upOne ( umzugs ) { for ( const umzug of umzugs ) { const pendingMigrations = await umzug . pending ( ) ; if ( pendingMigrations . length === 0 ) continue ; await umzug . up ( { step : 1 } ) ; break ; } } export async function downOne ( umzugs , migrationName ) { let found = false ; for ( const umzug of umzugs ) { try { await umzug . down ( { migrations : [ migrationName ] } ) ; found = true ; break ; } catch ( error ) { if ( error . message . match ( /Unable to find migration/ ) ) { } else throw error ; } } if ( ! found ) { throw new Error ( `could not find migration \`${ migrationName }\` in the migration directories` ) ; } } export async function importMigrationsFromDirectory ( sequelizeConfig , sequelizeInstance , logger , logLevel ) { const dirs = Array . isArray ( sequelizeConfig . migrations ) ? sequelizeConfig . migrations : [ sequelizeConfig . migrations ] ; const umzugs = [ ] ; for ( const dir of dirs ) { const context = sequelizeInstance . getQueryInterface ( ) ; const umzug = new Umzug ( { storage : new SequelizeStorage ( { sequelize : sequelizeInstance } ) , context , migrations : { glob : [ "*.{js,ts}" , { cwd : dir , ignore : "**/*.d.ts" } ] , resolve : ( { path : filePath , context } ) => { const migration = require ( filePath ) . default ? require ( filePath ) . default : require ( filePath ) ; return { name : path . parse ( filePath ) . name , up : async ( ) => migration . up ( context , Sequelize ) , down : async ( ) => migration . down ( context , Sequelize ) , } ; } , } , logger : null , } ) ; function logUmzugEvent ( name , eventName ) { logger ( `[migration] ${ name } ${ eventName }` , logLevel ) ; } umzug . on ( "migrating" , ( ev ) => logUmzugEvent ( ev . name , "migrating" ) ) ; umzug . on ( "migrated" , ( ev ) => logUmzugEvent ( ev . name , "migrated" ) ) ; umzug . on ( "reverting" , ( ev ) => logUmzugEvent ( ev . name , "reverting" ) ) ; umzug . on ( "reverted" , ( ev ) => logUmzugEvent ( ev . name , "reverted" ) ) ; const model = context . sequelize . models . SequelizeMeta ; await model . sync ( ) ; const rows = await model . findAll ( { where : { name : { [ Op . or ] : [ { [ Op . like ] : "%.js" } , { [ Op . like ] : "%.ts" } ] } , } , } ) ; for ( const row of rows ) { const oldName = row . name ; const newName = oldName . replace ( /\.ts$/ , "" ) . replace ( /\.js$/ , "" ) ; await model . update ( { name : newName } , { where : { name : oldName } } ) ; logger ( `[migration] renamed migration '${ oldName }' to '${ newName }'` , logLevel ) ; } umzugs . push ( umzug ) ; } return umzugs ; } export function getInjectedQueryInterface ( sequelizeConfig , sequelizeInstance ) { const queryInterface = sequelizeInstance . getQueryInterface ( ) ; if ( sequelizeConfig . schema && sequelizeConfig . schema !== "public" && sequelizeConfig . dialect && sequelizeConfig . dialect === "postgres" ) { queryInterface . addColumn = injectSchema ( queryInterface . addColumn , sequelizeConfig . schema ) ; queryInterface . removeColumn = injectSchema ( queryInterface . removeColumn , sequelizeConfig . schema ) ; queryInterface . renameColumn = injectSchema ( queryInterface . renameColumn , sequelizeConfig . schema ) ; } return queryInterface ; } export function injectSchema ( original , schema ) { return function ( ) { if ( typeof arguments [ 0 ] === "string" ) { arguments [ 0 ] = { tableName : arguments [ 0 ] , schema } ; } else if ( ! arguments [ 0 ] . schema ) { arguments [ 0 ] . schema = schema ; } return original . apply ( this , arguments ) ; } ; } }