add storing of content

This commit is contained in:
2022-01-07 23:38:55 +06:00
parent 3661c1ca73
commit 92ab01ee9d
18 changed files with 377 additions and 37 deletions
client/src/commonMain/kotlin/dev/inmo/postssystem/client
features
content
binary
server
build.gradle
src
jvmMain
kotlin
dev
inmo
postssystem
features
server
text
server
build.gradle
src
jvmMain
kotlin
dev
inmo
postssystem
features
files
client
src
commonMain
kotlin
dev
inmo
postssystem
features
common
src
commonMain
kotlin
dev
inmo
postssystem
jvmMain
kotlin
dev
inmo
postssystem
features
server
src
jvmMain
kotlin
dev
inmo
postssystem
features
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")
}
}
}

@ -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))
)
}
}