From 20e3fd693443f5f31945dbba184911d1d0ae5967 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Thu, 26 Nov 2020 02:39:57 +0600
Subject: [PATCH] add businessContentRepo

---
 .../inmo/postssystem/core/content/Content.kt  |   9 +-
 .../core/content/api/BusinessContentRepo.kt   | 120 ++++++++++++++++++
 .../core/content/api/ReadContentRepo.kt       |  18 +--
 .../core/content/api/WriteContentRepo.kt      |   9 +-
 4 files changed, 129 insertions(+), 27 deletions(-)
 create mode 100644 core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/BusinessContentRepo.kt

diff --git a/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/Content.kt b/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/Content.kt
index 68a0bdde..631f0c3b 100644
--- a/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/Content.kt
+++ b/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/Content.kt
@@ -11,8 +11,7 @@ typealias ContentId = String
 /**
  * Content which is planned to be registered in database
  */
-@Serializable
-sealed class Content
+interface Content
 
 /**
  * It is content, which was added by some external source to use inside of some plugins. For example, you can use
@@ -21,12 +20,12 @@ sealed class Content
 @Serializable
 data class SpecialContent(
     val internalId: ContentId
-) : Content()
+) : Content
 
 @Serializable
 data class TextContent(
     val text: String
-) : Content()
+) : Content
 
 @Serializable
 data class BinaryContent(
@@ -34,7 +33,7 @@ data class BinaryContent(
     val originalFileName: String,
     @Serializable(ByteArrayAllocatorSerializer::class)
     val dataAllocator: ByteArrayAllocator
-) : Content()
+) : Content
 
 val BinaryContent.isImage: Boolean
     get() = mimeType is KnownMimeTypes.Image
diff --git a/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/BusinessContentRepo.kt b/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/BusinessContentRepo.kt
new file mode 100644
index 00000000..564d8968
--- /dev/null
+++ b/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/BusinessContentRepo.kt
@@ -0,0 +1,120 @@
+package dev.inmo.postssystem.core.content.api
+
+import dev.inmo.micro_utils.pagination.*
+import dev.inmo.micro_utils.repos.Repo
+import dev.inmo.micro_utils.repos.UpdatedValuePair
+import dev.inmo.postssystem.core.content.*
+import dev.inmo.postssystem.core.generateContentId
+import kotlinx.coroutines.flow.*
+
+interface BusinessContentRepoReadHelpInterface : Repo {
+    suspend fun getKeysByPagination(pagination: Pagination): PaginationResult<ContentId>
+    suspend fun contains(contentId: ContentId): Boolean
+    suspend fun getType(contentId: ContentId): AdapterType?
+    suspend fun count(): Long
+}
+interface BusinessContentRepoWriteHelpInterface : Repo {
+    suspend fun deleteContentId(contentId: ContentId)
+    suspend fun saveType(contentId: ContentId, type: AdapterType): Boolean
+}
+interface BusinessContentRepoHelpInterface : BusinessContentRepoReadHelpInterface, BusinessContentRepoWriteHelpInterface
+
+typealias AdapterType = String
+
+interface BusinessContentRepoContentAdapter {
+    val type: AdapterType
+    suspend fun storeContent(contentId: ContentId, content: Content): Boolean
+    suspend fun getContent(contentId: ContentId): Content?
+    suspend fun removeContent(contentId: ContentId)
+}
+
+class BusinessReadContentRepo(
+    adapters: List<BusinessContentRepoContentAdapter>,
+    private val helperRepo: BusinessContentRepoReadHelpInterface,
+) : ReadContentRepo {
+    private val adaptersMap: Map<String, BusinessContentRepoContentAdapter> = adapters.map {
+        it.type to it
+    }.toMap()
+    override suspend fun contains(id: ContentId): Boolean = helperRepo.contains(id)
+
+    override suspend fun count(): Long = helperRepo.count()
+
+    override suspend fun getById(id: ContentId): RegisteredContent? = helperRepo.getType(id) ?.let {
+        adaptersMap[it] ?.getContent(id) ?.let { content ->
+            RegisteredContent(id, content)
+        }
+    }
+
+    override suspend fun getByPagination(
+        pagination: Pagination
+    ): PaginationResult<RegisteredContent> = helperRepo.getKeysByPagination(
+        pagination
+    ).let {
+        it.results.mapNotNull {
+            getById(it)
+        }.createPaginationResult(
+            it,
+            count()
+        )
+    }
+}
+
+class BusinessWriteContentRepo(
+    private val adapters: List<BusinessContentRepoContentAdapter>,
+    private val helperRepo: BusinessContentRepoHelpInterface
+) : WriteContentRepo {
+    private val adaptersMap = adapters.map { it.type to it }.toMap()
+    private val _deletedObjectsIdsFlow = MutableSharedFlow<ContentId>()
+    override val deletedObjectsIdsFlow: Flow<ContentId> = _deletedObjectsIdsFlow.asSharedFlow()
+    private val _newObjectsFlow = MutableSharedFlow<RegisteredContent>()
+    override val newObjectsFlow: Flow<RegisteredContent> = _newObjectsFlow.asSharedFlow()
+    private val _updatedObjectsFlow = MutableSharedFlow<RegisteredContent>()
+    override val updatedObjectsFlow: Flow<RegisteredContent> = _updatedObjectsFlow.asSharedFlow()
+
+    override suspend fun create(values: List<Content>): List<RegisteredContent> {
+        return values.mapNotNull { content ->
+            val contentId = generateContentId()
+            val adapter = adapters.firstOrNull { it.storeContent(contentId, content) } ?: return@mapNotNull null
+            if (!helperRepo.saveType(contentId, adapter.type)) {
+                adapter.removeContent(contentId)
+            }
+            RegisteredContent(contentId, content).also { _newObjectsFlow.emit(it) }
+        }
+    }
+
+    override suspend fun deleteById(ids: List<ContentId>) {
+        ids.forEach { contentId ->
+            adaptersMap[helperRepo.getType(contentId)] ?.removeContent(contentId) ?: adapters.forEach {
+                it.removeContent(contentId)
+            }
+            helperRepo.deleteContentId(contentId)
+            _deletedObjectsIdsFlow.emit(contentId)
+        }
+    }
+
+    override suspend fun update(id: ContentId, value: Content): RegisteredContent? {
+        adaptersMap[helperRepo.getType(id)] ?.removeContent(id) ?: adapters.forEach {
+            it.removeContent(id)
+        }
+        adapters.firstOrNull { it.storeContent(id, value) } ?: return null
+        return RegisteredContent(id, value).also { _updatedObjectsFlow.emit(it) }
+    }
+
+    override suspend fun update(values: List<UpdatedValuePair<ContentId, Content>>): List<RegisteredContent> {
+        return values.mapNotNull {
+            update(it.first, it.second)
+        }
+    }
+
+}
+
+class BusinessContentRepo(
+    adapters: List<BusinessContentRepoContentAdapter>,
+    helperRepo: BusinessContentRepoHelpInterface
+) : ContentRepo, ReadContentRepo by BusinessReadContentRepo(
+    adapters,
+    helperRepo
+), WriteContentRepo by BusinessWriteContentRepo(
+    adapters,
+    helperRepo
+)
diff --git a/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/ReadContentRepo.kt b/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/ReadContentRepo.kt
index 1fc6e7e6..f65fae5d 100644
--- a/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/ReadContentRepo.kt
+++ b/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/ReadContentRepo.kt
@@ -4,22 +4,10 @@ import dev.inmo.postssystem.core.content.ContentId
 import dev.inmo.postssystem.core.content.RegisteredContent
 import dev.inmo.micro_utils.pagination.Pagination
 import dev.inmo.micro_utils.pagination.PaginationResult
+import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
+import dev.inmo.micro_utils.repos.pagination.getAll
 
 /**
  * Simple read API by different properties of [dev.inmo.postssystem.core.content.Content].
  */
