From 4773a8139628e573348885e2904c2cf08936e443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Ma=C3=B1es=20Medina?= Date: Fri, 28 Jul 2023 13:38:37 +0200 Subject: [PATCH 1/3] WIP:Refactor migrations as a component --- Makefile | 4 ++ apps/migrations/Dockerfile | 5 --- .../migrations/KotlinMigration.kt | 29 ------------ apps/migrationsapp/Dockerfile | 5 +++ .../build.gradle.kts | 4 +- .../timetracking/migrationsapp/MigrateDb.kt | 12 +++++ components/migrations/build.gradle.kts | 19 ++++++++ .../migrations/KotlinMigration.kt | 45 ++++++++++++++++--- .../scripts/Migration202306231200.kt | 0 components/postgresdb/build.gradle.kts | 1 + .../PostgresTimeEntriesRepository.kt | 15 ------- .../postgresdb/TimeEntriesRepositoryTest.kt | 9 +--- 12 files changed, 83 insertions(+), 65 deletions(-) delete mode 100644 apps/migrations/Dockerfile delete mode 100644 apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt create mode 100644 apps/migrationsapp/Dockerfile rename apps/{migrations => migrationsapp}/build.gradle.kts (94%) create mode 100644 apps/migrationsapp/src/main/kotlin/com/agilogy/timetracking/migrationsapp/MigrateDb.kt create mode 100644 components/migrations/build.gradle.kts rename apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/MigrateDb.kt => components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt (50%) rename {apps => components}/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/scripts/Migration202306231200.kt (100%) diff --git a/Makefile b/Makefile index 907db04..d9c8d42 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,10 @@ build: test: ./gradlew test +.PHONY: format +format: + ./gradlew --continue ktlintFormat + .PHONY: deploy deploy: ./gradlew build -x test -x check diff --git a/apps/migrations/Dockerfile b/apps/migrations/Dockerfile deleted file mode 100644 index 9cdf9f6..0000000 --- a/apps/migrations/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM openjdk:17.0.1-jdk-slim - -COPY build/libs/migrations-1.0-SNAPSHOT.jar migrations.jar - -CMD [ "java", "-jar" , "migrations.jar"] diff --git a/apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt b/apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt deleted file mode 100644 index 08320ab..0000000 --- a/apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.agilogy.timetracking.migrations - -import kotlinx.coroutines.runBlocking -import org.flywaydb.core.api.MigrationVersion -import org.flywaydb.core.api.migration.Context -import org.flywaydb.core.api.migration.JavaMigration -import java.sql.Connection - -abstract class KotlinMigration(private val version: String, private val title: String) : JavaMigration { - - override fun getVersion(): MigrationVersion = MigrationVersion.fromVersion(version) - - override fun getDescription(): String = title - - override fun getChecksum(): Int? = null - - override fun canExecuteInTransaction(): Boolean = true - - final override fun migrate(context: Context) = runBlocking { - context.connection!!.use { - with(it) { - migrate() - } - } - } - - context(Connection) - abstract suspend fun migrate() -} diff --git a/apps/migrationsapp/Dockerfile b/apps/migrationsapp/Dockerfile new file mode 100644 index 0000000..20ffb0a --- /dev/null +++ b/apps/migrationsapp/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:17.0.1-jdk-slim + +COPY build/libs/migrationsapp-1.0-SNAPSHOT.jar migrationsapp.jar + +CMD [ "java", "-jar" , "migrationsapp.jar"] diff --git a/apps/migrations/build.gradle.kts b/apps/migrationsapp/build.gradle.kts similarity index 94% rename from apps/migrations/build.gradle.kts rename to apps/migrationsapp/build.gradle.kts index f4348ce..17cfcd7 100644 --- a/apps/migrations/build.gradle.kts +++ b/apps/migrationsapp/build.gradle.kts @@ -1,4 +1,4 @@ -import Dependencies.flywayCore + import Dependencies.kotestRunnerJunit import Dependencies.postgresql import Dependencies.slf4jProvider @@ -33,7 +33,7 @@ application { dependencies { implementation(slf4jProvider) implementation(postgresql) - implementation(flywayCore) + implementation(project(":migrations")) implementation(project(":db")) implementation(project(":postgresdb")) implementation(project(":herokupostgres")) diff --git a/apps/migrationsapp/src/main/kotlin/com/agilogy/timetracking/migrationsapp/MigrateDb.kt b/apps/migrationsapp/src/main/kotlin/com/agilogy/timetracking/migrationsapp/MigrateDb.kt new file mode 100644 index 0000000..43b362e --- /dev/null +++ b/apps/migrationsapp/src/main/kotlin/com/agilogy/timetracking/migrationsapp/MigrateDb.kt @@ -0,0 +1,12 @@ +package com.agilogy.timetracking.migrationsapp + +import arrow.continuations.SuspendApp +import com.agilogy.db.hikari.HikariCp +import com.agilogy.heroku.postgres.loadHerokuPostgresConfig +import com.agilogy.timetracking.migrations.runMigrations + +fun main() = SuspendApp { + val (jdbcUrl, username, password) = loadHerokuPostgresConfig() + val dataSource = HikariCp.dataSource(jdbcUrl, username, password) + dataSource.use { runMigrations(it) } +} diff --git a/components/migrations/build.gradle.kts b/components/migrations/build.gradle.kts new file mode 100644 index 0000000..a2f8520 --- /dev/null +++ b/components/migrations/build.gradle.kts @@ -0,0 +1,19 @@ +import Dependencies.flywayCore +import Dependencies.hikariCp +import Dependencies.kotestRunnerJunit +import Dependencies.postgresql +import Dependencies.slf4jProvider + +dependencies { + implementation(project(":domain")) + implementation(project(":db")) + api(flywayCore) + implementation(slf4jProvider) + + testImplementation(hikariCp) + testImplementation(postgresql) + testImplementation(kotestRunnerJunit) + testImplementation(testFixtures(project(":domain"))) + + testFixturesImplementation(project(":domain")) +} diff --git a/apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/MigrateDb.kt b/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt similarity index 50% rename from apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/MigrateDb.kt rename to components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt index 62f70d9..3ec3426 100644 --- a/apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/MigrateDb.kt +++ b/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt @@ -1,20 +1,47 @@ package com.agilogy.timetracking.migrations -import arrow.continuations.SuspendApp -import com.agilogy.heroku.postgres.loadHerokuPostgresConfig +import kotlinx.coroutines.runBlocking import org.flywaydb.core.Flyway +import org.flywaydb.core.api.MigrationVersion import org.flywaydb.core.api.configuration.FluentConfiguration +import org.flywaydb.core.api.migration.Context +import org.flywaydb.core.api.migration.JavaMigration import org.slf4j.LoggerFactory +import java.sql.Connection +import javax.sql.DataSource -fun main() = SuspendApp { - val (jdbcUrl, username, password) = loadHerokuPostgresConfig() +abstract class KotlinMigration(private val version: String, private val title: String) : JavaMigration { + override fun getVersion(): MigrationVersion = MigrationVersion.fromVersion(version) + + override fun getDescription(): String = title + + override fun getChecksum(): Int? = null + + override fun canExecuteInTransaction(): Boolean = true + + final override fun migrate(context: Context) = runBlocking { + context.connection!!.use { + with(it) { + migrate() + } + } + } + + context(Connection) + abstract suspend fun migrate() +} + +fun runMigrations( + dataSource: DataSource, + clean: Boolean = false, +) { val flywayConfig: FluentConfiguration = Flyway.configure() - .dataSource(jdbcUrl, username, password) + .dataSource(dataSource) .group(true) .outOfOrder(false) .baselineOnMigrate(true) - .locations("classpath:${this.javaClass.packageName}.scripts") + .locations("filesystem:./scripts") .loggers("slf4j") val validated = flywayConfig @@ -37,5 +64,9 @@ fun main() = SuspendApp { ) } } - flywayConfig.load().migrate() + if (clean) { + flywayConfig.cleanDisabled(false) + flywayConfig.load().clean() + } + println(flywayConfig.load().migrate()) } diff --git a/apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/scripts/Migration202306231200.kt b/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/scripts/Migration202306231200.kt similarity index 100% rename from apps/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/scripts/Migration202306231200.kt rename to components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/scripts/Migration202306231200.kt diff --git a/components/postgresdb/build.gradle.kts b/components/postgresdb/build.gradle.kts index 12f39bd..da1dd6e 100644 --- a/components/postgresdb/build.gradle.kts +++ b/components/postgresdb/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { testImplementation(postgresql) testImplementation(kotestRunnerJunit) testImplementation(testFixtures(project(":domain"))) + testImplementation(project(":migrations")) testFixturesImplementation(project(":domain")) } diff --git a/components/postgresdb/src/main/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/PostgresTimeEntriesRepository.kt b/components/postgresdb/src/main/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/PostgresTimeEntriesRepository.kt index 0e2cd5a..43e5fd0 100644 --- a/components/postgresdb/src/main/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/PostgresTimeEntriesRepository.kt +++ b/components/postgresdb/src/main/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/PostgresTimeEntriesRepository.kt @@ -28,21 +28,6 @@ class PostgresTimeEntriesRepository(private val dataSource: DataSource) : TimeEn private fun ResultSetView.project(columnIndex: Int): ProjectName? = string(columnIndex)?.let { ProjectName(it) } - companion object { - val dbMigrations = listOf( - """create table time_entries( - |id serial, - |developer text not null, - |project text not null, - |start timestamptz not null, - |"end" timestamptz not null - ) - """.trimMargin(), - """alter table time_entries add column zone_id text not null default 'Europe/Madrid'""", - """alter table time_entries alter column zone_id drop default""", - ) - } - override suspend fun saveTimeEntries(timeEntries: List) = dataSource.sql { val sql = """insert into time_entries(developer, project, start, "end", zone_id) values (?, ?, ?, ?, ?)""" batchUpdate(sql) { diff --git a/components/postgresdb/src/test/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/TimeEntriesRepositoryTest.kt b/components/postgresdb/src/test/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/TimeEntriesRepositoryTest.kt index a1c2ca9..2870614 100644 --- a/components/postgresdb/src/test/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/TimeEntriesRepositoryTest.kt +++ b/components/postgresdb/src/test/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/TimeEntriesRepositoryTest.kt @@ -10,6 +10,7 @@ import com.agilogy.timetracking.domain.ProjectName import com.agilogy.timetracking.domain.TimeEntriesRepository import com.agilogy.timetracking.domain.TimeEntry import com.agilogy.timetracking.domain.test.InMemoryTimeEntriesRepository +import com.agilogy.timetracking.migrations.runMigrations import io.kotest.core.spec.style.FunSpec import io.kotest.core.test.TestScope import org.junit.jupiter.api.Assertions.assertEquals @@ -42,13 +43,7 @@ class TimeEntriesRepositoryTest : FunSpec() { withTestDataSource { dataSource -> println("Recreating table time_entries") - kotlin.runCatching { dataSource.sql { Sql.update("drop table time_entries") } } - .recoverIf(Unit) { it is PSQLException && it.sqlState == PostgreSql.UndefinedTable }.getOrThrow() - PostgresTimeEntriesRepository.dbMigrations.forEach { dbMigration -> - dataSource.sql { - Sql.update(dbMigration) - } - } + runMigrations(dataSource, clean = true) f(PostgresTimeEntriesRepository(dataSource)) } } From 381a74dd2a545431dfe9774dbbcdaa3ccf7e4193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Ma=C3=B1es=20Medina?= Date: Fri, 28 Jul 2023 13:59:01 +0200 Subject: [PATCH 2/3] Change flyway locations --- .../com/agilogy/timetracking/migrations/KotlinMigration.kt | 2 +- .../drivenadapters/postgresdb/TimeEntriesRepositoryTest.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt b/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt index 3ec3426..011946c 100644 --- a/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt +++ b/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt @@ -41,7 +41,7 @@ fun runMigrations( .group(true) .outOfOrder(false) .baselineOnMigrate(true) - .locations("filesystem:./scripts") + .locations("classpath:${KotlinMigration::class.java.packageName}.scripts") .loggers("slf4j") val validated = flywayConfig diff --git a/components/postgresdb/src/test/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/TimeEntriesRepositoryTest.kt b/components/postgresdb/src/test/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/TimeEntriesRepositoryTest.kt index 2870614..6cb5d76 100644 --- a/components/postgresdb/src/test/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/TimeEntriesRepositoryTest.kt +++ b/components/postgresdb/src/test/kotlin/com/agilogy/timetracking/drivenadapters/postgresdb/TimeEntriesRepositoryTest.kt @@ -42,7 +42,6 @@ class TimeEntriesRepositoryTest : FunSpec() { } withTestDataSource { dataSource -> - println("Recreating table time_entries") runMigrations(dataSource, clean = true) f(PostgresTimeEntriesRepository(dataSource)) } From 79e1946c5d96c57efa45577d964509382e59518a Mon Sep 17 00:00:00 2001 From: Jordi Pradel Date: Fri, 28 Jul 2023 15:15:47 +0200 Subject: [PATCH 3/3] Fix migrations in test --- .../timetracking/migrations/KotlinMigration.kt | 17 ++++++----------- .../migrations/scripts/Migration202306231200.kt | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt b/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt index 011946c..738d1ec 100644 --- a/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt +++ b/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/KotlinMigration.kt @@ -1,5 +1,6 @@ package com.agilogy.timetracking.migrations +import com.agilogy.timetracking.migrations.scripts.Migration202306231200 import kotlinx.coroutines.runBlocking import org.flywaydb.core.Flyway import org.flywaydb.core.api.MigrationVersion @@ -21,10 +22,8 @@ abstract class KotlinMigration(private val version: String, private val title: S override fun canExecuteInTransaction(): Boolean = true final override fun migrate(context: Context) = runBlocking { - context.connection!!.use { - with(it) { - migrate() - } + with(context.connection!!) { + migrate() } } @@ -32,16 +31,13 @@ abstract class KotlinMigration(private val version: String, private val title: S abstract suspend fun migrate() } -fun runMigrations( - dataSource: DataSource, - clean: Boolean = false, -) { +fun runMigrations(dataSource: DataSource, clean: Boolean = false) { val flywayConfig: FluentConfiguration = Flyway.configure() .dataSource(dataSource) .group(true) .outOfOrder(false) .baselineOnMigrate(true) - .locations("classpath:${KotlinMigration::class.java.packageName}.scripts") + .locations("classpath:${Migration202306231200::class.java.packageName.replace('.', '/')}") .loggers("slf4j") val validated = flywayConfig @@ -65,8 +61,7 @@ fun runMigrations( } } if (clean) { - flywayConfig.cleanDisabled(false) - flywayConfig.load().clean() + flywayConfig.cleanDisabled(false).load().clean() } println(flywayConfig.load().migrate()) } diff --git a/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/scripts/Migration202306231200.kt b/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/scripts/Migration202306231200.kt index 643eb31..d7a3af9 100644 --- a/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/scripts/Migration202306231200.kt +++ b/components/migrations/src/main/kotlin/com/agilogy/timetracking/migrations/scripts/Migration202306231200.kt @@ -18,7 +18,7 @@ class Migration202306231200 : KotlinMigration("202306231200", "Initial migration |"end" timestamptz not null, |zone_id text not null |) - """, + """.trimMargin(), ) } }