155 lines
5.3 KiB
Kotlin
155 lines
5.3 KiB
Kotlin
package dev.inmo.postssystem.core.exposed
|
|
|
|
import dev.inmo.postssystem.core.content.*
|
|
import dev.inmo.postssystem.core.content.api.ContentRepo
|
|
import dev.inmo.postssystem.core.exposed.content.*
|
|
import dev.inmo.postssystem.core.generateContentId
|
|
import dev.inmo.micro_utils.pagination.*
|
|
import kotlinx.coroutines.channels.BroadcastChannel
|
|
import kotlinx.coroutines.channels.Channel
|
|
import kotlinx.coroutines.flow.Flow
|
|
import kotlinx.coroutines.flow.asFlow
|
|
import org.jetbrains.exposed.sql.*
|
|
import org.jetbrains.exposed.sql.transactions.transaction
|
|
|
|
private val Content.type
|
|
get() = when (this) {
|
|
is TextContent -> "text"
|
|
is BinaryContent -> "binary"
|
|
is SpecialContent -> "special"
|
|
}
|
|
|
|
private class ContentRepoDatabaseTable(
|
|
private val database: Database,
|
|
tableName: String = "",
|
|
private val textHolder: ContentHolderRepo<TextContent>,
|
|
private val binaryHolder: ContentHolderRepo<BinaryContent>,
|
|
private val specialHolder: ContentHolderRepo<SpecialContent>
|
|
) : Table(tableName), ContentRepo, ContentHolderRepo<Content> {
|
|
internal val idColumn = text("_id")
|
|
internal val typeColumn = text("type")
|
|
|
|
init {
|
|
transaction(
|
|
db = database
|
|
) {
|
|
SchemaUtils.createMissingTablesAndColumns(this@ContentRepoDatabaseTable)
|
|
}
|
|
}
|
|
|
|
private val contentCreatedBroadcastChannel = BroadcastChannel<RegisteredContent>(Channel.BUFFERED)
|
|
private val contentDeletedBroadcastChannel = BroadcastChannel<RegisteredContent>(Channel.BUFFERED)
|
|
override val contentCreatedFlow: Flow<RegisteredContent> = contentCreatedBroadcastChannel.asFlow()
|
|
override val contentDeletedFlow: Flow<RegisteredContent> = contentDeletedBroadcastChannel.asFlow()
|
|
|
|
private val String.holder
|
|
get() = when (this) {
|
|
"text" -> textHolder
|
|
"binary" -> binaryHolder
|
|
"special" -> specialHolder
|
|
else -> null
|
|
}
|
|
|
|
override suspend fun putContent(id: ContentId, content: Content) {
|
|
when (content) {
|
|
is TextContent -> textHolder.putContent(id, content)
|
|
is BinaryContent -> binaryHolder.putContent(id, content)
|
|
is SpecialContent -> specialHolder.putContent(id, content)
|
|
}
|
|
}
|
|
override suspend fun getContent(id: ContentId): Content? = transaction(
|
|
db = database
|
|
) {
|
|
select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(typeColumn)
|
|
} ?.holder ?.getContent(id)
|
|
|
|
override suspend fun removeContent(id: ContentId) {
|
|
transaction(
|
|
db = database
|
|
) {
|
|
select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(typeColumn)
|
|
} ?.holder ?.removeContent(id)
|
|
}
|
|
|
|
override suspend fun registerContent(content: Content): RegisteredContent? {
|
|
val id = generateContentId()
|
|
val type = content.type
|
|
return transaction(
|
|
db = database
|
|
) {
|
|
insert {
|
|
it[idColumn] = id
|
|
it[typeColumn] = type
|
|
}.getOrNull(idColumn)
|
|
} ?.let { id ->
|
|
putContent(id, content)
|
|
RegisteredContent(
|
|
id,
|
|
content
|
|
)
|
|
} ?.also {
|
|
contentCreatedBroadcastChannel.send(it)
|
|
} ?: null.also {
|
|
removeContent(id)
|
|
}
|
|
}
|
|
override suspend fun deleteContent(id: ContentId): Boolean {
|
|
val content = getContentById(id) ?: return false
|
|
return transaction(
|
|
db = database
|
|
) {
|
|
deleteWhere {
|
|
idColumn.eq(id)
|
|
} > 0
|
|
}.also {
|
|
if (it) {
|
|
removeContent(id)
|
|
contentDeletedBroadcastChannel.send(content)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun ResultRow.asRegisteredContent(content: Content): RegisteredContent? = get(idColumn).let {
|
|
RegisteredContent(
|
|
it,
|
|
content
|
|
)
|
|
}
|
|
|
|
override suspend fun getContentsIds(): Set<ContentId> {
|
|
return transaction(
|
|
db = database
|
|
) {
|
|
selectAll().map { it[idColumn] }
|
|
}.toSet()
|
|
}
|
|
override suspend fun getContentById(id: ContentId): RegisteredContent? {
|
|
val content = getContent(id) ?: return null
|
|
return transaction(
|
|
db = database
|
|
) {
|
|
select { idColumn.eq(id) }.limit(1).firstOrNull() ?.asRegisteredContent(content)
|
|
}
|
|
}
|
|
override suspend fun getContentByPagination(pagination: Pagination): PaginationResult<RegisteredContent> {
|
|
return transaction(
|
|
db = database
|
|
) {
|
|
selectAll().count() to selectAll().paginate(pagination).map { it[idColumn] }
|
|
}.let { (count, results) ->
|
|
results.mapNotNull { RegisteredContent(it, getContent(it) ?: return@mapNotNull null) }.createPaginationResult(
|
|
pagination,
|
|
count
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
class ExposedContentRepo (
|
|
database: Database,
|
|
tableName: String = "",
|
|
textHolder: ContentHolderRepo<TextContent> = TextContentHolderRepo(database),
|
|
binaryHolder: ContentHolderRepo<BinaryContent> = BinaryContentHolderRepo(database),
|
|
specialHolder: ContentHolderRepo<SpecialContent> = SpecialContentHolderRepo(database)
|
|
) : ContentRepo by ContentRepoDatabaseTable(database, tableName, textHolder, binaryHolder, specialHolder)
|