From eef54171696912f5256fdeac9f86baef6626a9d9 Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Mon, 21 Oct 2019 16:10:32 +0200 Subject: [PATCH 1/7] WIP --- build.sbt | 19 +++++++++++++++++++ .../sst/flyway/pureconfig/ConfigReaders.scala | 11 +++++++++++ .../sst/flyway/pureconfig/implicits.scala | 3 +++ .../com/avast/sst/flyway/FlywayConfig.scala | 3 +++ .../com/avast/sst/flyway/FlywayModule.scala | 17 +++++++++++++++++ project/Dependencies.scala | 1 + 6 files changed, 54 insertions(+) create mode 100644 flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala create mode 100644 flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/implicits.scala create mode 100644 flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala create mode 100644 flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala diff --git a/build.sbt b/build.sbt index 222b7465d..eef8ae08a 100644 --- a/build.sbt +++ b/build.sbt @@ -23,6 +23,8 @@ lazy val root = project doobieHikari, doobieHikariPureConfig, example, + flyway, + flywayPureConfig, http4sClientBlaze, http4sClientBlazePureConfig, http4sServer, @@ -101,6 +103,23 @@ lazy val example = project ) ) +lazy val flyway = project + .in(file("flyway")) + .settings(commonSettings) + .settings( + name := "sst-flyway", + libraryDependencies += Dependencies.flywayCore + ) + +lazy val flywayPureConfig = project + .in(file("flyway-pureconfig")) + .dependsOn(flyway) + .settings(commonSettings) + .settings( + name := "sst-flyway-pureconfig", + libraryDependencies += Dependencies.pureConfig + ) + lazy val http4sClientBlaze = project .in(file("http4s-client-blaze")) .settings(commonSettings) diff --git a/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala b/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala new file mode 100644 index 000000000..1b70ad6df --- /dev/null +++ b/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala @@ -0,0 +1,11 @@ +package com.avast.sst.flyway.pureconfig + +import com.avast.sst.flyway.FlywayConfig +import pureconfig.ConfigReader +import pureconfig.generic.semiauto.deriveReader + +trait ConfigReaders { + + implicit val configReader: ConfigReader[FlywayConfig] = deriveReader + +} diff --git a/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/implicits.scala b/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/implicits.scala new file mode 100644 index 000000000..c4d6da818 --- /dev/null +++ b/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/implicits.scala @@ -0,0 +1,3 @@ +package com.avast.sst.flyway.pureconfig + +object implicits extends ConfigReaders diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala new file mode 100644 index 000000000..eef43e9df --- /dev/null +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala @@ -0,0 +1,3 @@ +package com.avast.sst.flyway + +final case class FlywayConfig(url: String, username: String, password: String, baselineOnMigrate: Boolean = false) diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala new file mode 100644 index 000000000..539e97f30 --- /dev/null +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala @@ -0,0 +1,17 @@ +package com.avast.sst.flyway + +import cats.effect.Sync +import org.flywaydb.core.Flyway + +object FlywayModule { + + def make[F[_]: Sync](config: FlywayConfig): F[Flyway] = { + Flyway + .configure + .dataSource(config.url, config.username, config.password) + .baselineOnMigrate(config.baselineOnMigrate) + .load() + ??? + } + +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index ed5c0c26a..f53e21190 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -5,6 +5,7 @@ object Dependencies { val catsEffect = "org.typelevel" %% "cats-effect" % "2.0.0" val doobie = "org.tpolecat" %% "doobie-core" % Versions.doobie val doobieHikari = "org.tpolecat" %% "doobie-hikari" % Versions.doobie + val flywayCore = "org.flywaydb" % "flyway-core" % "6.0.7" val http4sBlazeClient = "org.http4s" %% "http4s-blaze-client" % Versions.http4s val http4sBlazeServer = "org.http4s" %% "http4s-blaze-server" % Versions.http4s val http4sDsl = "org.http4s" %% "http4s-dsl" % Versions.http4s From ffec2a03fd382d4a85e79e695c2bef8f89fb516b Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Mon, 21 Oct 2019 23:02:26 +0200 Subject: [PATCH 2/7] feat: Add flyway and flyway-pureconfig modules Closes #6 --- .../com/avast/sst/flyway/FlywayConfig.scala | 23 ++++++++++++- .../com/avast/sst/flyway/FlywayModule.scala | 34 +++++++++++++++---- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala index eef43e9df..bf86113b0 100644 --- a/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala @@ -1,3 +1,24 @@ package com.avast.sst.flyway -final case class FlywayConfig(url: String, username: String, password: String, baselineOnMigrate: Boolean = false) +final case class FlywayConfig(url: String, + username: String, + password: String, + baselineOnMigrate: Boolean = false, + baselineVersion: Option[String] = None, + baselineDescription: Option[String] = None, + batch: Boolean = false, + cleanDisabled: Boolean = false, + cleanOnValidationError: Boolean = false, + connectRetries: Int = 0, + encoding: String = "UTF-8", + group: Boolean = false, + ignoreFutureMigrations: Boolean = true, + ignoreIgnoredMigrations: Boolean = false, + ignoreMissingMigrations: Boolean = false, + ignorePendingMigrations: Boolean = false, + installedBy: Option[String] = None, + mixed: Boolean = false, + locations: List[String] = List.empty, + outOfOrder: Boolean = false, + validateOnMigrate: Boolean = true, + licenseKey: Option[String] = None) diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala index 539e97f30..debb20e29 100644 --- a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala @@ -6,12 +6,34 @@ import org.flywaydb.core.Flyway object FlywayModule { def make[F[_]: Sync](config: FlywayConfig): F[Flyway] = { - Flyway - .configure - .dataSource(config.url, config.username, config.password) - .baselineOnMigrate(config.baselineOnMigrate) - .load() - ??? + Sync[F].delay { + val builder = Flyway + .configure + .dataSource(config.url, config.username, config.password) + .baselineOnMigrate(config.baselineOnMigrate) + .batch(config.batch) + .cleanDisabled(config.cleanDisabled) + .cleanOnValidationError(config.cleanOnValidationError) + .connectRetries(config.connectRetries) + .encoding(config.encoding) + .group(config.group) + .ignoreFutureMigrations(config.ignoreFutureMigrations) + .ignoreIgnoredMigrations(config.ignoreIgnoredMigrations) + .ignoreMissingMigrations(config.ignoreMissingMigrations) + .ignorePendingMigrations(config.ignorePendingMigrations) + .mixed(config.mixed) + .outOfOrder(config.outOfOrder) + .validateOnMigrate(config.validateOnMigrate) + + config.baselineVersion.foreach(builder.baselineVersion) + config.baselineDescription.foreach(builder.baselineDescription) + config.installedBy.foreach(builder.installedBy) + if (config.locations.nonEmpty) builder.locations(config.locations: _*) + + config.licenseKey.foreach(builder.licenseKey) + + builder.load() + } } } From a06028786c6a9e10104bbd6620edf35ade8d5ad6 Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Tue, 22 Oct 2019 08:31:06 +0200 Subject: [PATCH 3/7] fix: compilation error --- flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala index debb20e29..62a50ff02 100644 --- a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala @@ -5,6 +5,7 @@ import org.flywaydb.core.Flyway object FlywayModule { + @SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements")) def make[F[_]: Sync](config: FlywayConfig): F[Flyway] = { Sync[F].delay { val builder = Flyway From 17a0c6d9fb5921730ebc9d9e1b1c01879e819417 Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Tue, 22 Oct 2019 12:48:08 +0200 Subject: [PATCH 4/7] refactor: PR changes --- .../sst/flyway/pureconfig/ConfigReaders.scala | 16 ++++++++++++++++ .../com/avast/sst/flyway/FlywayConfig.scala | 13 +++++++------ .../com/avast/sst/flyway/FlywayModule.scala | 5 +++-- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala b/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala index 1b70ad6df..f1a11068d 100644 --- a/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala +++ b/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala @@ -1,11 +1,27 @@ package com.avast.sst.flyway.pureconfig +import java.nio.charset.{Charset, IllegalCharsetNameException, UnsupportedCharsetException} + +import cats.syntax.either._ import com.avast.sst.flyway.FlywayConfig +import org.flywaydb.core.api.MigrationVersion import pureconfig.ConfigReader +import pureconfig.error.ExceptionThrown import pureconfig.generic.semiauto.deriveReader trait ConfigReaders { + implicit private[pureconfig] val charsetReader: ConfigReader[Charset] = ConfigReader[String].emap { value => + try { + Charset.forName(value).asRight + } catch { + case ex @ (_: IllegalArgumentException | _: IllegalCharsetNameException | _: UnsupportedCharsetException) => + ExceptionThrown(ex).asLeft + } + } + + implicit val migrationVersionReader: ConfigReader[MigrationVersion] = ConfigReader[String].map(MigrationVersion.fromVersion) + implicit val configReader: ConfigReader[FlywayConfig] = deriveReader } diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala index bf86113b0..75ccd2ee6 100644 --- a/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala @@ -1,16 +1,17 @@ package com.avast.sst.flyway -final case class FlywayConfig(url: String, - username: String, - password: String, - baselineOnMigrate: Boolean = false, - baselineVersion: Option[String] = None, +import java.nio.charset.{Charset, StandardCharsets} + +import org.flywaydb.core.api.MigrationVersion + +final case class FlywayConfig(baselineOnMigrate: Boolean = false, + baselineVersion: Option[MigrationVersion] = None, baselineDescription: Option[String] = None, batch: Boolean = false, cleanDisabled: Boolean = false, cleanOnValidationError: Boolean = false, connectRetries: Int = 0, - encoding: String = "UTF-8", + encoding: Charset = StandardCharsets.UTF_8, group: Boolean = false, ignoreFutureMigrations: Boolean = true, ignoreIgnoredMigrations: Boolean = false, diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala index 62a50ff02..c0d3918bb 100644 --- a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala @@ -1,16 +1,17 @@ package com.avast.sst.flyway import cats.effect.Sync +import javax.sql.DataSource import org.flywaydb.core.Flyway object FlywayModule { @SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements")) - def make[F[_]: Sync](config: FlywayConfig): F[Flyway] = { + def make[F[_]: Sync](dataSource: DataSource, config: FlywayConfig): F[Flyway] = { Sync[F].delay { val builder = Flyway .configure - .dataSource(config.url, config.username, config.password) + .dataSource(dataSource) .baselineOnMigrate(config.baselineOnMigrate) .batch(config.batch) .cleanDisabled(config.cleanDisabled) From 3cfcd6267e2437ac88a355d5f6390753ed470671 Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Tue, 22 Oct 2019 13:03:44 +0200 Subject: [PATCH 5/7] docs: Add some Flyway documentation --- build.sbt | 2 +- docs/index.md | 1 + example/mdoc/flyway.md | 24 +++++++++++++++++++ example/mdoc/index.md | 1 + .../com/avast/sst/flyway/FlywayModule.scala | 1 + 5 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 example/mdoc/flyway.md diff --git a/build.sbt b/build.sbt index eef8ae08a..c6e6c43ea 100644 --- a/build.sbt +++ b/build.sbt @@ -87,7 +87,7 @@ lazy val doobieHikariPureConfig = project lazy val example = project .in(file("example")) - .dependsOn(bundleZioHttp4sBlaze, doobieHikari, doobieHikariPureConfig, micrometerJmxPureConfig, sslConfig) + .dependsOn(bundleZioHttp4sBlaze, doobieHikari, doobieHikariPureConfig, flyway, flywayPureConfig, micrometerJmxPureConfig, sslConfig) .enablePlugins(MdocPlugin) .settings(commonSettings) .settings( diff --git a/docs/index.md b/docs/index.md index dc708e26f..34c4abf9a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,6 +10,7 @@ * [Module PureConfig](pureconfig.md) * [Module SSL Config](ssl-config.md) * [Module doobie](doobie.md) +* [Module Flyway](flyway.md) ## Getting Started diff --git a/example/mdoc/flyway.md b/example/mdoc/flyway.md new file mode 100644 index 000000000..c9a9361c1 --- /dev/null +++ b/example/mdoc/flyway.md @@ -0,0 +1,24 @@ +# Flyway + +[![Maven Central](https://img.shields.io/maven-central/v/com.avast/sst-flyway_2.12)](https://repo1.maven.org/maven2/com/avast/sst-flyway_2.12/) + +`libraryDependencies += "com.avast" %% "sst-flyway" % ""` + +This module initializes `Flyway` which can be used to do automated SQL DB migrations. See the [documentation of Flyway](https://flywaydb.org/documentation/) +on how to go about that. + +The method `make` requires `javax.sql.DataSource` which you can for example obtain from `doobie-hikari` module: + +```scala +import cats.effect.Resource +import com.avast.sst.doobie.DoobieHikariModule +import com.avast.sst.flyway.FlywayModule +import zio.Task +import zio.interop.catz._ +import zio.interop.catz.implicits._ + +for { + doobieTransactor <- DoobieHikariModule.make[Task](???, ???, ???, ???) + flyway <- Resource.liftF(FlywayModule.make[Task](doobieTransactor.kernel, ???)) +} yield () +``` diff --git a/example/mdoc/index.md b/example/mdoc/index.md index 3a95ed9f3..6868bef4a 100644 --- a/example/mdoc/index.md +++ b/example/mdoc/index.md @@ -10,6 +10,7 @@ * [Module PureConfig](pureconfig.md) * [Module SSL Config](ssl-config.md) * [Module doobie](doobie.md) +* [Module Flyway](flyway.md) ## Getting Started diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala index c0d3918bb..1ce50276d 100644 --- a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala @@ -6,6 +6,7 @@ import org.flywaydb.core.Flyway object FlywayModule { + /** Makes [[org.flywaydb.core.Flyway]] from the given [[javax.sql.DataSource]] and config. */ @SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements")) def make[F[_]: Sync](dataSource: DataSource, config: FlywayConfig): F[Flyway] = { Sync[F].delay { From 5328a0d7033f9f2fd6f6c2842b670c637c6d48cd Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Tue, 22 Oct 2019 13:04:05 +0200 Subject: [PATCH 6/7] docs: Add some Flyway documentation --- docs/flyway.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 docs/flyway.md diff --git a/docs/flyway.md b/docs/flyway.md new file mode 100644 index 000000000..175c6cc3b --- /dev/null +++ b/docs/flyway.md @@ -0,0 +1,25 @@ +# Flyway + +[![Maven Central](https://img.shields.io/maven-central/v/com.avast/sst-flyway_2.12)](https://repo1.maven.org/maven2/com/avast/sst-flyway_2.12/) + +`libraryDependencies += "com.avast" %% "sst-flyway" % ""` + +This module initializes `Flyway` which can be used to do automated SQL DB migrations. See the [documentation of Flyway](https://flywaydb.org/documentation/) +on how to go about that. + +The method `make` requires `javax.sql.DataSource` which you can for example obtain from `doobie-hikari` module: + +```scala +import cats.effect.Resource +import com.avast.sst.doobie.DoobieHikariModule +import com.avast.sst.flyway.FlywayModule +import zio.Task +import zio.interop.catz._ +import zio.interop.catz.implicits._ + +for { + doobieTransactor <- DoobieHikariModule.make[Task](???, ???, ???, ???) + flyway <- Resource.liftF(FlywayModule.make[Task](doobieTransactor.kernel, ???)) +} yield () +``` + From 9ad65f4212b0170cc0b36e61299d193bdbbb34d1 Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Tue, 22 Oct 2019 14:48:02 +0200 Subject: [PATCH 7/7] refactor: Minor PR tweaks --- .../com/avast/sst/flyway/pureconfig/ConfigReaders.scala | 9 ++------- .../main/scala/com/avast/sst/flyway/FlywayConfig.scala | 1 + .../main/scala/com/avast/sst/flyway/FlywayModule.scala | 1 + 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala b/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala index f1a11068d..43ed674b9 100644 --- a/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala +++ b/flyway-pureconfig/src/main/scala/com/avast/sst/flyway/pureconfig/ConfigReaders.scala @@ -1,6 +1,6 @@ package com.avast.sst.flyway.pureconfig -import java.nio.charset.{Charset, IllegalCharsetNameException, UnsupportedCharsetException} +import java.nio.charset.Charset import cats.syntax.either._ import com.avast.sst.flyway.FlywayConfig @@ -12,12 +12,7 @@ import pureconfig.generic.semiauto.deriveReader trait ConfigReaders { implicit private[pureconfig] val charsetReader: ConfigReader[Charset] = ConfigReader[String].emap { value => - try { - Charset.forName(value).asRight - } catch { - case ex @ (_: IllegalArgumentException | _: IllegalCharsetNameException | _: UnsupportedCharsetException) => - ExceptionThrown(ex).asLeft - } + Either.catchNonFatal(Charset.forName(value)).leftMap(ExceptionThrown.apply) } implicit val migrationVersionReader: ConfigReader[MigrationVersion] = ConfigReader[String].map(MigrationVersion.fromVersion) diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala index 75ccd2ee6..afefdf248 100644 --- a/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayConfig.scala @@ -6,6 +6,7 @@ import org.flywaydb.core.api.MigrationVersion final case class FlywayConfig(baselineOnMigrate: Boolean = false, baselineVersion: Option[MigrationVersion] = None, + targetVersion: Option[MigrationVersion] = None, baselineDescription: Option[String] = None, batch: Boolean = false, cleanDisabled: Boolean = false, diff --git a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala index 1ce50276d..9ce40de7e 100644 --- a/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala +++ b/flyway/src/main/scala/com/avast/sst/flyway/FlywayModule.scala @@ -29,6 +29,7 @@ object FlywayModule { .validateOnMigrate(config.validateOnMigrate) config.baselineVersion.foreach(builder.baselineVersion) + config.targetVersion.foreach(builder.target) config.baselineDescription.foreach(builder.baselineDescription) config.installedBy.foreach(builder.installedBy) if (config.locations.nonEmpty) builder.locations(config.locations: _*)