Fix auth header lost on multipart task completion requests

The createCompletionWithImages method used client.post() with a manual
MultiPartFormDataContent body, which can override the Authorization header.
Switch to submitFormWithBinaryData() (matching DocumentApi's working pattern)
which properly separates form data from request headers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-03-02 19:56:13 -06:00
parent cee6be8db2
commit 469d371635

View File

@@ -4,6 +4,7 @@ import com.example.casera.models.*
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.call.* import io.ktor.client.call.*
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.http.* import io.ktor.http.*
class TaskCompletionApi(private val client: HttpClient = ApiClient.httpClient) { class TaskCompletionApi(private val client: HttpClient = ApiClient.httpClient) {
@@ -100,34 +101,31 @@ class TaskCompletionApi(private val client: HttpClient = ApiClient.httpClient) {
imageFileNames: List<String> = emptyList() imageFileNames: List<String> = emptyList()
): ApiResult<WithSummaryResponse<TaskCompletionResponse>> { ): ApiResult<WithSummaryResponse<TaskCompletionResponse>> {
return try { return try {
val response = client.post("$baseUrl/task-completions/") { val response = client.submitFormWithBinaryData(
header("Authorization", "Token $token") url = "$baseUrl/task-completions/",
formData = formData {
// Add text fields
append("task_id", request.taskId.toString())
request.completedAt?.let { append("completed_at", it) }
request.actualCost?.let { append("actual_cost", it.toString()) }
request.notes?.let { append("notes", it) }
request.rating?.let { append("rating", it.toString()) }
setBody( // Add image files
io.ktor.client.request.forms.MultiPartFormDataContent( images.forEachIndexed { index, imageBytes ->
io.ktor.client.request.forms.formData { val fileName = imageFileNames.getOrNull(index) ?: "image_$index.jpg"
// Add text fields append(
append("task_id", request.taskId.toString()) "images",
request.completedAt?.let { append("completed_at", it) } imageBytes,
request.actualCost?.let { append("actual_cost", it.toString()) } Headers.build {
request.notes?.let { append("notes", it) } append(HttpHeaders.ContentType, "image/jpeg")
request.rating?.let { append("rating", it.toString()) } append(HttpHeaders.ContentDisposition, "filename=\"$fileName\"")
// Add image files
images.forEachIndexed { index, imageBytes ->
val fileName = imageFileNames.getOrNull(index) ?: "image_$index.jpg"
append(
"images",
imageBytes,
io.ktor.http.Headers.build {
append(HttpHeaders.ContentType, "image/jpeg")
append(HttpHeaders.ContentDisposition, "filename=\"$fileName\"")
}
)
} }
} )
) }
) }
) {
header("Authorization", "Token $token")
} }
if (response.status.isSuccess()) { if (response.status.isSuccess()) {