Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 25 additions & 21 deletions core/src/main/kotlin/net/samyn/kapper/internal/AutoConverter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,29 @@ import java.time.LocalDateTime
import java.time.LocalTime
import java.util.UUID
import kotlin.reflect.KClass
import kotlin.uuid.ExperimentalUuidApi

internal class AutoConverter(
// register converters
// we can/should extend this to allow users to register custom converters.
private val converters: Map<KClass<*>, (Any) -> Any> =
mapOf(
UUID::class to ::convertUUID,
LocalDate::class to ::convertLocalDate,
LocalDateTime::class to ::convertLocalDateTime,
LocalTime::class to ::convertLocalTime,
Instant::class to ::convertInstant,
),
) {
fun convert(
value: Any,
target: KClass<*>,
): Any =
// Kover considers this not covered, which I think is a bug. Will reproduce in separate project and raise issue.
converters[target]?.invoke(value) ?: throw KapperUnsupportedOperationException(
"Cannot auto-convert from ${value.javaClass} to ${target.qualifiedName}",
)
}
internal class AutoConverter
@OptIn(ExperimentalUuidApi::class)
constructor(
// register converters
// we can/should extend this to allow users to register custom converters.
private val converters: Map<KClass<*>, (Any) -> Any> =
mapOf(
UUID::class to ::convertUUID,
kotlin.uuid.Uuid::class to ::convertUUID,
LocalDate::class to ::convertLocalDate,
LocalDateTime::class to ::convertLocalDateTime,
LocalTime::class to ::convertLocalTime,
Instant::class to ::convertInstant,
),
) {
fun convert(
value: Any,
target: KClass<*>,
): Any =
// Kover considers this not covered, which I think is a bug. Will reproduce in separate project and raise issue.
converters[target]?.invoke(value) ?: throw KapperUnsupportedOperationException(
"Cannot auto-convert from ${value.javaClass} to ${target.qualifiedName}",
)
}
7 changes: 7 additions & 0 deletions core/src/main/kotlin/net/samyn/kapper/internal/Converters.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import java.time.LocalTime
import java.util.Calendar
import java.util.Date
import java.util.UUID
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.toJavaUuid

internal fun convertInstant(value: Any): Instant =
when (value) {
Expand Down Expand Up @@ -57,6 +59,7 @@ internal fun convertLocalDate(value: Any): LocalDate =
)
}

