I'm discovering the use of Google APIs batching ca...
# ktor
o
I'm discovering the use of Google APIs batching capabilities and at the same time, trying to use them with Ktor client. I noticed the
MultiPartFormDataContent
and
ContentType.MultiPart.Mixed.withParameter("boundary", boundary)
but I'm a bit lost on the next steps. Does anyone achieve this already?
So, my main pain point as of today, is to determine how to provide the sub requests given the initial
MultiPartFormDataContent
It seems to work by doing that, but I'm a bit lost on the way to interpret and handle the response
Copy code
val response = httpClient.post("<https://tasks.googleapis.com/batch>") {
    // FIXME what would be a better boundary name?
    val boundary = "batch_${Clock.System.now().toEpochMilliseconds().toString().encodeBase64()}"
    val contentType = ContentType.MultiPart.Mixed.withParameter("boundary", boundary)
    setBody(
        MultiPartFormDataContent(
            listOf(
                PartData.FormItem(
                    value = "GET /tasks/v1/users/@me/lists HTTP/1.1",
                    dispose = {},
                    partHeaders = headersOf("Content-Type", "application/http"),
                ),
                PartData.FormItem(
                    value = "GET /tasks/v1/users/@me/liPLOPsts HTTP/1.1",
                    dispose = {},
                    partHeaders = headersOf("Content-Type", "application/http"),
                ),
            ),
            boundary,
            contentType
        )
    )
}
I feel that I missed the point when I have to parse the HTTP body myself while using a tool like Ktor
Copy code
if (response.status.isSuccess()) {
    val contentType = response.headers["Content-Type"]
        ?: error("No Content-Type header")
    println("Content-Type: $contentType")
    when {
        contentType.startsWith("multipart/mixed") -> {
            val boundary = contentType
                .split("; ")
                .onEach(::println)
                .find { it.startsWith("boundary=") }
                ?.removePrefix("boundary=")
                ?: error("No boundary in Content-Type header")

            val batchBody = response.bodyAsText().removeSuffix("--$boundary--\r\n")
            val parts = batchBody.split("--$boundary").mapNotNull { part ->
                if (part.isBlank()) return@mapNotNull null  // Skip empty segments
                val partData = part.trim().split("\r\n\r\n", limit = 3)
                // TODO check status line
                val body = partData.getOrElse(2) { "" }
                Json.decodeFromString<ResourceListResponse<TaskList>>(body)
            }
                .onEach { println(it) }
        }

        else -> error("Unexpected Content-Type: $contentType")
    }
} else {
    throw ClientRequestException(response, response.bodyAsText())
}
Ultimately, I'd like to create a
HttpResponse
from each batch subpart, but can't figure out how to do so.
I noticed the
Multipart.kt
impl but struggle to use it for my needs. I also found https://youtrack.jetbrains.com/issue/KTOR-6632/Support-receiving-multipart-data-with-Ktor-client which goes in the same direction, but using an internal API
maybe @leonid have some info (being assigned to the issue)
o
The correct tag is @e5l
🙇 1
e
Hey, let me try bumping this!
🙇 1