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