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: 9 additions & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies {
dokka(projects.exposed.exposedSpringBoot4Starter)
dokka(projects.exposed.springTransaction)
dokka(projects.exposed.spring7Transaction)
dokka(projects.exposed.exposedDaoR2dbc)

// Kover aggregated coverage dependencies
// Include all source modules for coverage aggregation
Expand All @@ -60,6 +61,7 @@ dependencies {
kover(project(":exposed-migration-jdbc"))
kover(project(":exposed-migration-r2dbc"))
kover(project(":exposed-r2dbc"))
kover(project(":exposed-dao-r2dbc"))

// Include test modules to ensure their tests are executed and coverage is collected
kover(project(":exposed-tests"))
Expand All @@ -71,12 +73,11 @@ repositories {
mavenCentral()
}

val sampleProjects = setOf("exposed-dao-showcase-jdbc", "exposed-dao-showcase-r2dbc")
val unpublishedProjects = setOf("exposed-tests", "exposed-r2dbc-tests", "exposed-jdbc-r2dbc-tests", "exposed-dao-r2dbc-tests", "exposed-dao-r2dbc") + sampleProjects

allprojects {
if (this.name != "exposed-tests" &&
this.name != "exposed-r2dbc-tests" &&
this.name != "exposed-jdbc-r2dbc-tests" &&
this != rootProject
) {
if (this.name !in unpublishedProjects && this != rootProject) {
apply(plugin = "com.vanniktech.maven.publish")
apply(plugin = "signing")
this@allprojects.mavenPublishing {
Expand All @@ -91,10 +92,11 @@ allprojects {
}

apiValidation {
ignoredProjects.addAll(listOf("exposed-tests", "exposed-bom", "exposed-r2dbc-tests", "exposed-jdbc-r2dbc-tests"))
ignoredProjects.addAll(listOf("exposed-tests", "exposed-bom", "exposed-r2dbc-tests", "exposed-jdbc-r2dbc-tests", "exposed-dao-r2dbc-tests", "exposed-dao-r2dbc") + sampleProjects)
}

subprojects {
if (name in sampleProjects) return@subprojects
configureDetekt()

dependencies {
Expand All @@ -103,7 +105,7 @@ subprojects {
}

subprojects {
if (name == "exposed-bom") return@subprojects
if (name == "exposed-bom" || name in sampleProjects) return@subprojects

apply(plugin = rootProject.libs.plugins.jvm.get().pluginId)
apply(plugin = rootProject.libs.plugins.kover.get().pluginId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jetbrains.exposed.v1.core

import org.jetbrains.exposed.v1.core.dao.id.CompositeID
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.statements.api.ExposedBlob
import java.math.BigDecimal
Expand All @@ -14,7 +15,9 @@ class QueryParameter<T>(
/** Returns the column type of this expression. */
override val columnType: IColumnType<T & Any>
) : ExpressionWithColumnType<T>() {
internal val compositeValue: CompositeID? = (value as? EntityID<*>)?.value as? CompositeID
internal val compositeValue: CompositeID? = (value as? EntityID<*>)
?.takeIf { it.table is CompositeIdTable }
?.value as? CompositeID

override fun toQueryBuilder(queryBuilder: QueryBuilder) {
queryBuilder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ import org.jetbrains.exposed.v1.dao.entityCache
import org.jetbrains.exposed.v1.jdbc.JdbcTransaction
import org.jetbrains.exposed.v1.jdbc.selectAll
import org.jetbrains.exposed.v1.tests.DatabaseTestsBase
import org.jetbrains.exposed.v1.tests.MISSING_R2DBC_TEST
import org.jetbrains.exposed.v1.tests.shared.assertEquals
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertNotNull

@Tag(MISSING_R2DBC_TEST)
class EncryptedColumnDaoTests : DatabaseTestsBase() {
object TestTable : IntIdTable() {
val varchar = encryptedVarchar("varchar", 100, Algorithms.AES_256_PBE_GCM("passwd", "12345678"))
Expand Down
88 changes: 88 additions & 0 deletions exposed-dao-r2dbc-tests/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm") apply true
alias(libs.plugins.serialization)
}

kotlin {
jvmToolchain(17)

compilerOptions {
optIn.add("kotlin.time.ExperimentalTime")
optIn.add("kotlin.uuid.ExperimentalUuidApi")
optIn.add("kotlinx.coroutines.ExperimentalCoroutinesApi")
optIn.add("kotlinx.coroutines.DelicateCoroutinesApi")
}
}

repositories {
mavenCentral()
}

dependencies {
implementation(libs.kotlinx.coroutines.reactive)
implementation(libs.kotlinx.coroutines.debug)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.r2dbc.spi)

implementation(kotlin("test-junit5"))
implementation(libs.junit5)
testRuntimeOnly(libs.junit.platform.launcher)

implementation(project(":exposed-core"))
implementation(project(":exposed-r2dbc"))
implementation(project(":exposed-dao-r2dbc"))
implementation(project(":exposed-r2dbc-tests"))
testImplementation(project(":exposed-java-time"))
testImplementation(project(":exposed-kotlin-datetime"))
testImplementation(project(":exposed-jodatime"))
testImplementation(project(":exposed-json"))
testImplementation(project(":exposed-crypt"))
testImplementation(project(":exposed-money"))

implementation(libs.slf4j)
implementation(libs.log4j.slf4j.impl)
implementation(libs.log4j.api)
implementation(libs.log4j.core)
testImplementation(libs.moneta)

testRuntimeOnly(libs.r2dbc.pool)
testImplementation(libs.r2dbc.h2) {
exclude(group = "com.h2database", module = "h2")
}
testRuntimeOnly(libs.r2dbc.mariadb)
testRuntimeOnly(libs.r2dbc.mysql)
testRuntimeOnly(libs.r2dbc.oracle)
testImplementation(libs.r2dbc.postgresql)
testRuntimeOnly(libs.r2dbc.sqlserver)

testImplementation(libs.logcaptor)
}

tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}

tasks.withType<JavaCompile>().configureEach {
targetCompatibility = "17"
}

tasks.withType<Test>().configureEach {
if (JavaVersion.VERSION_11 > JavaVersion.current()) {
jvmArgs = listOf("-XX:MaxPermSize=256m")
}
testLogging {
events.addAll(listOf(TestLogEvent.PASSED, TestLogEvent.FAILED, TestLogEvent.SKIPPED))
showStandardStreams = true
exceptionFormat = TestExceptionFormat.FULL
}

useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.jetbrains.exposed.dao.r2dbc.tests

import org.jetbrains.exposed.v1.core.transactions.nullableTransactionScope
import org.jetbrains.exposed.v1.r2dbc.tests.TestDB

internal var currentTestDB by nullableTransactionScope<TestDB>()
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.jetbrains.exposed.dao.r2dbc.tests.crypt

import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.singleOrNull
import org.jetbrains.exposed.r2dbc.dao.IntR2dbcEntity
import org.jetbrains.exposed.r2dbc.dao.IntR2dbcEntityClass
import org.jetbrains.exposed.r2dbc.dao.entityCache
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.crypt.Algorithms
import org.jetbrains.exposed.v1.crypt.encryptedBinary
import org.jetbrains.exposed.v1.crypt.encryptedVarchar
import org.jetbrains.exposed.v1.r2dbc.R2dbcTransaction
import org.jetbrains.exposed.v1.r2dbc.selectAll
import org.jetbrains.exposed.v1.r2dbc.tests.R2dbcDatabaseTestsBase
import org.jetbrains.exposed.v1.r2dbc.tests.shared.assertEquals
import org.junit.jupiter.api.assertNotNull
import kotlin.test.Test

class R2dbcEncryptedColumnDaoTests : R2dbcDatabaseTestsBase() {
object TestTable : IntIdTable() {
val varchar = encryptedVarchar("varchar", 100, Algorithms.AES_256_PBE_GCM("passwd", "12345678"))
val binary = encryptedBinary("binary", 100, Algorithms.AES_256_PBE_GCM("passwd", "12345678"))
}

class ETest(id: EntityID<Int>) : IntR2dbcEntity(id) {
companion object : IntR2dbcEntityClass<ETest>(TestTable)

var varchar by TestTable.varchar
var binary by TestTable.binary
}

@Test
fun testEncryptedColumnsWithCachedEntities() {
val varcharValue = "varchar"
val binaryValue = "binary".toByteArray()

fun R2dbcTransaction.assertNotNullWithCorrectFields(actualEntity: ETest?) {
assertNotNull(actualEntity)
assertEquals(varcharValue, actualEntity.varchar)
assertEquals(binaryValue.contentToString(), actualEntity.binary.contentToString())
}

withTables(TestTable) {
val entity = ETest.new {
varchar = varcharValue
binary = binaryValue
}

// confirm new entity has been cached
assertNotNull(entityCache.find(ETest, entity.id))

// findById() should get cached entity without calling wrapRows()
val cachedEntity1 = ETest.findById(entity.id)
assertNotNullWithCorrectFields(cachedEntity1)

// but find() should skip cache & call wrapRows()
val foundEntity1 = ETest.find { TestTable.id eq entity.id }.singleOrNull()
assertNotNullWithCorrectFields(foundEntity1)

// DSL result passed to wrapRow() also skips the cache
TestTable.selectAll().first().let {
val foundEntity2 = ETest.wrapRow(it)
assertNotNullWithCorrectFields(foundEntity2)
}
}
}

@Test
fun testEncryptedColumnsWithDao() {
withTables(TestTable) {
val varcharValue = "varchar"
val binaryValue = "binary".toByteArray()

val entity = ETest.new {
varchar = varcharValue
binary = binaryValue
}
assertEquals(varcharValue, entity.varchar)
assertEquals(binaryValue, entity.binary)

TestTable.selectAll().first().let {
assertEquals(varcharValue, it[TestTable.varchar])
assertEquals(String(binaryValue), String(it[TestTable.binary]))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.jetbrains.exposed.dao.r2dbc.tests.demo.dao

import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import org.jetbrains.exposed.r2dbc.dao.IntR2dbcEntity
import org.jetbrains.exposed.r2dbc.dao.IntR2dbcEntityClass
import org.jetbrains.exposed.r2dbc.dao.relationships.referencedOnSuspend
import org.jetbrains.exposed.v1.core.StdOutSqlLogger
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.core.greaterEq
import org.jetbrains.exposed.v1.r2dbc.R2dbcDatabase
import org.jetbrains.exposed.v1.r2dbc.SchemaUtils
import org.jetbrains.exposed.v1.r2dbc.tests.TestDB
import org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction
import org.junit.jupiter.api.Assumptions
import kotlin.test.Test

object Users : IntIdTable() {
val name = varchar("name", 50).index()
val city = reference("city", Cities)
val age = integer("age")
}

object Cities : IntIdTable() {
val name = varchar("name", 50)
}

class User(id: EntityID<Int>) : IntR2dbcEntity(id) {
companion object : IntR2dbcEntityClass<User>(Users)

var name by Users.name
val city by City referencedOnSuspend Users.city
var age by Users.age
}

class City(id: EntityID<Int>) : IntR2dbcEntity(id) {
companion object : IntR2dbcEntityClass<City>(Cities)

var name by Cities.name
val users by User referrersOnSuspend Users.city
}

fun main() = runBlocking {
Assumptions.assumeTrue(TestDB.H2_V2 in TestDB.enabledDialects())
R2dbcDatabase.connect("r2dbc:h2:mem:///test", user = "root", password = "")

suspendTransaction {
addLogger(StdOutSqlLogger)

SchemaUtils.create(Cities, Users)

val stPete = City.new {
name = "St. Petersburg"
}

val munich = City.new {
name = "Munich"
}

User.new {
name = "a"
city set stPete
age = 5
}

User.new {
name = "b"
city set stPete
age = 27
}

User.new {
name = "c"
city set munich
age = 42
}

println("Cities: ${City.all().toList().joinToString { it.name }}")
println("Users in ${stPete.name}: ${stPete.users().toList().joinToString { it.name }}")
println("Adults: ${User.find { Users.age greaterEq 18 }.toList().joinToString { it.name }}")
}
}

class SamplesDao {
@Test
fun ensureSamplesDoesntCrash() {
main()
}
}
Loading
Loading