You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

98 lines
2.6 KiB

3 years ago
  1. package wow.doge.http4sdemo
  2. import scala.jdk.CollectionConverters._
  3. import cats.effect.Sync
  4. import com.typesafe.config.Config
  5. import com.typesafe.config.ConfigFactory
  6. import com.typesafe.scalalogging.LazyLogging
  7. import org.flywaydb.core.Flyway
  8. import org.flywaydb.core.api.Location
  9. import org.flywaydb.core.api.configuration.FluentConfiguration
  10. import pureconfig.ConfigConvert
  11. import pureconfig.ConfigSource
  12. import pureconfig.generic.semiauto._
  13. final case class JdbcDatabaseConfig(
  14. url: String,
  15. driver: String,
  16. user: Option[String],
  17. password: Option[String],
  18. migrationsTable: String,
  19. migrationsLocations: List[String]
  20. )
  21. object JdbcDatabaseConfig {
  22. def loadFromGlobal[F[_]: Sync](
  23. configNamespace: String
  24. ): F[JdbcDatabaseConfig] =
  25. Sync[F].suspend {
  26. val config = ConfigFactory.load()
  27. load(config.getConfig(configNamespace))
  28. }
  29. // Integration with PureConfig
  30. implicit val configConvert: ConfigConvert[JdbcDatabaseConfig] =
  31. deriveConvert
  32. def load[F[_]: Sync](config: Config): F[JdbcDatabaseConfig] =
  33. Sync[F].delay {
  34. ConfigSource.fromConfig(config).loadOrThrow[JdbcDatabaseConfig]
  35. }
  36. }
  37. object DBMigrations extends LazyLogging {
  38. def migrate[F[_]: Sync](config: JdbcDatabaseConfig): F[Int] =
  39. Sync[F].delay {
  40. logger.info(
  41. "Running migrations from locations: " +
  42. config.migrationsLocations.mkString(", ")
  43. )
  44. val count = unsafeMigrate(config)
  45. logger.info(s"Executed $count migrations")
  46. count
  47. }
  48. private def unsafeMigrate(config: JdbcDatabaseConfig): Int = {
  49. val m: FluentConfiguration = Flyway.configure
  50. .dataSource(
  51. config.url,
  52. config.user.orNull,
  53. config.password.orNull
  54. )
  55. .group(true)
  56. .outOfOrder(false)
  57. .table(config.migrationsTable)
  58. .locations(
  59. config.migrationsLocations
  60. .map(new Location(_))
  61. .toList: _*
  62. )
  63. .baselineOnMigrate(true)
  64. logValidationErrorsIfAny(m)
  65. m.load()
  66. .migrate()
  67. .migrationsExecuted
  68. }
  69. private def logValidationErrorsIfAny(m: FluentConfiguration): Unit = {
  70. val validated = m
  71. .ignorePendingMigrations(true)
  72. .load()
  73. .validateWithResult()
  74. if (!validated.validationSuccessful)
  75. for (error <- validated.invalidMigrations.asScala)
  76. logger.warn(s"""
  77. |Failed validation:
  78. | - version: ${error.version}
  79. | - path: ${error.filepath}
  80. | - description: ${error.description}
  81. | - errorCode: ${error.errorDetails.errorCode}
  82. | - errorMessage: ${error.errorDetails.errorMessage}
  83. """.stripMargin.strip)
  84. }
  85. }