Skip to content

Commit 1b91d03

Browse files
authored
fix: actionable error message in ExposedConnectionProvider (#32)
## Summary - `ExposedConnectionProvider.withConnection` previously delegated to `TransactionManager.current()`, which throws a generic Exposed `IllegalStateException("No transaction in context")` when called outside a transaction. The message gives no hint about okapi. - Wrap with `TransactionManager.currentOrNull()` and throw a clearer `IllegalStateException` naming the okapi method and the missing `transaction { }` block. Brings `ExposedConnectionProvider` to parity with how `JdbcConnectionProvider` (test helper) reports its own missing-scope precondition. - Added two unit tests: one for the negative path (asserts the message contains both okapi method name and the `Exposed transaction { } block` hint), one for the positive path (asserts the active Exposed connection is supplied to the block). ## Motivation Follow-up to PR #26 review feedback (`code-reviewer` Suggestion #6 on the second review round). Three-line code change but it materially improves diagnostics for users who hit this misconfiguration in Ktor + Exposed setups. ## Test plan - [x] `./gradlew :okapi-exposed:test --tests "*ExposedConnectionProviderTest*"` — both cases pass - [x] `./gradlew build` — full project build, all existing tests pass - [x] `./gradlew ktlintFormat` — no formatting issues
1 parent b42d914 commit 1b91d03

2 files changed

Lines changed: 42 additions & 4 deletions

File tree

‎okapi-exposed/src/main/kotlin/com/softwaremill/okapi/exposed/ExposedConnectionProvider.kt‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@ import java.sql.Connection
1313
* `transaction(database) { }` block completes — so this provider performs no cleanup.
1414
*
1515
* Use when your application manages transactions via Exposed (e.g. Ktor + Exposed apps).
16-
* Must be called from within an active Exposed transaction; otherwise
17-
* `TransactionManager.current()` throws.
16+
* Must be called from within an active Exposed transaction; otherwise [withConnection]
17+
* throws an [IllegalStateException] pointing the caller at the missing `transaction { }`
18+
* block, instead of letting Exposed's own less specific error surface.
1819
*/
1920
class ExposedConnectionProvider : ConnectionProvider {
2021
override fun <T> withConnection(block: (Connection) -> T): T {
21-
val connection = TransactionManager.current().connection.connection as Connection
22-
return block(connection)
22+
val transaction = TransactionManager.currentOrNull()
23+
?: throw IllegalStateException(
24+
"ExposedConnectionProvider.withConnection must be called within an Exposed transaction { } block",
25+
)
26+
return block(transaction.connection.connection as Connection)
2327
}
2428
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.softwaremill.okapi.exposed
2+
3+
import io.kotest.assertions.throwables.shouldThrow
4+
import io.kotest.core.spec.style.FunSpec
5+
import io.kotest.matchers.shouldBe
6+
import io.kotest.matchers.string.shouldContain
7+
import org.jetbrains.exposed.v1.jdbc.Database
8+
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
9+
10+
class ExposedConnectionProviderTest : FunSpec({
11+
12+
val provider = ExposedConnectionProvider()
13+
14+
test("throws IllegalStateException with actionable message when called outside an Exposed transaction") {
15+
val ex = shouldThrow<IllegalStateException> {
16+
provider.withConnection { /* unreachable */ }
17+
}
18+
ex.message shouldContain "ExposedConnectionProvider.withConnection"
19+
ex.message shouldContain "Exposed transaction { } block"
20+
}
21+
22+
test("supplies the active Exposed transaction's connection to the block") {
23+
val db = Database.connect(
24+
"jdbc:h2:mem:exposed_provider_test_${System.nanoTime()};DB_CLOSE_DELAY=-1",
25+
driver = "org.h2.Driver",
26+
)
27+
28+
val connectionWasOpen: Boolean = transaction(db) {
29+
provider.withConnection { conn -> !conn.isClosed }
30+
}
31+
32+
connectionWasOpen shouldBe true
33+
}
34+
})

0 commit comments

Comments
 (0)