"use strict"; /* * @adonisjs/lucid * * (c) Harminder Virk * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const pretty_hrtime_1 = __importDefault(require("pretty-hrtime")); const standalone_1 = require("@adonisjs/core/build/standalone"); const utils_1 = require("../../src/utils"); const prettyPrint_1 = require("../../src/Helpers/prettyPrint"); /** * Base class to execute migrations and print logs */ class MigrationsBase extends standalone_1.BaseCommand { constructor() { super(...arguments); /** * Should print one-liner compact output */ Object.defineProperty(this, "compactOutput", { enumerable: true, configurable: true, writable: true, value: false }); } /** * Not a valid connection */ printNotAValidConnection(connection) { this.logger.error(`"${connection}" is not a valid connection name. Double check "config/database" file`); } /** * Prompts to take consent for running migrations in production */ async takeProductionConstent() { /** * Do not prompt when CLI is not interactive */ if (!this.isInteractive) { return false; } const question = 'You are in production environment. Want to continue running migrations?'; try { return await this.prompt.confirm(question); } catch (error) { return false; } } /** * Returns beautified log message string */ printLogMessage(file, direction) { const color = file.status === 'pending' ? 'gray' : file.status === 'completed' ? 'green' : 'red'; const arrow = this.colors[color]('❯'); const message = file.status === 'pending' ? direction === 'up' ? 'migrating' : 'reverting' : file.status === 'completed' ? direction === 'up' ? 'migrated' : 'reverted' : 'error'; this.logger.logUpdate(`${arrow} ${this.colors[color](message)} ${file.file.name}`); } /** * Pretty print sql queries of a file */ prettyPrintSql(file, connectionName) { console.log(this.logger.colors.gray(`------------- ${file.file.name} -------------`)); console.log(); file.queries.map((sql) => { (0, prettyPrint_1.prettyPrint)({ connection: connectionName, sql: sql, ddl: true, method: (0, utils_1.getDDLMethod)(sql), bindings: [], }); console.log(); }); console.log(this.logger.colors.gray('------------- END -------------')); } /** * Log final status with verbose output */ logVerboseFinalStatus(migrator, duration) { switch (migrator.status) { case 'completed': const completionMessage = migrator.direction === 'up' ? 'Migrated in' : 'Reverted in'; console.log(`\n${completionMessage} ${this.colors.cyan((0, pretty_hrtime_1.default)(duration))}`); break; case 'skipped': const message = migrator.direction === 'up' ? 'Already up to date' : 'Already at latest batch'; console.log(this.colors.cyan(message)); break; case 'error': this.logger.fatal(migrator.error); this.exitCode = 1; break; } } /** * Log final status with compact output */ logCompactFinalStatus(processedFiles, migrator, duration) { let output = ''; let message = ''; let isUp = migrator.direction === 'up'; switch (migrator.status) { case 'completed': message = `❯ ${isUp ? 'Executed' : 'Reverted'} ${processedFiles.size} migrations`; output = this.colors.grey(message + ` (${(0, pretty_hrtime_1.default)(duration)})`); break; case 'skipped': message = `❯ ${isUp ? 'Already up to date' : 'Already at latest batch'}`; output = this.colors.grey(message); break; case 'error': const skippedMigrations = Object.values(migrator.migratedFiles).filter((file) => file.status === 'pending').length; message = `❯ Executed ${processedFiles.size} migrations, 1 error, ${skippedMigrations} skipped`; console.log(this.colors.red(message)); console.log('\n' + this.colors.red(migrator.error.message)); this.exitCode = 1; break; } this.logger.log(output); } /** * Runs the migrations using the migrator */ async runMigrations(migrator, connectionName) { /** * Pretty print SQL in dry run and return early */ if (migrator.dryRun) { await migrator.run(); Object.keys(migrator.migratedFiles).forEach((file) => { this.prettyPrintSql(migrator.migratedFiles[file], connectionName); }); return; } /** * A set of files processed and emitted using event emitter. */ const processedFiles = new Set(); let start; let duration; /** * Starting to process a new migration file */ migrator.on('migration:start', (file) => { processedFiles.add(file.file.name); if (!this.compactOutput) { this.printLogMessage(file, migrator.direction); } }); /** * Migration completed */ migrator.on('migration:completed', (file) => { if (!this.compactOutput) { this.printLogMessage(file, migrator.direction); this.logger.logUpdatePersist(); } }); /** * Migration error */ migrator.on('migration:error', (file) => { if (!this.compactOutput) { this.printLogMessage(file, migrator.direction); this.logger.logUpdatePersist(); } }); /** * Migration completed */ migrator.on('upgrade:version', ({ from, to }) => { this.logger.info(`Upgrading migrations version from "${from}" to "${to}"`); }); migrator.on('start', () => (start = process.hrtime())); migrator.on('end', () => (duration = process.hrtime(start))); /** * Run migrations */ await migrator.run(); /** * Log all pending files. This will happen, when one of the migration * fails with an error and then the migrator stops emitting events. */ Object.keys(migrator.migratedFiles).forEach((file) => { if (!processedFiles.has(file) && !this.compactOutput) { this.printLogMessage(migrator.migratedFiles[file], migrator.direction); } }); if (this.compactOutput) { this.logCompactFinalStatus(processedFiles, migrator, duration); } else { this.logVerboseFinalStatus(migrator, duration); } } } exports.default = MigrationsBase;