UNPKG

@sqb/migrator

Version:

Database migrator for SQB

145 lines (144 loc) 6.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DbMigrator = void 0; const strict_typed_events_1 = require("strict-typed-events"); const pg_migration_adapter_js_1 = require("./adapters/pg-migration-adapter.js"); const migration_adapter_js_1 = require("./migration-adapter.js"); const migration_package_js_1 = require("./migration-package.js"); const types_js_1 = require("./types.js"); class DbMigrator extends strict_typed_events_1.AsyncEventEmitter { async execute(options) { if (!options.connection.dialect) throw new TypeError(`You must provide connection.dialect`); const migrationPackage = await migration_package_js_1.MigrationPackage.load(options.migrationPackage); let minVersion = migrationPackage.migrations.reduce((a, m) => Math.min(a, m.version), Number.MAX_SAFE_INTEGER); if (minVersion === Number.MAX_SAFE_INTEGER) minVersion = 0; const maxVersion = migrationPackage.migrations.reduce((a, m) => Math.max(a, m.version), 0); const targetVersion = Math.min(options?.targetVersion || Number.MAX_SAFE_INTEGER, maxVersion); if (targetVersion && targetVersion < minVersion) { // noinspection ExceptionCaughtLocallyJS throw new Error(`Version mismatch. Target schema version (${targetVersion}) is lower than ` + `migration package min version (${minVersion})`); } let migrationAdapter; switch (options.connection.dialect) { case 'postgres': { migrationAdapter = await pg_migration_adapter_js_1.PgMigrationAdapter.create({ ...options, migrationPackage, }); break; } default: throw new TypeError(`Migration adapter for "${options.connection.dialect}" dialect is not implemented yet`); } let needBackup = false; try { if (migrationAdapter.version && migrationAdapter.version < minVersion - 1) { // noinspection ExceptionCaughtLocallyJS throw new Error(`This package can migrate starting from ${minVersion - 1} but current version is ${migrationAdapter.version}`); } const { migrations } = migrationPackage; // calculate total scripts; const total = migrations.reduce((i, x) => i + x.tasks.length, 0); needBackup = !!migrations.find(x => !!x.backup); await this.emitAsync('start'); let task; await migrationAdapter.lockSchema(); if (needBackup) { await this.emitAsync('backup'); await migrationAdapter.backupDatabase(); } // Execute migration tasks let migrationIndex = -1; for (const migration of migrations) { migrationIndex++; if (migration.version > targetVersion || migrationAdapter.version >= migration.version) continue; await this.emitAsync('migration-start', { migration, total: migrations.length, index: migrationIndex, }); for (let index = 0; index < migration.tasks.length; index++) { task = migration.tasks[index]; await this.emitAsync('task-start', { migration, task, total, index }); await migrationAdapter.update({ status: types_js_1.MigrationStatus.busy }); await migrationAdapter.writeEvent({ event: migration_adapter_js_1.MigrationAdapter.EventKind.started, version: migration.version, title: task.title, filename: task.filename, message: `Task "${task.title}" started`, }); try { await migrationAdapter.executeTask(migrationPackage, migration, task, { schema: options.connection.schema, ...options.scriptVariables, }); await migrationAdapter.writeEvent({ event: migration_adapter_js_1.MigrationAdapter.EventKind.success, version: migration.version, title: task.title, filename: task.filename, message: `Task "${task.title}" completed`, }); } catch (e) { await migrationAdapter.writeEvent({ event: migration_adapter_js_1.MigrationAdapter.EventKind.error, version: migration.version, title: task.title, filename: task.filename, message: String(e), details: e.message + '\n\n' + Object.keys(e) .filter(k => e[k] != null) .map(k => k + ': ' + e[k]) .join('\n'), }); // noinspection ExceptionCaughtLocallyJS throw e; } await this.emitAsync('task-finish', { migration, task, total, index, }); } await migrationAdapter.update({ version: migration.version, status: types_js_1.MigrationStatus.idle, }); await this.emitAsync('migration-finish', { migration, total: migrations.length, index: migrationIndex, }); } } catch (e) { if (needBackup) { await this.emitAsync('restore'); await migrationAdapter.restoreDatabase(); } throw e; } finally { try { await migrationAdapter.unlockSchema(); } finally { await migrationAdapter.close(); } } await this.emitAsync('finish'); return true; } } exports.DbMigrator = DbMigrator;