Skip to content

Commit

Permalink
implement url object including templating and headers
Browse files Browse the repository at this point in the history
  • Loading branch information
Jens Zettelmeyer committed Dec 16, 2023
1 parent 746fabf commit 030aa3a
Show file tree
Hide file tree
Showing 13 changed files with 737 additions and 82 deletions.
24 changes: 22 additions & 2 deletions end2end-tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ dependencies {

tasks {

val generateCode by creating(JavaExec::class) {
val generateOkioCode by creating(JavaExec::class) {
inputs.files(apiFile)
outputs.dir(generationDir)
outputs.cacheIf { true }
Expand All @@ -58,9 +58,29 @@ tasks {
dependsOn(":shadowJar")
}

val generateJdkClientCode by creating(JavaExec::class) {
inputs.files(apiFile)
outputs.dir(generationDir)
outputs.cacheIf { true }
classpath = rootProject.files("./build/libs/fabrikt-${rootProject.version}.jar")
mainClass.set("com.cjbooms.fabrikt.cli.CodeGen")
args = listOf(
"--output-directory", generationDir,
"--base-package", "com.example.jdk_client",
"--api-file", apiFile,
"--targets", "http_models",
"--http-client-target", "JDK_HTTP",
"--targets", "client",
"--http-client-opts", "resilience4j"
)
dependsOn(":jar")
dependsOn(":shadowJar")
}

withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "17"
dependsOn(generateCode)
dependsOn(generateOkioCode)
dependsOn(generateJdkClientCode)
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
package com.cjbooms.fabrikt.clients.jdk

import com.example.jdk_client.client.ApiClientException
import com.example.jdk_client.client.ApiRedirectException
import com.example.jdk_client.client.ApiServerException
import com.example.jdk_client.client.ExamplePath2Client
import com.example.jdk_client.client.ExamplePath3SubresourceClient
import com.example.jdk_client.client.ExamplePath1Client
import com.example.jdk_client.models.Failure
import com.example.jdk_client.models.FirstModel
import com.example.jdk_client.models.QueryResult
import com.fasterxml.jackson.databind.ObjectMapper
import com.github.tomakehurst.wiremock.WireMockServer
import com.github.tomakehurst.wiremock.common.ConsoleNotifier
import com.github.tomakehurst.wiremock.core.WireMockConfiguration
import com.marcinziolo.kotlin.wiremock.*
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.*
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import java.net.ServerSocket
import java.net.http.HttpClient
import java.util.*
import java.util.stream.Stream

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class JdkTest {
private val port: Int = ServerSocket(0).use { socket -> socket.localPort }

private val wiremock: WireMockServer = WireMockServer(
WireMockConfiguration.options().port(port).notifier(
ConsoleNotifier(true)
))

private val mapper = ObjectMapper()
private val httpClient = HttpClient.newHttpClient()
private val examplePath1Client = ExamplePath1Client(mapper, "http://localhost:$port", httpClient)
private val examplePath2Client = ExamplePath2Client(mapper, "http://localhost:$port", httpClient)
private val examplePath3Client = ExamplePath3SubresourceClient(mapper, "http://localhost:$port", httpClient)

private val uuid = UUID.randomUUID()
private val failure = Failure(traceId = uuid,
error = "testError",
errorCode = "testErrorCode")

@Suppress("unused")
private fun path2ErrorCodes(): Stream<Int> = Stream.of(400, 422, 423)

@BeforeEach
fun setUp() {
wiremock.start()
}

@AfterEach
fun afterEach() {
wiremock.resetAll()
wiremock.stop()
}

@Test
fun `throws an exception if 404 is returned`() {
wiremock.get {
url like "/example-path-1"
} returns {
statusCode = 404
}

val result = assertThrows<ApiClientException> {
examplePath1Client.getExamplePath1()
}
Assertions.assertThat(result.statusCode).isEqualTo(404)
}

@Test
fun `returns data when no query parameters are send`(testInfo: TestInfo) {
wiremock.get {
url like "/example-path-1"
} returns {
statusCode = 200
body = mapper.writeValueAsString(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

val result = examplePath1Client.getExamplePath1()

Assertions.assertThat(result.data).isEqualTo(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

@Test
fun `adds query_param2 to the query`(testInfo: TestInfo) {
wiremock.get {
urlPath like "/example-path-1"
queryParams contains "query_param2" like "10"
} returns {
statusCode = 200
body = mapper.writeValueAsString(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

val result = examplePath1Client.getExamplePath1(queryParam2 = 10)

Assertions.assertThat(result.data).isEqualTo(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

@Test
fun `adds explode_list_query_param to the query`(testInfo: TestInfo) {
wiremock.get {
urlPath like "/example-path-1"
queryParams contains "explode_list_query_param" like "list"
queryParams contains "explode_list_query_param" like "of"
queryParams contains "explode_list_query_param" like "parameters"
} returns {
statusCode = 200
body = mapper.writeValueAsString(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

val result = examplePath1Client.getExamplePath1(explodeListQueryParam = listOf("list", "of", "parameters"))

Assertions.assertThat(result.data).isEqualTo(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

@Test
fun `adds additional headers to the query`(testInfo: TestInfo) {
wiremock.get {
urlPath like "/example-path-1"
headers contains "awesome" like "header"
} returns {
statusCode = 200
body = mapper.writeValueAsString(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

val result = examplePath1Client.getExamplePath1(additionalHeaders = mapOf("awesome" to "header"))

Assertions.assertThat(result.data).isEqualTo(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

@Test
fun `send body with post request`(testInfo: TestInfo) {
val content = FirstModel(id = testInfo.displayName)
wiremock.post {
urlPath like "/example-path-1"
body equalTo mapper.writeValueAsString(content)
} returns {
statusCode = 201
}

val result = examplePath1Client.postExamplePath1(content)
Assertions.assertThat(result.statusCode).isEqualTo(201)
}

@ParameterizedTest
@MethodSource("path2ErrorCodes")
fun `throws an exception if a 4xx http status code is returned`(errorCode: Int) {
wiremock.get {
urlPath like "/example-path-2/$errorCode"
} returns {
statusCode = errorCode
body = mapper.writeValueAsString(failure)
}

val result = assertThrows<ApiClientException> {
examplePath2Client.getExamplePath2PathParam(errorCode.toString(), 10)
}

Assertions.assertThat(result.statusCode).isEqualTo(errorCode)
Assertions.assertThat(mapper.readValue(result.message, Failure::class.java)).isEqualTo(failure)
}

@Test
fun `throws an exception if a http status code 500 is returned`() {
wiremock.get {
urlPath like "/example-path-2/500"
} returns {
statusCode = 500
body = mapper.writeValueAsString(failure)
}

val result = assertThrows<ApiServerException> {
examplePath2Client.getExamplePath2PathParam("500", 10)
}

Assertions.assertThat(result.statusCode).isEqualTo(500)
Assertions.assertThat(mapper.readValue(result.message, Failure::class.java)).isEqualTo(failure)
}

@Test
fun `throws an exception if a http status code 304 is returned`() {
wiremock.get {
urlPath like "/example-path-2/304"
} returns {
statusCode = 304
}

val result = assertThrows<ApiRedirectException> {
examplePath2Client.getExamplePath2PathParam("304", 10)
}

Assertions.assertThat(result.statusCode).isEqualTo(304)
}


@Test
fun `head returns 200`() {
wiremock.head {
urlPath like "/example-path-2/head200"
} returns {
statusCode = 200
}

val result = examplePath2Client.headOperationIdExample("head200")

Assertions.assertThat(result.statusCode).isEqualTo(200)
}

@Test
fun `put returns 204`() {
val model = FirstModel(id = "put", secondAttr = "204")
wiremock.put {
urlPath like "/example-path-2/put204"
body equalTo mapper.writeValueAsString(model)
headers contains "If-Match" like "match"
} returns {
statusCode = 204
}

val result = examplePath2Client.putExamplePath2PathParam(firstModel = model, pathParam = "put204", ifMatch = "match")

Assertions.assertThat(result.statusCode).isEqualTo(204)
}

@Test
fun `put returns 204 with sub resource`() {
val model = FirstModel(id = "put", secondAttr = "304")
wiremock.put {
urlPath like "/example-path-3/put304/subresource"
body equalTo mapper.writeValueAsString(model)
headers contains "If-Match" like "match"
} returns {
statusCode = 204
}

val result = examplePath3Client.putExamplePath3PathParamSubresource(firstModel = model, pathParam = "put304", ifMatch = "match")

Assertions.assertThat(result.statusCode).isEqualTo(204)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.cjbooms.fabrikt.clients.jdk

import com.example.jdk_client.client.Url
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.net.URI

class UrlTest {

@Test
fun testExpansion() {
val url = Url("http://bla.blub/{a}/test/{b}")
.addPathParam("a", "123")
.addPathParam("b", "abc")
.addQueryParam("expand", "true")
.addQueryParam("whatever", "654")

assertThat(url.toUri())
.isEqualTo(URI.create("http://bla.blub/123/test/abc?expand=true&whatever=654"))
}

@Test
fun testWithoutQueryParams() {
val url = Url("http://bla.blub/{a}/test/{b}")
.addPathParam("a", "123")
.addPathParam("b", "abc")

assertThat(url.toUri())
.isEqualTo(URI.create("http://bla.blub/123/test/abc"))
}

@Test
fun testArrayWithoutExplode() {
val url = Url("http://bla.blub/test")
.addQueryParam("queryParam", listOf("a", "b"), true)

assertThat(url.toUri())
.isEqualTo(URI.create("http://bla.blub/test?queryParam=a&queryParam=b"))
}

@Test
fun testArrayWithExplode() {
val url = Url("http://bla.blub/test")
.addQueryParam("queryParam", listOf("a", "b"), false)

assertThat(url.toUri())
.isEqualTo(URI.create("http://bla.blub/test?queryParam=a,b"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ import com.cjbooms.fabrikt.model.SourceApi
import java.nio.file.Path

class JDKHttpClientGenerator(
@Suppress("unused") packages: Packages,
@Suppress("unused") api: SourceApi,
@Suppress("unused") srcPath: Path,
packages: Packages,
api: SourceApi,
srcPath: Path,
) : ClientGenerator {
private val simpleClientGenerator = JDKHttpSimpleClientGenerator(packages, api, srcPath)

override fun generate(options: Set<ClientCodeGenOptionType>): Clients {
TODO("Not yet implemented")
val simpleClient = simpleClientGenerator.generateDynamicClientCode()

return Clients(simpleClient)
}

override fun generateLibrary(options: Set<ClientCodeGenOptionType>): Collection<GeneratedFile> {
TODO("Not yet implemented")

return simpleClientGenerator.generateLibrary()
}
}
Loading

0 comments on commit 030aa3a

Please sign in to comment.