rollback method

Future<MigrationReport> rollback({
  1. int steps = 1,
})

Rolls back the most recently applied migrations.

Implementation

Future<MigrationReport> rollback({int steps = 1}) async {
  if (steps < 1) {
    throw ArgumentError.value(steps, 'steps', 'Must be >= 1');
  }

  final schema = _defaultSchema;
  if (schema != null) {
    await _schemaDriver.setCurrentSchema(schema);
  }

  await _ledger.ensureInitialized();
  final applied = await _ledger.readApplied();
  if (applied.isEmpty) {
    return const MigrationReport([]);
  }
  final targets = applied.reversed.take(steps).toList();

  // Emit batch started
  final batchStopwatch = Stopwatch()..start();
  if (_emitEvents) {
    _events.emit(
      MigrationBatchStartedEvent(
        direction: MigrationDirection.down,
        count: targets.length,
        batch: null,
      ),
    );
  }

  final actions = <MigrationAction>[];

  for (var i = 0; i < targets.length; i++) {
    final record = targets[i];
    final descriptor = _descriptorById[record.id.toString()];
    if (descriptor == null) {
      throw StateError(
        'No migration descriptor registered for ${record.id}.',
      );
    }

    final migrationId = descriptor.id.toString();
    final migrationName = descriptor.id.slug;

    // Emit migration started
    if (_emitEvents) {
      _events.emit(
        MigrationStartedEvent(
          migrationId: migrationId,
          migrationName: migrationName,
          direction: MigrationDirection.down,
          index: i + 1,
          total: targets.length,
        ),
      );
    }

    final stopwatch = Stopwatch()..start();

    try {
      final plan = await _planResolver(descriptor, MigrationDirection.down);
      await _schemaDriver.applySchemaPlan(plan);
      stopwatch.stop();
      await _ledger.remove(record.id);

      // Emit migration completed
      if (_emitEvents) {
        _events.emit(
          MigrationCompletedEvent(
            migrationId: migrationId,
            migrationName: migrationName,
            direction: MigrationDirection.down,
            duration: stopwatch.elapsed,
          ),
        );
      }

      actions.add(
        MigrationAction(
          descriptor: descriptor,
          operation: MigrationOperation.rollback,
          appliedAt: DateTime.now().toUtc(),
          duration: stopwatch.elapsed,
        ),
      );
    } catch (error, stackTrace) {
      stopwatch.stop();
      // Emit migration failed
      if (_emitEvents) {
        _events.emit(
          MigrationFailedEvent(
            migrationId: migrationId,
            migrationName: migrationName,
            direction: MigrationDirection.down,
            error: error,
            stackTrace: stackTrace,
          ),
        );
      }
      rethrow;
    }
  }

  batchStopwatch.stop();
  if (_emitEvents) {
    _events.emit(
      MigrationBatchCompletedEvent(
        direction: MigrationDirection.down,
        count: actions.length,
        duration: batchStopwatch.elapsed,
      ),
    );
  }

  return MigrationReport(actions);
}