add storing of content
This commit is contained in:
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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())
|
||||
}
|
||||
}
|
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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