code: /*
   * Copyright (c) 2022 Airbyte, Inc., all rights reserved.
   */
  
  package io.airbyte.db.instance.development;
  
  import io.airbyte.db.instance.DatabaseMigrator;
  import io.airbyte.db.instance.FlywayDatabaseMigrator;
  import java.io.IOException;
  import java.util.Collections;
  import java.util.List;
  import java.util.Optional;
  import org.flywaydb.core.Flyway;
  import org.flywaydb.core.api.MigrationInfo;
  import org.flywaydb.core.api.MigrationVersion;
  import org.flywaydb.core.api.configuration.Configuration;
  import org.flywaydb.core.api.configuration.FluentConfiguration;
  import org.flywaydb.core.api.output.BaselineResult;
  import org.flywaydb.core.api.output.MigrateResult;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * This migrator can prepare the database with previous migrations, and only run the latest
   * migration for testing. It is used in {@link MigrationDevHelper#runLastMigration}.
   */
  public class DevDatabaseMigrator implements DatabaseMigrator {
  
    private static final Logger LOGGER = LoggerFactory.getLogger(DevDatabaseMigrator.class);
  
    // A migrator that can run all migrations.
    private final DatabaseMigrator fullMigrator;
    // A migrator that will not run the last migration. It prepares the database to the state right
    // before the last migration.
    private final DatabaseMigrator baselineMigrator;
  
    public DevDatabaseMigrator(final FlywayDatabaseMigrator fullMigrator) {
      this.fullMigrator = fullMigrator;
      this.baselineMigrator = getBaselineMigrator(fullMigrator);
    }
  
    private static class NoOpDatabaseMigrator implements DatabaseMigrator {
  
      @Override
      public MigrateResult migrate() {
        return null;
      }
  
      @Override
      public List<MigrationInfo> list() {
        return Collections.emptyList();
      }
  
      @Override
      public MigrationInfo getLatestMigration() {
        return null;
      }
  
      @Override
      public BaselineResult createBaseline() {
        return null;
      }
  
      @Override
      public String dumpSchema() {
        return "";
      }
  
    }
  
    /**
     * Create a baseline migration from a full migrator. The baseline migrator does not run the last
     * migration, which will be usually the migration to be tested.
     */
    private static DatabaseMigrator getBaselineMigrator(final FlywayDatabaseMigrator fullMigrator) {
      final Configuration fullConfig = fullMigrator.getFlyway().getConfiguration();
      final FluentConfiguration baselineConfig = Flyway.configure()
          .dataSource(fullConfig.getDataSource())
          .baselineVersion(fullConfig.getBaselineVersion())
          .baselineDescription(fullConfig.getBaselineDescription())
          .baselineOnMigrate(fullConfig.isBaselineOnMigrate())
          .installedBy(fullConfig.getInstalledBy())
          .table(fullConfig.getTable())
          .locations(fullConfig.getLocations());
  
      final Optional<MigrationVersion> secondToLastMigrationVersion = MigrationDevHelper.getSecondToLastMigrationVersion(fullMigrator);
      if (secondToLastMigrationVersion.isEmpty()) {
        LOGGER.info("There is zero or one migration. No extra baseline setup is needed.");
        return new NoOpDatabaseMigrator();
      }
  
      // Set the baseline flyway config to not run the last migration by setting the target migration
      // version.
      LOGGER.info("Baseline migrator target version: {}", secondToLastMigrationVersion.get());
      baselineConfig.target(secondToLastMigrationVersion.get());
  
      return new FlywayDatabaseMigrator(fullMigrator.getDatabase(), baselineConfig.load());
    }
  
    @Override
    public MigrateResult migrate() {
      return fullMigrator.migrate();
    }
  
    @Override
    public List<MigrationInfo> list() {
      return fullMigrator.list();
    }
  
    @Override
    public MigrationInfo getLatestMigration() {
      return fullMigrator.getLatestMigration();
    }
  
    @Override
    public BaselineResult createBaseline() {
      fullMigrator.createBaseline();
      // Run all previous migration except for the last one to establish the baseline database state.
      baselineMigrator.migrate();
      return fullMigrator.createBaseline();
    }
  
    @Override
    public String dumpSchema() throws IOException {
      return fullMigrator.dumpSchema();
    }
  
  }
  