core/core/exposed/src/jvmMain/kotlin/dev/inmo/postssystem/core/exposed/ExposedPostsRepo.kt

216 lines
7.0 KiB
Kotlin
Raw Normal View History

2020-11-25 08:08:45 +00:00
package dev.inmo.postssystem.core.exposed
2019-11-11 04:02:37 +00:00
2020-11-25 08:08:45 +00:00
import dev.inmo.postssystem.core.content.ContentId
import dev.inmo.postssystem.core.generatePostId
import dev.inmo.postssystem.core.post.*
import dev.inmo.postssystem.core.post.repo.PostsRepo
import com.soywiz.klock.*
2020-11-25 07:53:15 +00:00
import dev.inmo.micro_utils.pagination.*
2019-11-11 04:02:37 +00:00
import kotlinx.coroutines.channels.BroadcastChannel
2020-07-30 09:28:11 +00:00
import kotlinx.coroutines.channels.Channel
2019-11-11 04:02:37 +00:00
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import org.jetbrains.exposed.sql.*
2020-04-10 06:35:33 +00:00
import org.jetbrains.exposed.sql.transactions.transaction
2019-11-11 04:02:37 +00:00
2020-07-30 09:34:32 +00:00
private class PostsRepoContentRelations(
2019-11-11 04:02:37 +00:00
private val database: Database
) : Table() {
private val postIdColumn = text("postId")
private val contentIdColumn = text("contentId")
init {
2020-07-30 09:34:32 +00:00
transaction (
db = database
) {
SchemaUtils.createMissingTablesAndColumns(this@PostsRepoContentRelations)
2019-11-11 04:02:37 +00:00
}
}
2020-07-31 05:28:27 +00:00
fun getPostContents(postId: PostId): List<ContentId> {
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
select { postIdColumn.eq(postId) }.map { it[contentIdColumn] }
}
}
2020-07-31 05:28:27 +00:00
fun getContentPosts(contentId: ContentId): List<PostId> {
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
select { contentIdColumn.eq(contentId) }.map { it[postIdColumn] }
}
}
2020-07-31 05:28:27 +00:00
fun linkPostAndContents(postId: PostId, vararg contentIds: ContentId) {
transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
val leftToPut = contentIds.toSet() - getPostContents(postId)
leftToPut.forEach { contentId ->
insert {
it[postIdColumn] = postId
it[contentIdColumn] = contentId
}
}
}
}
2020-07-31 05:28:27 +00:00
fun unlinkPostAndContents(postId: PostId, vararg contentIds: ContentId): Boolean {
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
deleteWhere {
postIdColumn.eq(postId).and(contentIdColumn.inList(contentIds.toList()))
} > 0
}
}
}
private val dateTimeFormat = DateFormat("EEE, dd MMM yyyy HH:mm:ss z")
2020-07-30 09:28:11 +00:00
private class PostsRepoDatabaseTable(
private val database: Database,
tableName: String = ""
) : PostsRepo, Table(tableName) {
2020-07-30 09:34:32 +00:00
private val contentsTable = PostsRepoContentRelations(database)
2019-11-11 04:02:37 +00:00
private val idColumn = text("postId")
private val creationDateColumn = text("creationDate").default(
DateTime.now().toString(dateTimeFormat)
)
2019-11-11 04:02:37 +00:00
2020-07-30 09:28:11 +00:00
private val postCreatedBroadcastChannel = BroadcastChannel<RegisteredPost>(Channel.BUFFERED)
2019-11-11 04:02:37 +00:00
override val postCreatedFlow: Flow<RegisteredPost> = postCreatedBroadcastChannel.asFlow()
2020-07-30 09:28:11 +00:00
private val postDeletedBroadcastChannel = BroadcastChannel<RegisteredPost>(Channel.BUFFERED)
2019-11-11 04:02:37 +00:00
override val postDeletedFlow: Flow<RegisteredPost> = postDeletedBroadcastChannel.asFlow()
2020-07-30 09:28:11 +00:00
private val postUpdatedBroadcastChannel = BroadcastChannel<RegisteredPost>(Channel.BUFFERED)
2019-11-11 04:02:37 +00:00
override val postUpdatedFlow: Flow<RegisteredPost> = postUpdatedBroadcastChannel.asFlow()
init {
2020-07-31 05:28:27 +00:00
transaction (db = database) {
2020-07-30 09:28:11 +00:00
SchemaUtils.createMissingTablesAndColumns(this@PostsRepoDatabaseTable)
2019-11-11 04:02:37 +00:00
}
}
2020-07-31 05:28:27 +00:00
private fun ResultRow.toRegisteredPost(): RegisteredPost = get(idColumn).let { id ->
2019-11-11 04:02:37 +00:00
SimpleRegisteredPost(
id,
contentsTable.getPostContents(id),
dateTimeFormat.parse(get(creationDateColumn)).local.unixMillis
2019-11-11 04:02:37 +00:00
)
}
override suspend fun createPost(post: Post): RegisteredPost? {
val id = (post as? RegisteredPost) ?.let { it.id } ?: generatePostId()
2020-07-31 05:28:27 +00:00
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
insert {
it[idColumn] = id
}
contentsTable.linkPostAndContents(id, *post.content.toTypedArray())
select { idColumn.eq(id) }.firstOrNull() ?.toRegisteredPost()
} ?.also {
postCreatedBroadcastChannel.send(it)
}
}
override suspend fun deletePost(id: PostId): Boolean {
val post = getPostById(id) ?: return false
2020-07-31 05:28:27 +00:00
return (transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
deleteWhere { idColumn.eq(id) }
} > 0).also {
if (it) {
postDeletedBroadcastChannel.send(post)
contentsTable.unlinkPostAndContents(id, *post.content.toTypedArray())
}
}
}
override suspend fun updatePostContent(postId: PostId, post: Post): Boolean {
2020-07-31 05:28:27 +00:00
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
val alreadyLinked = contentsTable.getPostContents(postId)
val toRemove = alreadyLinked - post.content
val toInsert = post.content - alreadyLinked
val updated = (toRemove.isNotEmpty() && contentsTable.unlinkPostAndContents(postId, *toRemove.toTypedArray())) || toInsert.isNotEmpty()
if (toInsert.isNotEmpty()) {
contentsTable.linkPostAndContents(postId, *toInsert.toTypedArray())
}
updated
}.also {
if (it) {
getPostById(postId) ?.also { updatedPost -> postUpdatedBroadcastChannel.send(updatedPost) }
}
}
}
override suspend fun getPostsIds(): Set<PostId> {
2020-07-31 05:28:27 +00:00
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
selectAll().map { it[idColumn] }.toSet()
}
}
override suspend fun getPostById(id: PostId): RegisteredPost? {
2020-07-31 05:28:27 +00:00
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
select { idColumn.eq(id) }.firstOrNull() ?.toRegisteredPost()
}
}
override suspend fun getPostsByContent(id: ContentId): List<RegisteredPost> {
2020-07-31 05:28:27 +00:00
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2019-11-11 04:02:37 +00:00
val postsIds = contentsTable.getContentPosts(id)
select { idColumn.inList(postsIds) }.map { it.toRegisteredPost() }
}
}
override suspend fun getPostsByCreatingDates(
from: DateTime,
to: DateTime,
pagination: Pagination
): PaginationResult<RegisteredPost> {
2020-07-31 05:28:27 +00:00
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
select { creationDateColumn.between(from, to) }.paginate(
pagination
).map {
it.toRegisteredPost()
}.createPaginationResult(
pagination,
selectAll().count()
)
2019-11-11 04:02:37 +00:00
}
}
2019-11-24 19:15:31 +00:00
override suspend fun getPostsByPagination(pagination: Pagination): PaginationResult<RegisteredPost> {
2020-07-31 05:28:27 +00:00
return transaction(
2020-07-30 09:34:32 +00:00
db = database
) {
2020-04-10 06:54:06 +00:00
val posts = selectAll().paginate(pagination).orderBy(creationDateColumn).map {
2019-11-11 04:02:37 +00:00
it.toRegisteredPost()
}
val postsNumber = selectAll().count()
2020-04-10 06:54:06 +00:00
posts.createPaginationResult(pagination, postsNumber)
2019-11-11 04:02:37 +00:00
}
}
}
2020-07-30 09:28:11 +00:00
class ExposedPostsRepo (
database: Database,
tableName: String = ""
) : PostsRepo by PostsRepoDatabaseTable(database, tableName)