Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
cosmin-marginean committed May 11, 2024
1 parent 82a6dfb commit eb1e912
Show file tree
Hide file tree
Showing 31 changed files with 858 additions and 697 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
invirtVersion = 0.8.30
invirtVersion = 0.9.0
kotlinVersion = 1.9.23
http4kVersion = 5.17.0.0
mockkVersion = 1.13.9
Expand Down
1 change: 1 addition & 0 deletions invirt-mongodb/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins {
dependencies {
val mongoDriverVersion: String by project

implementation(project(":invirt-utils"))
implementation(project(":invirt-data"))
api("org.mongodb:mongodb-driver-kotlin-sync:${mongoDriverVersion}")

Expand Down
48 changes: 0 additions & 48 deletions invirt-mongodb/src/main/kotlin/invirt/data/mongodb/MongoEntity.kt

This file was deleted.

29 changes: 0 additions & 29 deletions invirt-mongodb/src/main/kotlin/invirt/data/mongodb/collection.kt

This file was deleted.

This file was deleted.

52 changes: 52 additions & 0 deletions invirt-mongodb/src/main/kotlin/invirt/mongodb/Mongo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package invirt.mongodb

import com.mongodb.TransactionOptions
import com.mongodb.WriteConcern
import com.mongodb.kotlin.client.ClientSession
import com.mongodb.kotlin.client.MongoClient
import com.mongodb.kotlin.client.MongoDatabase
import io.github.oshai.kotlinlogging.KotlinLogging
import org.bson.BsonInt64
import org.bson.Document
import java.net.URI

private val log = KotlinLogging.logger {}

class Mongo(private val connectionString: String) {

val databaseName: String = URI(connectionString).path.replace("^/".toRegex(), "")

internal val mongoClient: MongoClient by lazy {
MongoClient.create(connectionString)
}

val database: MongoDatabase by lazy {
val db = mongoClient.getDatabase(databaseName)
db.runCommand(Document("ping", BsonInt64(1)))
log.info { "Successfully pinged MongoDB database '${databaseName}'" }
db
}

init {
log.info { "MongoDB connection string: ${connectionString.replace("://.*@".toRegex(), "://*****@")}" }
if (databaseName.isEmpty()) {
throw IllegalArgumentException("Database missing from connection string")
}
}

fun <Result> runInTransaction(block: (ClientSession) -> Result): Result {
val session = mongoClient.startSession()
return try {
session.startTransaction(TransactionOptions.builder().writeConcern(WriteConcern.MAJORITY).build())
val result = block(session)
session.commitTransaction()
result
} catch (e: Throwable) {
log.error(e) { "MongoDB transaction error: ${e.message}" }
session.abortTransaction()
throw e
} finally {
session.close()
}
}
}
29 changes: 29 additions & 0 deletions invirt-mongodb/src/main/kotlin/invirt/mongodb/StoredEntity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package invirt.mongodb

import java.time.Instant
import kotlin.reflect.full.findAnnotation

interface StoredEntity {
val id: String

@Indexed
var version: Long

@Indexed
val createdAt: Instant

@Indexed
var updatedAt: Instant
}

inline fun <reified Entity : StoredEntity> collectionName(): String {
val annotation = Entity::class.findAnnotation<MongoCollection>()
?: throw IllegalStateException("Class ${Entity::class} doesn't have an @CollectionName annotation")
return annotation.name
}

fun <Entity : StoredEntity> Entity.updated(): Entity {
version++
updatedAt = mongoNow()
return this
}
28 changes: 28 additions & 0 deletions invirt-mongodb/src/main/kotlin/invirt/mongodb/annotations.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package invirt.mongodb

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class MongoCollection(val name: String)

@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Indexed(
val order: Order = Order.ASC,
val caseInsensitive: Boolean = true,
vararg val fields: String
) {

enum class Order {
ASC,
DESC
}
}

@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class TextIndexed(
vararg val fields: String
)
36 changes: 36 additions & 0 deletions invirt-mongodb/src/main/kotlin/invirt/mongodb/collection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package invirt.mongodb

import com.mongodb.client.model.ReplaceOptions
import com.mongodb.kotlin.client.ClientSession
import com.mongodb.kotlin.client.MongoCollection
import org.bson.conversions.Bson

fun <E : StoredEntity> MongoCollection<E>.save(entity: E): E {
replaceOne(byId(entity.id), entity.updated(), ReplaceOptions().upsert(true))
return entity
}

fun <E : StoredEntity> MongoCollection<E>.txSave(session: ClientSession, entity: E): E {
replaceOne(session, byId(entity.id), entity.updated(), ReplaceOptions().upsert(true))
return entity
}

fun <E : StoredEntity> MongoCollection<E>.get(id: String): E? {
return findOne(byId(id))
}

fun <E : StoredEntity> MongoCollection<E>.findOne(filter: Bson): E? {
val list = find(filter).toList()
if (list.size > 1) {
throw IllegalStateException("More than one document found for filter $filter")
}
return list.firstOrNull()
}

fun <E : StoredEntity> MongoCollection<E>.delete(id: String): Boolean {
return deleteOne(byId(id)).deletedCount == 1L
}

fun <E : StoredEntity> MongoCollection<E>.txDelete(session: ClientSession, id: String): Boolean {
return deleteOne(session, byId(id)).deletedCount == 1L
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package invirt.data.mongodb
package invirt.mongodb

import com.mongodb.client.model.Sorts
import com.mongodb.kotlin.client.FindIterable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package invirt.data.mongodb
package invirt.mongodb

import com.mongodb.client.model.Filters
import org.bson.conversions.Bson
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package invirt.data.mongodb
package invirt.mongodb

import com.mongodb.client.model.Collation
import com.mongodb.client.model.CollationStrength
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,21 @@
package invirt.data.mongodb
package invirt.mongodb

import com.mongodb.client.model.*
import com.mongodb.kotlin.client.MongoCollection
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlin.reflect.KClass
import kotlin.reflect.full.*
import kotlin.reflect.full.createType
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.full.memberProperties

private val log = KotlinLogging.logger {}

@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Indexed(
val order: Order = Order.ASC,
val caseInsensitive: Boolean = true,
vararg val fields: String
) {

enum class Order {
ASC,
DESC
}
}

@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class TextIndexed(
vararg val fields: String
)

fun <T : MongoEntity> MongoCollection<T>.createEntityIndexes() {
fun <E : StoredEntity> MongoCollection<E>.createEntityIndexes() {
createPropertyIndexes(this.documentClass.kotlin)

// Handling Timestamped explicitly, because annotations are not inherited in Kotlin
// so we cannot simply add @Indexed on Timestamped.createdAt
if (this.documentClass.kotlin.isSubclassOf(TimestampedEntity::class)) {
createPropertyIndexes(TimestampedEntity::class)
}
// Handling these explicitly, because annotations are not inherited in Kotlin
createPropertyIndexes(StoredEntity::class)
}

private fun MongoCollection<*>.createPropertyIndexes(cls: KClass<*>) {
Expand Down
10 changes: 10 additions & 0 deletions invirt-mongodb/src/main/kotlin/invirt/mongodb/mongo-database.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package invirt.mongodb

import com.mongodb.kotlin.client.MongoCollection
import com.mongodb.kotlin.client.MongoDatabase

inline fun <reified E : StoredEntity> MongoDatabase.collection(): MongoCollection<E> {
val collection = getCollection<E>(collectionName<E>())
collection.createEntityIndexes()
return collection
}
9 changes: 9 additions & 0 deletions invirt-mongodb/src/main/kotlin/invirt/mongodb/mongo-utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package invirt.mongodb

import java.time.Instant
import java.time.temporal.ChronoUnit

/**
* MongoDB only support millisecond precision
*/
fun mongoNow(): Instant = Instant.now().truncatedTo(ChronoUnit.MILLIS)
Loading

0 comments on commit eb1e912

Please sign in to comment.