small refactor

This commit is contained in:
InsanusMokrassar 2020-07-30 15:34:32 +06:00
parent 3795b85827
commit a6a7634076
11 changed files with 132 additions and 61 deletions

View File

@ -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)

View File

@ -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()
}

View File

@ -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())

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -15,7 +15,9 @@ class ExposedPublishingKeysRepo(
override val primaryKey: PrimaryKey = PrimaryKey(postIdColumn, triggerControlKeyColumn)
init {
transaction(database) {
newSuspendedTransaction(
db = database
) {
SchemaUtils.createMissingTablesAndColumns(this@ExposedPublishingKeysRepo)
}
}

View File

@ -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>
}

View File

@ -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
*/

View File

@ -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
}