diff --git a/core/src/main/kotlin/com/avsystem/justworks/core/gen/client/BodyGenerator.kt b/core/src/main/kotlin/com/avsystem/justworks/core/gen/client/BodyGenerator.kt index 133ab02..85bbe6f 100644 --- a/core/src/main/kotlin/com/avsystem/justworks/core/gen/client/BodyGenerator.kt +++ b/core/src/main/kotlin/com/avsystem/justworks/core/gen/client/BodyGenerator.kt @@ -82,9 +82,11 @@ internal object BodyGenerator { beginControlFlow("$CLIENT.%M(%L)", httpMethodFun, urlString) addCommonRequestParts(params) - optionalGuard(endpoint.requestBody?.required ?: false, BODY) { - addStatement("%M(%T.Json)", CONTENT_TYPE_FUN, CONTENT_TYPE_APPLICATION) - addStatement("%M(%L)", SET_BODY_FUN, BODY) + if (endpoint.requestBody != null) { + optionalGuard(endpoint.requestBody.required, BODY) { + addStatement("%M(%T.Json)", CONTENT_TYPE_FUN, CONTENT_TYPE_APPLICATION) + addStatement("%M(%L)", SET_BODY_FUN, BODY) + } } // Don't endControlFlow here — the outer buildFunctionBody closes with .toResult() @@ -198,7 +200,7 @@ internal object BodyGenerator { private fun buildUrlString(endpoint: Endpoint, params: Map>): CodeBlock { val (format, args) = params[ParameterLocation.PATH] .orEmpty() - .fold($$"${'$'}{$$BASE_URL}" + endpoint.path to emptyList()) { (format, args), param -> + .fold($$"${%L}" + endpoint.path to listOf(BASE_URL)) { (format, args), param -> format.replace("{${param.name}}", $$"${%M(%L)}") to args + ENCODE_PARAM_FUN + param.name.toCamelCase() } return CodeBlock.of("%P", CodeBlock.of(format, *args.toTypedArray())) diff --git a/core/src/test/kotlin/com/avsystem/justworks/core/gen/ClientGeneratorTest.kt b/core/src/test/kotlin/com/avsystem/justworks/core/gen/ClientGeneratorTest.kt index 5f295ec..efb0094 100644 --- a/core/src/test/kotlin/com/avsystem/justworks/core/gen/ClientGeneratorTest.kt +++ b/core/src/test/kotlin/com/avsystem/justworks/core/gen/ClientGeneratorTest.kt @@ -536,6 +536,42 @@ class ClientGeneratorTest { assertFalse(body.contains("submitForm"), "Should NOT contain submitForm for JSON") } + // -- No body: endpoint without requestBody should not emit setBody or contentType -- + + @Test + fun `endpoint without requestBody does not generate body null check`() { + val ep = endpoint( + method = HttpMethod.GET, + operationId = "listPets", + requestBody = null, + ) + val cls = clientClass(ep) + val funSpec = cls.funSpecs.first { it.name == "listPets" } + val body = funSpec.body.toString() + assertFalse(body.contains("setBody"), "Should NOT contain setBody when no requestBody") + assertFalse(body.contains("contentType"), "Should NOT set contentType when no requestBody") + assertFalse(body.contains("if (body"), "Should NOT check body != null when no requestBody") + } + + // -- URL interpolation: baseUrl must be interpolated, not literal -- + + @Test + fun `generated URL interpolates baseUrl property`() { + val ep = endpoint( + path = "/pets/{petId}", + operationId = "getPet", + parameters = listOf( + Parameter("petId", ParameterLocation.PATH, true, TypeRef.Primitive(PrimitiveType.LONG), null), + ), + ) + val cls = clientClass(ep) + val funSpec = cls.funSpecs.first { it.name == "getPet" } + val body = funSpec.body.toString() + // Must contain ${baseUrl} as interpolation, not ${'$'}{baseUrl} (escaped/literal) + assertTrue(body.contains("\${baseUrl}"), "Expected \${baseUrl} interpolation in URL") + assertFalse(body.contains("\${'$'}{baseUrl}"), "baseUrl must not be escaped as literal text") + } + // -- CONT-02: Form-urlencoded code generation -- @Test