add storing of content
This commit is contained in:
client/src/commonMain/kotlin/dev/inmo/postssystem/client
features
content
binary
server
server
text
server
files
client
src
commonMain
kotlin
dev
inmo
postssystem
features
files
common
src
commonMain
kotlin
dev
inmo
postssystem
features
files
jvmMain
kotlin
dev
inmo
postssystem
features
files
common
server
src
jvmMain
kotlin
dev
inmo
postssystem
features
files
server/src/main/java/dev/inmo/postssystem/server
@ -10,7 +10,8 @@ kotlin {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api project(":postssystem.features.content.binary.common")
|
||||
api project(":postssystem.features.common.server")
|
||||
api project(":postssystem.features.content.server")
|
||||
api project(":postssystem.features.files.server")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
85
features/content/binary/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/binary/server/BinaryServerContentStorage.kt
Normal file
85
features/content/binary/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/binary/server/BinaryServerContentStorage.kt
Normal file
@ -0,0 +1,85 @@
|
||||
package dev.inmo.postssystem.features.content.binary.server
|
||||
|
||||
import dev.inmo.micro_utils.pagination.*
|
||||
import dev.inmo.micro_utils.repos.UpdatedValuePair
|
||||
import dev.inmo.postssystem.features.content.binary.common.BinaryContent
|
||||
import dev.inmo.postssystem.features.content.common.*
|
||||
import dev.inmo.postssystem.features.content.server.ServerContentStorage
|
||||
import dev.inmo.postssystem.features.files.common.*
|
||||
import dev.inmo.postssystem.features.files.common.storage.FilesStorage
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class BinaryServerContentStorage(
|
||||
private val filesStorage: FilesStorage
|
||||
) : ServerContentStorage<BinaryContent> {
|
||||
private val FileId.asContentId
|
||||
get() = ContentId(string)
|
||||
private val ContentId.asFileId
|
||||
get() = FileId(string)
|
||||
private val FullFileInfoStorageWrapper.asRegisteredContent
|
||||
get() = RegisteredContent(
|
||||
id.asContentId,
|
||||
BinaryContent(
|
||||
fileInfo.name,
|
||||
fileInfo.mimeType,
|
||||
fileInfo.byteArrayAllocator
|
||||
)
|
||||
)
|
||||
private val BinaryContent.asFullFileInfo
|
||||
get() = FullFileInfo(
|
||||
filename,
|
||||
mimeType,
|
||||
bytesAllocator
|
||||
)
|
||||
override val deletedObjectsIdsFlow: Flow<ContentId> = filesStorage.deletedObjectsIdsFlow.map { it.asContentId }
|
||||
override val newObjectsFlow: Flow<RegisteredContent> = filesStorage.newObjectsFlow.map { it.asRegisteredContent }
|
||||
override val updatedObjectsFlow: Flow<RegisteredContent> = filesStorage.updatedObjectsFlow.map { it.asRegisteredContent }
|
||||
|
||||
override suspend fun create(values: List<BinaryContent>): List<RegisteredContent> {
|
||||
return filesStorage.create(
|
||||
values.map { it.asFullFileInfo }
|
||||
).map { it.asRegisteredContent }
|
||||
}
|
||||
|
||||
override suspend fun deleteById(ids: List<ContentId>) {
|
||||
filesStorage.deleteById(ids.map { it.asFileId })
|
||||
}
|
||||
|
||||
override suspend fun update(id: ContentId, value: BinaryContent): RegisteredContent? {
|
||||
return filesStorage.update(
|
||||
id.asFileId,
|
||||
value.asFullFileInfo
|
||||
) ?.asRegisteredContent
|
||||
}
|
||||
|
||||
override suspend fun update(values: List<UpdatedValuePair<ContentId, BinaryContent>>): List<RegisteredContent> {
|
||||
return filesStorage.update(
|
||||
values.map { (id, content) ->
|
||||
id.asFileId to content.asFullFileInfo
|
||||
}
|
||||
).map {
|
||||
it.asRegisteredContent
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun contains(id: ContentId): Boolean {
|
||||
return filesStorage.contains(id.asFileId)
|
||||
}
|
||||
|
||||
override suspend fun count(): Long = filesStorage.count()
|
||||
|
||||
override suspend fun getById(id: ContentId): RegisteredContent? {
|
||||
return filesStorage.getFullFileInfo(id.asFileId) ?.asRegisteredContent
|
||||
}
|
||||
|
||||
override suspend fun getByPagination(pagination: Pagination): PaginationResult<RegisteredContent> {
|
||||
return filesStorage.getByPagination(pagination).let {
|
||||
it.changeResultsUnchecked(
|
||||
it.results.mapNotNull {
|
||||
filesStorage.getFullFileInfo(it.id) ?.asRegisteredContent
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ kotlin {
|
||||
dependencies {
|
||||
api project(":postssystem.features.content.common")
|
||||
api project(":postssystem.features.common.server")
|
||||
api project(":postssystem.features.content.server")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,4 @@ package dev.inmo.postssystem.features.content.server
|
||||
import dev.inmo.micro_utils.repos.CRUDRepo
|
||||
import dev.inmo.postssystem.features.content.common.*
|
||||
|
||||
interface ServerContentStorage : ServerReadContentStorage, ServerWriteContentStorage, CRUDRepo<RegisteredContent, ContentId, Content>
|
||||
interface ServerContentStorage<T: Content> : ServerReadContentStorage, ServerWriteContentStorage<T>, CRUDRepo<RegisteredContent, ContentId, T>
|
||||
|
117
features/content/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/server/ServerContentStorageAggregator.kt
Normal file
117
features/content/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/server/ServerContentStorageAggregator.kt
Normal file
@ -0,0 +1,117 @@
|
||||
package dev.inmo.postssystem.features.content.server
|
||||
|
||||
import dev.inmo.micro_utils.pagination.*
|
||||
import dev.inmo.micro_utils.repos.UpdatedValuePair
|
||||
import dev.inmo.postssystem.features.content.common.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ServerContentStorageAggregator(
|
||||
private val storages: List<ServerContentStorageWrapper<*>>,
|
||||
private val scope: CoroutineScope
|
||||
) : ServerContentStorage<Content> {
|
||||
override val deletedObjectsIdsFlow: Flow<ContentId>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val newObjectsFlow: Flow<RegisteredContent>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val updatedObjectsFlow: Flow<RegisteredContent>
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override suspend fun create(values: List<Content>): List<RegisteredContent> {
|
||||
return values.groupBy {
|
||||
storages.firstOrNull { storage -> storage.mayHandle(it) }
|
||||
}.let {
|
||||
(it - null) as Map<ServerContentStorageWrapper<*>, List<Content>>
|
||||
}.flatMap { (storage, content) ->
|
||||
storage.create(content)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteById(ids: List<ContentId>) {
|
||||
storages.map {
|
||||
scope.launch { it.deleteById(ids) }
|
||||
}.joinAll()
|
||||
}
|
||||
|
||||
override suspend fun update(id: ContentId, value: Content): RegisteredContent? {
|
||||
return storages.mapNotNull {
|
||||
it.takeIf { it.mayHandle(value) }
|
||||
}.firstNotNullOfOrNull { it.update(id, value) }
|
||||
}
|
||||
|
||||
override suspend fun update(values: List<UpdatedValuePair<ContentId, Content>>): List<RegisteredContent> {
|
||||
return values.groupBy { (_, content) ->
|
||||
storages.firstOrNull { storage -> storage.mayHandle(content) }
|
||||
}.let {
|
||||
(it - null) as Map<ServerContentStorageWrapper<*>, List<UpdatedValuePair<ContentId, Content>>>
|
||||
}.flatMap { (storage, values) ->
|
||||
storage.update(values)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun contains(id: ContentId): Boolean {
|
||||
val contains = CompletableDeferred<Boolean>()
|
||||
storages.map {
|
||||
scope.launch {
|
||||
if (it.contains(id)) {
|
||||
contains.complete(true)
|
||||
}
|
||||
}.also { job ->
|
||||
contains.invokeOnCompletion { job.cancel() }
|
||||
}
|
||||
}.joinAll()
|
||||
return if (contains.isCompleted) {
|
||||
contains.getCompleted()
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun count(): Long {
|
||||
return storages.map {
|
||||
scope.async {
|
||||
it.count()
|
||||
}
|
||||
}.awaitAll().sum()
|
||||
}
|
||||
|
||||
override suspend fun getById(id: ContentId): RegisteredContent? {
|
||||
val result = CompletableDeferred<RegisteredContent>()
|
||||
|
||||
storages.map {
|
||||
scope.launch {
|
||||
val content = it.getById(id)
|
||||
if (content != null) {
|
||||
result.complete(content)
|
||||
}
|
||||
}.also { job ->
|
||||
result.invokeOnCompletion { job.cancel() }
|
||||
}
|
||||
}.joinAll()
|
||||
|
||||
return if (result.isCompleted) {
|
||||
result.getCompleted()
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getByPagination(
|
||||
pagination: Pagination
|
||||
): PaginationResult<RegisteredContent> {
|
||||
val currentResults = mutableListOf<RegisteredContent>()
|
||||
|
||||
for (it in storages) {
|
||||
val currentPagination = PaginationByIndexes((currentResults.size + pagination.firstIndex), pagination.lastIndex)
|
||||
val wrapperResults = it.getByPagination(currentPagination)
|
||||
|
||||
currentResults.addAll(wrapperResults.results)
|
||||
|
||||
if (currentResults.size >= pagination.size) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return currentResults.createPaginationResult(pagination, count())
|
||||
}
|
||||
}
|
47
features/content/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/server/ServerContentStorageWrapper.kt
Normal file
47
features/content/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/server/ServerContentStorageWrapper.kt
Normal file
@ -0,0 +1,47 @@
|
||||
package dev.inmo.postssystem.features.content.server
|
||||
|
||||
import dev.inmo.micro_utils.repos.UpdatedValuePair
|
||||
import dev.inmo.postssystem.features.content.common.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class ServerContentStorageWrapper<T: Content>(
|
||||
private val originalStorage: ServerContentStorage<T>,
|
||||
private val klass: KClass<T>
|
||||
): ServerContentStorage<Content>, ServerReadContentStorage by originalStorage {
|
||||
override val deletedObjectsIdsFlow: Flow<ContentId> by originalStorage::deletedObjectsIdsFlow
|
||||
override val newObjectsFlow: Flow<RegisteredContent> by originalStorage::newObjectsFlow
|
||||
override val updatedObjectsFlow: Flow<RegisteredContent> by originalStorage::updatedObjectsFlow
|
||||
|
||||
fun mayHandle(content: Content) = klass.isInstance(content)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun asHandlableContent(content: Content) = if (mayHandle(content)) content as T else null
|
||||
|
||||
override suspend fun create(values: List<Content>): List<RegisteredContent> {
|
||||
return originalStorage.create(
|
||||
values.mapNotNull {
|
||||
asHandlableContent(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun deleteById(ids: List<ContentId>) = originalStorage.deleteById(ids)
|
||||
|
||||
override suspend fun update(id: ContentId, value: Content): RegisteredContent? {
|
||||
return originalStorage.update(id, asHandlableContent(value) ?: return null)
|
||||
}
|
||||
|
||||
override suspend fun update(values: List<UpdatedValuePair<ContentId, Content>>): List<RegisteredContent> {
|
||||
return originalStorage.update(
|
||||
values.mapNotNull { (id, content) ->
|
||||
id to (asHandlableContent(content) ?: return@mapNotNull null)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline fun <reified T: Content> ServerContentStorage<T>.wrap() = ServerContentStorageWrapper(
|
||||
this,
|
||||
T::class
|
||||
)
|
@ -3,4 +3,4 @@ package dev.inmo.postssystem.features.content.server
|
||||
import dev.inmo.micro_utils.repos.WriteCRUDRepo
|
||||
import dev.inmo.postssystem.features.content.common.*
|
||||
|
||||
interface ServerWriteContentStorage : WriteCRUDRepo<RegisteredContent, ContentId, Content>
|
||||
interface ServerWriteContentStorage<T: Content> : WriteCRUDRepo<RegisteredContent, ContentId, T>
|
||||
|
@ -11,6 +11,7 @@ kotlin {
|
||||
dependencies {
|
||||
api project(":postssystem.features.content.text.common")
|
||||
api project(":postssystem.features.common.server")
|
||||
api project(":postssystem.features.content.server")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
53
features/content/text/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/text/server/TextServerContentStorage.kt
Normal file
53
features/content/text/server/src/jvmMain/kotlin/dev/inmo/postssystem/features/content/text/server/TextServerContentStorage.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package dev.inmo.postssystem.features.content.text.server
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.exposed.AbstractExposedCRUDRepo
|
||||
import dev.inmo.micro_utils.repos.exposed.initTable
|
||||
import dev.inmo.postssystem.features.content.common.ContentId
|
||||
import dev.inmo.postssystem.features.content.common.RegisteredContent
|
||||
import dev.inmo.postssystem.features.content.server.ServerContentStorage
|
||||
import dev.inmo.postssystem.features.content.text.common.TextContent
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||
import org.jetbrains.exposed.sql.statements.UpdateStatement
|
||||
|
||||
class TextServerContentStorage(
|
||||
override val database: Database
|
||||
) : ServerContentStorage<TextContent>,
|
||||
AbstractExposedCRUDRepo<RegisteredContent, ContentId, TextContent>(tableName = "TextContent") {
|
||||
|
||||
val idColumn = text("id")
|
||||
private val textColumn = text("text")
|
||||
|
||||
override val selectByIds: SqlExpressionBuilder.(List<ContentId>) -> Op<Boolean> = {
|
||||
idColumn.inList(it.map { it.string })
|
||||
}
|
||||
override val selectById: SqlExpressionBuilder.(ContentId) -> Op<Boolean> = {
|
||||
idColumn.eq(it.string)
|
||||
}
|
||||
override val ResultRow.asObject: RegisteredContent
|
||||
get() = RegisteredContent(
|
||||
ContentId(get(idColumn)),
|
||||
TextContent(get(textColumn))
|
||||
)
|
||||
|
||||
init {
|
||||
initTable()
|
||||
}
|
||||
|
||||
override fun insert(value: TextContent, it: InsertStatement<Number>) {
|
||||
it[idColumn] = uuid4().toString()
|
||||
it[textColumn] = value.text
|
||||
}
|
||||
|
||||
override fun update(id: ContentId, value: TextContent, it: UpdateStatement) {
|
||||
it[textColumn] = value.text
|
||||
}
|
||||
|
||||
override fun InsertStatement<Number>.asObject(value: TextContent): RegisteredContent {
|
||||
return RegisteredContent(
|
||||
ContentId(get(idColumn)),
|
||||
TextContent(get(textColumn))
|
||||
)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user