import {Directive, OptionDefinition, CLIDirective} from '../../cli' import {Container, Inject, Injectable} from '../../di' import {Collection} from '../../util' import {Bus, EventHandlerSubscription} from '../../support/bus' import {Migrator} from '../migrations/Migrator' import {Migrations} from '../services/Migrations' import {RollingBackMigrationEvent} from '../migrations/events/RollingBackMigrationEvent' import {RolledBackMigrationEvent} from '../migrations/events/RolledBackMigrationEvent' import {NothingToMigrateError} from '../migrations/NothingToMigrateError' /** * CLI directive that undoes applied migrations using the default Migrator. * @fixme Support dry run mode */ @Injectable() @CLIDirective() export class RollbackDirective extends Directive { @Inject() protected readonly bus!: Bus @Inject('injector') protected readonly injector!: Container @Inject() protected readonly migrations!: Migrations /** Event bus subscriptions. */ protected subscriptions: Collection = new Collection() getKeywords(): string | string[] { return ['rollback'] } getDescription(): string { return 'roll-back applied migrations' } getOptions(): OptionDefinition[] { return [ '--identifier -i {name} | roll-back a specific migration, by identifier', ] } getHelpText(): string { return [ 'Use this command to undo one or more migrations that were applied.', '', 'By default, the command will undo all of the migrations applied the last time the migrate command was run.', '', 'To undo a specific migration, pass its identifier using the --identifier option.', '', ].join('\n') } async handle(): Promise { await this.registerListeners() const identifier = this.option('identifier') let identifiers if ( identifier ) { identifiers = [identifier] } let error try { await (this.injector.make(Migrator)).rollback(identifiers) } catch (e) { if ( e instanceof NothingToMigrateError ) { this.info(e.message) } else { error = e this.error(e) } } finally { await this.removeListeners() } if ( error ) { throw error } } /** * Register event-bus listeners to print messages for the user. * @protected */ protected async registerListeners(): Promise { this.subscriptions.push(await this.bus.subscribe(RollingBackMigrationEvent, event => { this.info(`Rolling-back migration ${event.migration.identifier}...`) })) this.subscriptions.push(await this.bus.subscribe(RolledBackMigrationEvent, event => { this.success(`Rolled-back migration: ${event.migration.identifier}`) })) } /** Remove event bus listeners before finish. */ protected async removeListeners(): Promise { await this.subscriptions.awaitMapCall('unsubscribe') } }