From 835d68099c5db904fecdbf5a6eb29ffb5451acd6 Mon Sep 17 00:00:00 2001 From: timemanx <1146030+timemanx@users.noreply.github.com> Date: Fri, 10 Jan 2025 02:58:09 +0900 Subject: [PATCH] Update Ktor to 3.0.3 (#2066) ### :pencil: Description Updated Ktor and updated failing tests and fixed compile issues resulting from the update. Also updated some libraries. ### :link: Related Issues #2037 --------- Co-authored-by: TheDome0 --- .../examples/server/ktor/GraphQLModule.kt | 8 ++--- gradle/libs.versions.toml | 22 ++++++------ .../ktor-graalvm-server/build.gradle.kts | 2 +- .../graalvm/maven-graalvm-server/pom.xml | 2 +- .../com/expediagroup/scalars/Application.kt | 4 --- .../scalars/CustomScalarKotlinxTests.kt | 34 +++++++++---------- .../expediagroup/ktor/jackson/Application.kt | 4 --- .../ktor/jackson/ApplicationTest.kt | 8 ++--- .../expediagroup/ktor/kotlinx/Application.kt | 4 --- .../ktor/kotlinx/ApplicationTest.kt | 8 ++--- .../expediagroup/polymorphic/Application.kt | 4 --- .../polymorphic/PolymorphicKotlinxTests.kt | 16 ++++----- .../http/spec/ktor/GraphQLModule.kt | 4 +-- .../graphql/server/ktor/GraphQLRoutes.kt | 8 ++--- .../server/ktor/KtorGraphQLRequestParser.kt | 3 ++ .../graphql/server/ktor/GraphQLPluginTest.kt | 32 ++++++++++------- .../ktor/KtorGraphQLRequestParserTest.kt | 32 +++++++++++------ 17 files changed, 100 insertions(+), 95 deletions(-) diff --git a/examples/server/ktor-server/src/main/kotlin/com/expediagroup/graphql/examples/server/ktor/GraphQLModule.kt b/examples/server/ktor-server/src/main/kotlin/com/expediagroup/graphql/examples/server/ktor/GraphQLModule.kt index 54a456408d..eb0943a36d 100644 --- a/examples/server/ktor-server/src/main/kotlin/com/expediagroup/graphql/examples/server/ktor/GraphQLModule.kt +++ b/examples/server/ktor-server/src/main/kotlin/com/expediagroup/graphql/examples/server/ktor/GraphQLModule.kt @@ -37,14 +37,14 @@ import io.ktor.server.application.Application import io.ktor.server.application.install import io.ktor.server.plugins.cors.routing.CORS import io.ktor.server.plugins.statuspages.StatusPages -import io.ktor.server.routing.Routing +import io.ktor.server.routing.routing import io.ktor.server.websocket.WebSockets import io.ktor.server.websocket.pingPeriod -import java.time.Duration +import kotlin.time.Duration.Companion.seconds fun Application.graphQLModule() { install(WebSockets) { - pingPeriod = Duration.ofSeconds(1) + pingPeriod = 1.seconds contentConverter = JacksonWebsocketContentConverter() } install(StatusPages) { @@ -78,7 +78,7 @@ fun Application.graphQLModule() { contextFactory = CustomGraphQLContextFactory() } } - install(Routing) { + routing { graphQLGetRoute() graphQLPostRoute() graphQLSubscriptionsRoute() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 408935a199..1c4829b0ec 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,18 +1,18 @@ [versions] android-plugin = "8.5.0" classgraph = "4.8.174" -dataloader = "3.3.0" -federation = "5.1.0" -graphql-java = "22.1" +dataloader = "3.4.0" +federation = "5.2.0" +graphql-java = "22.3" graalvm = "0.10.2" jackson = "2.17.1" # kotlin version has to match the compile-testing compiler version kotlin = "2.0.0" -kotlinx-benchmark = "0.4.11" -kotlinx-coroutines = "1.8.1" +kotlinx-benchmark = "0.4.13" +kotlinx-coroutines = "1.9.0" # TODO kotlin 1.9 upgrade: fix GraphQLTestUtils and GenerateKotlinxClientIT kotlinx-serialization = "1.6.3" -ktor = "2.3.12" +ktor = "3.0.3" fastjson2 = "2.0.53" maven-plugin-annotation = "3.13.1" maven-plugin-api = "3.9.8" @@ -33,16 +33,16 @@ compile-testing = "0.5.1" icu = "75.1" junit = "5.10.2" logback = "1.5.6" -mockk = "1.13.11" +mockk = "1.13.13" rxjava = "3.1.8" wiremock = "3.7.0" # plugins -detekt = "1.23.6" -dokka = "1.9.20" +detekt = "1.23.7" +dokka = "2.0.0" jacoco = "0.8.12" -ktlint-core = "1.3.0" -ktlint-plugin = "12.1.1" +ktlint-core = "1.5.0" +ktlint-plugin = "12.1.2" maven-plugin-development = "0.4.3" nexus-publish-plugin = "2.0.0" plugin-publish = "1.2.1" diff --git a/integration/graalvm/ktor-graalvm-server/build.gradle.kts b/integration/graalvm/ktor-graalvm-server/build.gradle.kts index b692f4ad19..bc3b137421 100644 --- a/integration/graalvm/ktor-graalvm-server/build.gradle.kts +++ b/integration/graalvm/ktor-graalvm-server/build.gradle.kts @@ -32,7 +32,7 @@ graalvmNative { binaries { named("main") { verbose.set(true) - buildArgs.add("--initialize-at-build-time=io.ktor,kotlin,ch.qos.logback,org.slf4j") + buildArgs.add("--initialize-at-build-time=io.ktor,kotlin,kotlinx.io,ch.qos.logback,org.slf4j") buildArgs.add("-H:+ReportExceptionStackTraces") jvmArgs("-Xmx6g") } diff --git a/integration/graalvm/maven-graalvm-server/pom.xml b/integration/graalvm/maven-graalvm-server/pom.xml index 755b6e763b..441f9b16b9 100644 --- a/integration/graalvm/maven-graalvm-server/pom.xml +++ b/integration/graalvm/maven-graalvm-server/pom.xml @@ -170,7 +170,7 @@ true - --initialize-at-build-time=io.ktor,kotlin,ch.qos.logback,org.slf4j + --initialize-at-build-time=io.ktor,kotlin,kotlinx.io,ch.qos.logback,org.slf4j -H:+ReportExceptionStackTraces diff --git a/integration/gradle-plugin-integration-tests/client-generator/custom-scalars-kotlinx/src/main/kotlin/com/expediagroup/scalars/Application.kt b/integration/gradle-plugin-integration-tests/client-generator/custom-scalars-kotlinx/src/main/kotlin/com/expediagroup/scalars/Application.kt index 53194fcfe9..abbb62bca8 100644 --- a/integration/gradle-plugin-integration-tests/client-generator/custom-scalars-kotlinx/src/main/kotlin/com/expediagroup/scalars/Application.kt +++ b/integration/gradle-plugin-integration-tests/client-generator/custom-scalars-kotlinx/src/main/kotlin/com/expediagroup/scalars/Application.kt @@ -5,11 +5,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application -import io.ktor.server.application.call -import io.ktor.server.application.install import io.ktor.server.response.respond import io.ktor.server.response.respondText -import io.ktor.server.routing.Routing import io.ktor.server.routing.get import io.ktor.server.routing.post import io.ktor.server.routing.routing @@ -20,7 +17,6 @@ fun Application.graphQLModule() { val jacksonObjectMapper: ObjectMapper = jacksonObjectMapper() val ktorGraphQLServer: KtorGraphQLServer = KtorGraphQLServer(jacksonObjectMapper) - install(Routing) routing { post("graphql") { val result = ktorGraphQLServer.execute(call.request) diff --git a/integration/gradle-plugin-integration-tests/client-generator/custom-scalars-kotlinx/src/test/kotlin/com/expediagroup/scalars/CustomScalarKotlinxTests.kt b/integration/gradle-plugin-integration-tests/client-generator/custom-scalars-kotlinx/src/test/kotlin/com/expediagroup/scalars/CustomScalarKotlinxTests.kt index 61fa33cfe0..b3120b9793 100644 --- a/integration/gradle-plugin-integration-tests/client-generator/custom-scalars-kotlinx/src/test/kotlin/com/expediagroup/scalars/CustomScalarKotlinxTests.kt +++ b/integration/gradle-plugin-integration-tests/client-generator/custom-scalars-kotlinx/src/test/kotlin/com/expediagroup/scalars/CustomScalarKotlinxTests.kt @@ -31,11 +31,11 @@ class CustomScalarKotlinxTests { @Test fun `verify custom scalars are correctly serialized and deserialized`() { - val engine = embeddedServer(CIO, port = 0, module = Application::graphQLModule) + val embeddedServer = embeddedServer(CIO, port = 0, module = Application::graphQLModule) try { - engine.start() + embeddedServer.start() runBlocking { - val port = engine.resolvedConnectors().first().port + val port = embeddedServer.engine.resolvedConnectors().first().port val client = GraphQLKtorClient(url = URL("http://localhost:$port/graphql")) val undefinedLocaleQuery = LocaleQuery(variables = LocaleQuery.Variables()) @@ -52,17 +52,17 @@ class CustomScalarKotlinxTests { assertEquals(ULocale.CANADA, localeResult.data?.localeQuery) } } finally { - engine.stop(1000, 1000) + embeddedServer.stop(1000, 1000) } } @Test fun `verify undefined optionals are correctly serialized and deserialized`() { - val engine = embeddedServer(CIO, port = 0, module = Application::graphQLModule) + val embeddedServer = embeddedServer(CIO, port = 0, module = Application::graphQLModule) try { - engine.start() + embeddedServer.start() runBlocking { - val port = engine.resolvedConnectors().first().port + val port = embeddedServer.engine.resolvedConnectors().first().port val client = GraphQLKtorClient(url = URL("http://localhost:$port/graphql")) val undefinedWrapperQuery = OptionalScalarQuery(variables = OptionalScalarQuery.Variables()) @@ -97,15 +97,15 @@ class CustomScalarKotlinxTests { assertEquals(0, defaultResult.optionalUUIDList?.size) } } finally { - engine.stop(1000, 1000) + embeddedServer.stop(1000, 1000) } } @Test fun `verify null optionals are correctly serialized and deserialized`() { - val engine = embeddedServer(CIO, port = 8080, module = Application::graphQLModule) + val embeddedServer = embeddedServer(CIO, port = 8080, module = Application::graphQLModule) try { - engine.start() + embeddedServer.start() runBlocking { val client = GraphQLKtorClient(url = URL("http://localhost:8080/graphql")) @@ -142,15 +142,15 @@ class CustomScalarKotlinxTests { assertNull(nullResult.optionalUUIDList) } } finally { - engine.stop(1000, 1000) + embeddedServer.stop(1000, 1000) } } @Test fun `verify defined optionals are correctly serialized and deserialized`() { - val engine = embeddedServer(CIO, port = 8080, module = Application::graphQLModule) + val embeddedServer = embeddedServer(CIO, port = 8080, module = Application::graphQLModule) try { - engine.start() + embeddedServer.start() runBlocking { val client = GraphQLKtorClient(url = URL("http://localhost:8080/graphql")) @@ -187,15 +187,15 @@ class CustomScalarKotlinxTests { assertEquals(randomUUID, result.optionalUUIDList?.get(0)) } } finally { - engine.stop(1000, 1000) + embeddedServer.stop(1000, 1000) } } @Test fun `verify serialization and deserialization of required type`() { - val engine = embeddedServer(CIO, port = 8080, module = Application::graphQLModule) + val embeddedServer = embeddedServer(CIO, port = 8080, module = Application::graphQLModule) try { - engine.start() + embeddedServer.start() runBlocking { val client = GraphQLKtorClient(url = URL("http://localhost:8080/graphql")) @@ -232,7 +232,7 @@ class CustomScalarKotlinxTests { assertEquals(wrapper.requiredUUIDList[0], result.requiredUUIDList[0]) } } finally { - engine.stop(1000, 1000) + embeddedServer.stop(1000, 1000) } } } diff --git a/integration/gradle-plugin-integration-tests/client-generator/ktor-jackson/src/main/kotlin/com/expediagroup/ktor/jackson/Application.kt b/integration/gradle-plugin-integration-tests/client-generator/ktor-jackson/src/main/kotlin/com/expediagroup/ktor/jackson/Application.kt index 2eae7b669e..326305113d 100644 --- a/integration/gradle-plugin-integration-tests/client-generator/ktor-jackson/src/main/kotlin/com/expediagroup/ktor/jackson/Application.kt +++ b/integration/gradle-plugin-integration-tests/client-generator/ktor-jackson/src/main/kotlin/com/expediagroup/ktor/jackson/Application.kt @@ -5,11 +5,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application -import io.ktor.server.application.call -import io.ktor.server.application.install import io.ktor.server.response.respond import io.ktor.server.response.respondText -import io.ktor.server.routing.Routing import io.ktor.server.routing.get import io.ktor.server.routing.post import io.ktor.server.routing.routing @@ -20,7 +17,6 @@ fun Application.graphQLModule() { val jacksonObjectMapper: ObjectMapper = jacksonObjectMapper() val ktorGraphQLServer: KtorGraphQLServer = KtorGraphQLServer(jacksonObjectMapper) - install(Routing) routing { post("graphql") { val result = ktorGraphQLServer.execute(call.request) diff --git a/integration/gradle-plugin-integration-tests/client-generator/ktor-jackson/src/test/kotlin/com/expediagroup/ktor/jackson/ApplicationTest.kt b/integration/gradle-plugin-integration-tests/client-generator/ktor-jackson/src/test/kotlin/com/expediagroup/ktor/jackson/ApplicationTest.kt index 7fd9056517..575edf2b43 100644 --- a/integration/gradle-plugin-integration-tests/client-generator/ktor-jackson/src/test/kotlin/com/expediagroup/ktor/jackson/ApplicationTest.kt +++ b/integration/gradle-plugin-integration-tests/client-generator/ktor-jackson/src/test/kotlin/com/expediagroup/ktor/jackson/ApplicationTest.kt @@ -18,11 +18,11 @@ import kotlin.test.assertNull class ApplicationTest { @Test fun `verify ktor client can execute queries`() { - val engine = embeddedServer(CIO, port = 0, module = Application::graphQLModule) + val embeddedServer = embeddedServer(CIO, port = 0, module = Application::graphQLModule) try { - engine.start() + embeddedServer.start() runBlocking { - val port = engine.resolvedConnectors().first().port + val port = embeddedServer.engine.resolvedConnectors().first().port val client = GraphQLKtorClient(url = URL("http://localhost:$port/graphql")) val result = client.execute(TestQuery(variables = TestQuery.Variables(name = OptionalInput.Defined("junit")))) @@ -40,7 +40,7 @@ class ApplicationTest { assertEquals(ExampleEnum.ONE.name, testObject.choice.name) } } finally { - engine.stop(1000, 1000) + embeddedServer.stop(1000, 1000) } } } diff --git a/integration/gradle-plugin-integration-tests/client-generator/ktor-kotlinx/src/main/kotlin/com/expediagroup/ktor/kotlinx/Application.kt b/integration/gradle-plugin-integration-tests/client-generator/ktor-kotlinx/src/main/kotlin/com/expediagroup/ktor/kotlinx/Application.kt index 87b3c536e6..0c515abae3 100644 --- a/integration/gradle-plugin-integration-tests/client-generator/ktor-kotlinx/src/main/kotlin/com/expediagroup/ktor/kotlinx/Application.kt +++ b/integration/gradle-plugin-integration-tests/client-generator/ktor-kotlinx/src/main/kotlin/com/expediagroup/ktor/kotlinx/Application.kt @@ -5,11 +5,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application -import io.ktor.server.application.call -import io.ktor.server.application.install import io.ktor.server.response.respond import io.ktor.server.response.respondText -import io.ktor.server.routing.Routing import io.ktor.server.routing.get import io.ktor.server.routing.post import io.ktor.server.routing.routing @@ -20,7 +17,6 @@ fun Application.graphQLModule() { val jacksonObjectMapper: ObjectMapper = jacksonObjectMapper() val ktorGraphQLServer: KtorGraphQLServer = KtorGraphQLServer(jacksonObjectMapper) - install(Routing) routing { post("graphql") { val result = ktorGraphQLServer.execute(call.request) diff --git a/integration/gradle-plugin-integration-tests/client-generator/ktor-kotlinx/src/test/kotlin/com/expediagroup/ktor/kotlinx/ApplicationTest.kt b/integration/gradle-plugin-integration-tests/client-generator/ktor-kotlinx/src/test/kotlin/com/expediagroup/ktor/kotlinx/ApplicationTest.kt index 4910749fb8..c94f3e0615 100644 --- a/integration/gradle-plugin-integration-tests/client-generator/ktor-kotlinx/src/test/kotlin/com/expediagroup/ktor/kotlinx/ApplicationTest.kt +++ b/integration/gradle-plugin-integration-tests/client-generator/ktor-kotlinx/src/test/kotlin/com/expediagroup/ktor/kotlinx/ApplicationTest.kt @@ -18,11 +18,11 @@ import kotlin.test.assertNull class ApplicationTest { @Test fun `verify ktor client can execute queries`() { - val engine = embeddedServer(CIO, port = 0, module = Application::graphQLModule) + val embeddedServer = embeddedServer(CIO, port = 0, module = Application::graphQLModule) try { - engine.start() + embeddedServer.start() runBlocking { - val port = engine.resolvedConnectors().first().port + val port = embeddedServer.engine.resolvedConnectors().first().port val client = GraphQLKtorClient(url = URL("http://localhost:$port/graphql")) val result = client.execute(TestQuery(variables = TestQuery.Variables(name = OptionalInput.Defined("junit")))) @@ -40,7 +40,7 @@ class ApplicationTest { assertEquals(ExampleEnum.ONE.name, testObject.choice.name) } } finally { - engine.stop(1000, 1000) + embeddedServer.stop(1000, 1000) } } } diff --git a/integration/gradle-plugin-integration-tests/client-generator/polymorphic-types-kotlinx/src/main/kotlin/com/expediagroup/polymorphic/Application.kt b/integration/gradle-plugin-integration-tests/client-generator/polymorphic-types-kotlinx/src/main/kotlin/com/expediagroup/polymorphic/Application.kt index 399c0fc9c9..15f362d573 100644 --- a/integration/gradle-plugin-integration-tests/client-generator/polymorphic-types-kotlinx/src/main/kotlin/com/expediagroup/polymorphic/Application.kt +++ b/integration/gradle-plugin-integration-tests/client-generator/polymorphic-types-kotlinx/src/main/kotlin/com/expediagroup/polymorphic/Application.kt @@ -5,11 +5,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application -import io.ktor.server.application.call -import io.ktor.server.application.install import io.ktor.server.response.respond import io.ktor.server.response.respondText -import io.ktor.server.routing.Routing import io.ktor.server.routing.get import io.ktor.server.routing.post import io.ktor.server.routing.routing @@ -20,7 +17,6 @@ fun Application.graphQLModule() { val jacksonObjectMapper: ObjectMapper = jacksonObjectMapper() val ktorGraphQLServer: KtorGraphQLServer = KtorGraphQLServer(jacksonObjectMapper) - install(Routing) routing { post("graphql") { val result = ktorGraphQLServer.execute(call.request) diff --git a/integration/gradle-plugin-integration-tests/client-generator/polymorphic-types-kotlinx/src/test/kotlin/com/expediagroup/polymorphic/PolymorphicKotlinxTests.kt b/integration/gradle-plugin-integration-tests/client-generator/polymorphic-types-kotlinx/src/test/kotlin/com/expediagroup/polymorphic/PolymorphicKotlinxTests.kt index ba6f3d72ed..508520bd85 100644 --- a/integration/gradle-plugin-integration-tests/client-generator/polymorphic-types-kotlinx/src/test/kotlin/com/expediagroup/polymorphic/PolymorphicKotlinxTests.kt +++ b/integration/gradle-plugin-integration-tests/client-generator/polymorphic-types-kotlinx/src/test/kotlin/com/expediagroup/polymorphic/PolymorphicKotlinxTests.kt @@ -26,11 +26,11 @@ class PolymorphicKotlinxTests { @Test fun `verify polymorphic queries are correctly serialized and deserialized`() { - val engine = embeddedServer(CIO, port = 0, module = Application::graphQLModule) + val embeddedServer = embeddedServer(CIO, port = 0, module = Application::graphQLModule) try { - engine.start() + embeddedServer.start() runBlocking { - val port = engine.resolvedConnectors().first().port + val port = embeddedServer.engine.resolvedConnectors().first().port val client = GraphQLKtorClient(url = URL("http://localhost:$port/graphql")) val query = CompletePolymorphicQuery(variables = CompletePolymorphicQuery.Variables(input = "foo")) @@ -46,16 +46,16 @@ class PolymorphicKotlinxTests { assertNull(nullResponse.data?.unionQuery) } } finally { - engine.stop(1000, 1000) + embeddedServer.stop(1000, 1000) } } @OptIn(ExperimentalSerializationApi::class) @Test fun `verify polymorphic queries fallbacks are correctly serialized and deserialized`() { - val engine = embeddedServer(CIO, port = 0, module = Application::graphQLModule) + val embeddedServer = embeddedServer(CIO, port = 0, module = Application::graphQLModule) try { - engine.start() + embeddedServer.start() runBlocking { // need to register fallback logic val serializerWithFallback = GraphQLClientKotlinxSerializer(jsonBuilder = { @@ -68,7 +68,7 @@ class PolymorphicKotlinxTests { } } }) - val port = engine.resolvedConnectors().first().port + val port = embeddedServer.engine.resolvedConnectors().first().port val client = GraphQLKtorClient(url = URL("http://localhost:$port/graphql"), serializer = serializerWithFallback) val fallbackQuery = PartialPolymorphicQuery(variables = PartialPolymorphicQuery.Variables(input = "bar")) @@ -79,7 +79,7 @@ class PolymorphicKotlinxTests { assertTrue(unionResult is DefaultBasicUnionImplementation) } } finally { - engine.stop(1000, 1000) + embeddedServer.stop(1000, 1000) } } } diff --git a/integration/graphql-http-spec/ktor-server/src/main/kotlin/com/expediagroup/http/spec/ktor/GraphQLModule.kt b/integration/graphql-http-spec/ktor-server/src/main/kotlin/com/expediagroup/http/spec/ktor/GraphQLModule.kt index 698f70d93c..47202b522b 100644 --- a/integration/graphql-http-spec/ktor-server/src/main/kotlin/com/expediagroup/http/spec/ktor/GraphQLModule.kt +++ b/integration/graphql-http-spec/ktor-server/src/main/kotlin/com/expediagroup/http/spec/ktor/GraphQLModule.kt @@ -20,7 +20,7 @@ import com.expediagroup.graphql.server.ktor.graphQLGetRoute import com.expediagroup.graphql.server.ktor.graphQLPostRoute import io.ktor.server.application.Application import io.ktor.server.application.install -import io.ktor.server.routing.Routing +import io.ktor.server.routing.routing fun Application.graphQLModule() { install(GraphQL) { @@ -31,7 +31,7 @@ fun Application.graphQLModule() { ) } } - install(Routing) { + routing { graphQLGetRoute() graphQLPostRoute() } diff --git a/servers/graphql-kotlin-ktor-server/src/main/kotlin/com/expediagroup/graphql/server/ktor/GraphQLRoutes.kt b/servers/graphql-kotlin-ktor-server/src/main/kotlin/com/expediagroup/graphql/server/ktor/GraphQLRoutes.kt index 7ec6e232f2..4ec2ea2ab2 100644 --- a/servers/graphql-kotlin-ktor-server/src/main/kotlin/com/expediagroup/graphql/server/ktor/GraphQLRoutes.kt +++ b/servers/graphql-kotlin-ktor-server/src/main/kotlin/com/expediagroup/graphql/server/ktor/GraphQLRoutes.kt @@ -21,8 +21,6 @@ import com.expediagroup.graphql.server.execution.subscription.GRAPHQL_WS_PROTOCO import com.fasterxml.jackson.databind.ObjectMapper import io.ktor.http.ContentType import io.ktor.serialization.jackson.jackson -import io.ktor.server.application.call -import io.ktor.server.application.install import io.ktor.server.application.plugin import io.ktor.server.plugins.contentnegotiation.ContentNegotiation import io.ktor.server.response.respondText @@ -115,11 +113,11 @@ fun Route.graphiQLRoute( graphQLEndpoint: String = "graphql", subscriptionsEndpoint: String = "subscriptions", ): Route { - val contextPath = this.environment?.rootPath + val contextPath = this.application.rootPath val graphiQL = GraphQL::class.java.classLoader.getResourceAsStream("graphql-graphiql.html")?.bufferedReader()?.use { reader -> reader.readText() - .replace("\${graphQLEndpoint}", if (contextPath.isNullOrBlank()) graphQLEndpoint else "$contextPath/$graphQLEndpoint") - .replace("\${subscriptionsEndpoint}", if (contextPath.isNullOrBlank()) subscriptionsEndpoint else "$contextPath/$subscriptionsEndpoint") + .replace("\${graphQLEndpoint}", if (contextPath.isBlank()) graphQLEndpoint else "$contextPath/$graphQLEndpoint") + .replace("\${subscriptionsEndpoint}", if (contextPath.isBlank()) subscriptionsEndpoint else "$contextPath/$subscriptionsEndpoint") } ?: throw IllegalStateException("Unable to load GraphiQL") return get(endpoint) { call.respondText(graphiQL, ContentType.Text.Html) diff --git a/servers/graphql-kotlin-ktor-server/src/main/kotlin/com/expediagroup/graphql/server/ktor/KtorGraphQLRequestParser.kt b/servers/graphql-kotlin-ktor-server/src/main/kotlin/com/expediagroup/graphql/server/ktor/KtorGraphQLRequestParser.kt index 7c055d1bdd..36f4111b8e 100644 --- a/servers/graphql-kotlin-ktor-server/src/main/kotlin/com/expediagroup/graphql/server/ktor/KtorGraphQLRequestParser.kt +++ b/servers/graphql-kotlin-ktor-server/src/main/kotlin/com/expediagroup/graphql/server/ktor/KtorGraphQLRequestParser.kt @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.type.MapType import com.fasterxml.jackson.databind.type.TypeFactory import io.ktor.http.HttpMethod +import io.ktor.server.plugins.CannotTransformContentToTypeException import io.ktor.server.request.ApplicationRequest import io.ktor.server.request.receive import java.io.IOException @@ -75,6 +76,8 @@ open class KtorGraphQLRequestParser( private suspend fun parsePostRequest(request: ApplicationRequest): GraphQLServerRequest? = try { request.call.receive() + } catch (e: CannotTransformContentToTypeException) { + throw e } catch (e: IOException) { throw IllegalStateException("Invalid HTTP request - unable to parse GraphQL request from POST payload", e) } diff --git a/servers/graphql-kotlin-ktor-server/src/test/kotlin/com/expediagroup/graphql/server/ktor/GraphQLPluginTest.kt b/servers/graphql-kotlin-ktor-server/src/test/kotlin/com/expediagroup/graphql/server/ktor/GraphQLPluginTest.kt index 77602c42f4..5efaa8b6bd 100644 --- a/servers/graphql-kotlin-ktor-server/src/test/kotlin/com/expediagroup/graphql/server/ktor/GraphQLPluginTest.kt +++ b/servers/graphql-kotlin-ktor-server/src/test/kotlin/com/expediagroup/graphql/server/ktor/GraphQLPluginTest.kt @@ -36,8 +36,9 @@ import io.ktor.http.contentType import io.ktor.serialization.jackson.jackson import io.ktor.server.application.Application import io.ktor.server.application.install +import io.ktor.server.config.ApplicationConfig import io.ktor.server.plugins.statuspages.StatusPages -import io.ktor.server.routing.Routing +import io.ktor.server.routing.routing import io.ktor.server.testing.testApplication import io.ktor.websocket.Frame import io.ktor.websocket.readText @@ -104,7 +105,7 @@ class GraphQLPluginTest { flow: Int! } """.trimIndent() - testApplication { + testModule { val response = client.get("/sdl") assertEquals(HttpStatusCode.OK, response.status) assertEquals(expectedSchema, response.bodyAsText().trim()) @@ -113,7 +114,7 @@ class GraphQLPluginTest { @Test fun `server should handle valid GET requests`() { - testApplication { + testModule { val response = client.get("/graphql") { parameter("query", "query HelloQuery(\$name: String){ hello(name: \$name) }") parameter("operationName", "HelloQuery") @@ -126,7 +127,7 @@ class GraphQLPluginTest { @Test fun `server should return Method Not Allowed for Mutation GET requests`() { - testApplication { + testModule { val response = client.get("/graphql") { parameter("query", "mutation { foo }") } @@ -146,7 +147,7 @@ class GraphQLPluginTest { @Test fun `server should return Bad Request for invalid GET requests`() { - testApplication { + testModule { val response = client.get("/graphql") assertEquals(HttpStatusCode.BadRequest, response.status) } @@ -154,7 +155,7 @@ class GraphQLPluginTest { @Test fun `server should handle valid POST requests`() { - testApplication { + testModule { val client = createClient { install(ContentNegotiation) { jackson() @@ -171,7 +172,7 @@ class GraphQLPluginTest { @Test fun `server should handle valid POST batch requests`() { - testApplication { + testModule { val client = createClient { install(ContentNegotiation) { jackson() @@ -195,7 +196,7 @@ class GraphQLPluginTest { @Test fun `server should return Bad Request for invalid POST requests with correct content type`() { - testApplication { + testModule { val response = client.post("/graphql") { contentType(ContentType.Application.Json) } @@ -205,7 +206,7 @@ class GraphQLPluginTest { @Test fun `server should return Unsupported Media Type for POST requests with invalid content type`() { - testApplication { + testModule { val response = client.post("/graphql") assertEquals(HttpStatusCode.UnsupportedMediaType, response.status) } @@ -213,7 +214,7 @@ class GraphQLPluginTest { @Test fun `server should handle subscription requests`() { - testApplication { + testModule { val client = createClient { install(ContentNegotiation) { jackson() @@ -243,7 +244,7 @@ class GraphQLPluginTest { @Test fun `server should provide GraphiQL endpoint`() { - testApplication { + testModule { val response = client.get("/graphiql") assertEquals(HttpStatusCode.OK, response.status) @@ -270,7 +271,7 @@ fun Application.testGraphQLModule() { } } install(io.ktor.server.websocket.WebSockets) - install(Routing) { + routing { graphQLGetRoute() graphQLPostRoute() graphQLSubscriptionsRoute() @@ -278,3 +279,10 @@ fun Application.testGraphQLModule() { graphiQLRoute() } } + +private fun testModule(block: suspend io.ktor.server.testing.ApplicationTestBuilder.() -> kotlin.Unit) = testApplication { + environment { + config = ApplicationConfig(("application.conf")) + } + block() +} diff --git a/servers/graphql-kotlin-ktor-server/src/test/kotlin/com/expediagroup/graphql/server/ktor/KtorGraphQLRequestParserTest.kt b/servers/graphql-kotlin-ktor-server/src/test/kotlin/com/expediagroup/graphql/server/ktor/KtorGraphQLRequestParserTest.kt index 5e6a6c86b1..cc19eff86e 100644 --- a/servers/graphql-kotlin-ktor-server/src/test/kotlin/com/expediagroup/graphql/server/ktor/KtorGraphQLRequestParserTest.kt +++ b/servers/graphql-kotlin-ktor-server/src/test/kotlin/com/expediagroup/graphql/server/ktor/KtorGraphQLRequestParserTest.kt @@ -4,7 +4,9 @@ import com.expediagroup.graphql.server.types.GraphQLBatchRequest import com.expediagroup.graphql.server.types.GraphQLRequest import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.ktor.http.HttpMethod +import io.ktor.server.request.ApplicationReceivePipeline import io.ktor.server.request.ApplicationRequest +import io.ktor.server.testing.TestApplicationCall import io.ktor.server.testing.TestApplicationRequest import io.mockk.coEvery import io.mockk.every @@ -113,13 +115,18 @@ class KtorGraphQLRequestParserTest { @Test fun `parseRequest should return request if method is POST`() = runTest { val mockRequest = GraphQLRequest("query MyFoo { foo }", "MyFoo", mapOf("a" to 1)) - val serverRequest = mockk(relaxed = true) { - every { call } returns mockk(relaxed = true) { + val serverRequest = TestApplicationRequest( + call = mockk(relaxed = true) { + every { application } returns mockk(relaxed = true) { + every { receivePipeline } returns ApplicationReceivePipeline() + } + coEvery { receiveNullable(any()) } answers { callOriginal() } every { attributes.getOrNull(any()) } returns null coEvery { request.pipeline.execute(any(), any()) } returns mockRequest - } - every { local.method } returns HttpMethod.Post - } + }, + closeRequest = true, + method = HttpMethod.Post + ) val graphQLRequest = parser.parseRequest(serverRequest) assertNotNull(graphQLRequest) @@ -135,13 +142,18 @@ class KtorGraphQLRequestParserTest { val mockRequest2 = GraphQLRequest("query MyBar { bar }", "MyBar") val mockRequest = GraphQLBatchRequest(listOf(mockRequest1, mockRequest2)) - val serverRequest = mockk(relaxed = true) { - every { call } returns mockk(relaxed = true) { + val serverRequest = TestApplicationRequest( + call = mockk(relaxed = true) { + every { application } returns mockk(relaxed = true) { + every { receivePipeline } returns ApplicationReceivePipeline() + } + coEvery { receiveNullable(any()) } answers { callOriginal() } every { attributes.getOrNull(any()) } returns null coEvery { request.pipeline.execute(any(), any()) } returns mockRequest - } - every { local.method } returns HttpMethod.Post - } + }, + closeRequest = true, + method = HttpMethod.Post + ) val graphQLServerRequest = parser.parseRequest(serverRequest) assertNotNull(graphQLServerRequest)