diff --git a/PostsSystem.drawio b/PostsSystem.drawio index a7cfdd76..53f0b27e 100644 --- a/PostsSystem.drawio +++ b/PostsSystem.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index e4239360..ffcbb98d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,14 +12,14 @@ kotlin_coroutines_version=1.4.2 kotlin_serialisation_core_version=1.0.1 kotlin_exposed_version=0.28.1 -ktor_version=1.4.2 +ktor_version=1.4.3 klockVersion=2.0.0 uuidVersion=0.2.3 exposed_version=0.28.1 test_sqlite_version=3.32.3.2 -microutils_version=0.4.7 +microutils_version=0.4.9 javax_activation_version=1.1.1 diff --git a/publishing/api/src/commonMain/kotlin/dev/inmo/postssystem/core/publishing/PublishingTrigger.kt b/publishing/api/src/commonMain/kotlin/dev/inmo/postssystem/core/publishing/PublishingTrigger.kt index dfdbdc08..37c5d191 100644 --- a/publishing/api/src/commonMain/kotlin/dev/inmo/postssystem/core/publishing/PublishingTrigger.kt +++ b/publishing/api/src/commonMain/kotlin/dev/inmo/postssystem/core/publishing/PublishingTrigger.kt @@ -1,14 +1,15 @@ package dev.inmo.postssystem.core.publishing +import dev.inmo.micro_utils.repos.create import dev.inmo.postssystem.core.post.PostId import dev.inmo.postssystem.core.post.RegisteredPost import dev.inmo.postssystem.core.post.repo.PostsRepo -import dev.inmo.postssystem.core.publishing.repos.PublishedPostsWriteRepo -import dev.inmo.postssystem.core.publishing.repos.PublishingKeysRepo +import dev.inmo.postssystem.core.publishing.repos.* import kotlinx.coroutines.flow.* +import kotlinx.serialization.Serializable interface PublishingTrigger { - val postingTriggeredFlow: SharedFlow + val postingTriggeredFlow: SharedFlow suspend fun triggerPosting( triggerControlKey: TriggerControlKey @@ -20,23 +21,17 @@ class BusinessPublishingTrigger( private val publishedPostsRepo: PublishedPostsWriteRepo, private val publishingKeysRepo: PublishingKeysRepo ) : PublishingTrigger { - private val _postingTriggeredFlow: MutableSharedFlow = MutableSharedFlow() - override val postingTriggeredFlow: SharedFlow = _postingTriggeredFlow.asSharedFlow() + private val _postingTriggeredFlow: MutableSharedFlow = MutableSharedFlow() + override val postingTriggeredFlow: SharedFlow = _postingTriggeredFlow.asSharedFlow() override suspend fun triggerPosting(triggerControlKey: TriggerControlKey): PostId? { val postId = publishingKeysRepo.getPostIdByTriggerControlKey(triggerControlKey) ?: return null publishingKeysRepo.unsetPostTriggerControlKey(postId) return postsRepo.getPostById(postId) ?.let { post -> - publishedPostsRepo.registerPublishedPost(post) ?.let { publishedPost -> - if (postsRepo.deletePost(postId)) { - _postingTriggeredFlow.emit(post) - postId - } else { - publishedPostsRepo.unpublishPost(publishedPost.id) - null - } + publishedPostsRepo.create(post).firstOrNull() ?.also { + _postingTriggeredFlow.emit(it) } - } + } ?.id } } diff --git a/publishing/api/src/commonMain/kotlin/dev/inmo/postssystem/core/publishing/repos/PublishedPostsRepo.kt b/publishing/api/src/commonMain/kotlin/dev/inmo/postssystem/core/publishing/repos/PublishedPostsRepo.kt index a4d51998..4f7e6bb4 100644 --- a/publishing/api/src/commonMain/kotlin/dev/inmo/postssystem/core/publishing/repos/PublishedPostsRepo.kt +++ b/publishing/api/src/commonMain/kotlin/dev/inmo/postssystem/core/publishing/repos/PublishedPostsRepo.kt @@ -1,36 +1,40 @@ package dev.inmo.postssystem.core.publishing.repos +import com.soywiz.klock.DateTime +import dev.inmo.micro_utils.pagination.* +import dev.inmo.micro_utils.repos.* +import dev.inmo.postssystem.core.UnixMillis import dev.inmo.postssystem.core.post.PostId import dev.inmo.postssystem.core.post.RegisteredPost import dev.inmo.postssystem.core.post.repo.PostsRepo -import dev.inmo.postssystem.core.post.repo.ReadPostsRepo import kotlinx.coroutines.flow.Flow +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient -interface PublishedPostsReadRepo : ReadPostsRepo -interface PublishedPostsWriteRepo { - val postPublishedFlow: Flow - val postUnpublishedFlow: Flow - - suspend fun registerPublishedPost(registeredPost: RegisteredPost): RegisteredPost? - suspend fun unpublishPost(postId: PostId): Boolean? +typealias PublishedPostId = String +@Serializable +data class PublishedPost( + val id: PublishedPostId, + val post: RegisteredPost, + private val publicationTime: UnixMillis +) { + @Transient + val publicationDateTime = DateTime(publicationTime) } -interface PublishedPostsRepo : PublishedPostsReadRepo, PublishedPostsWriteRepo - -class BusinessPublishedPostsRepo( - private val realPostsRepoWithPublishedPosts: PostsRepo -) : PublishedPostsRepo, ReadPostsRepo by realPostsRepoWithPublishedPosts { - override val postPublishedFlow: Flow - get() = realPostsRepoWithPublishedPosts.postCreatedFlow - override val postUnpublishedFlow: Flow - get() = realPostsRepoWithPublishedPosts.postDeletedFlow - - override suspend fun registerPublishedPost( - registeredPost: RegisteredPost - ): RegisteredPost? = realPostsRepoWithPublishedPosts.createPost(registeredPost) - - override suspend fun unpublishPost( - postId: PostId - ): Boolean? = realPostsRepoWithPublishedPosts.deletePost(postId) - +interface PublishedPostsReadRepo : ReadCRUDRepo { + suspend fun getPostPublishing( + postId: PostId, + pagination: Pagination, + reversed: Boolean = false + ): PaginationResult } +suspend inline fun PostId.isPublished(lookIn: PublishedPostsReadRepo): Boolean { + return lookIn.getPostPublishing(this, FirstPagePagination(1)).results.isNotEmpty() +} +suspend inline fun RegisteredPost.isPublished(lookIn: PublishedPostsReadRepo): Boolean { + return lookIn.getPostPublishing(id, FirstPagePagination(1)).results.isNotEmpty() +} +interface PublishedPostsWriteRepo : WriteCRUDRepo + +interface PublishedPostsRepo : PublishedPostsReadRepo, PublishedPostsWriteRepo, CRUDRepo diff --git a/publishing/exposed/src/jvmMain/kotlin/dev/inmo/postssystem/core/publishing/exposed/ExposedPublishedPostsRepo.kt b/publishing/exposed/src/jvmMain/kotlin/dev/inmo/postssystem/core/publishing/exposed/ExposedPublishedPostsRepo.kt new file mode 100644 index 00000000..ee4bd794 --- /dev/null +++ b/publishing/exposed/src/jvmMain/kotlin/dev/inmo/postssystem/core/publishing/exposed/ExposedPublishedPostsRepo.kt @@ -0,0 +1,81 @@ +package dev.inmo.postssystem.core.publishing.exposed + +import com.soywiz.klock.DateTime +import dev.inmo.micro_utils.coroutines.launchSynchronously +import dev.inmo.micro_utils.pagination.* +import dev.inmo.micro_utils.pagination.utils.reverse +import dev.inmo.micro_utils.repos.exposed.AbstractExposedCRUDRepo +import dev.inmo.micro_utils.repos.exposed.initTable +import dev.inmo.postssystem.core.generateId +import dev.inmo.postssystem.core.post.PostId +import dev.inmo.postssystem.core.post.RegisteredPost +import dev.inmo.postssystem.core.post.repo.ReadPostsRepo +import dev.inmo.postssystem.core.publishing.repos.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.statements.InsertStatement +import org.jetbrains.exposed.sql.statements.UpdateStatement +import org.jetbrains.exposed.sql.transactions.transaction + +class ExposedPublishedPostsRepo( + override val database: Database, + private val postsRepo: ReadPostsRepo, + tableName: String = "PublishedPostsRepo", + private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default) +) : PublishedPostsRepo, + AbstractExposedCRUDRepo( + tableName = tableName + ) { + private val idColumn = text("id") + private val postIdColumn = text("postId") + private val dateTimeColumn = double("dateTime").clientDefault { + DateTime.now().unixMillis + } + + override val selectByIds: SqlExpressionBuilder.(List) -> Op = { idColumn.inList(it) } + override val InsertStatement.asObject: PublishedPost get() = TODO() + + override fun InsertStatement.asObject(value: RegisteredPost): PublishedPost = PublishedPost( + get(idColumn), + value, + get(dateTimeColumn) + ) + + override val selectById: SqlExpressionBuilder.(PublishedPostId) -> Op = { idColumn.eq(it) } + override val ResultRow.asObject: PublishedPost + get() = PublishedPost( + get(idColumn), + requireNotNull(launchSynchronously(scope) { postsRepo.getPostById(get(postIdColumn)) }) { "Post with id \"${get(postIdColumn)}\" not found" }, + get(dateTimeColumn) + ) + + init { + initTable() + } + + override fun insert(value: RegisteredPost, it: InsertStatement) { + it[idColumn] = generateId() + it[postIdColumn] = value.id + } + + override fun update(id: PublishedPostId, value: RegisteredPost, it: UpdateStatement) { + it[postIdColumn] = value.id + } + + override suspend fun getPostPublishing( + postId: PostId, + pagination: Pagination, + reversed: Boolean + ): PaginationResult = transaction(database) { + val query = select { postIdColumn.eq(postId) } + query.paginate(pagination, postIdColumn, reversed).map { + it[idColumn] + } to query.count() + }.let { (results, count) -> + results.createPaginationResult( + if (reversed) pagination.reverse(count) else pagination, + count + ) + } +} \ No newline at end of file