diff --git a/core/src/main/kotlin/com/avsystem/justworks/core/gen/Names.kt b/core/src/main/kotlin/com/avsystem/justworks/core/gen/Names.kt index 0b40277..788ac79 100644 --- a/core/src/main/kotlin/com/avsystem/justworks/core/gen/Names.kt +++ b/core/src/main/kotlin/com/avsystem/justworks/core/gen/Names.kt @@ -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") 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 0eb9952..7581597 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 @@ -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 @@ -197,6 +198,7 @@ internal object BodyGenerator { private fun CodeBlock.Builder.addCommonRequestParts(params: Map>) { addStatement("${APPLY_AUTH}()") addHeaderParams(params) + addCookieParams(params) addQueryParams(params) } @@ -233,6 +235,18 @@ internal object BodyGenerator { } } + private fun CodeBlock.Builder.addCookieParams(params: Map>) { + 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>) { val queryParams = params[ParameterLocation.QUERY] if (!queryParams.isNullOrEmpty()) { diff --git a/core/src/main/kotlin/com/avsystem/justworks/core/gen/client/ClientGenerator.kt b/core/src/main/kotlin/com/avsystem/justworks/core/gen/client/ClientGenerator.kt index 88469a4..c5cdcb8 100644 --- a/core/src/main/kotlin/com/avsystem/justworks/core/gen/client/ClientGenerator.kt +++ b/core/src/main/kotlin/com/avsystem/justworks/core/gen/client/ClientGenerator.kt @@ -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)) diff --git a/core/src/main/kotlin/com/avsystem/justworks/core/model/ApiSpec.kt b/core/src/main/kotlin/com/avsystem/justworks/core/model/ApiSpec.kt index dba29e8..fda4e50 100644 --- a/core/src/main/kotlin/com/avsystem/justworks/core/model/ApiSpec.kt +++ b/core/src/main/kotlin/com/avsystem/justworks/core/model/ApiSpec.kt @@ -63,11 +63,11 @@ data class Parameter( val description: String?, ) -// todo: add cookie enum class ParameterLocation { PATH, QUERY, - HEADER + HEADER, + COOKIE } data class RequestBody( 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 c98d642..7f7b9c4 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 @@ -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 diff --git a/core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserTest.kt b/core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserTest.kt index 603e151..8a5ba17 100644 --- a/core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserTest.kt +++ b/core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserTest.kt @@ -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 =