small refactor
This commit is contained in:
parent
3795b85827
commit
a6a7634076
@ -1,15 +1,17 @@
|
||||
package com.insanusmokrassar.postssystem.core.exposed
|
||||
|
||||
import com.insanusmokrassar.postssystem.core.content.*
|
||||
import com.insanusmokrassar.postssystem.core.content.api.ContentAPI
|
||||
import com.insanusmokrassar.postssystem.core.content.api.ContentRepo
|
||||
import com.insanusmokrassar.postssystem.core.exposed.content.*
|
||||
import com.insanusmokrassar.postssystem.core.utils.generateContentId
|
||||
import com.insanusmokrassar.postssystem.core.utils.pagination.*
|
||||
import com.insanusmokrassar.postssystem.exposed.commons.paginate
|
||||
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.experimental.newSuspendedTransaction
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
private val Content.type
|
||||
@ -19,18 +21,20 @@ private val Content.type
|
||||
is SpecialContent -> "special"
|
||||
}
|
||||
|
||||
private class ContentAPIDatabaseTable(
|
||||
private class ContentRepoDatabaseTable(
|
||||
private val database: Database,
|
||||
private val textHolder: ContentHolderRepo<TextContent>,
|
||||
private val binaryHolder: ContentHolderRepo<BinaryContent>,
|
||||
private val specialHolder: ContentHolderRepo<SpecialContent>
|
||||
) : Table("ContentAPI"), ContentAPI, ContentHolderRepo<Content> {
|
||||
) : Table("ContentRepo"), ContentRepo, ContentHolderRepo<Content> {
|
||||
internal val idColumn = text("_id")
|
||||
internal val typeColumn = text("type")
|
||||
|
||||
init {
|
||||
transaction(database) {
|
||||
SchemaUtils.createMissingTablesAndColumns(this@ContentAPIDatabaseTable)
|
||||
transaction(
|
||||
db = database
|
||||
) {
|
||||
SchemaUtils.createMissingTablesAndColumns(this@ContentRepoDatabaseTable)
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,11 +58,16 @@ private class ContentAPIDatabaseTable(
|
||||
is SpecialContent -> specialHolder.putContent(id, content)
|
||||
}
|
||||
}
|
||||
override suspend fun getContent(id: ContentId): Content? = transaction(database) {
|
||||
override suspend fun getContent(id: ContentId): Content? = newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(typeColumn)
|
||||
} ?.holder ?.getContent(id)
|
||||
|
||||
override suspend fun removeContent(id: ContentId) {
|
||||
transaction(database) {
|
||||
newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(typeColumn)
|
||||
} ?.holder ?.removeContent(id)
|
||||
}
|
||||
@ -66,7 +75,9 @@ private class ContentAPIDatabaseTable(
|
||||
override suspend fun registerContent(content: Content): RegisteredContent? {
|
||||
val id = generateContentId()
|
||||
val type = content.type
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
insert {
|
||||
it[idColumn] = id
|
||||
it[typeColumn] = type
|
||||
@ -85,7 +96,9 @@ private class ContentAPIDatabaseTable(
|
||||
}
|
||||
override suspend fun deleteContent(id: ContentId): Boolean {
|
||||
val content = getContentById(id) ?: return false
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
deleteWhere {
|
||||
idColumn.eq(id)
|
||||
} > 0
|
||||
@ -105,18 +118,24 @@ private class ContentAPIDatabaseTable(
|
||||
}
|
||||
|
||||
override suspend fun getContentsIds(): Set<ContentId> {
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
selectAll().map { it[idColumn] }
|
||||
}.toSet()
|
||||
}
|
||||
override suspend fun getContentById(id: ContentId): RegisteredContent? {
|
||||
val content = getContent(id) ?: return null
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
select { idColumn.eq(id) }.limit(1).firstOrNull() ?.asRegisteredContent(content)
|
||||
}
|
||||
}
|
||||
override suspend fun getContentByPagination(pagination: Pagination): PaginationResult<out RegisteredContent> {
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
selectAll().count() to selectAll().paginate(pagination).map { it[idColumn] }
|
||||
}.let { (count, results) ->
|
||||
results.mapNotNull { RegisteredContent(it, getContent(it) ?: return@mapNotNull null) }.createPaginationResult(
|
||||
@ -127,9 +146,9 @@ private class ContentAPIDatabaseTable(
|
||||
}
|
||||
}
|
||||
|
||||
class ExposedContentAPI (
|
||||
class ExposedContentRepo (
|
||||
database: Database,
|
||||
textHolder: ContentHolderRepo<TextContent> = TextContentHolderRepo(database),
|
||||
binaryHolder: ContentHolderRepo<BinaryContent> = BinaryContentHolderRepo(database),
|
||||
specialHolder: ContentHolderRepo<SpecialContent> = SpecialContentHolderRepo(database)
|
||||
) : ContentAPI by ContentAPIDatabaseTable(database, textHolder, binaryHolder, specialHolder)
|
||||
) : ContentRepo by ContentRepoDatabaseTable(database, textHolder, binaryHolder, specialHolder)
|
@ -5,40 +5,50 @@ import com.insanusmokrassar.postssystem.core.post.*
|
||||
import com.insanusmokrassar.postssystem.core.post.repo.PostsRepo
|
||||
import com.insanusmokrassar.postssystem.core.utils.generatePostId
|
||||
import com.insanusmokrassar.postssystem.core.utils.pagination.*
|
||||
import com.insanusmokrassar.postssystem.exposed.commons.paginate
|
||||
import com.soywiz.klock.*
|
||||
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.experimental.newSuspendedTransaction
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
private class PostsAPIContentRelations(
|
||||
private class PostsRepoContentRelations(
|
||||
private val database: Database
|
||||
) : Table() {
|
||||
private val postIdColumn = text("postId")
|
||||
private val contentIdColumn = text("contentId")
|
||||
|
||||
init {
|
||||
transaction(database) {
|
||||
SchemaUtils.createMissingTablesAndColumns(this@PostsAPIContentRelations)
|
||||
transaction (
|
||||
db = database
|
||||
) {
|
||||
SchemaUtils.createMissingTablesAndColumns(this@PostsRepoContentRelations)
|
||||
}
|
||||
}
|
||||
|
||||
fun getPostContents(postId: PostId): List<ContentId> {
|
||||
return transaction(database) {
|
||||
suspend fun getPostContents(postId: PostId): List<ContentId> {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
select { postIdColumn.eq(postId) }.map { it[contentIdColumn] }
|
||||
}
|
||||
}
|
||||
|
||||
fun getContentPosts(contentId: ContentId): List<PostId> {
|
||||
return transaction(database) {
|
||||
suspend fun getContentPosts(contentId: ContentId): List<PostId> {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
select { contentIdColumn.eq(contentId) }.map { it[postIdColumn] }
|
||||
}
|
||||
}
|
||||
|
||||
fun linkPostAndContents(postId: PostId, vararg contentIds: ContentId) {
|
||||
transaction(database) {
|
||||
suspend fun linkPostAndContents(postId: PostId, vararg contentIds: ContentId) {
|
||||
newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
val leftToPut = contentIds.toSet() - getPostContents(postId)
|
||||
leftToPut.forEach { contentId ->
|
||||
insert {
|
||||
@ -48,8 +58,10 @@ private class PostsAPIContentRelations(
|
||||
}
|
||||
}
|
||||
}
|
||||
fun unlinkPostAndContents(postId: PostId, vararg contentIds: ContentId): Boolean {
|
||||
return transaction(database) {
|
||||
suspend fun unlinkPostAndContents(postId: PostId, vararg contentIds: ContentId): Boolean {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
deleteWhere {
|
||||
postIdColumn.eq(postId).and(contentIdColumn.inList(contentIds.toList()))
|
||||
} > 0
|
||||
@ -62,7 +74,7 @@ private val dateTimeFormat = DateFormat("EEE, dd MMM yyyy HH:mm:ss z")
|
||||
private class PostsRepoDatabaseTable(
|
||||
private val database: Database
|
||||
) : PostsRepo, Table() {
|
||||
private val contentsTable = PostsAPIContentRelations(database)
|
||||
private val contentsTable = PostsRepoContentRelations(database)
|
||||
|
||||
private val idColumn = text("postId")
|
||||
private val creationDateColumn = text("creationDate").default(
|
||||
@ -80,12 +92,14 @@ private class PostsRepoDatabaseTable(
|
||||
override val postUpdatedFlow: Flow<RegisteredPost> = postUpdatedBroadcastChannel.asFlow()
|
||||
|
||||
init {
|
||||
transaction(database) {
|
||||
transaction (
|
||||
db = database
|
||||
) {
|
||||
SchemaUtils.createMissingTablesAndColumns(this@PostsRepoDatabaseTable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ResultRow.toRegisteredPost(): RegisteredPost = get(idColumn).let { id ->
|
||||
private suspend fun ResultRow.toRegisteredPost(): RegisteredPost = get(idColumn).let { id ->
|
||||
SimpleRegisteredPost(
|
||||
id,
|
||||
contentsTable.getPostContents(id),
|
||||
@ -95,7 +109,9 @@ private class PostsRepoDatabaseTable(
|
||||
|
||||
override suspend fun createPost(post: Post): RegisteredPost? {
|
||||
val id = generatePostId()
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
insert {
|
||||
it[idColumn] = id
|
||||
}
|
||||
@ -108,7 +124,9 @@ private class PostsRepoDatabaseTable(
|
||||
|
||||
override suspend fun deletePost(id: PostId): Boolean {
|
||||
val post = getPostById(id) ?: return false
|
||||
return (transaction(database) {
|
||||
return (newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
deleteWhere { idColumn.eq(id) }
|
||||
} > 0).also {
|
||||
if (it) {
|
||||
@ -119,7 +137,9 @@ private class PostsRepoDatabaseTable(
|
||||
}
|
||||
|
||||
override suspend fun updatePostContent(postId: PostId, post: Post): Boolean {
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
val alreadyLinked = contentsTable.getPostContents(postId)
|
||||
val toRemove = alreadyLinked - post.content
|
||||
val toInsert = post.content - alreadyLinked
|
||||
@ -135,32 +155,42 @@ private class PostsRepoDatabaseTable(
|
||||
}
|
||||
}
|
||||
override suspend fun getPostsIds(): Set<PostId> {
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
selectAll().map { it[idColumn] }.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPostById(id: PostId): RegisteredPost? {
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
select { idColumn.eq(id) }.firstOrNull() ?.toRegisteredPost()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPostsByContent(id: ContentId): List<RegisteredPost> {
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
val postsIds = contentsTable.getContentPosts(id)
|
||||
select { idColumn.inList(postsIds) }.map { it.toRegisteredPost() }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPostsByCreatingDates(from: DateTime, to: DateTime): List<RegisteredPost> {
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
select { creationDateColumn.between(from, to) }.map { it.toRegisteredPost() }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPostsByPagination(pagination: Pagination): PaginationResult<RegisteredPost> {
|
||||
return transaction(database) {
|
||||
return newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
val posts = selectAll().paginate(pagination).orderBy(creationDateColumn).map {
|
||||
it.toRegisteredPost()
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
package com.insanusmokrassar.postssystem.core.exposed
|
||||
|
||||
import com.insanusmokrassar.postssystem.core.utils.pagination.Pagination
|
||||
import com.insanusmokrassar.postssystem.core.utils.pagination.firstIndex
|
||||
import org.jetbrains.exposed.sql.Query
|
||||
|
||||
fun Query.paginate(pagination: Pagination) = limit(pagination.size, pagination.firstIndex.toLong())
|
@ -4,6 +4,7 @@ import com.insanusmokrassar.postssystem.core.content.BinaryContent
|
||||
import com.insanusmokrassar.postssystem.core.content.ContentId
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.statements.api.ExposedBlob
|
||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
private class BinaryContentHolderRepoTable(
|
||||
@ -16,12 +17,16 @@ private class BinaryContentHolderRepoTable(
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(idColumn)
|
||||
|
||||
init {
|
||||
transaction(database) {
|
||||
transaction (
|
||||
db = database
|
||||
) {
|
||||
SchemaUtils.createMissingTablesAndColumns(this@BinaryContentHolderRepoTable)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getContent(id: ContentId): BinaryContent? = transaction(database) {
|
||||
override suspend fun getContent(id: ContentId): BinaryContent? = newSuspendedTransaction (
|
||||
db = database
|
||||
) {
|
||||
select {
|
||||
idColumn.eq(id)
|
||||
}.limit(1).firstOrNull() ?.let {
|
||||
@ -36,13 +41,17 @@ private class BinaryContentHolderRepoTable(
|
||||
}
|
||||
|
||||
override suspend fun removeContent(id: ContentId) {
|
||||
transaction(database) {
|
||||
newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
deleteWhere { idColumn.eq(id) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun putContent(id: ContentId, content: BinaryContent) {
|
||||
transaction(database) {
|
||||
newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
insert {
|
||||
it[idColumn] = id
|
||||
it[originalFileNameColumn] = content.originalFileName
|
||||
|
@ -3,6 +3,7 @@ package com.insanusmokrassar.postssystem.core.exposed.content
|
||||
import com.insanusmokrassar.postssystem.core.content.ContentId
|
||||
import com.insanusmokrassar.postssystem.core.content.SpecialContent
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
private class SpecialContentHolderRepoTable(
|
||||
@ -13,12 +14,16 @@ private class SpecialContentHolderRepoTable(
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(idColumn)
|
||||
|
||||
init {
|
||||
transaction(database) {
|
||||
transaction(
|
||||
db = database
|
||||
) {
|
||||
SchemaUtils.createMissingTablesAndColumns(this@SpecialContentHolderRepoTable)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getContent(id: ContentId): SpecialContent? = transaction(database) {
|
||||
override suspend fun getContent(id: ContentId): SpecialContent? = newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
select {
|
||||
idColumn.eq(id)
|
||||
}.limit(1).firstOrNull() ?.get(internalIdColumn) ?.let {
|
||||
@ -27,13 +32,17 @@ private class SpecialContentHolderRepoTable(
|
||||
}
|
||||
|
||||
override suspend fun removeContent(id: ContentId) {
|
||||
transaction(database) {
|
||||
newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
deleteWhere { idColumn.eq(id) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun putContent(id: ContentId, content: SpecialContent) {
|
||||
transaction(database) {
|
||||
newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
insert {
|
||||
it[idColumn] = id
|
||||
it[internalIdColumn] = content.internalId
|
||||
|
@ -3,6 +3,7 @@ package com.insanusmokrassar.postssystem.core.exposed.content
|
||||
import com.insanusmokrassar.postssystem.core.content.ContentId
|
||||
import com.insanusmokrassar.postssystem.core.content.TextContent
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
private class TextContentHolderRepoTable(
|
||||
@ -13,12 +14,16 @@ private class TextContentHolderRepoTable(
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(idColumn)
|
||||
|
||||
init {
|
||||
transaction(database) {
|
||||
transaction(
|
||||
db = database
|
||||
) {
|
||||
SchemaUtils.createMissingTablesAndColumns(this@TextContentHolderRepoTable)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getContent(id: ContentId): TextContent? = transaction(database) {
|
||||
override suspend fun getContent(id: ContentId): TextContent? = newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
select {
|
||||
idColumn.eq(id)
|
||||
}.limit(1).firstOrNull() ?.get(textColumn) ?.let {
|
||||
@ -27,13 +32,17 @@ private class TextContentHolderRepoTable(
|
||||
}
|
||||
|
||||
override suspend fun removeContent(id: ContentId) {
|
||||
transaction(database) {
|
||||
newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
deleteWhere { idColumn.eq(id) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun putContent(id: ContentId, content: TextContent) {
|
||||
transaction(database) {
|
||||
newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
insert {
|
||||
it[idColumn] = id
|
||||
it[textColumn] = content.text
|
||||
|
@ -9,7 +9,7 @@ import java.sql.Connection
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ExposedContentAPICommonTests {
|
||||
class ExposedContentRepoCommonTests {
|
||||
private val tempFolder = System.getProperty("java.io.tmpdir")!!
|
||||
|
||||
@Test
|
||||
@ -25,7 +25,7 @@ class ExposedContentAPICommonTests {
|
||||
it.delete()
|
||||
it.deleteOnExit()
|
||||
}
|
||||
ExposedContentAPI(
|
||||
ExposedContentRepo(
|
||||
Database.Companion.connect("jdbc:sqlite:$it", driver = "org.sqlite.JDBC").also {
|
||||
it.transactionManager.defaultIsolationLevel = Connection.TRANSACTION_SERIALIZABLE
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ class ExposedPublishingKeysRepo(
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(postIdColumn, triggerControlKeyColumn)
|
||||
|
||||
init {
|
||||
transaction(database) {
|
||||
newSuspendedTransaction(
|
||||
db = database
|
||||
) {
|
||||
SchemaUtils.createMissingTablesAndColumns(this@ExposedPublishingKeysRepo)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package com.insanusmokrassar.postssystem.core.content.api
|
||||
import com.insanusmokrassar.postssystem.core.content.RegisteredContent
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ContentAPI : ReadContentAPI, WriteContentAPI {
|
||||
interface ContentRepo : ReadContentRepo, WriteContentRepo {
|
||||
val contentCreatedFlow: Flow<RegisteredContent>
|
||||
val contentDeletedFlow: Flow<RegisteredContent>
|
||||
}
|
@ -8,7 +8,7 @@ import com.insanusmokrassar.postssystem.core.utils.pagination.PaginationResult
|
||||
/**
|
||||
* Simple read API by different properties of [com.insanusmokrassar.postssystem.core.content.Content].
|
||||
*/
|
||||
interface ReadContentAPI {
|
||||
interface ReadContentRepo {
|
||||
/**
|
||||
* @return [Set] of [ContentId] wich currently known in this instance of API
|
||||
*/
|
@ -2,7 +2,7 @@ package com.insanusmokrassar.postssystem.core.content.api
|
||||
|
||||
import com.insanusmokrassar.postssystem.core.content.*
|
||||
|
||||
interface WriteContentAPI {
|
||||
interface WriteContentRepo {
|
||||
suspend fun registerContent(content: Content): RegisteredContent?
|
||||
suspend fun deleteContent(id: ContentId): Boolean
|
||||
}
|
Loading…
Reference in New Issue
Block a user