@sqb/migrator
Version:
Database migrator for SQB
145 lines (144 loc) • 6.67 kB
JavaScript
;
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;