Skip to content

Commit 153d312

Browse files
committed
feat: add support for flyway
1 parent 3dfa6ff commit 153d312

File tree

8 files changed

+166
-93
lines changed

8 files changed

+166
-93
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ libraryDependencies += Seq(
2929
"com.github.j5ik2o" %% "docker-controller-scala-redis" % version, // optional
3030
"com.github.j5ik2o" %% "docker-controller-scala-elasticmq" % version, // optional
3131
"com.github.j5ik2o" %% "docker-controller-scala-postgresql" % version, // optional
32+
"com.github.j5ik2o" %% "docker-controller-scala-flyway" % version, // optional
3233
)
3334
```
3435

build.sbt

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,17 @@ val `docker-controller-scala-kafka` = (project in file("docker-controller-scala-
141141
`docker-controller-scala-scalatest` % Test
142142
)
143143

144+
val `docker-controller-scala-flyway` = (project in file("docker-controller-scala-flyway"))
145+
.settings(baseSettings)
146+
.settings(
147+
name := "docker-controller-scala-flyway",
148+
libraryDependencies ++= Seq(
149+
"org.flywaydb" % "flyway-core" % "7.10.0",
150+
scalatest.scalatest % Test,
151+
logback.classic % Test
152+
)
153+
).dependsOn(`docker-controller-scala-core`, `docker-controller-scala-scalatest` % Test)
154+
144155
val `docker-controller-scala-mysql` = (project in file("docker-controller-scala-mysql"))
145156
.settings(baseSettings)
146157
.settings(
@@ -150,7 +161,11 @@ val `docker-controller-scala-mysql` = (project in file("docker-controller-scala-
150161
logback.classic % Test,
151162
mysql.connectorJava % Test
152163
)
153-
).dependsOn(`docker-controller-scala-core`, `docker-controller-scala-scalatest` % Test)
164+
).dependsOn(
165+
`docker-controller-scala-core`,
166+
`docker-controller-scala-scalatest` % Test,
167+
`docker-controller-scala-flyway` % Test
168+
)
154169

155170
val `docker-controller-scala-postgresql` = (project in file("docker-controller-scala-postgresql"))
156171
.settings(baseSettings)
@@ -161,7 +176,11 @@ val `docker-controller-scala-postgresql` = (project in file("docker-controller-s
161176
logback.classic % Test,
162177
postgresql.postgresql % Test
163178
)
164-
).dependsOn(`docker-controller-scala-core`, `docker-controller-scala-scalatest` % Test)
179+
).dependsOn(
180+
`docker-controller-scala-core`,
181+
`docker-controller-scala-scalatest` % Test,
182+
`docker-controller-scala-flyway` % Test
183+
)
165184

166185
val `docker-controller-scala-redis` = (project in file("docker-controller-scala-redis"))
167186
.settings(baseSettings)
@@ -174,16 +193,6 @@ val `docker-controller-scala-redis` = (project in file("docker-controller-scala-
174193
)
175194
).dependsOn(`docker-controller-scala-core`, `docker-controller-scala-scalatest` % Test)
176195

177-
val `docker-controller-scala-flyway` = (project in file("docker-controller-scala-flyway"))
178-
.settings(baseSettings)
179-
.settings(
180-
name := "docker-controller-scala-flyway",
181-
libraryDependencies ++= Seq(
182-
scalatest.scalatest % Test,
183-
logback.classic % Test
184-
)
185-
).dependsOn(`docker-controller-scala-core`, `docker-controller-scala-scalatest` % Test)
186-
187196
val `docker-controller-scala-elasticmq` = (project in file("docker-controller-scala-elasticmq"))
188197
.settings(baseSettings)
189198
.settings(

docker-controller-scala-flyway/src/main/scala/com/github/j5ik2o/dockerController/flyway/FlywayController.scala

Lines changed: 0 additions & 60 deletions
This file was deleted.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.github.j5ik2o.dockerController.flyway
2+
3+
import org.flywaydb.core.Flyway
4+
import org.flywaydb.core.api.callback.Callback
5+
import org.flywaydb.core.internal.jdbc.DriverDataSource
6+
7+
import javax.sql.DataSource
8+
import scala.jdk.CollectionConverters._
9+
10+
final case class PlaceholderConfig(
11+
placeholderReplacement: Boolean = false,
12+
placeholders: Map[String, String] = Map.empty,
13+
placeholderPrefix: Option[String] = None,
14+
placeholderSuffix: Option[String] = None
15+
)
16+
17+
final case class FlywayConfig(
18+
locations: Seq[String],
19+
callbacks: Seq[Callback] = Seq.empty,
20+
placeholderConfig: Option[PlaceholderConfig] = None
21+
)
22+
23+
final case class FlywayConfigWithDataSource(driverDataSource: DataSource, config: FlywayConfig)
24+
25+
final case class FlywayContext(flyway: Flyway, config: FlywayConfigWithDataSource)
26+
27+
trait FlywaySpecSupport {
28+
protected def flywayDriverClassName: String
29+
protected def flywayDbHost: String
30+
protected def flywayDbHostPort: Int
31+
protected def flywayDbName: String
32+
protected def flywayDbUserName: String
33+
protected def flywayDbPassword: String
34+
protected def flywayJDBCUrl: String
35+
36+
private def createFlywayContext(flywayConfigWithDataSource: FlywayConfigWithDataSource): FlywayContext = {
37+
val configure = Flyway.configure()
38+
configure.dataSource(flywayConfigWithDataSource.driverDataSource)
39+
configure.locations(flywayConfigWithDataSource.config.locations: _*)
40+
configure.callbacks(flywayConfigWithDataSource.config.callbacks: _*)
41+
flywayConfigWithDataSource.config.placeholderConfig.foreach { pc =>
42+
configure.placeholderReplacement(pc.placeholderReplacement)
43+
configure.placeholders(pc.placeholders.asJava)
44+
pc.placeholderPrefix.foreach { pp =>
45+
configure.placeholderPrefix(pp)
46+
}
47+
pc.placeholderSuffix.foreach { ps =>
48+
configure.placeholderSuffix(ps)
49+
}
50+
}
51+
FlywayContext(configure.load(), flywayConfigWithDataSource)
52+
}
53+
54+
private def createFlywayDataSource: DataSource = new DriverDataSource(
55+
getClass.getClassLoader,
56+
flywayDriverClassName,
57+
flywayJDBCUrl,
58+
flywayDbUserName,
59+
flywayDbPassword
60+
)
61+
// s"jdbc:mysql://$flywayDbHost:$flywayDbHostPort/$flywayDbName?useSSL=false&user=$flywayDbUserName&password=$flywayDbPassword",
62+
63+
protected def createFlywayContext(flywayConfig: FlywayConfig): FlywayContext = createFlywayContext(
64+
FlywayConfigWithDataSource(createFlywayDataSource, flywayConfig)
65+
)
66+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATE TABLE `users` (
2+
`id` bigint NOT NULL ,
3+
`name` varchar(255),
4+
PRIMARY KEY (`id`)
5+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

docker-controller-scala-mysql/src/test/scala/com/github/j5ik2o/dockerController/MySQLControllerSpec.scala

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
11
package com.github.j5ik2o.dockerController
22

3+
import com.github.j5ik2o.dockerController.flyway.{ FlywayConfig, FlywaySpecSupport }
34
import com.github.j5ik2o.dockerController.mysql.MySQLController
45
import org.scalatest.freespec.AnyFreeSpec
56

67
import java.sql.{ Connection, DriverManager, ResultSet, Statement }
78
import scala.concurrent.duration.{ Duration, DurationInt }
89
import scala.util.control.NonFatal
910

10-
class MySQLControllerSpec extends AnyFreeSpec with DockerControllerSpecSupport {
11+
class MySQLControllerSpec extends AnyFreeSpec with DockerControllerSpecSupport with FlywaySpecSupport {
1112
val testTimeFactor: Int = sys.env.getOrElse("TEST_TIME_FACTOR", "1").toInt
1213
logger.debug(s"testTimeFactor = $testTimeFactor")
1314

14-
val hostPort: Int = RandomPortUtil.temporaryServerPort()
15-
val rootPassword: String = "test"
15+
val hostPort: Int = RandomPortUtil.temporaryServerPort()
16+
val rootPassword: String = "test"
17+
18+
override protected def flywayDriverClassName: String = classOf[com.mysql.cj.jdbc.Driver].getName
19+
override protected def flywayDbHost: String = dockerHost
20+
override protected def flywayDbHostPort: Int = hostPort
21+
override protected def flywayDbName: String = "test"
22+
override protected def flywayDbUserName: String = "root"
23+
override protected def flywayDbPassword: String = rootPassword
24+
25+
override protected def flywayJDBCUrl: String =
26+
s"jdbc:mysql://$flywayDbHost:$flywayDbHostPort/$flywayDbName?useSSL=false&user=$flywayDbUserName&password=$flywayDbPassword"
27+
1628
val controller: MySQLController = MySQLController(dockerClient)(hostPort, rootPassword, databaseName = Some("test"))
1729
override protected val dockerControllers: Vector[DockerController] = Vector(controller)
1830

@@ -35,14 +47,17 @@ class MySQLControllerSpec extends AnyFreeSpec with DockerControllerSpecSupport {
3547
var stmt: Statement = null
3648
var resultSet: ResultSet = null
3749
try {
38-
Class.forName("com.mysql.cj.jdbc.Driver")
39-
conn = DriverManager.getConnection(
40-
s"jdbc:mysql://$dockerHost:$hostPort/test?user=root&password=$rootPassword"
41-
)
50+
Class.forName(flywayDriverClassName)
51+
conn = DriverManager.getConnection(flywayJDBCUrl)
4252
stmt = conn.createStatement
43-
resultSet = stmt.executeQuery("SELECT 1 FROM DUAL")
44-
while (resultSet.next())
45-
assert(resultSet.getInt(1) == 1)
53+
val result = stmt.executeUpdate("INSERT INTO users VALUES(1, 'kato')")
54+
assert(result == 1)
55+
resultSet = stmt.executeQuery("SELECT * FROM users")
56+
while (resultSet.next()) {
57+
val id = resultSet.getInt("id")
58+
val name = resultSet.getString("name")
59+
println(s"id = $id, name = $name")
60+
}
4661
} catch {
4762
case NonFatal(ex) =>
4863
ex.printStackTrace()
@@ -57,4 +72,10 @@ class MySQLControllerSpec extends AnyFreeSpec with DockerControllerSpecSupport {
5772
}
5873
}
5974
}
75+
76+
override protected def afterStartContainers(): Unit = {
77+
val flywayContext = createFlywayContext(FlywayConfig(Seq("flyway")))
78+
flywayContext.flyway.migrate()
79+
}
80+
6081
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CREATE TABLE users (
2+
id integer PRIMARY KEY,
3+
name varchar(255)
4+
);

docker-controller-scala-postgresql/src/test/scala/com/github/j5ik2o/dockerController/postgresql/PostgreSQLControllerSpec.scala

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.j5ik2o.dockerController.postgresql
22

3+
import com.github.j5ik2o.dockerController.flyway.{ FlywayConfig, FlywaySpecSupport }
34
import com.github.j5ik2o.dockerController.{
45
DockerController,
56
DockerControllerSpecSupport,
@@ -12,15 +13,31 @@ import java.sql.{ Connection, DriverManager, ResultSet, Statement }
1213
import scala.concurrent.duration._
1314
import scala.util.control.NonFatal
1415

15-
class PostgreSQLControllerSpec extends AnyFreeSpec with DockerControllerSpecSupport {
16+
class PostgreSQLControllerSpec extends AnyFreeSpec with DockerControllerSpecSupport with FlywaySpecSupport {
1617
val testTimeFactor: Int = sys.env.getOrElse("TEST_TIME_FACTOR", "1").toInt
1718
logger.debug(s"testTimeFactor = $testTimeFactor")
1819

1920
val hostPort: Int = RandomPortUtil.temporaryServerPort()
21+
val dbName = "test"
2022
val rootUserName: String = "postgres"
2123
val rootPassword: Option[String] = Some("test")
2224

23-
val controller: PostgreSQLController = PostgreSQLController(dockerClient)(hostPort, rootUserName, rootPassword)
25+
override protected def flywayDriverClassName: String = classOf[org.postgresql.Driver].getName
26+
override protected def flywayDbHost: String = dockerHost
27+
override protected def flywayDbHostPort: Int = hostPort
28+
override protected def flywayDbName: String = dbName
29+
override protected def flywayDbUserName: String = rootUserName
30+
override protected def flywayDbPassword: String = rootPassword.get
31+
32+
override protected def flywayJDBCUrl: String = s"jdbc:postgresql://$flywayDbHost:$flywayDbHostPort/$flywayDbName"
33+
34+
val controller: PostgreSQLController =
35+
PostgreSQLController(dockerClient)(
36+
flywayDbHostPort,
37+
flywayDbUserName,
38+
rootPassword,
39+
databaseName = Some(flywayDbName)
40+
)
2441
override protected val dockerControllers: Vector[DockerController] = Vector(controller)
2542

2643
override protected val waitPredicatesSettings: Map[DockerController, WaitPredicateSetting] =
@@ -31,27 +48,32 @@ class PostgreSQLControllerSpec extends AnyFreeSpec with DockerControllerSpecSupp
3148
dockerHost,
3249
hostPort,
3350
(1 * testTimeFactor).seconds,
34-
Some((5 * testTimeFactor).seconds)
51+
Some((8 * testTimeFactor).seconds)
3552
)
3653
)
3754
)
3855

39-
"MySQLController" - {
56+
"PostgreSQLController" - {
4057
"run" in {
4158
var conn: Connection = null
4259
var stmt: Statement = null
4360
var resultSet: ResultSet = null
4461
try {
45-
Class.forName("org.postgresql.Driver")
62+
Class.forName(flywayDriverClassName)
4663
conn = DriverManager.getConnection(
47-
s"jdbc:postgresql://$dockerHost:$hostPort/postgres",
48-
rootUserName,
49-
rootPassword.get
64+
flywayJDBCUrl,
65+
flywayDbUserName,
66+
flywayDbPassword
5067
)
5168
stmt = conn.createStatement
52-
resultSet = stmt.executeQuery("SELECT 1 FROM DUAL")
53-
while (resultSet.next())
54-
assert(resultSet.getInt(1) == 1)
69+
val result = stmt.executeUpdate("INSERT INTO users VALUES(1, 'kato')")
70+
assert(result == 1)
71+
resultSet = stmt.executeQuery("SELECT * FROM users")
72+
while (resultSet.next()) {
73+
val id = resultSet.getInt("id")
74+
val name = resultSet.getString("name")
75+
println(s"id = $id, name = $name")
76+
}
5577
} catch {
5678
case NonFatal(ex) =>
5779
ex.printStackTrace()
@@ -66,4 +88,9 @@ class PostgreSQLControllerSpec extends AnyFreeSpec with DockerControllerSpecSupp
6688
}
6789
}
6890
}
91+
92+
override protected def afterStartContainers(): Unit = {
93+
val flywayContext = createFlywayContext(FlywayConfig(Seq("flyway")))
94+
flywayContext.flyway.migrate()
95+
}
6996
}

0 commit comments

Comments
 (0)