UNPKG

morpheus4j

Version:

Morpheus is a migration tool for Neo4j. It aims to be a simple and intuitive way to migrate your database.

107 lines 4.86 kB
"use strict"; /* eslint-disable no-await-in-loop */ Object.defineProperty(exports, "__esModule", { value: true }); exports.MigrationService = void 0; const constants_1 = require("../constants"); const errors_1 = require("../errors"); const types_1 = require("../types"); const logger_1 = require("./logger"); const utils_1 = require("./utils"); class MigrationService { repository; fileService; latestVersion; constructor(repository, fileService) { this.repository = repository; this.fileService = fileService; } async migrate(options) { try { let state = await this.repository.getMigrationState(); if (!state.baselineExists) { await this.initializeDatabase(); // Re-fetch state state = await this.repository.getMigrationState(); } this.latestVersion = state.latestMigration?.version; await this.validateMigrations(state.appliedMigrations); const pendingMigrations = await this.getPendingMigrations(); if (pendingMigrations.length === 0) { logger_1.Logger.info('Database is up to date'); return; } await (options?.dryRun ? this.previewMigrations(pendingMigrations) : this.applyMigrations(pendingMigrations, options?.transactionMode)); } catch (error) { throw new errors_1.MigrationError(`Migration failed: ${error instanceof Error ? error.message : String(error)}`); } } async applyMigrations(fileNames, transactionMode = types_1.TransactionMode.PER_MIGRATION) { for (const fileName of fileNames) { const migration = await this.fileService.prepareMigration(fileName); logger_1.Logger.info(`Executing migration: ${fileName}`); const startTime = Date.now(); // Use the new executeQueries method if (transactionMode === types_1.TransactionMode.PER_STATEMENT) { for (const statement of migration.statements) { await this.repository.executeQuery({ statement }); } } else { await this.repository.executeQueries(migration.statements.map((statement) => ({ statement }))); } const duration = Date.now() - startTime; const migrationNode = { checksum: (0, utils_1.generateChecksum)(migration.statements), description: migration.description, source: fileName, type: 'CYPHER', version: migration.version, }; await this.repository.applyMigration(migrationNode, this.latestVersion, duration); this.latestVersion = migration.version; } } async getPendingMigrations() { const files = await this.fileService.getFileNamesFromMigrationsFolder(); return files .filter((fileName) => { const version = this.fileService.getMigrationVersionFromFileName(fileName); return this.latestVersion === constants_1.BASELINE || this.fileService.compareVersions(version, this.latestVersion) > 0; }) .sort((a, b) => this.fileService.compareVersions(a, b)); } async initializeDatabase() { // Initialize schema first await this.repository.initializeSchema(); // Then create baseline node await this.repository.initializeBaseline(); logger_1.Logger.info('Initialized database with schema and baseline'); } async previewMigrations(fileNames) { logger_1.Logger.info('Dry run - no changes will be made to the database'); for (const fileName of fileNames) { const migration = await this.fileService.prepareMigration(fileName); logger_1.Logger.info(`Would execute migration: ${fileName}`); logger_1.Logger.info('Statements:'); for (const stmt of migration.statements) { logger_1.Logger.info(stmt); } } logger_1.Logger.info(`Dry run complete - ${fileNames.length} migration(s) pending`); } async validateMigrations(migrations) { for (const migration of migrations) { const { statements } = await this.fileService.prepareMigration(migration.node.source); const currentChecksum = (0, utils_1.generateChecksum)(statements); if (currentChecksum !== migration.node.checksum) { throw new errors_1.MigrationError(`Checksum mismatch for ${migration.node.source}. ` + 'The migration file has been modified after it was applied.'); } } } } exports.MigrationService = MigrationService; //# sourceMappingURL=migrate.service.js.map