Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/src/main/kotlin/com/avsystem/justworks/core/gen/Names.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ val POST_FUN = MemberName("io.ktor.client.request", "post")
val PUT_FUN = MemberName("io.ktor.client.request", "put")
val DELETE_FUN = MemberName("io.ktor.client.request", "delete")
val PATCH_FUN = MemberName("io.ktor.client.request", "patch")
val HEAD_FUN = MemberName("io.ktor.client.request", "head")
val OPTIONS_FUN = MemberName("io.ktor.client.request", "options")
val REQUEST_FUN = MemberName("io.ktor.client.request", "request")

// ============================================================================
// Ktor Forms & Multipart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import com.avsystem.justworks.core.gen.FORM_DATA_FUN
import com.avsystem.justworks.core.gen.GET_FUN
import com.avsystem.justworks.core.gen.HEADERS_CLASS
import com.avsystem.justworks.core.gen.HEADERS_FUN
import com.avsystem.justworks.core.gen.HEAD_FUN
import com.avsystem.justworks.core.gen.HTTP_HEADERS
import com.avsystem.justworks.core.gen.HTTP_METHOD_CLASS
import com.avsystem.justworks.core.gen.OPTIONS_FUN
import com.avsystem.justworks.core.gen.PARAMETERS_FUN
import com.avsystem.justworks.core.gen.PATCH_FUN
import com.avsystem.justworks.core.gen.POST_FUN
import com.avsystem.justworks.core.gen.PUT_FUN
import com.avsystem.justworks.core.gen.REQUEST_FUN
import com.avsystem.justworks.core.gen.SAFE_CALL
import com.avsystem.justworks.core.gen.SET_BODY_FUN
import com.avsystem.justworks.core.gen.SUBMIT_FORM_FUN
Expand Down Expand Up @@ -77,9 +80,15 @@ internal object BodyGenerator {
HttpMethod.PUT -> PUT_FUN
HttpMethod.DELETE -> DELETE_FUN
HttpMethod.PATCH -> PATCH_FUN
HttpMethod.HEAD -> HEAD_FUN
HttpMethod.OPTIONS -> OPTIONS_FUN
HttpMethod.TRACE -> REQUEST_FUN
}

beginControlFlow("$CLIENT.%M(%L)", httpMethodFun, urlString)
if (endpoint.method == HttpMethod.TRACE) {
addStatement("method = %T(%S)", HTTP_METHOD_CLASS, "TRACE")
}
Comment on lines 88 to +91
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TRACE method assignment is hard-coded as the string literal "TRACE" here (and also in addHttpMethodIfNeeded). To avoid duplication and keep behavior consistent if the enum/value changes, consider deriving it from endpoint.method.name or extracting a small helper for setting custom methods.

Copilot uses AI. Check for mistakes.
addCommonRequestParts(params)

if (endpoint.requestBody != null) {
Expand Down Expand Up @@ -193,7 +202,11 @@ internal object BodyGenerator {

private fun CodeBlock.Builder.addHttpMethodIfNeeded(method: HttpMethod) {
if (method != HttpMethod.POST) {
addStatement("method = %T.%L", HTTP_METHOD_CLASS, method.name.toPascalCase())
if (method == HttpMethod.TRACE) {
addStatement("method = %T(%S)", HTTP_METHOD_CLASS, "TRACE")
} else {
addStatement("method = %T.%L", HTTP_METHOD_CLASS, method.name.toPascalCase())
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ enum class HttpMethod {
POST,
PUT,
DELETE,
PATCH
PATCH,
HEAD,
OPTIONS,
TRACE
}

data class Parameter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ class ClientGeneratorTest {
HttpMethod.PUT to "updatePet",
HttpMethod.DELETE to "deletePet",
HttpMethod.PATCH to "patchPet",
HttpMethod.HEAD to "headPet",
HttpMethod.OPTIONS to "optionsPet",
HttpMethod.TRACE to "tracePet",
)
val endpoints = methods.map { (method, opId) -> endpoint(method = method, operationId = opId) }.toTypedArray()
val cls = clientClass(*endpoints)
Expand All @@ -151,6 +154,24 @@ class ClientGeneratorTest {
funBodies["patchPet"]!!.contains("request.patch(") || funBodies["patchPet"]!!.contains("request.`patch`("),
"PATCH method expected",
)
assertTrue(
funBodies["headPet"]!!.contains("request.head(") || funBodies["headPet"]!!.contains("request.`head`("),
"HEAD method expected",
)
assertTrue(
funBodies["optionsPet"]!!.contains("request.options(") ||
funBodies["optionsPet"]!!.contains("request.`options`("),
"OPTIONS method expected",
)
assertTrue(
funBodies["tracePet"]!!.contains("request.request(") ||
funBodies["tracePet"]!!.contains("request.`request`("),
"TRACE method expected (via request builder)",
)
assertTrue(
funBodies["tracePet"]!!.contains("HttpMethod(\"TRACE\")"),
"TRACE should set explicit HttpMethod",
)
}

// -- CLNT-04: Path parameters become function parameters --
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,45 @@ class SpecParserTest : SpecParserTestBase() {
assertEquals("Pet", itemType.schemaName)
}

// -- SPEC-01b: HEAD, OPTIONS, TRACE parsing --

@Test
fun `parse spec with HEAD, OPTIONS and TRACE methods`() {
val spec = parseSpec(
"""
openapi: 3.0.0
info:
title: Test
version: 1.0.0
paths:
/health:
head:
operationId: healthHead
tags: [Health]
responses:
'200':
description: OK
options:
operationId: healthOptions
tags: [Health]
responses:
'200':
description: OK
trace:
operationId: healthTrace
tags: [Health]
responses:
'200':
description: OK
""".trimIndent().toTempFile(),
)

assertEquals(3, spec.endpoints.size)
assertEquals(HttpMethod.HEAD, spec.endpoints.find { it.operationId == "healthHead" }?.method)
assertEquals(HttpMethod.OPTIONS, spec.endpoints.find { it.operationId == "healthOptions" }?.method)
assertEquals(HttpMethod.TRACE, spec.endpoints.find { it.operationId == "healthTrace" }?.method)
Comment on lines +196 to +199
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test, using find { ... }?.method means a missing endpoint will fail with a less-informative expected <METHOD> but was null. Consider using ?: fail("<opId> endpoint not found") (as done elsewhere in this file) to make failures easier to diagnose.

Copilot uses AI. Check for mistakes.
}

// -- SPEC-02: $ref resolution --

@Test
Expand Down
Loading