-interface ReadContentRepo {
-    /**
-     * @return [Set] of [ContentId] wich currently known in this instance of API
-     */
-    suspend fun getContentsIds(): Set<ContentId>
-    /**
-     * @return [RegisteredContent] if it is available by [id]
-     */
-    suspend fun getContentById(id: ContentId): RegisteredContent?
-
-    /**
-     * @return all [RegisteredContent] by pages basing on their creation date
-     */
-    suspend fun getContentByPagination(pagination: Pagination): PaginationResult<RegisteredContent>
-}
\ No newline at end of file
+interface ReadContentRepo : ReadStandardCRUDRepo<RegisteredContent, ContentId>
diff --git a/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/WriteContentRepo.kt b/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/WriteContentRepo.kt
index 5fb9d19a..368dcbfa 100644
--- a/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/WriteContentRepo.kt
+++ b/core/api/src/commonMain/kotlin/dev/inmo/postssystem/core/content/api/WriteContentRepo.kt
@@ -1,12 +1,7 @@
 package dev.inmo.postssystem.core.content.api
 
+import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
 import dev.inmo.postssystem.core.content.*
 import kotlinx.coroutines.flow.Flow
 
-interface WriteContentRepo {
-    val contentCreatedFlow: Flow<RegisteredContent>
-    val contentDeletedFlow: Flow<RegisteredContent>
-
-    suspend fun registerContent(content: Content): RegisteredContent?
-    suspend fun deleteContent(id: ContentId): Boolean
-}
+interface WriteContentRepo : WriteStandardCRUDRepo<RegisteredContent, ContentId, Content>