temp progress on binary correct including
This commit is contained in:
features/common/common
build.gradle
src
commonMain
kotlin
dev
inmo
postssystem
features
common
common
jsMain
kotlin
dev
inmo
postssystem
features
common
common
jvmMain
kotlin
dev
inmo
postssystem
features
common
common
services/posts
client
src
commonMain
kotlin
dev
inmo
postssystem
jsMain
kotlin
dev
inmo
postssystem
services
posts
client
jvmMain
kotlin
dev
inmo
postssystem
services
posts
client
common
server
src
jvmMain
kotlin
dev
inmo
postssystem
services
@ -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(
|
||||
|
15
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/TempFileIdentifierInputProvider.kt
Normal file
15
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/TempFileIdentifierInputProvider.kt
Normal file
@ -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")
|
||||
}
|
||||
}
|
12
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/TempUpload.kt
Normal file
12
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/TempUpload.kt
Normal file
@ -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
|
||||
|
8
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/ui/create/PostCreateUIModel.kt
Normal file
8
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/ui/create/PostCreateUIModel.kt
Normal file
@ -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>)
|
||||
}
|
14
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/ui/create/PostCreateUIState.kt
Normal file
14
services/posts/client/src/commonMain/kotlin/dev/inmo/postssystem/services/posts/client/ui/create/PostCreateUIState.kt
Normal file
@ -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()
|
||||
|
||||
}
|
50
services/posts/client/src/jsMain/kotlin/dev/inmo/postssystem/services/posts/client/ActualTempUpload.kt
Normal file
50
services/posts/client/src/jsMain/kotlin/dev/inmo/postssystem/services/posts/client/ActualTempUpload.kt
Normal file
@ -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()
|
||||
}
|
||||
|
38
services/posts/client/src/jvmMain/kotlin/dev/inmo/postssystem/services/posts/client/ActualTempUpload.kt
Normal file
38
services/posts/client/src/jvmMain/kotlin/dev/inmo/postssystem/services/posts/client/ActualTempUpload.kt
Normal file
@ -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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user