temp progress on binary correct including

This commit is contained in:
2022-03-09 13:49:00 +06:00
parent 186bfd7ac0
commit 99b953635e
15 changed files with 342 additions and 180 deletions
features/common/common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
common
jsMain
kotlin
dev
inmo
postssystem
features
jvmMain
kotlin
dev
inmo
postssystem
features
services/posts
client
src
commonMain
jsMain
kotlin
dev
inmo
postssystem
services
jvmMain
kotlin
dev
inmo
postssystem
services
common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
services
posts
server
src
jvmMain

@ -7,6 +7,8 @@ import dev.inmo.micro_utils.ktor.common.encodeHex
import dev.inmo.micro_utils.repos.ktor.common.crud.createRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.updateRouting
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.removeRoute
import dev.inmo.postssystem.features.common.common.FileBasedInputProvider
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
import dev.inmo.postssystem.features.content.common.*
import dev.inmo.postssystem.features.posts.common.PostId
import dev.inmo.postssystem.features.posts.common.RegisteredPost
@ -20,9 +22,15 @@ import kotlinx.serialization.builtins.*
class ClientWritePostsService(
private val baseUrl: String,
private val unifiedRequester: UnifiedRequester
unifiedRequester: UnifiedRequester
) : WritePostsService {
private val root = buildStandardUrl(baseUrl, postsRootPath)
private val unifiedRequester = UnifiedRequester(
unifiedRequester.client,
unifiedRequester.serialFormat.createWithSerializerModuleExtension {
contextual()
}
)
private val contentEitherSerializer = EitherSerializer(ContentId.serializer(), ContentSerializer)
private val contentsEitherSerializer = ListSerializer(contentEitherSerializer)
@ -36,83 +44,59 @@ class ClientWritePostsService(
root,
removeRoute
)
private val tempUploadFullPath = buildStandardUrl(
baseUrl,
postsCreateTempPathPart
)
private suspend fun prepareContent(content: Content): Content? {
return (content as? BinaryContent) ?.let {
when (val provider = it.inputProvider) {
is FileBasedInputProvider -> {
val fileId = unifiedRequester.tempUpload(
tempUploadFullPath,
provider.file
)
it.copy(inputProvider = TempFileIdentifierInputProvider(fileId))
}
is TempFileIdentifierInputProvider -> it
else -> return@prepareContent null
}
} ?: content
}
override suspend fun create(newPost: FullNewPost): RegisteredPost? {
return if (newPost.content.any { it is BinaryContent }) {
val answer = unifiedRequester.client.post<ByteArray>(createFullPath) {
formData {
newPost.content.forEachIndexed { i, content ->
when (content) {
is BinaryContent -> append(
i.toString(),
InputProvider(block = content.inputProvider::invoke),
headers {
append(HttpHeaders.ContentType, content.mimeType.raw)
append(HttpHeaders.ContentDisposition, "filename=\"${content.filename.name}\"")
}.build()
)
else -> append(
i.toString(),
unifiedRequester.serialFormat.encodeHex(ContentSerializer, content)
)
}
}
}
}
unifiedRequester.serialFormat.decodeFromByteArray(RegisteredPost.serializer().nullable, answer)
} else {
unifiedRequester.unipost(
createFullPath,
contentsSerializer to newPost.content,
RegisteredPost.serializer().nullable
)
val mappedContent = newPost.content.mapNotNull {
prepareContent(it)
}
val mappedPost = newPost.copy(
content = mappedContent
)
return unifiedRequester.unipost(
createFullPath,
contentsSerializer to mappedPost.content,
RegisteredPost.serializer().nullable
)
}
override suspend fun update(
postId: PostId,
content: List<Either<ContentId, Content>>
): RegisteredPost? {
return if (content.any { it.optionalT2.data is BinaryContent }) {
val answer = unifiedRequester.client.post<ByteArray>(createFullPath) {
formData {
content.forEachIndexed { i, eitherContent ->
eitherContent.onFirst {
append(
i.toString(),
unifiedRequester.serialFormat.encodeHex(contentEitherSerializer, it.either())
)
}.onSecond {
when (it) {
is BinaryContent -> append(
i.toString(),
InputProvider(block = it.inputProvider::invoke),
headers {
append(HttpHeaders.ContentType, it.mimeType.raw)
append(HttpHeaders.ContentDisposition, "filename=\"${it.filename.name}\"")
}.build()
)
else -> append(
i.toString(),
unifiedRequester.serialFormat.encodeHex(contentEitherSerializer, it.either())
)
}
}
}
}
}
unifiedRequester.serialFormat.decodeFromByteArray(RegisteredPost.serializer().nullable, answer)
} else {
unifiedRequester.unipost(
buildStandardUrl(
root,
updateRouting,
postsPostIdParameter to unifiedRequester.encodeUrlQueryValue(PostId.serializer(), postId)
),
contentsEitherSerializer to content,
RegisteredPost.serializer().nullable
)
val mappedContent = content.mapNotNull {
it.mapOnSecond { content ->
prepareContent(content) ?.either() ?: return@mapNotNull null
} ?: it
}
return unifiedRequester.unipost(
buildStandardUrl(
root,
updateRouting,
postsPostIdParameter to unifiedRequester.encodeUrlQueryValue(PostId.serializer(), postId)
),
contentsEitherSerializer to mappedContent,
RegisteredPost.serializer().nullable
)
}
override suspend fun remove(postId: PostId) = unifiedRequester.unipost(

@ -0,0 +1,15 @@
package dev.inmo.postssystem.services.posts.client
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
import dev.inmo.postssystem.features.files.common.FileId
import io.ktor.utils.io.core.Input
import kotlinx.serialization.Serializable
@Serializable
internal data class TempFileIdentifierInputProvider(
private val tempFile: FileId
) : SimpleInputProvider {
override fun invoke(): Input {
TODO("Not yet implemented")
}
}

@ -0,0 +1,12 @@
package dev.inmo.postssystem.services.posts.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import dev.inmo.postssystem.features.files.common.FileId
internal expect suspend fun UnifiedRequester.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (Long, Long) -> Unit = { _, _ -> }
): FileId

@ -0,0 +1,8 @@
package dev.inmo.postssystem.services.posts.client.ui.create
import dev.inmo.postssystem.features.common.common.UIModel
import dev.inmo.postssystem.features.content.common.Content
interface PostCreateUIModel : UIModel<PostCreateUIState> {
suspend fun create(content: List<Content>)
}

@ -0,0 +1,14 @@
package dev.inmo.postssystem.services.posts.client.ui.create
import kotlinx.serialization.Serializable
@Serializable
sealed class PostCreateUIState {
@Serializable
object Init : PostCreateUIState()
@Serializable
object Uploading : PostCreateUIState()
@Serializable
object Completed : PostCreateUIState()
}

@ -0,0 +1,50 @@
package dev.inmo.postssystem.services.posts.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import dev.inmo.postssystem.features.files.common.FileId
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.GlobalScope.coroutineContext
import kotlinx.coroutines.job
import org.w3c.xhr.*
internal actual suspend fun UnifiedRequester.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (Long, Long) -> Unit
): FileId {
val formData = FormData()
val answer = CompletableDeferred<FileId>()
formData.append(
"data",
file
)
val request = XMLHttpRequest()
request.responseType = XMLHttpRequestResponseType.TEXT
request.upload.onprogress = {
onUpload(it.loaded.toLong(), it.total.toLong())
}
request.onload = {
if (request.status == 200.toShort()) {
answer.complete(FileId(request.responseText))
} else {
answer.completeExceptionally(Exception("Something went wrong"))
}
}
request.onerror = {
answer.completeExceptionally(Exception("Something went wrong"))
}
request.open("POST", fullTempUploadDraftPath, true)
request.send(formData)
coroutineContext.job.invokeOnCompletion {
runCatching {
request.abort()
}
}
return answer.await()
}

@ -0,0 +1,38 @@
package dev.inmo.postssystem.services.posts.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.filename
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import dev.inmo.micro_utils.ktor.client.inputProvider
import dev.inmo.postssystem.features.common.common.mimeType
import dev.inmo.postssystem.features.files.common.FileId
import io.ktor.client.features.onUpload
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
internal actual suspend fun UnifiedRequester.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (Long, Long) -> Unit
): FileId {
val inputProvider = file.inputProvider()
val fileId = client.submitFormWithBinaryData<String>(
fullTempUploadDraftPath,
formData = formData {
append(
"data",
inputProvider,
Headers.build {
append(HttpHeaders.ContentType, file.mimeType.raw)
append(HttpHeaders.ContentDisposition, "filename=\"${file.filename.string}\"")
}
)
}
) {
onUpload(onUpload)
}
return FileId(fileId)
}