@OptIn(ExperimentalUuidApi::class)
internal fun convertUUID(value: Any): UUID =
when (value) {
is String -> {
Expand All @@ -74,6 +77,10 @@ internal fun convertUUID(value: Any): UUID =
value.asUUID()
}

is kotlin.uuid.Uuid -> {
value.toJavaUuid()
}

else -> {
throw KapperUnsupportedOperationException(
"Cannot auto-convert from ${value.javaClass} to UUID",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import java.sql.Timestamp
import java.time.Instant
import java.util.Date
import java.util.UUID
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.toJavaUuid

// TODO: this could be more sophisticated by allowing type conversion hints.
// TODO: check what hibernate does for these conversions.
Expand Down Expand Up @@ -72,6 +74,7 @@ internal object SQLTypesConverter {
return result
}

@OptIn(ExperimentalUuidApi::class)
fun PreparedStatement.setParameter(
index: Int,
value: Any?,
Expand All @@ -95,6 +98,13 @@ internal object SQLTypesConverter {
setObject(index, value)
}
}
is kotlin.uuid.Uuid -> {
if (DbFlavour.MYSQL == dbFlavour) {
setString(index, value.toString())
} else {
setObject(index, value.toJavaUuid())
}
}
is Instant -> setTimestamp(index, Timestamp.from(value))
is Date -> setDate(index, java.sql.Date(value.time))
else -> setObject(index, value)
Expand Down
9 changes: 9 additions & 0 deletions core/src/test/kotlin/net/samyn/kapper/AutoConverterTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import java.time.LocalTime
import java.util.Date
import java.util.UUID
import kotlin.reflect.KClass
import kotlin.uuid.ExperimentalUuidApi

class AutoConverterTest {
private val mockConverter = mockk<(Any) -> Any>()
Expand Down Expand Up @@ -59,6 +60,14 @@ class AutoConverterTest {
}
}

@OptIn(ExperimentalUuidApi::class)
@Test
fun `supports kotlin UUID`() {
shouldNotThrow<KapperUnsupportedOperationException> {
autoConverter.convert("123e4567-e89b-12d3-a456-426614174000", kotlin.uuid.Uuid::class)
}
}

@Test
fun `supports LocalDate`() {
shouldNotThrow<KapperUnsupportedOperationException> {
Expand Down
20 changes: 15 additions & 5 deletions core/src/test/kotlin/net/samyn/kapper/ConverterTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import java.time.LocalDateTime
import java.time.LocalTime
import java.util.Date
import java.util.UUID
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.toJavaUuid

class ConverterTest {
@Nested
Expand All @@ -36,6 +38,14 @@ class ConverterTest {
autoConvertedUuid.shouldBe(uuid)
}

@OptIn(ExperimentalUuidApi::class)
@Test
fun `when kotlin UUID convert`() {
val uuid = kotlin.uuid.Uuid.parse("123e4567-e89b-12d3-a456-426614174000")
val autoConvertedUuid = convertUUID(uuid)
autoConvertedUuid.shouldBe(uuid.toJavaUuid())
}

@Test
fun `when int cannot convert to UUID`() {
shouldThrow<KapperUnsupportedOperationException> {
Expand Down Expand Up @@ -74,23 +84,23 @@ class ConverterTest {
@Test
fun `convert valid LocalTime to Instant`() {
val time = LocalTime.of(12, 30) // 12:30 PM
val instant = convertInstant(time) as Instant
val instant = convertInstant(time)
val expectedInstant = LocalDate.now().atTime(12, 30).toInstant(java.time.ZoneOffset.UTC)
instant.shouldBe(expectedInstant)
}

@Test
fun `convert LocalTime at midnight to Instant`() {
val midnight = LocalTime.MIDNIGHT
val instant = convertInstant(midnight) as Instant
val instant = convertInstant(midnight)
val expectedInstant = LocalDate.now().atTime(0, 0).toInstant(java.time.ZoneOffset.UTC)
instant.shouldBe(expectedInstant)
}

@Test
fun `convert valid LocalDateTime to Instant`() {
val now = LocalDateTime.now()
val instant = convertInstant(now) as Instant
val instant = convertInstant(now)
val expectedInstant = now.toInstant(java.time.ZoneOffset.UTC)
instant.shouldBe(expectedInstant)
}
Expand All @@ -108,7 +118,7 @@ class ConverterTest {
@Test
fun `convert valid Instant to LocalDateTime`() {
val now = Instant.now()
val localDateTime = convertLocalDateTime(now) as LocalDateTime
val localDateTime = convertLocalDateTime(now)
val expectedDt = LocalDateTime.ofInstant(now, java.time.ZoneOffset.UTC)
localDateTime.shouldBe(expectedDt)
}
Expand All @@ -126,7 +136,7 @@ class ConverterTest {
@Test
fun `convert valid Instant to LocalTime`() {
val now = Instant.now()
val locaTime = convertLocalTime(now) as LocalTime
val locaTime = convertLocalTime(now)
val expectedTime = LocalTime.ofInstant(now, java.time.ZoneOffset.UTC)
locaTime.shouldBe(expectedTime)
}
Expand Down
44 changes: 35 additions & 9 deletions core/src/test/kotlin/net/samyn/kapper/SQLTypesConverterTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import java.time.LocalTime
import java.time.temporal.ChronoUnit
import java.util.Date
import java.util.UUID
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.toJavaUuid

@OptIn(ExperimentalUuidApi::class)
class SQLTypesConverterTest {
companion object {
val statement = mockk<PreparedStatement>(relaxed = true)
Expand Down Expand Up @@ -55,18 +58,41 @@ class SQLTypesConverterTest {
),
)

private val uuid = UUID.randomUUID()

@JvmStatic
fun parameterWithConvertTests() =
listOf(
arguments(
named("UUID", uuid),
{ i: Int, v: Any? -> statement.setObject(i, v) },
uuid,
DbFlavour.POSTGRESQL,
),
arguments(named("UUID", uuid), statement::setString, uuid.toString(), DbFlavour.MYSQL),
UUID.randomUUID().let {
arguments(
named("java-UUID-pg", it),
{ i: Int, v: Any? -> statement.setObject(i, v) },
it,
DbFlavour.POSTGRESQL,
)
},
UUID.randomUUID().let {
arguments(
named("java-UUID-mysql", it),
statement::setString,
it.toString(),
DbFlavour.MYSQL,
)
},
kotlin.uuid.Uuid.random().let {
arguments(
named("kotlin-UUID-pg", it),
{ i: Int, v: Any? -> statement.setObject(i, v) },
it.toJavaUuid(),
DbFlavour.POSTGRESQL,
)
},
kotlin.uuid.Uuid.random().let {
arguments(
named("kotlin-UUID-mysql", it),
statement::setString,
it.toString(),
DbFlavour.MYSQL,
)
},
)

@JvmStatic
Expand Down