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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ val SET_BODY_FUN = MemberName("io.ktor.client.request", "setBody")
val CONTENT_TYPE_FUN = MemberName("io.ktor.http", "contentType")
val CONTENT_TYPE_APPLICATION = ClassName("io.ktor.http", "ContentType", "Application")
val HEADERS_FUN = MemberName("io.ktor.client.request", "headers")
val COOKIE_FUN = MemberName("io.ktor.client.request", "cookie")

val GET_FUN = MemberName("io.ktor.client.request", "get")
val POST_FUN = MemberName("io.ktor.client.request", "post")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.avsystem.justworks.core.gen.BODY
import com.avsystem.justworks.core.gen.CLIENT
import com.avsystem.justworks.core.gen.CONTENT_TYPE_APPLICATION
import com.avsystem.justworks.core.gen.CONTENT_TYPE_FUN
import com.avsystem.justworks.core.gen.COOKIE_FUN
import com.avsystem.justworks.core.gen.DELETE_FUN
import com.avsystem.justworks.core.gen.ENCODE_PARAM_FUN
import com.avsystem.justworks.core.gen.FORM_DATA_FUN
Expand Down Expand Up @@ -197,6 +198,7 @@ internal object BodyGenerator {
private fun CodeBlock.Builder.addCommonRequestParts(params: Map<ParameterLocation, List<Parameter>>) {
addStatement("${APPLY_AUTH}()")
addHeaderParams(params)
addCookieParams(params)
addQueryParams(params)
}

Expand Down Expand Up @@ -233,6 +235,18 @@ internal object BodyGenerator {
}
}

private fun CodeBlock.Builder.addCookieParams(params: Map<ParameterLocation, List<Parameter>>) {
val cookieParams = params[ParameterLocation.COOKIE]
if (!cookieParams.isNullOrEmpty()) {
for (param in cookieParams) {
val paramName = param.name.toCamelCase()
optionalGuard(param.required, paramName) {
addStatement("%M(%S, %M(%L))", COOKIE_FUN, param.name, ENCODE_PARAM_FUN, paramName)
}
}
}
}

private fun CodeBlock.Builder.addQueryParams(params: Map<ParameterLocation, List<Parameter>>) {
val queryParams = params[ParameterLocation.QUERY]
if (!queryParams.isNullOrEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,11 @@ internal object ClientGenerator {
buildNullableParameter(param.schema, param.name, param.required)
}

funBuilder.addParameters(pathParams + queryParams + headerParams)
val cookieParams = params[ParameterLocation.COOKIE].orEmpty().map { param ->
buildNullableParameter(param.schema, param.name, param.required)
}

funBuilder.addParameters(pathParams + queryParams + headerParams + cookieParams)

if (endpoint.requestBody != null) {
funBuilder.addParameters(buildBodyParams(endpoint.requestBody))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ data class Parameter(
val description: String?,
)

// todo: add cookie
enum class ParameterLocation {
PATH,
QUERY,
HEADER
HEADER,
COOKIE
}

data class RequestBody(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,72 @@ class ClientGeneratorTest {
assertTrue(body.contains("append(\"X-Request-Id\""), "Expected header append inside headers block")
}

// -- CLNT-09b: Cookie parameters --

@Test
fun `cookie parameters become function parameters`() {
val ep = endpoint(
operationId = "listPets",
parameters =
listOf(
Parameter(
"session",
ParameterLocation.COOKIE,
true,
TypeRef.Primitive(PrimitiveType.STRING),
null,
),
),
)
val cls = clientClass(ep)
val funSpec = cls.funSpecs.first { it.name == "listPets" }
val param = funSpec.parameters.first { it.name == "session" }
assertEquals("kotlin.String", param.type.toString())
}

@Test
fun `required cookie parameters are emitted via cookie call`() {
val ep = endpoint(
operationId = "listPets",
parameters =
listOf(
Parameter(
"session",
ParameterLocation.COOKIE,
true,
TypeRef.Primitive(PrimitiveType.STRING),
null,
),
),
)
val cls = clientClass(ep)
val funSpec = cls.funSpecs.first { it.name == "listPets" }
val body = funSpec.body.toString()
assertTrue(body.contains("cookie(\"session\""), "Expected cookie call in generated body")
}

@Test
fun `optional cookie parameters are null-guarded`() {
val ep = endpoint(
operationId = "listPets",
parameters =
listOf(
Parameter(
"session",
ParameterLocation.COOKIE,
false,
TypeRef.Primitive(PrimitiveType.STRING),
null,
),
),
)
val cls = clientClass(ep)
val funSpec = cls.funSpecs.first { it.name == "listPets" }
val body = funSpec.body.toString()
assertTrue(body.contains("if (session != null)"), "Expected null guard for optional cookie")
assertTrue(body.contains("cookie(\"session\""), "Expected cookie call in generated body")
}

// -- CLNT-10: Client constructor has baseUrl parameter --

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,41 @@ class SpecParserTest : SpecParserTestBase() {
assertTrue(petIdParam.required, "Path parameter should be required")
}

@Test
fun `parses cookie parameter location`() {
val spec = File.createTempFile("cookie-spec", ".yaml").apply { deleteOnExit() }
spec.writeText(
"""
openapi: 3.0.0
info:
title: Cookie API
version: 1.0.0
paths:
/session:
get:
operationId: getSession
parameters:
- name: session
in: cookie
required: true
schema:
type: string
responses:
'200':
description: ok
""".trimIndent(),
)
val parsed = parseSpec(spec)
val getSession =
parsed.endpoints.find { it.operationId == "getSession" }
?: fail("getSession endpoint not found")
val sessionParam =
getSession.parameters.find { it.name == "session" }
?: fail("session parameter not found")
assertEquals(ParameterLocation.COOKIE, sessionParam.location)
assertTrue(sessionParam.required, "Cookie parameter should be required")
}

@Test
fun `parsed POST pets has requestBody referencing NewPet`() {
val createPet =
Expand Down
Loading