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.

99 lines
2.7 KiB

3 years ago
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. @SuppressWarnings(Array("org.wartremover.warts.Null"))
  49. private def unsafeMigrate(config: JdbcDatabaseConfig): Int = {
  50. val m: FluentConfiguration = Flyway.configure
  51. .dataSource(
  52. config.url,
  53. config.user.orNull,
  54. config.password.orNull
  55. )
  56. .group(true)
  57. .outOfOrder(false)
  58. .table(config.migrationsTable)
  59. .locations(
  60. config.migrationsLocations
  61. .map(new Location(_))
  62. .toList: _*
  63. )
  64. .baselineOnMigrate(true)
  65. logValidationErrorsIfAny(m)
  66. m.load()
  67. .migrate()
  68. .migrationsExecuted
  69. }
  70. private def logValidationErrorsIfAny(m: FluentConfiguration): Unit = {
  71. val validated = m
  72. .ignorePendingMigrations(true)
  73. .load()
  74. .validateWithResult()
  75. if (!validated.validationSuccessful)
  76. for (error <- validated.invalidMigrations.asScala)
  77. logger.warn(s"""
  78. |Failed validation:
  79. | - version: ${error.version}
  80. | - path: ${error.filepath}
  81. | - description: ${error.description}
  82. | - errorCode: ${error.errorDetails.errorCode}
  83. | - errorMessage: ${error.errorDetails.errorMessage}
  84. """.stripMargin.strip)
  85. }
  86. }