diff --git a/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/GraphQLClientJacksonSerializer.kt b/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/GraphQLClientJacksonSerializer.kt index 7cc8cfc48f..b835e1e95f 100644 --- a/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/GraphQLClientJacksonSerializer.kt +++ b/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/GraphQLClientJacksonSerializer.kt @@ -18,6 +18,7 @@ package com.expediagroup.graphql.client.jackson import com.expediagroup.graphql.client.jackson.types.JacksonGraphQLResponse import com.expediagroup.graphql.client.serializer.GraphQLClientSerializer +import com.expediagroup.graphql.client.types.GraphQLClientRequest import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.JavaType import com.fasterxml.jackson.databind.ObjectMapper @@ -35,7 +36,9 @@ class GraphQLClientJacksonSerializer(private val mapper: ObjectMapper = jacksonO mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE) } - override fun serialize(request: Any): String = mapper.writeValueAsString(request) + override fun serialize(request: GraphQLClientRequest<*>): String = mapper.writeValueAsString(request) + + override fun serialize(requests: List>): String = mapper.writeValueAsString(requests) override fun deserialize(rawResponse: String, responseType: KClass): JacksonGraphQLResponse = mapper.readValue(rawResponse, parameterizedType(responseType)) diff --git a/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/GraphQLClientJacksonSerializerTest.kt b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/GraphQLClientJacksonSerializerTest.kt index ce1d032394..ef3d35b39e 100644 --- a/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/GraphQLClientJacksonSerializerTest.kt +++ b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/GraphQLClientJacksonSerializerTest.kt @@ -21,9 +21,11 @@ import com.expediagroup.graphql.client.jackson.data.FirstQuery import com.expediagroup.graphql.client.jackson.data.OtherQuery import com.expediagroup.graphql.client.jackson.data.PolymorphicQuery import com.expediagroup.graphql.client.jackson.data.ScalarQuery +import com.expediagroup.graphql.client.jackson.data.enums.TestEnum import com.expediagroup.graphql.client.jackson.data.polymorphicquery.SecondInterfaceImplementation import com.expediagroup.graphql.client.jackson.types.JacksonGraphQLError import com.expediagroup.graphql.client.jackson.types.JacksonGraphQLResponse +import com.expediagroup.graphql.client.jackson.types.JacksonGraphQLSourceLocation import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.junit.jupiter.api.Test import java.util.UUID @@ -31,6 +33,9 @@ import kotlin.test.assertEquals class GraphQLClientJacksonSerializerTest { + private val testMapper = jacksonObjectMapper() + private val serializer = GraphQLClientJacksonSerializer(testMapper) + @Test fun `verify we can serialize GraphQLClientRequest`() { val testQuery = FirstQuery(FirstQuery.Variables(input = 1.0f)) @@ -42,10 +47,8 @@ class GraphQLClientJacksonSerializerTest { |} """.trimMargin() - val mapper = jacksonObjectMapper() - val serializer = GraphQLClientJacksonSerializer(mapper) val result = serializer.serialize(testQuery) - assertEquals(mapper.readTree(expected), mapper.readTree(result)) + assertEquals(testMapper.readTree(expected), testMapper.readTree(result)) } @Test @@ -63,10 +66,8 @@ class GraphQLClientJacksonSerializerTest { |}] """.trimMargin() - val mapper = jacksonObjectMapper() - val serializer = GraphQLClientJacksonSerializer(mapper) val result = serializer.serialize(queries) - assertEquals(mapper.readTree(expected), mapper.readTree(result)) + assertEquals(testMapper.readTree(expected), testMapper.readTree(result)) } @Test @@ -74,18 +75,47 @@ class GraphQLClientJacksonSerializerTest { val testQuery = FirstQuery(variables = FirstQuery.Variables()) val expected = JacksonGraphQLResponse( data = FirstQuery.Result("hello world"), - errors = listOf(JacksonGraphQLError(message = "test error message")), - extensions = mapOf("extVal" to 123, "extList" to listOf("ext1", "ext2"), "extMap" to mapOf("1" to 1, "2" to 2)) + errors = listOf( + JacksonGraphQLError( + message = "test error message", + locations = listOf(JacksonGraphQLSourceLocation(1, 1)), + path = listOf("firstQuery", 0), + extensions = mapOf("errorExt" to 123) + ) + ), + extensions = mapOf( + "extBool" to true, + "extDouble" to 1.5, + "extInt" to 123, + "extList" to listOf("ext1", "ext2"), + "extMap" to mapOf("1" to 1, "2" to 2.0), + "extNull" to null, + "extString" to "extra" + ) ) val rawResponse = """{ | "data": { "stringResult" : "hello world" }, - | "errors": [{ "message" : "test error message" }], - | "extensions" : { "extVal" : 123, "extList" : ["ext1", "ext2"], "extMap" : { "1" : 1, "2" : 2} } + | "errors": [{ + | "message": "test error message", + | "locations": [{ "line": 1, "column": 1 }], + | "path": [ "firstQuery", 0 ], + | "extensions": { + | "errorExt": 123 + | } + | }], + | "extensions" : { + | "extBool": true, + | "extDouble": 1.5, + | "extInt": 123, + | "extList": ["ext1", "ext2"], + | "extMap": { "1" : 1, "2" : 2.0 }, + | "extNull": null, + | "extString": "extra" + | } |} """.trimMargin() - val serializer = GraphQLClientJacksonSerializer() val result = serializer.deserialize(rawResponse, testQuery.responseType()) assertEquals(expected, result) } @@ -114,7 +144,6 @@ class GraphQLClientJacksonSerializerTest { |}] """.trimMargin() - val serializer = GraphQLClientJacksonSerializer() val result = serializer.deserialize(rawResponses, listOf(testQuery.responseType(), otherQuery.responseType())) assertEquals(expected, result) } @@ -132,11 +161,27 @@ class GraphQLClientJacksonSerializerTest { | } |} """.trimMargin() - val serializer = GraphQLClientJacksonSerializer() + val result = serializer.deserialize(polymorphicResponse, PolymorphicQuery().responseType()) assertEquals(SecondInterfaceImplementation(123, 1.2f), result.data?.polymorphicResult) } + @Test + fun `verify we can serialize custom scalars`() { + val randomUUID = UUID.randomUUID() + val scalarQuery = ScalarQuery(variables = ScalarQuery.Variables(alias = "1234", custom = com.expediagroup.graphql.client.jackson.data.scalars.UUID(randomUUID))) + val rawQuery = + """{ + | "query": "SCALAR_QUERY", + | "operationName": "ScalarQuery", + | "variables": { "alias": "1234", "custom": "$randomUUID" } + |} + """.trimMargin() + + val serialized = serializer.serialize(scalarQuery) + assertEquals(testMapper.readTree(rawQuery), testMapper.readTree(serialized)) + } + @Test fun `verify we can deserialize custom scalars`() { val expectedUUID = UUID.randomUUID() @@ -148,8 +193,8 @@ class GraphQLClientJacksonSerializerTest { | } |} """.trimMargin() - val serializer = GraphQLClientJacksonSerializer() - val result = serializer.deserialize(scalarResponse, ScalarQuery().responseType()) + + val result = serializer.deserialize(scalarResponse, ScalarQuery(ScalarQuery.Variables()).responseType()) assertEquals("1234", result.data?.scalarAlias) assertEquals(expectedUUID, result.data?.customScalar?.value) } @@ -162,8 +207,33 @@ class GraphQLClientJacksonSerializerTest { |} """.trimMargin() - val serializer = GraphQLClientJacksonSerializer() - val result = serializer.deserialize(unknownResponse, EnumQuery().responseType()) - assertEquals(EnumQuery.TestEnum.__UNKNOWN, result.data?.enumResult) + val result = serializer.deserialize(unknownResponse, EnumQuery(EnumQuery.Variables()).responseType()) + assertEquals(TestEnum.__UNKNOWN, result.data?.enumResult) + } + + @Test + fun `verify we can serialize enums with custom names`() { + val query = EnumQuery(variables = EnumQuery.Variables(enum = TestEnum.THREE)) + val rawQuery = + """{ + | "query": "ENUM_QUERY", + | "operationName": "EnumQuery", + | "variables": { "enum": "three" } + |} + """.trimMargin() + + val serialized = serializer.serialize(query) + assertEquals(testMapper.readTree(rawQuery), testMapper.readTree(serialized)) + } + + @Test + fun `verify we can deserialize enums with custom names`() { + val rawResponse = + """{ + | "data": { "enumResult": "three" } + |} + """.trimMargin() + val deserialized = serializer.deserialize(rawResponse, EnumQuery(EnumQuery.Variables()).responseType()) + assertEquals(TestEnum.THREE, deserialized.data?.enumResult) } } diff --git a/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/EnumQuery.kt b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/EnumQuery.kt index beccea59c6..cb106b05e6 100644 --- a/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/EnumQuery.kt +++ b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/EnumQuery.kt @@ -16,23 +16,22 @@ package com.expediagroup.graphql.client.jackson.data +import com.expediagroup.graphql.client.jackson.data.enums.TestEnum import com.expediagroup.graphql.client.types.GraphQLClientRequest -import com.fasterxml.jackson.annotation.JsonEnumDefaultValue import kotlin.reflect.KClass -class EnumQuery : GraphQLClientRequest { +class EnumQuery( + override val variables: Variables +) : GraphQLClientRequest { override val query: String = "ENUM_QUERY" override val operationName: String = "EnumQuery" override fun responseType(): KClass = Result::class - enum class TestEnum { - ONE, - TWO, - @JsonEnumDefaultValue - __UNKNOWN - } + data class Variables( + val enum: TestEnum? = null + ) data class Result( val enumResult: TestEnum = TestEnum.__UNKNOWN diff --git a/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/ScalarQuery.kt b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/ScalarQuery.kt index b0c4fe99e0..4e77851943 100644 --- a/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/ScalarQuery.kt +++ b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/ScalarQuery.kt @@ -23,13 +23,20 @@ import kotlin.reflect.KClass // typealiases would be in separate file typealias ID = String -class ScalarQuery : GraphQLClientRequest { +class ScalarQuery( + override val variables: Variables +) : GraphQLClientRequest { override val query: String = "SCALAR_QUERY" override val operationName: String = "ScalarQuery" override fun responseType(): KClass = Result::class + data class Variables( + val alias: ID? = null, + val custom: UUID? = null + ) + data class Result( val scalarAlias: ID, val customScalar: UUID diff --git a/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/enums/TestEnum.kt b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/enums/TestEnum.kt new file mode 100644 index 0000000000..5be09c4b4d --- /dev/null +++ b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/enums/TestEnum.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2021 Expedia, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expediagroup.graphql.client.jackson.data.enums + +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue +import com.fasterxml.jackson.annotation.JsonProperty + +enum class TestEnum { + ONE, + TWO, + @JsonProperty("three") + THREE, + @JsonEnumDefaultValue + __UNKNOWN +} diff --git a/clients/graphql-kotlin-client-serialization/build.gradle.kts b/clients/graphql-kotlin-client-serialization/build.gradle.kts index 400ee3647f..e206a099d2 100644 --- a/clients/graphql-kotlin-client-serialization/build.gradle.kts +++ b/clients/graphql-kotlin-client-serialization/build.gradle.kts @@ -20,7 +20,7 @@ tasks { limit { counter = "INSTRUCTION" value = "COVEREDRATIO" - minimum = "0.75".toBigDecimal() + minimum = "0.73".toBigDecimal() } } } diff --git a/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinxSerializer.kt b/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinxSerializer.kt index b0b0e60248..3d9b1192cd 100644 --- a/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinxSerializer.kt +++ b/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinxSerializer.kt @@ -16,9 +16,9 @@ package com.expediagroup.graphql.client.serialization -import com.expediagroup.graphql.client.serialization.serializers.AnyKSerializer import com.expediagroup.graphql.client.serialization.types.KotlinxGraphQLResponse import com.expediagroup.graphql.client.serializer.GraphQLClientSerializer +import com.expediagroup.graphql.client.types.GraphQLClientRequest import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray @@ -33,7 +33,8 @@ import kotlin.reflect.full.createType */ class GraphQLClientKotlinxSerializer(private val jsonBuilder: JsonBuilder.() -> Unit = {}) : GraphQLClientSerializer { - private val serializerCache = ConcurrentHashMap, KSerializer>>() + private val responseSerializerCache = ConcurrentHashMap, KSerializer>>() + private val requestSerializerCache = ConcurrentHashMap, KSerializer>() private val json = Json { ignoreUnknownKeys = true @@ -43,7 +44,14 @@ class GraphQLClientKotlinxSerializer(private val jsonBuilder: JsonBuilder.() -> encodeDefaults = true } - override fun serialize(request: Any): String = json.encodeToString(AnyKSerializer, request) + override fun serialize(request: GraphQLClientRequest<*>): String = json.encodeToString(requestSerializer(request), request) + + override fun serialize(requests: List>): String { + val serializedRequests = requests.map { request -> + json.encodeToString(requestSerializer(request), request) + } + return "[${serializedRequests.joinToString(",")}]" + } override fun deserialize(rawResponse: String, responseType: KClass): KotlinxGraphQLResponse = json.decodeFromString( responseSerializer(responseType) as KSerializer>, @@ -64,9 +72,14 @@ class GraphQLClientKotlinxSerializer(private val jsonBuilder: JsonBuilder.() -> } } + private fun requestSerializer(request: GraphQLClientRequest<*>): KSerializer = + requestSerializerCache.computeIfAbsent(request::class) { + json.serializersModule.serializer(request::class.createType()) + } + private fun responseSerializer(resultType: KClass): KSerializer> = - serializerCache.computeIfAbsent(resultType) { - val resultTypeSerializer = serializer(resultType.createType()) + responseSerializerCache.computeIfAbsent(resultType) { + val resultTypeSerializer = json.serializersModule.serializer(resultType.createType()) KotlinxGraphQLResponse.serializer( resultTypeSerializer ) diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/GraphQLClientKotlinXSerializerTest.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinXSerializerTest.kt similarity index 54% rename from clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/GraphQLClientKotlinXSerializerTest.kt rename to clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinXSerializerTest.kt index 823adbc3a7..b9724e1666 100644 --- a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/GraphQLClientKotlinXSerializerTest.kt +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinXSerializerTest.kt @@ -14,45 +14,54 @@ * limitations under the License. */ -package com.expediagroup.graphql.client.serialization.types - -import com.expediagroup.graphql.client.serialization.GraphQLClientKotlinxSerializer -import com.expediagroup.graphql.client.serialization.types.data.EnumQuery -import com.expediagroup.graphql.client.serialization.types.data.FirstQuery -import com.expediagroup.graphql.client.serialization.types.data.OtherQuery -import com.expediagroup.graphql.client.serialization.types.data.PolymorphicQuery -import com.expediagroup.graphql.client.serialization.types.data.ScalarQuery -import com.expediagroup.graphql.client.serialization.types.data.polymorphicquery.SecondInterfaceImplementation +package com.expediagroup.graphql.client.serialization + +import com.expediagroup.graphql.client.serialization.data.EnumQuery +import com.expediagroup.graphql.client.serialization.data.FirstQuery +import com.expediagroup.graphql.client.serialization.data.OtherQuery +import com.expediagroup.graphql.client.serialization.data.PolymorphicQuery +import com.expediagroup.graphql.client.serialization.data.ScalarQuery +import com.expediagroup.graphql.client.serialization.data.enums.TestEnum +import com.expediagroup.graphql.client.serialization.data.polymorphicquery.SecondInterfaceImplementation +import com.expediagroup.graphql.client.serialization.types.KotlinxGraphQLError +import com.expediagroup.graphql.client.serialization.types.KotlinxGraphQLResponse +import com.expediagroup.graphql.client.serialization.types.KotlinxGraphQLSourceLocation +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import org.junit.jupiter.api.Test import java.util.UUID import kotlin.test.assertEquals class GraphQLClientKotlinXSerializerTest { + private val serializer = GraphQLClientKotlinxSerializer() + private val json = Json + @Test fun `verify we can serialize GraphQLClientRequest`() { val testQuery = FirstQuery(FirstQuery.Variables(input = 1.0f)) - val expected = - """{"operationName":"FirstQuery","query":"FIRST_QUERY","variables":{"input":1.0}}""" - - val serializer = GraphQLClientKotlinxSerializer() val result = serializer.serialize(testQuery) - assertEquals(expected, result) + val deserialized: FirstQuery = json.decodeFromString(result) + assertEquals(testQuery.variables, deserialized.variables) } @Test fun `verify we can serialize batch GraphQLClientRequest`() { val queries = listOf(FirstQuery(FirstQuery.Variables(input = 1.0f)), OtherQuery()) - val expected = - """[{"operationName":"FirstQuery","query":"FIRST_QUERY","variables":{"input":1.0}},{"operationName":"OtherQuery","query":"OTHER_QUERY","variables":null}]""" - val serializer = GraphQLClientKotlinxSerializer() val result = serializer.serialize(queries) - assertEquals(expected, result) + val serializedQueries = result.substring(1, result.length - 1).split(Regex("(?<=\\}),(?=\\{)")) + assertEquals(2, serializedQueries.size) + + val first: FirstQuery = json.decodeFromString(serializedQueries[0]) + assertEquals(queries[0].variables, first.variables) + + val second: OtherQuery = json.decodeFromString(serializedQueries[1]) + assertEquals(queries[1].variables, second.variables) } @Test - fun `verify we can deserialize JacksonGraphQLResponse`() { + fun `verify we can deserialize KotlinxGraphQLResponse`() { val testQuery = FirstQuery(variables = FirstQuery.Variables()) val expected = KotlinxGraphQLResponse( data = FirstQuery.Result("hello world"), @@ -60,65 +69,49 @@ class GraphQLClientKotlinXSerializerTest { KotlinxGraphQLError( message = "test error message", locations = listOf(KotlinxGraphQLSourceLocation(1, 1)), - path = listOf("stringResult", 1, "leaf"), + path = listOf("firstQuery", 0), extensions = mapOf("errorExt" to 123) ) ), extensions = mapOf( - "booleanVal" to false, - "doubleVal" to 1.234, - "intVal" to 1234, - "listVal" to listOf("val1", 2), - "mapVal" to mapOf("first" to 42, "second" to "whatever", "third" to null), - "nullVal" to null + "extBool" to true, + "extDouble" to 1.5, + "extInt" to 123, + "extList" to listOf("ext1", "ext2"), + "extMap" to mapOf("1" to 1, "2" to 2.0), + "extNull" to null, + "extString" to "extra" ) ) - val rawResponse = """{ - | "data": { - | "stringResult": "hello world" - | }, + | "data": { "stringResult" : "hello world" }, | "errors": [{ | "message": "test error message", - | "locations": [{ - | "line": 1, - | "column": 1 - | }], - | "path": [ - | "stringResult", - | 1, - | "leaf" - | ], + | "locations": [{ "line": 1, "column": 1 }], + | "path": [ "firstQuery", 0 ], | "extensions": { | "errorExt": 123 | } | }], - | "extensions": { - | "booleanVal": false, - | "doubleVal": 1.234, - | "intVal": 1234, - | "listVal": [ - | "val1", - | 2 - | ], - | "mapVal": { - | "first": 42, - | "second": "whatever", - | "third": null - | }, - | "nullVal": null + | "extensions" : { + | "extBool": true, + | "extDouble": 1.5, + | "extInt": 123, + | "extList": ["ext1", "ext2"], + | "extMap": { "1" : 1, "2" : 2.0 }, + | "extNull": null, + | "extString": "extra" | } |} """.trimMargin() - val serializer = GraphQLClientKotlinxSerializer() val result = serializer.deserialize(rawResponse, testQuery.responseType()) assertEquals(expected, result) } @Test - fun `verify we can deserialize batch JacksonGraphQLResponse`() { + fun `verify we can deserialize batch KotlinxGraphQLResponse`() { val testQuery = FirstQuery(variables = FirstQuery.Variables()) val otherQuery = OtherQuery() val expected = listOf( @@ -141,7 +134,6 @@ class GraphQLClientKotlinXSerializerTest { |}] """.trimMargin() - val serializer = GraphQLClientKotlinxSerializer() val result = serializer.deserialize(rawResponses, listOf(testQuery.responseType(), otherQuery.responseType())) assertEquals(expected, result) } @@ -159,11 +151,21 @@ class GraphQLClientKotlinXSerializerTest { | } |} """.trimMargin() - val serializer = GraphQLClientKotlinxSerializer() + val result = serializer.deserialize(polymorphicResponse, PolymorphicQuery().responseType()) assertEquals(SecondInterfaceImplementation(123, 1.2f), result.data?.polymorphicResult) } + @Test + fun `verify we can serialize custom scalars`() { + val randomUUID = UUID.randomUUID() + val scalarQuery = ScalarQuery(variables = ScalarQuery.Variables(alias = "1234", custom = com.expediagroup.graphql.client.serialization.data.scalars.UUID(randomUUID))) + + val serialized = serializer.serialize(scalarQuery) + val deserialized: ScalarQuery = json.decodeFromString(serialized) + assertEquals(scalarQuery.variables, deserialized.variables) + } + @Test fun `verify we can deserialize custom scalars`() { val expectedUUID = UUID.randomUUID() @@ -175,8 +177,8 @@ class GraphQLClientKotlinXSerializerTest { | } |} """.trimMargin() - val serializer = GraphQLClientKotlinxSerializer() - val result = serializer.deserialize(scalarResponse, ScalarQuery().responseType()) + + val result = serializer.deserialize(scalarResponse, ScalarQuery(ScalarQuery.Variables()).responseType()) assertEquals("1234", result.data?.scalarAlias) assertEquals(expectedUUID, result.data?.customScalar?.value) } @@ -189,8 +191,27 @@ class GraphQLClientKotlinXSerializerTest { |} """.trimMargin() - val serializer = GraphQLClientKotlinxSerializer() - val result = serializer.deserialize(unknownResponse, EnumQuery().responseType()) - assertEquals(EnumQuery.TestEnum.__UNKNOWN, result.data?.enumResult) + val result = serializer.deserialize(unknownResponse, EnumQuery(EnumQuery.Variables()).responseType()) + assertEquals(TestEnum.__UNKNOWN, result.data?.enumResult) + } + + @Test + fun `verify we can serialize enums with custom names`() { + val query = EnumQuery(variables = EnumQuery.Variables(enum = TestEnum.THREE)) + + val serialized = serializer.serialize(query) + val deserialized: EnumQuery = json.decodeFromString(serialized) + assertEquals(query.variables, deserialized.variables) + } + + @Test + fun `verify we can deserialize enums with custom names`() { + val rawResponse = + """{ + | "data": { "enumResult": "three" } + |} + """.trimMargin() + val deserialized = serializer.deserialize(rawResponse, EnumQuery(EnumQuery.Variables()).responseType()) + assertEquals(TestEnum.THREE, deserialized.data?.enumResult) } } diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/EnumQuery.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/EnumQuery.kt similarity index 76% rename from clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/EnumQuery.kt rename to clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/EnumQuery.kt index 3c54eedf75..efac8ce38e 100644 --- a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/EnumQuery.kt +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/EnumQuery.kt @@ -14,25 +14,27 @@ * limitations under the License. */ -package com.expediagroup.graphql.client.serialization.types.data +package com.expediagroup.graphql.client.serialization.data +import com.expediagroup.graphql.client.serialization.data.enums.TestEnum import com.expediagroup.graphql.client.types.GraphQLClientRequest import kotlinx.serialization.Serializable import kotlin.reflect.KClass @Serializable -class EnumQuery : GraphQLClientRequest { +class EnumQuery( + override val variables: Variables +) : GraphQLClientRequest { override val query: String = "ENUM_QUERY" override val operationName: String = "EnumQuery" override fun responseType(): KClass = Result::class - enum class TestEnum { - ONE, - TWO, - __UNKNOWN - } + @Serializable + data class Variables( + val enum: TestEnum? = null + ) @Serializable data class Result( diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/FirstQuery.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/FirstQuery.kt similarity index 94% rename from clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/FirstQuery.kt rename to clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/FirstQuery.kt index 88aa91719b..facec7baf4 100644 --- a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/FirstQuery.kt +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/FirstQuery.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.expediagroup.graphql.client.serialization.types.data +package com.expediagroup.graphql.client.serialization.data import com.expediagroup.graphql.client.types.GraphQLClientRequest import kotlinx.serialization.Serializable diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/OtherQuery.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/OtherQuery.kt similarity index 94% rename from clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/OtherQuery.kt rename to clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/OtherQuery.kt index 093de141a9..7807e6b2ab 100644 --- a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/OtherQuery.kt +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/OtherQuery.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.expediagroup.graphql.client.serialization.types.data +package com.expediagroup.graphql.client.serialization.data import com.expediagroup.graphql.client.types.GraphQLClientRequest import kotlinx.serialization.Serializable diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/PolymorphicQuery.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/PolymorphicQuery.kt similarity index 87% rename from clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/PolymorphicQuery.kt rename to clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/PolymorphicQuery.kt index f6d1205b15..6e868db96a 100644 --- a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/PolymorphicQuery.kt +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/PolymorphicQuery.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.expediagroup.graphql.client.serialization.types.data +package com.expediagroup.graphql.client.serialization.data -import com.expediagroup.graphql.client.serialization.types.data.polymorphicquery.BasicInterface +import com.expediagroup.graphql.client.serialization.data.polymorphicquery.BasicInterface import com.expediagroup.graphql.client.types.GraphQLClientRequest import kotlinx.serialization.Serializable import kotlin.reflect.KClass diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/ScalarQuery.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/ScalarQuery.kt similarity index 74% rename from clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/ScalarQuery.kt rename to clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/ScalarQuery.kt index babcdc881a..9f321a9163 100644 --- a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/ScalarQuery.kt +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/ScalarQuery.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.expediagroup.graphql.client.serialization.types.data +package com.expediagroup.graphql.client.serialization.data -import com.expediagroup.graphql.client.serialization.types.data.scalars.UUID +import com.expediagroup.graphql.client.serialization.data.scalars.UUID import com.expediagroup.graphql.client.types.GraphQLClientRequest import kotlinx.serialization.Serializable import kotlin.reflect.KClass @@ -24,13 +24,22 @@ import kotlin.reflect.KClass // typealiases would be in separate file typealias ID = String -class ScalarQuery : GraphQLClientRequest { +@Serializable +class ScalarQuery( + override val variables: Variables +) : GraphQLClientRequest { override val query: String = "SCALAR_QUERY" override val operationName: String = "ScalarQuery" override fun responseType(): KClass = Result::class + @Serializable + data class Variables( + val alias: ID? = null, + val custom: UUID? = null + ) + @Serializable data class Result( val scalarAlias: ID, diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/enums/TestEnum.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/enums/TestEnum.kt new file mode 100644 index 0000000000..098db0a75e --- /dev/null +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/enums/TestEnum.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2021 Expedia, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expediagroup.graphql.client.serialization.data.enums + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +enum class TestEnum { + ONE, + TWO, + @SerialName("three") + THREE, + __UNKNOWN +} diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/polymorphicquery/BasicInterface.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/polymorphicquery/BasicInterface.kt similarity index 94% rename from clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/polymorphicquery/BasicInterface.kt rename to clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/polymorphicquery/BasicInterface.kt index 810648591d..bd5d26c385 100644 --- a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/polymorphicquery/BasicInterface.kt +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/polymorphicquery/BasicInterface.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.expediagroup.graphql.client.serialization.types.data.polymorphicquery +package com.expediagroup.graphql.client.serialization.data.polymorphicquery import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/scalars/UUID.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/scalars/UUID.kt similarity index 96% rename from clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/scalars/UUID.kt rename to clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/scalars/UUID.kt index e146ad5530..f6e89afe77 100644 --- a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/data/scalars/UUID.kt +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/scalars/UUID.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.expediagroup.graphql.client.serialization.types.data.scalars +package com.expediagroup.graphql.client.serialization.data.scalars import com.expediagroup.graphql.client.converter.ScalarConverter import kotlinx.serialization.KSerializer diff --git a/clients/graphql-kotlin-client/src/main/kotlin/com/expediagroup/graphql/client/serializer/GraphQLClientSerializer.kt b/clients/graphql-kotlin-client/src/main/kotlin/com/expediagroup/graphql/client/serializer/GraphQLClientSerializer.kt index dd369489d9..75ceb80ece 100644 --- a/clients/graphql-kotlin-client/src/main/kotlin/com/expediagroup/graphql/client/serializer/GraphQLClientSerializer.kt +++ b/clients/graphql-kotlin-client/src/main/kotlin/com/expediagroup/graphql/client/serializer/GraphQLClientSerializer.kt @@ -16,6 +16,7 @@ package com.expediagroup.graphql.client.serializer +import com.expediagroup.graphql.client.types.GraphQLClientRequest import com.expediagroup.graphql.client.types.GraphQLClientResponse import java.util.ServiceLoader import kotlin.reflect.KClass @@ -34,7 +35,12 @@ interface GraphQLClientSerializer { /** * Serialize GraphQLClientRequest (or batch request) to a raw String representation. */ - fun serialize(request: Any): String + fun serialize(request: GraphQLClientRequest<*>): String + + /** + * Serialize GraphQLClientRequest (or batch request) to a raw String representation. + */ + fun serialize(requests: List>): String /** * Deserialize raw response String to a target GraphQLClientResponse. diff --git a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateGraphQLEnumTypeSpec.kt b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateGraphQLEnumTypeSpec.kt index 00107d3253..54c3cb29b9 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateGraphQLEnumTypeSpec.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateGraphQLEnumTypeSpec.kt @@ -19,11 +19,15 @@ package com.expediagroup.graphql.plugin.client.generator.types import com.expediagroup.graphql.plugin.client.generator.GraphQLClientGeneratorContext import com.expediagroup.graphql.plugin.client.generator.GraphQLSerializer import com.fasterxml.jackson.annotation.JsonEnumDefaultValue +import com.fasterxml.jackson.annotation.JsonProperty import com.squareup.kotlinpoet.AnnotationSpec import com.squareup.kotlinpoet.TypeSpec import graphql.Directives.DeprecatedDirective import graphql.language.EnumTypeDefinition import graphql.language.StringValue +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.util.Locale internal const val UNKNOWN_VALUE = "__UNKNOWN_VALUE" @@ -50,13 +54,31 @@ internal fun generateGraphQLEnumTypeSpec(context: GraphQLClientGeneratorContext, .build() ) } - enumTypeSpecBuilder.addEnumConstant(enumValueDefinition.name, enumValueTypeSpecBuilder.build()) + val enumName = enumValueDefinition.name.toUpperCase(Locale.US) + if (enumName != enumValueDefinition.name) { + if (context.serializer == GraphQLSerializer.JACKSON) { + enumValueTypeSpecBuilder.addAnnotation( + AnnotationSpec.builder(JsonProperty::class.java) + .addMember("%S", enumValueDefinition.name) + .build() + ) + } else { + enumValueTypeSpecBuilder.addAnnotation( + AnnotationSpec.builder(SerialName::class.java) + .addMember("%S", enumValueDefinition.name) + .build() + ) + } + } + enumTypeSpecBuilder.addEnumConstant(enumName, enumValueTypeSpecBuilder.build()) } val unknownTypeSpec = TypeSpec.anonymousClassBuilder() .addKdoc("%L", "This is a default enum value that will be used when attempting to deserialize unknown value.") if (context.serializer == GraphQLSerializer.JACKSON) { unknownTypeSpec.addAnnotation(JsonEnumDefaultValue::class) + } else { + enumTypeSpecBuilder.addAnnotation(Serializable::class) } enumTypeSpecBuilder.addEnumConstant(UNKNOWN_VALUE, unknownTypeSpec.build()) diff --git a/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/enums/enums/CustomEnum.kt b/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/enums/enums/CustomEnum.kt index 6af80775e8..0e70c6ee80 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/enums/enums/CustomEnum.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/enums/enums/CustomEnum.kt @@ -1,6 +1,7 @@ package com.expediagroup.graphql.generated.enums import com.fasterxml.jackson.annotation.JsonEnumDefaultValue +import com.fasterxml.jackson.annotation.JsonProperty import kotlin.Deprecated /** @@ -23,6 +24,12 @@ enum class CustomEnum { */ TWO, + /** + * Lowercase enum value + */ + @JsonProperty("four") + FOUR, + /** * This is a default enum value that will be used when attempting to deserialize unknown value. */ diff --git a/plugins/client/graphql-kotlin-client-generator/src/test/data/kotlinx/enums/enums/CustomEnum.kt b/plugins/client/graphql-kotlin-client-generator/src/test/data/kotlinx/enums/enums/CustomEnum.kt index ae8a65aa9a..48b6a4cc0b 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/test/data/kotlinx/enums/enums/CustomEnum.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/test/data/kotlinx/enums/enums/CustomEnum.kt @@ -1,10 +1,13 @@ package com.expediagroup.graphql.generated.enums import kotlin.Deprecated +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable /** * Custom enum description */ +@Serializable enum class CustomEnum { /** * First enum value @@ -22,6 +25,12 @@ enum class CustomEnum { */ TWO, + /** + * Lowercase enum value + */ + @SerialName("four") + FOUR, + /** * This is a default enum value that will be used when attempting to deserialize unknown value. */ diff --git a/plugins/client/graphql-kotlin-client-generator/src/test/data/kotlinx/multiple_queries/enums/CustomEnum.kt b/plugins/client/graphql-kotlin-client-generator/src/test/data/kotlinx/multiple_queries/enums/CustomEnum.kt index ae8a65aa9a..48b6a4cc0b 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/test/data/kotlinx/multiple_queries/enums/CustomEnum.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/test/data/kotlinx/multiple_queries/enums/CustomEnum.kt @@ -1,10 +1,13 @@ package com.expediagroup.graphql.generated.enums import kotlin.Deprecated +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable /** * Custom enum description */ +@Serializable enum class CustomEnum { /** * First enum value @@ -22,6 +25,12 @@ enum class CustomEnum { */ TWO, + /** + * Lowercase enum value + */ + @SerialName("four") + FOUR, + /** * This is a default enum value that will be used when attempting to deserialize unknown value. */ diff --git a/plugins/client/graphql-kotlin-client-generator/src/test/resources/testSchema.graphql b/plugins/client/graphql-kotlin-client-generator/src/test/resources/testSchema.graphql index 0ca455adc1..c6f41999b6 100755 --- a/plugins/client/graphql-kotlin-client-generator/src/test/resources/testSchema.graphql +++ b/plugins/client/graphql-kotlin-client-generator/src/test/resources/testSchema.graphql @@ -144,6 +144,8 @@ enum CustomEnum { THREE @deprecated(reason : "only goes up to two") "Second enum value" TWO + "Lowercase enum value" + four } "Custom scalar representing UUID" scalar UUID