temporal result

This commit is contained in:
2022-01-17 14:45:12 +06:00
parent ef372ab520
commit a651d8560e
15 changed files with 137 additions and 51 deletions
client/src/jsMain/kotlin/dev/inmo/postssystem/client/utils
features
common
common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
content
binary
common
src
commonMain
server
src
jvmMain
kotlin
dev
inmo
postssystem
features
common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
content
files
common
src
commonMain
kotlin
dev
inmo
postssystem
features
files
jvmMain
kotlin
dev
inmo
gradle/wrapper
targets/telegram/publication/server/src/jvmMain/kotlin/dev/inmo/postssystem/targets/telegram/publication/server

@ -10,7 +10,7 @@ import org.w3c.files.Blob
fun triggerDownloadFile(fullFileInfo: FullFileInfo) { fun triggerDownloadFile(fullFileInfo: FullFileInfo) {
val hiddenElement = document.createElement("a") as HTMLAnchorElement val hiddenElement = document.createElement("a") as HTMLAnchorElement
val url = URL.createObjectURL(Blob(arrayOf(fullFileInfo.byteArrayAllocator().toArrayBuffer()))) val url = URL.createObjectURL(Blob(arrayOf(fullFileInfo.inputProvider().toArrayBuffer())))
hiddenElement.href = url hiddenElement.href = url
hiddenElement.target = "_blank" hiddenElement.target = "_blank"
hiddenElement.download = fullFileInfo.name.name hiddenElement.download = fullFileInfo.name.name

@ -4,9 +4,8 @@ import dev.inmo.postssystem.features.files.common.FullFileInfo
import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.mime_types.KnownMimeTypes import dev.inmo.micro_utils.mime_types.KnownMimeTypes
import dev.inmo.micro_utils.mime_types.findBuiltinMimeType import dev.inmo.micro_utils.mime_types.findBuiltinMimeType
import kotlinx.coroutines.CoroutineScope import io.ktor.utils.io.core.ByteReadPacket
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.ArrayBuffer
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
@ -14,28 +13,64 @@ import org.w3c.files.FileReader
import org.w3c.files.get import org.w3c.files.get
fun uploadFileCallbackForHTMLInputChange( fun uploadFileCallbackForHTMLInputChange(
output: MutableStateFlow<FullFileInfo?>, onSet: (FullFileInfo) -> Unit
scope: CoroutineScope
): (Event) -> Unit = { ): (Event) -> Unit = {
(it.target as? HTMLInputElement) ?.apply { (it.target as? HTMLInputElement) ?.apply {
files ?.also { files -> files ?.also { files ->
files[0] ?.also { file -> files[0] ?.also { file ->
scope.launch { val reader: FileReader = FileReader()
val reader: FileReader = FileReader()
reader.onload = { reader.onload = {
val bytes = ((it.target.asDynamic()).result as ArrayBuffer).toByteArray() val bytes = ((it.target.asDynamic()).result as ArrayBuffer).toByteArray()
output.value = FullFileInfo( onSet(
FullFileInfo(
FileName(file.name), FileName(file.name),
findBuiltinMimeType(file.type) ?: KnownMimeTypes.Any, findBuiltinMimeType(file.type) ?: KnownMimeTypes.Any,
bytes.asAllocator ) {
) ByteReadPacket(bytes)
Unit }
} )
reader.readAsArrayBuffer(file)
} }
reader.readAsArrayBuffer(file)
} }
} }
} }
} }
fun fileCallbackForHTMLInputChange(
onSet: (MPPFile) -> Unit
): (Event) -> Unit = {
(it.target as? HTMLInputElement) ?.apply {
files ?.also { files ->
files[0] ?.also { file ->
onSet(file)
}
}
}
}
fun uploadFileCallbackForHTMLInputChange(
output: MutableState<FullFileInfo?>
): (Event) -> Unit = uploadFileCallbackForHTMLInputChange {
output.value = it
}
fun uploadFileCallbackForHTMLInputChange(
output: MutableStateFlow<FullFileInfo?>
): (Event) -> Unit = uploadFileCallbackForHTMLInputChange {
output.value = it
}
fun fileCallbackForHTMLInputChange(
output: MutableState<MPPFile?>
): (Event) -> Unit = fileCallbackForHTMLInputChange {
output.value = it
}
fun fileCallbackForHTMLInputChange(
output: MutableStateFlow<MPPFile?>
): (Event) -> Unit = fileCallbackForHTMLInputChange {
output.value = it
}

@ -15,6 +15,7 @@ kotlin {
api "io.insert-koin:koin-core:$koin_version" api "io.insert-koin:koin-core:$koin_version"
api "com.benasher44:uuid:$uuid_version" api "com.benasher44:uuid:$uuid_version"
api "com.soywiz.korlibs.klock:klock:$klock_version" api "com.soywiz.korlibs.klock:klock:$klock_version"
api "io.ktor:ktor-http:$ktor_version"
} }
} }
jvmMain { jvmMain {

@ -0,0 +1,30 @@
package dev.inmo.postssystem.features.common.common
import dev.inmo.micro_utils.common.ByteArrayAllocatorSerializer
import io.ktor.utils.io.core.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
typealias SimpleInputProvider = () -> Input
object SimpleInputProviderSerializer : KSerializer<SimpleInputProvider> {
override val descriptor: SerialDescriptor
get() = ByteArrayAllocatorSerializer.descriptor
override fun deserialize(decoder: Decoder): SimpleInputProvider {
val allocator = ByteArrayAllocatorSerializer.deserialize(decoder)
return {
ByteReadPacket(allocator.invoke())
}
}
override fun serialize(encoder: Encoder, value: SimpleInputProvider) {
ByteArrayAllocatorSerializer.serialize(
encoder
) {
value().readBytes()
}
}
}

@ -1,14 +0,0 @@
package dev.inmo.postssystem.features.content.binary.common
import dev.inmo.micro_utils.common.ByteArrayAllocator
import dev.inmo.micro_utils.common.FileName
import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.postssystem.features.content.common.Content
import kotlinx.serialization.Serializable
@Serializable
data class BinaryContent(
val filename: FileName,
val mimeType: MimeType,
val bytesAllocator: ByteArrayAllocator
) : Content

@ -6,6 +6,6 @@ import kotlinx.serialization.modules.PolymorphicModuleBuilder
object BinaryContentSerializerModuleConfigurator : ContentSerializersModuleConfigurator.Element { object BinaryContentSerializerModuleConfigurator : ContentSerializersModuleConfigurator.Element {
override fun PolymorphicModuleBuilder<Content>.invoke() { override fun PolymorphicModuleBuilder<Content>.invoke() {
subclass(BinaryContent::class, BinaryContent.serializer()) subclass(DefaultBinaryContent::class, DefaultBinaryContent.serializer())
} }
} }

@ -0,0 +1,14 @@
package dev.inmo.postssystem.features.content.binary.common
import dev.inmo.micro_utils.common.FileName
import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
import dev.inmo.postssystem.features.content.common.BinaryContent
import kotlinx.serialization.Serializable
@Serializable
data class DefaultBinaryContent(
override val filename: FileName,
override val mimeType: MimeType,
override val inputProvider: SimpleInputProvider
) : BinaryContent

@ -2,7 +2,7 @@ package dev.inmo.postssystem.features.content.binary.server
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.UpdatedValuePair import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.postssystem.features.content.binary.common.BinaryContent import dev.inmo.postssystem.features.content.binary.common.DefaultBinaryContent
import dev.inmo.postssystem.features.content.common.* import dev.inmo.postssystem.features.content.common.*
import dev.inmo.postssystem.features.content.server.ServerContentStorage import dev.inmo.postssystem.features.content.server.ServerContentStorage
import dev.inmo.postssystem.features.files.common.* import dev.inmo.postssystem.features.files.common.*
@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.map
class BinaryServerContentStorage( class BinaryServerContentStorage(
private val filesStorage: FilesStorage private val filesStorage: FilesStorage
) : ServerContentStorage<BinaryContent> { ) : ServerContentStorage<DefaultBinaryContent> {
private val FileId.asContentId private val FileId.asContentId
get() = ContentId(string) get() = ContentId(string)
private val ContentId.asFileId private val ContentId.asFileId
@ -20,23 +20,23 @@ class BinaryServerContentStorage(
private val FullFileInfoStorageWrapper.asRegisteredContent private val FullFileInfoStorageWrapper.asRegisteredContent
get() = RegisteredContent( get() = RegisteredContent(
id.asContentId, id.asContentId,
BinaryContent( DefaultBinaryContent(
fileInfo.name, fileInfo.name,
fileInfo.mimeType, fileInfo.mimeType,
fileInfo.byteArrayAllocator fileInfo.inputProvider
) )
) )
private val BinaryContent.asFullFileInfo private val DefaultBinaryContent.asFullFileInfo
get() = FullFileInfo( get() = FullFileInfo(
filename, filename,
mimeType, mimeType,
bytesAllocator inputProvider
) )
override val deletedObjectsIdsFlow: Flow<ContentId> = filesStorage.deletedObjectsIdsFlow.map { it.asContentId } override val deletedObjectsIdsFlow: Flow<ContentId> = filesStorage.deletedObjectsIdsFlow.map { it.asContentId }
override val newObjectsFlow: Flow<RegisteredContent> = filesStorage.newObjectsFlow.map { it.asRegisteredContent } override val newObjectsFlow: Flow<RegisteredContent> = filesStorage.newObjectsFlow.map { it.asRegisteredContent }
override val updatedObjectsFlow: Flow<RegisteredContent> = filesStorage.updatedObjectsFlow.map { it.asRegisteredContent } override val updatedObjectsFlow: Flow<RegisteredContent> = filesStorage.updatedObjectsFlow.map { it.asRegisteredContent }
override suspend fun create(values: List<BinaryContent>): List<RegisteredContent> { override suspend fun create(values: List<DefaultBinaryContent>): List<RegisteredContent> {
return filesStorage.create( return filesStorage.create(
values.map { it.asFullFileInfo } values.map { it.asFullFileInfo }
).map { it.asRegisteredContent } ).map { it.asRegisteredContent }
@ -46,14 +46,14 @@ class BinaryServerContentStorage(
filesStorage.deleteById(ids.map { it.asFileId }) filesStorage.deleteById(ids.map { it.asFileId })
} }
override suspend fun update(id: ContentId, value: BinaryContent): RegisteredContent? { override suspend fun update(id: ContentId, value: DefaultBinaryContent): RegisteredContent? {
return filesStorage.update( return filesStorage.update(
id.asFileId, id.asFileId,
value.asFullFileInfo value.asFullFileInfo
) ?.asRegisteredContent ) ?.asRegisteredContent
} }
override suspend fun update(values: List<UpdatedValuePair<ContentId, BinaryContent>>): List<RegisteredContent> { override suspend fun update(values: List<UpdatedValuePair<ContentId, DefaultBinaryContent>>): List<RegisteredContent> {
return filesStorage.update( return filesStorage.update(
values.map { (id, content) -> values.map { (id, content) ->
id.asFileId to content.asFullFileInfo id.asFileId to content.asFullFileInfo

@ -11,6 +11,7 @@ kotlin {
commonMain { commonMain {
dependencies { dependencies {
api project(":postssystem.features.common.common") api project(":postssystem.features.common.common")
api "dev.inmo:micro_utils.mime_types:$microutils_version"
} }
} }
} }

@ -1,5 +1,8 @@
package dev.inmo.postssystem.features.content.common package dev.inmo.postssystem.features.content.common
import dev.inmo.micro_utils.common.FileName
import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@ -13,7 +16,21 @@ value class ContentId(val string: String)
* @see ContentSerializersModuleConfigurator.Element * @see ContentSerializersModuleConfigurator.Element
* @see ContentSerializersModuleConfigurator * @see ContentSerializersModuleConfigurator
*/ */
interface Content sealed interface Content
/**
* This type of content represents simple content which is easy to serialize/deserialize and to use
*/
interface SimpleContent : Content
/**
* This type represents some binary data which can be sent with multipart and deserialized from it
*/
interface BinaryContent : Content {
val filename: FileName
val mimeType: MimeType
val inputProvider: SimpleInputProvider
}
/** /**
* Content which is already registered in database. Using its [id] you can retrieve all known * Content which is already registered in database. Using its [id] you can retrieve all known

@ -3,6 +3,8 @@ package dev.inmo.postssystem.features.files.common
import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.mime_types.MimeType import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
import dev.inmo.postssystem.features.common.common.SimpleInputProviderSerializer
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -28,8 +30,8 @@ data class MetaFileInfo(override val name: FileName, override val mimeType: Mime
data class FullFileInfo( data class FullFileInfo(
override val name: FileName, override val name: FileName,
override val mimeType: MimeType, override val mimeType: MimeType,
@Serializable(ByteArrayAllocatorSerializer::class) @Serializable(SimpleInputProviderSerializer::class)
val byteArrayAllocator: ByteArrayAllocator val inputProvider: SimpleInputProvider
) : FileInfo ) : FileInfo
fun FullFileInfo.toMetaFileInfo() = MetaFileInfo(name, mimeType) fun FullFileInfo.toMetaFileInfo() = MetaFileInfo(name, mimeType)

@ -3,6 +3,7 @@ package dev.inmo.postssystem.features.files.common
import dev.inmo.postssystem.features.files.common.storage.ReadFilesStorage import dev.inmo.postssystem.features.files.common.storage.ReadFilesStorage
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import io.ktor.utils.io.streams.asInput
import java.io.File import java.io.File
class DiskReadFilesStorage( class DiskReadFilesStorage(
@ -38,7 +39,7 @@ class DiskReadFilesStorage(
it.fileInfo.name, it.fileInfo.name,
it.fileInfo.mimeType it.fileInfo.mimeType
) { ) {
id.file.readBytes() id.file.inputStream().asInput()
} }
) )
} }

@ -36,7 +36,7 @@ class WriteDistFilesStorage(
file = newId.file file = newId.file
} while (file.exists()) } while (file.exists())
metasKeyValueRepo.set(newId, it.toMetaFileInfo()) metasKeyValueRepo.set(newId, it.toMetaFileInfo())
file.writeBytes(it.byteArrayAllocator()) file.writeBytes(it.inputProvider())
FullFileInfoStorageWrapper(newId, it) FullFileInfoStorageWrapper(newId, it)
} }
@ -52,7 +52,7 @@ class WriteDistFilesStorage(
override suspend fun update( override suspend fun update(
id: FileId, id: FileId,
value: FullFileInfo value: FullFileInfo
): FullFileInfoStorageWrapper? = id.file.takeIf { it.exists() } ?.writeBytes(value.byteArrayAllocator()) ?.let { ): FullFileInfoStorageWrapper? = id.file.takeIf { it.exists() } ?.writeBytes(value.inputProvider()) ?.let {
val result = FullFileInfoStorageWrapper(id, value.copy()) val result = FullFileInfoStorageWrapper(id, value.copy())
metasKeyValueRepo.set(id, value.toMetaFileInfo()) metasKeyValueRepo.set(id, value.toMetaFileInfo())

@ -1,4 +1,4 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

@ -1,13 +1,12 @@
package dev.inmo.postssystem.targets.telegram.publication.server package dev.inmo.postssystem.targets.telegram.publication.server
import dev.inmo.micro_utils.mime_types.KnownMimeTypes import dev.inmo.micro_utils.mime_types.KnownMimeTypes
import dev.inmo.postssystem.features.content.binary.common.BinaryContent import dev.inmo.postssystem.features.content.binary.common.DefaultBinaryContent
import dev.inmo.postssystem.features.content.text.common.TextContent import dev.inmo.postssystem.features.content.text.common.TextContent
import dev.inmo.postssystem.features.publication.server.PublicationPost import dev.inmo.postssystem.features.publication.server.PublicationPost
import dev.inmo.postssystem.features.publication.server.PublicationTarget import dev.inmo.postssystem.features.publication.server.PublicationTarget
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.utils.shortcuts.executeUnsafe import dev.inmo.tgbotapi.extensions.utils.shortcuts.executeUnsafe
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.requests.send.SendTextMessage import dev.inmo.tgbotapi.requests.send.SendTextMessage
import dev.inmo.tgbotapi.requests.send.media.* import dev.inmo.tgbotapi.requests.send.media.*
@ -23,7 +22,7 @@ class PublicationTargetTelegram(
post.content.mapNotNull { post.content.mapNotNull {
val content = it.content val content = it.content
when (content) { when (content) {
is BinaryContent -> { is DefaultBinaryContent -> {
val storageFile by lazy { val storageFile by lazy {
StorageFile(content.filename.name, content.bytesAllocator()).asMultipartFile() StorageFile(content.filename.name, content.bytesAllocator()).asMultipartFile()
} }