Skip to content
Open
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
16 changes: 14 additions & 2 deletions mapper/src/main/kotlin/com/github/andrewoma/kwery/mapper/Column.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ data class Column<C, T>(
/**
* True if the column is nullable
*/
val isNullable: Boolean
val isNullable: Boolean,

/**
* A converter which will be used in [Table.mapMapper]
* default value is set to maintain source compatibility with possible constructor invocations
*/
val mapConverter: (String) -> T = NoMapConverter
) {
/**
* A type-safe variant of `to`
Expand All @@ -64,4 +70,10 @@ data class Column<C, T>(
override fun toString(): String {
return "Column($name id=$id version=$version nullable=$isNullable)" // Prevent NPE in debugger on "property"
}
}

companion object {
val NoMapConverter: (String) -> Nothing =
{ throw UnsupportedOperationException("there's no mapConverter on this column") }
}

}
54 changes: 43 additions & 11 deletions mapper/src/main/kotlin/com/github/andrewoma/kwery/mapper/Table.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,17 @@ abstract class Table<T : Any, ID>(val name: String, val config: TableConfigurati
id: Boolean = false,
version: Boolean = false,
notNull: Boolean = !property.returnType.isMarkedNullable,
default: R = default(property.returnType),
default: R = default<R>(property.returnType),
converter: Converter<R> = converter(property.returnType),
name: String? = null,
selectByDefault: Boolean = true): DelegatedColumn<R> {
selectByDefault: Boolean = true,
mapConverter: (String) -> R = Column.NoMapConverter): DelegatedColumn<R> {

val column = Column<T, R>(
{ property.get(it) }, default, converter,
name ?: "", id, version, selectByDefault, !notNull,
mapConverter)

val column = Column<T, R>({ property.get(it) }, default, converter, name ?: "", id, version, selectByDefault, !notNull)
return DelegatedColumn(column)
}

Expand All @@ -112,11 +117,15 @@ abstract class Table<T : Any, ID>(val name: String, val config: TableConfigurati
default: R = default<R>(property.returnType),
converter: Converter<R> = converter(property.returnType),
name: String? = null,
selectByDefault: Boolean = true

selectByDefault: Boolean = true,
mapConverter: (String) -> R = Column.NoMapConverter
): DelegatedColumn<R> {

val column = Column<T, R>({ property.get(path(it)) }, default, converter, name ?: "", id, version, selectByDefault, !notNull)
val column = Column<T, R>(
{ property.get(path(it)) }, default, converter,
name ?: "", id, version, selectByDefault, !notNull,
mapConverter)

return DelegatedColumn(column)
}

Expand All @@ -130,11 +139,15 @@ abstract class Table<T : Any, ID>(val name: String, val config: TableConfigurati
version: Boolean = false,
converter: Converter<R> = converter(property.returnType),
name: String? = null,
selectByDefault: Boolean = true

selectByDefault: Boolean = true,
mapConverter: (String) -> R = Column.NoMapConverter
): DelegatedColumn<R?> {

val column = Column<T, R?>({ path(it)?.let { property.get(it) } }, null, optional(converter), name ?: "", id, version, selectByDefault, true)
val column = Column<T, R?>(
{ path(it)?.let { property.get(it) } }, null, optional(converter),
name ?: "", id, version, selectByDefault, true,
mapConverter)

return DelegatedColumn(column)
}

Expand Down Expand Up @@ -199,13 +212,32 @@ abstract class Table<T : Any, ID>(val name: String, val config: TableConfigurati
return map
}

fun rowMapper(columns: Set<Column<T, *>> = defaultColumns, nf: (Column<T, *>) -> String = columnName): (Row) -> T {
fun rowMapper(
columns: Set<Column<T, *>> = defaultColumns, nf: (Column<T, *>) -> String = columnName
): (Row) -> T {
return { row ->
create(object : Value<T> {
override fun <R> of(column: Column<T, R>): R =
if (column in columns) column.converter.from(row, nf(column)) else column.defaultValue
})
}
}

fun mapMapper(
columns: Set<Column<T, *>> = defaultColumns, nf: (Column<T, *>) -> String = columnName
): (Map<String, String>) -> T {
return { map ->
create(object : Value<T> {
override fun <R> of(column: Column<T, R>): R {
return if (columns.contains(column)) column.converter.from(row, nf(column)) else column.defaultValue
val fieldName = nf(column)

return if (column in columns && fieldName in map)
column.mapConverter(map[fieldName]!!)
else
column.defaultValue
}
})
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.github.andrewoma.kwery.mappertest

import com.github.andrewoma.kwery.mapper.Column
import com.github.andrewoma.kwery.mapper.SimpleConverter
import com.github.andrewoma.kwery.mapper.Table
import com.github.andrewoma.kwery.mapper.Value
import org.junit.Test
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import kotlin.test.assertEquals

class MapMapperTest {

@Test
fun testMapMapping() {
val id = UUID.randomUUID()
val now = LocalDateTime.now()
val something = mapOf(
"id" to id.toString(),
"name" to "Whatever",
"lastUpdated" to now.format(DateTimeFormatter.ISO_DATE_TIME)!!
)

val mapped = someTable.mapMapper()(something)

assertEquals(id, mapped.id)
assertEquals("Whatever", mapped.name)
assertEquals(now, mapped.lastUpdated)
}

class Something(
val id: UUID,
val name: String,
val lastUpdated: LocalDateTime
)

object someTable : Table<Something, UUID>(" won't ever be used ") {

val Id
by col(Something::id, name = "id", id = true, default = DefaultUuid, converter = UuidConverter, mapConverter = UUID::fromString)

val Name
by col(Something::name, name = "name", mapConverter = { it })

val LastUpdated
by col(Something::lastUpdated, name = "lastUpdated", mapConverter = { LocalDateTime.parse(it, DateTimeFormatter.ISO_DATE_TIME) })


override fun idColumns(id: UUID): Set<Pair<Column<Something, *>, *>> =
setOf(Id of id)

override fun create(value: Value<Something>): Something = Something(
value of Id,
value of Name,
value of LastUpdated
)

}

}

private val DefaultUuid = UUID(0, 0)

object UuidConverter : SimpleConverter<UUID>(
{ row, name -> row.obj(name) as UUID }
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class Issue11_12 {
val emailColumn = Column(
User::email, null,
optional(stringConverter),
"email", false, false, true, false
"email", false, false, true, false,
Column.NoMapConverter
)

usersTable.addColumn(emailColumn)
Expand Down