From 75e3fc673b8a7f052d0df8ba89e151d49ddc7bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 09:45:14 +0100 Subject: [PATCH 01/10] Add okapi-mysql compile and test dependencies to okapi-spring-boot --- okapi-spring-boot/build.gradle.kts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/okapi-spring-boot/build.gradle.kts b/okapi-spring-boot/build.gradle.kts index e0ef746..5c4666e 100644 --- a/okapi-spring-boot/build.gradle.kts +++ b/okapi-spring-boot/build.gradle.kts @@ -15,6 +15,9 @@ dependencies { // Optional postgres store autoconfiguration — compileOnly + @ConditionalOnClass guards runtime absence compileOnly(project(":okapi-postgres")) + // Optional mysql store autoconfiguration — compileOnly + @ConditionalOnClass guards runtime absence + compileOnly(project(":okapi-mysql")) + // Optional Liquibase migration support — compileOnly, activated only when liquibase-core is on the runtime classpath compileOnly(libs.liquibaseCore) @@ -25,6 +28,7 @@ dependencies { testImplementation(libs.exposedCore) testImplementation(libs.springBootAutoconfigure) testImplementation(project(":okapi-postgres")) + testImplementation(project(":okapi-mysql")) // E2E test dependencies testImplementation(project(":okapi-http")) @@ -34,5 +38,7 @@ dependencies { testImplementation(libs.liquibaseCore) testImplementation(libs.testcontainersPostgresql) testImplementation(libs.postgresql) + testImplementation(libs.testcontainersMysql) + testImplementation(libs.mysql) testImplementation(libs.wiremock) } From 2f1a528729b21a64544265f7678626ff8adc3034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 09:46:57 +0100 Subject: [PATCH 02/10] Fix proxyBeanMethods and rename Postgres Liquibase bean to okapiPostgresLiquibase --- .../okapi/springboot/OutboxAutoConfiguration.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt index 05ccc4e..620d3ab 100644 --- a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt +++ b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt @@ -102,7 +102,7 @@ class OutboxAutoConfiguration { * when `outbox-postgres` is on the classpath. * Skipped if the application provides its own [OutboxStore] bean. */ - @Configuration + @Configuration(proxyBeanMethods = false) @ConditionalOnClass(PostgresOutboxStore::class) class PostgresStoreConfiguration { @Bean @@ -115,18 +115,18 @@ class OutboxAutoConfiguration { * - a [DataSource] bean is available * * The migration is idempotent (CREATE TABLE IF NOT EXISTS) and uses a separate - * Liquibase bean named `okapiLiquibase` to avoid conflicting with the application's + * Liquibase bean named `okapiPostgresLiquibase` to avoid conflicting with the application's * own Liquibase configuration. * - * To opt out, define your own bean named `okapiLiquibase` or include the okapi + * To opt out, define your own bean named `okapiPostgresLiquibase` or include the okapi * changelog manually in your master changelog: * `classpath:com/softwaremill/okapi/db/changelog.xml` */ - @Bean("okapiLiquibase") + @Bean("okapiPostgresLiquibase") @ConditionalOnClass(SpringLiquibase::class) @ConditionalOnBean(DataSource::class) - @ConditionalOnMissingBean(name = ["okapiLiquibase"]) - fun okapiLiquibase(dataSource: DataSource): SpringLiquibase = SpringLiquibase().apply { + @ConditionalOnMissingBean(name = ["okapiPostgresLiquibase"]) + fun okapiPostgresLiquibase(dataSource: DataSource): SpringLiquibase = SpringLiquibase().apply { this.dataSource = dataSource changeLog = "classpath:com/softwaremill/okapi/db/changelog.xml" } From f5292c1687b1b12d5c4b80c555d78a8dab4b930d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 09:48:20 +0100 Subject: [PATCH 03/10] Add MysqlStoreConfiguration for MySQL autoconfiguration --- .../springboot/OutboxAutoConfiguration.kt | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt index 620d3ab..f9e08ac 100644 --- a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt +++ b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt @@ -7,6 +7,7 @@ import com.softwaremill.okapi.core.OutboxProcessor import com.softwaremill.okapi.core.OutboxPublisher import com.softwaremill.okapi.core.OutboxStore import com.softwaremill.okapi.core.RetryPolicy +import com.softwaremill.okapi.mysql.MysqlOutboxStore import com.softwaremill.okapi.postgres.PostgresOutboxStore import liquibase.integration.spring.SpringLiquibase import org.springframework.beans.factory.ObjectProvider @@ -32,7 +33,9 @@ import javax.sql.DataSource * and routed by the `type` field in each entry's deliveryMetadata. * * Optional beans with defaults: - * - [OutboxStore] — auto-configured to [PostgresOutboxStore] if `outbox-postgres` is on the classpath + * - [OutboxStore] — auto-configured to [PostgresOutboxStore] or [MysqlOutboxStore] + * depending on which module (`okapi-postgres` / `okapi-mysql`) is on the classpath. + * If both are present, Postgres takes priority. Override by defining your own `@Bean OutboxStore`. * - [Clock] — defaults to [Clock.systemUTC] * - [RetryPolicy] — defaults to `maxRetries = 5` * - [PlatformTransactionManager] — if absent, each store call runs in its own transaction @@ -131,4 +134,31 @@ class OutboxAutoConfiguration { changeLog = "classpath:com/softwaremill/okapi/db/changelog.xml" } } + + /** + * Auto-configures [MysqlOutboxStore] and Liquibase schema migration + * when `okapi-mysql` is on the classpath. + * Skipped if the application provides its own [OutboxStore] bean. + * + * When both `okapi-postgres` and `okapi-mysql` are on the classpath, + * [PostgresStoreConfiguration] takes priority (declared first). + * Override by defining your own `@Bean OutboxStore`. + */ + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(MysqlOutboxStore::class) + class MysqlStoreConfiguration { + @Bean + @ConditionalOnMissingBean(OutboxStore::class) + fun outboxStore(clock: ObjectProvider): OutboxStore = + MysqlOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) + + @Bean("okapiMysqlLiquibase") + @ConditionalOnClass(SpringLiquibase::class) + @ConditionalOnBean(DataSource::class) + @ConditionalOnMissingBean(name = ["okapiMysqlLiquibase"]) + fun okapiMysqlLiquibase(dataSource: DataSource): SpringLiquibase = SpringLiquibase().apply { + this.dataSource = dataSource + changeLog = "classpath:com/softwaremill/okapi/db/mysql/changelog.xml" + } + } } From 9ed6a970dbf304e59096fc7709779c657f32211e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 09:52:31 +0100 Subject: [PATCH 04/10] Add MySQL E2E test for Spring Boot autoconfiguration --- .../springboot/OutboxMysqlEndToEndTest.kt | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 okapi-spring-boot/src/test/kotlin/com/softwaremill/okapi/springboot/OutboxMysqlEndToEndTest.kt diff --git a/okapi-spring-boot/src/test/kotlin/com/softwaremill/okapi/springboot/OutboxMysqlEndToEndTest.kt b/okapi-spring-boot/src/test/kotlin/com/softwaremill/okapi/springboot/OutboxMysqlEndToEndTest.kt new file mode 100644 index 0000000..18153ba --- /dev/null +++ b/okapi-spring-boot/src/test/kotlin/com/softwaremill/okapi/springboot/OutboxMysqlEndToEndTest.kt @@ -0,0 +1,196 @@ +package com.softwaremill.okapi.springboot + +import com.github.tomakehurst.wiremock.WireMockServer +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo +import com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig +import com.softwaremill.okapi.core.OutboxEntryProcessor +import com.softwaremill.okapi.core.OutboxMessage +import com.softwaremill.okapi.core.OutboxProcessor +import com.softwaremill.okapi.core.OutboxPublisher +import com.softwaremill.okapi.core.OutboxStatus +import com.softwaremill.okapi.core.RetryPolicy +import com.softwaremill.okapi.http.HttpMessageDeliverer +import com.softwaremill.okapi.http.ServiceUrlResolver +import com.softwaremill.okapi.http.httpDeliveryInfo +import com.softwaremill.okapi.mysql.MysqlOutboxStore +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe +import liquibase.Liquibase +import liquibase.database.DatabaseFactory +import liquibase.database.jvm.JdbcConnection +import liquibase.resource.ClassLoaderResourceAccessor +import org.jetbrains.exposed.v1.jdbc.Database +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.testcontainers.containers.MySQLContainer +import java.sql.DriverManager +import java.time.Clock + +class OutboxMysqlEndToEndTest : + BehaviorSpec({ + val mysql = MySQLContainer("mysql:8.0") + val wiremock = WireMockServer(wireMockConfig().dynamicPort()) + + lateinit var store: MysqlOutboxStore + lateinit var publisher: OutboxPublisher + lateinit var processor: OutboxProcessor + + beforeSpec { + mysql.start() + wiremock.start() + + Database.connect( + url = mysql.jdbcUrl, + driver = mysql.driverClassName, + user = mysql.username, + password = mysql.password, + ) + + val connection = DriverManager.getConnection(mysql.jdbcUrl, mysql.username, mysql.password) + val db = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(JdbcConnection(connection)) + Liquibase("com/softwaremill/okapi/db/mysql/changelog.xml", ClassLoaderResourceAccessor(), db) + .use { it.update("") } + connection.close() + + val clock = Clock.systemUTC() + store = MysqlOutboxStore(clock) + publisher = OutboxPublisher(store, clock) + + val urlResolver = ServiceUrlResolver { "http://localhost:${wiremock.port()}" } + val deliverer = HttpMessageDeliverer(urlResolver) + val entryProcessor = OutboxEntryProcessor(deliverer, RetryPolicy(maxRetries = 3), clock) + processor = OutboxProcessor(store, entryProcessor) + } + + afterSpec { + wiremock.stop() + mysql.stop() + } + + beforeEach { + wiremock.resetAll() + transaction { exec("DELETE FROM outbox") } + } + + given("a message published within a transaction") { + `when`("the HTTP endpoint returns 200") { + wiremock.stubFor( + post(urlEqualTo("/api/notify")) + .willReturn(aResponse().withStatus(200)), + ) + + transaction { + publisher.publish( + OutboxMessage("order.created", """{"orderId":"abc-123"}"""), + httpDeliveryInfo { + serviceName = "notification-service" + endpointPath = "/api/notify" + }, + ) + } + + transaction { processor.processNext() } + + val requests = wiremock.findAll(postRequestedFor(urlEqualTo("/api/notify"))) + val counts = transaction { store.countByStatuses() } + + then("WireMock receives exactly one POST request") { + requests.size shouldBe 1 + } + then("request body matches the published payload") { + requests.first().bodyAsString shouldBe """{"orderId":"abc-123"}""" + } + then("entry is marked as DELIVERED") { + counts[OutboxStatus.DELIVERED] shouldBe 1L + } + } + + `when`("the HTTP endpoint returns 500") { + wiremock.stubFor( + post(urlEqualTo("/api/notify")) + .willReturn(aResponse().withStatus(500).withBody("Internal Server Error")), + ) + + transaction { + publisher.publish( + OutboxMessage("order.created", """{"orderId":"xyz-456"}"""), + httpDeliveryInfo { + serviceName = "notification-service" + endpointPath = "/api/notify" + }, + ) + } + + transaction { processor.processNext() } + + val counts = transaction { store.countByStatuses() } + + then("entry stays PENDING (retriable failure, retries remaining)") { + counts[OutboxStatus.PENDING] shouldBe 1L + } + then("no DELIVERED entries") { + counts[OutboxStatus.DELIVERED] shouldBe 0L + } + } + + `when`("the HTTP endpoint returns 400") { + wiremock.stubFor( + post(urlEqualTo("/api/notify")) + .willReturn(aResponse().withStatus(400).withBody("Bad Request")), + ) + + transaction { + publisher.publish( + OutboxMessage("order.created", """{"orderId":"err-789"}"""), + httpDeliveryInfo { + serviceName = "notification-service" + endpointPath = "/api/notify" + }, + ) + } + + transaction { processor.processNext() } + + val counts = transaction { store.countByStatuses() } + + then("entry is immediately FAILED (permanent failure)") { + counts[OutboxStatus.FAILED] shouldBe 1L + } + then("no PENDING or DELIVERED entries") { + counts[OutboxStatus.PENDING] shouldBe 0L + counts[OutboxStatus.DELIVERED] shouldBe 0L + } + } + + `when`("the endpoint is unreachable") { + wiremock.stubFor( + post(urlEqualTo("/api/notify")) + .willReturn( + aResponse().withFault( + com.github.tomakehurst.wiremock.http.Fault.CONNECTION_RESET_BY_PEER, + ), + ), + ) + + transaction { + publisher.publish( + OutboxMessage("order.created", """{"orderId":"net-000"}"""), + httpDeliveryInfo { + serviceName = "notification-service" + endpointPath = "/api/notify" + }, + ) + } + + transaction { processor.processNext() } + + val counts = transaction { store.countByStatuses() } + + then("entry stays PENDING (retriable network failure)") { + counts[OutboxStatus.PENDING] shouldBe 1L + } + } + } + }) From a3cfc0283338b8036fc8f4646d779f28dd6a744b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 09:56:35 +0100 Subject: [PATCH 05/10] Fix ktlint formatting --- .../softwaremill/okapi/springboot/OutboxAutoConfiguration.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt index f9e08ac..991064e 100644 --- a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt +++ b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt @@ -149,8 +149,7 @@ class OutboxAutoConfiguration { class MysqlStoreConfiguration { @Bean @ConditionalOnMissingBean(OutboxStore::class) - fun outboxStore(clock: ObjectProvider): OutboxStore = - MysqlOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) + fun outboxStore(clock: ObjectProvider): OutboxStore = MysqlOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) @Bean("okapiMysqlLiquibase") @ConditionalOnClass(SpringLiquibase::class) From 0da4a1caecde955dd41ddefbd75b4413bc2959d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 10:07:05 +0100 Subject: [PATCH 06/10] Add AI working docs directories to .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3f2dd53..076e153 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,7 @@ bin/ ### Claude Code ### .claude/ .ai/ -CLAUDE.md \ No newline at end of file +CLAUDE.md +docs/specs/ +docs/plans/ +docs/superpowers/ \ No newline at end of file From 4d8cc7837d2f06aa6046baee9a2bf9faba2cc083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 10:13:06 +0100 Subject: [PATCH 07/10] Remove redundant KDoc that restates what annotations already express --- .../springboot/OutboxAutoConfiguration.kt | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt index 991064e..76b2c09 100644 --- a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt +++ b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt @@ -100,11 +100,6 @@ class OutboxAutoConfiguration { ) } - /** - * Auto-configures [PostgresOutboxStore] and Liquibase schema migration - * when `outbox-postgres` is on the classpath. - * Skipped if the application provides its own [OutboxStore] bean. - */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(PostgresOutboxStore::class) class PostgresStoreConfiguration { @@ -112,19 +107,6 @@ class OutboxAutoConfiguration { @ConditionalOnMissingBean(OutboxStore::class) fun outboxStore(clock: ObjectProvider): OutboxStore = PostgresOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) - /** - * Runs okapi Liquibase migrations automatically when both: - * - `liquibase-core` is on the classpath (i.e. the application already uses Liquibase) - * - a [DataSource] bean is available - * - * The migration is idempotent (CREATE TABLE IF NOT EXISTS) and uses a separate - * Liquibase bean named `okapiPostgresLiquibase` to avoid conflicting with the application's - * own Liquibase configuration. - * - * To opt out, define your own bean named `okapiPostgresLiquibase` or include the okapi - * changelog manually in your master changelog: - * `classpath:com/softwaremill/okapi/db/changelog.xml` - */ @Bean("okapiPostgresLiquibase") @ConditionalOnClass(SpringLiquibase::class) @ConditionalOnBean(DataSource::class) @@ -135,15 +117,7 @@ class OutboxAutoConfiguration { } } - /** - * Auto-configures [MysqlOutboxStore] and Liquibase schema migration - * when `okapi-mysql` is on the classpath. - * Skipped if the application provides its own [OutboxStore] bean. - * - * When both `okapi-postgres` and `okapi-mysql` are on the classpath, - * [PostgresStoreConfiguration] takes priority (declared first). - * Override by defining your own `@Bean OutboxStore`. - */ + /** When both Postgres and MySQL modules are on the classpath, [PostgresStoreConfiguration] takes priority. */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(MysqlOutboxStore::class) class MysqlStoreConfiguration { From 83ee1c11ecaeaa270b091c7c8451fa6b76e2cb35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 10:31:55 +0100 Subject: [PATCH 08/10] Remove redundant comments from build.gradle.kts --- okapi-spring-boot/build.gradle.kts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/okapi-spring-boot/build.gradle.kts b/okapi-spring-boot/build.gradle.kts index 5c4666e..eba7cc8 100644 --- a/okapi-spring-boot/build.gradle.kts +++ b/okapi-spring-boot/build.gradle.kts @@ -5,20 +5,11 @@ plugins { dependencies { implementation(project(":okapi-core")) - // Spring provided by the consuming application — compileOnly keeps this module free of version lock-in compileOnly(libs.springContext) compileOnly(libs.springTx) - - // Spring Boot autoconfiguration support — compileOnly so we don't force Spring Boot on consumers compileOnly(libs.springBootAutoconfigure) - - // Optional postgres store autoconfiguration — compileOnly + @ConditionalOnClass guards runtime absence compileOnly(project(":okapi-postgres")) - - // Optional mysql store autoconfiguration — compileOnly + @ConditionalOnClass guards runtime absence compileOnly(project(":okapi-mysql")) - - // Optional Liquibase migration support — compileOnly, activated only when liquibase-core is on the runtime classpath compileOnly(libs.liquibaseCore) testImplementation(libs.kotestRunnerJunit5) @@ -29,8 +20,6 @@ dependencies { testImplementation(libs.springBootAutoconfigure) testImplementation(project(":okapi-postgres")) testImplementation(project(":okapi-mysql")) - - // E2E test dependencies testImplementation(project(":okapi-http")) testImplementation(libs.exposedJdbc) testImplementation(libs.exposedJson) From 63fe9004639380d0f445b75c8d36790912fc8a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 11:46:36 +0100 Subject: [PATCH 09/10] Guard Liquibase beans on their store bean being active When both okapi-postgres and okapi-mysql are on the classpath, only the winning store's Liquibase migration should run. Return concrete types from outboxStore() so @ConditionalOnBean can detect which store won. --- .gitignore | 2 +- .../okapi/springboot/OutboxAutoConfiguration.kt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 076e153..86a412e 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,4 @@ bin/ CLAUDE.md docs/specs/ docs/plans/ -docs/superpowers/ \ No newline at end of file +docs/superpowers/ diff --git a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt index 76b2c09..ccd7263 100644 --- a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt +++ b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt @@ -105,11 +105,11 @@ class OutboxAutoConfiguration { class PostgresStoreConfiguration { @Bean @ConditionalOnMissingBean(OutboxStore::class) - fun outboxStore(clock: ObjectProvider): OutboxStore = PostgresOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) + fun outboxStore(clock: ObjectProvider): PostgresOutboxStore = PostgresOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) @Bean("okapiPostgresLiquibase") @ConditionalOnClass(SpringLiquibase::class) - @ConditionalOnBean(DataSource::class) + @ConditionalOnBean(value = [DataSource::class, PostgresOutboxStore::class]) @ConditionalOnMissingBean(name = ["okapiPostgresLiquibase"]) fun okapiPostgresLiquibase(dataSource: DataSource): SpringLiquibase = SpringLiquibase().apply { this.dataSource = dataSource @@ -123,11 +123,11 @@ class OutboxAutoConfiguration { class MysqlStoreConfiguration { @Bean @ConditionalOnMissingBean(OutboxStore::class) - fun outboxStore(clock: ObjectProvider): OutboxStore = MysqlOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) + fun outboxStore(clock: ObjectProvider): MysqlOutboxStore = MysqlOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) @Bean("okapiMysqlLiquibase") @ConditionalOnClass(SpringLiquibase::class) - @ConditionalOnBean(DataSource::class) + @ConditionalOnBean(value = [DataSource::class, MysqlOutboxStore::class]) @ConditionalOnMissingBean(name = ["okapiMysqlLiquibase"]) fun okapiMysqlLiquibase(dataSource: DataSource): SpringLiquibase = SpringLiquibase().apply { this.dataSource = dataSource From f442dfbf07cd237f3d9c8c69e3a9bbb3a7d84bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Kobyli=C5=84ski?= Date: Wed, 25 Mar 2026 12:39:15 +0100 Subject: [PATCH 10/10] Fix ktlint line length violations in outboxStore methods --- .../okapi/springboot/OutboxAutoConfiguration.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt index ccd7263..013e0af 100644 --- a/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt +++ b/okapi-spring-boot/src/main/kotlin/com/softwaremill/okapi/springboot/OutboxAutoConfiguration.kt @@ -105,7 +105,8 @@ class OutboxAutoConfiguration { class PostgresStoreConfiguration { @Bean @ConditionalOnMissingBean(OutboxStore::class) - fun outboxStore(clock: ObjectProvider): PostgresOutboxStore = PostgresOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) + fun outboxStore(clock: ObjectProvider): PostgresOutboxStore = + PostgresOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) @Bean("okapiPostgresLiquibase") @ConditionalOnClass(SpringLiquibase::class) @@ -123,7 +124,8 @@ class OutboxAutoConfiguration { class MysqlStoreConfiguration { @Bean @ConditionalOnMissingBean(OutboxStore::class) - fun outboxStore(clock: ObjectProvider): MysqlOutboxStore = MysqlOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) + fun outboxStore(clock: ObjectProvider): MysqlOutboxStore = + MysqlOutboxStore(clock = clock.getIfAvailable { Clock.systemUTC() }) @Bean("okapiMysqlLiquibase") @ConditionalOnClass(SpringLiquibase::class)