diff --git a/clients/graphql-kotlin-client-jackson/build.gradle.kts b/clients/graphql-kotlin-client-jackson/build.gradle.kts index 96ddeff4a1..985c457fd8 100644 --- a/clients/graphql-kotlin-client-jackson/build.gradle.kts +++ b/clients/graphql-kotlin-client-jackson/build.gradle.kts @@ -14,7 +14,7 @@ tasks { limit { counter = "INSTRUCTION" value = "COVEREDRATIO" - minimum = "0.85".toBigDecimal() + minimum = "0.89".toBigDecimal() } } } 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 b835e1e95f..112d6b8b75 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 @@ -19,6 +19,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.annotation.JsonInclude import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.JavaType import com.fasterxml.jackson.databind.ObjectMapper @@ -34,6 +35,7 @@ class GraphQLClientJacksonSerializer(private val mapper: ObjectMapper = jacksonO init { mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE) + mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY) } override fun serialize(request: GraphQLClientRequest<*>): String = mapper.writeValueAsString(request) diff --git a/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/serializers/OptionalInputSerializer.kt b/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/serializers/OptionalInputSerializer.kt new file mode 100644 index 0000000000..8c410c82fe --- /dev/null +++ b/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/serializers/OptionalInputSerializer.kt @@ -0,0 +1,42 @@ +/* + * 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.serializers + +import com.expediagroup.graphql.client.jackson.types.OptionalInput +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.JsonSerializer +import com.fasterxml.jackson.databind.SerializerProvider + +class OptionalInputSerializer : JsonSerializer>() { + + override fun isEmpty(provider: SerializerProvider, value: OptionalInput<*>?): Boolean { + return value == OptionalInput.Undefined + } + + override fun serialize(value: OptionalInput<*>, gen: JsonGenerator, serializers: SerializerProvider) { + when (value) { + is OptionalInput.Undefined -> return + is OptionalInput.Defined -> { + if (value.value == null) { + serializers.defaultNullValueSerializer.serialize(value.value, gen, serializers) + } else { + serializers.findValueSerializer(value.value::class.java).serialize(value.value, gen, serializers) + } + } + } + } +} diff --git a/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/types/OptionalInput.kt b/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/types/OptionalInput.kt new file mode 100644 index 0000000000..d93219fc8f --- /dev/null +++ b/clients/graphql-kotlin-client-jackson/src/main/kotlin/com/expediagroup/graphql/client/jackson/types/OptionalInput.kt @@ -0,0 +1,41 @@ +/* + * 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.types + +import com.expediagroup.graphql.client.jackson.serializers.OptionalInputSerializer +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonValue +import com.fasterxml.jackson.databind.annotation.JsonSerialize + +@JsonSerialize(using = OptionalInputSerializer::class) +sealed class OptionalInput { + /** + * Represents missing/undefined value. + */ + @JsonSerialize(using = OptionalInputSerializer::class) + object Undefined : OptionalInput() { + override fun toString() = "Undefined" + } + + /** + * Wrapper holding explicitly specified value including NULL. + */ + @JsonSerialize(using = OptionalInputSerializer::class) + class Defined @JsonCreator constructor(@JsonValue val value: U?) : OptionalInput() { + override fun toString(): String = "Defined(value=$value)" + } +} 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 ef3d35b39e..74c4275b40 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 @@ -18,6 +18,7 @@ package com.expediagroup.graphql.client.jackson import com.expediagroup.graphql.client.jackson.data.EnumQuery import com.expediagroup.graphql.client.jackson.data.FirstQuery +import com.expediagroup.graphql.client.jackson.data.InputQuery import com.expediagroup.graphql.client.jackson.data.OtherQuery import com.expediagroup.graphql.client.jackson.data.PolymorphicQuery import com.expediagroup.graphql.client.jackson.data.ScalarQuery @@ -26,6 +27,7 @@ import com.expediagroup.graphql.client.jackson.data.polymorphicquery.SecondInter 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.expediagroup.graphql.client.jackson.types.OptionalInput import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.junit.jupiter.api.Test import java.util.UUID @@ -61,8 +63,7 @@ class GraphQLClientJacksonSerializerTest { | "variables": { "input": 1.0 } |},{ | "query": "OTHER_QUERY", - | "operationName": "OtherQuery", - | "variables": null + | "operationName": "OtherQuery" |}] """.trimMargin() @@ -236,4 +237,29 @@ class GraphQLClientJacksonSerializerTest { val deserialized = serializer.deserialize(rawResponse, EnumQuery(EnumQuery.Variables()).responseType()) assertEquals(TestEnum.THREE, deserialized.data?.enumResult) } + + @Test + fun `verify we can serialize optional inputs`() { + val query = InputQuery( + variables = InputQuery.Variables( + requiredInput = 123, + optionalIntInput = OptionalInput.Defined(123), + optionalStringInput = OptionalInput.Defined(null) + ) + ) + val rawQuery = + """{ + | "query": "INPUT_QUERY", + | "operationName": "InputQuery", + | "variables": { + | "requiredInput": 123, + | "optionalIntInput": 123, + | "optionalStringInput": null + | } + |} + """.trimMargin() + + val serialized = serializer.serialize(query) + assertEquals(testMapper.readTree(rawQuery), testMapper.readTree(serialized)) + } } diff --git a/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/InputQuery.kt b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/InputQuery.kt new file mode 100644 index 0000000000..060cf5891f --- /dev/null +++ b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/data/InputQuery.kt @@ -0,0 +1,42 @@ +/* + * 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 + +import com.expediagroup.graphql.client.jackson.types.OptionalInput +import com.expediagroup.graphql.client.types.GraphQLClientRequest +import kotlin.reflect.KClass + +class InputQuery( + override val variables: Variables +) : GraphQLClientRequest { + override val query: String = "INPUT_QUERY" + + override val operationName: String = "InputQuery" + + override fun responseType(): KClass = Result::class + + data class Variables( + val requiredInput: Int, + val optionalIntInput: OptionalInput = OptionalInput.Undefined, + val optionalStringInput: OptionalInput = OptionalInput.Undefined, + val optionalBooleanInput: OptionalInput = OptionalInput.Undefined + ) + + data class Result( + val stringResult: String + ) +} diff --git a/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/serializers/OptionalInputSerializerTest.kt b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/serializers/OptionalInputSerializerTest.kt new file mode 100644 index 0000000000..d98ca9a88d --- /dev/null +++ b/clients/graphql-kotlin-client-jackson/src/test/kotlin/com/expediagroup/graphql/client/jackson/serializers/OptionalInputSerializerTest.kt @@ -0,0 +1,73 @@ +/* + * 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.serializers + +import com.expediagroup.graphql.client.jackson.types.OptionalInput +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class OptionalInputSerializerTest { + + private val mapper = jacksonObjectMapper() + init { + mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + } + + @Test + fun `verify undefined value is serialized to empty JSON`() { + val undefined = InputWrapper() + assertEquals("{}", mapper.writeValueAsString(undefined)) + } + + @Test + fun `verify null value is serialized correctly`() { + val explicitNull = InputWrapper( + optionalInput = OptionalInput.Defined(null), + optionalInputObject = OptionalInput.Defined(null), + optionalInputList = OptionalInput.Defined(null) + ) + assertEquals("""{"optionalInput":null,"optionalInputObject":null,"optionalInputList":null}""", mapper.writeValueAsString(explicitNull)) + } + + @Test + fun `verify defined values are serialized correctly`() { + val defined = InputWrapper( + optionalInput = OptionalInput.Defined(123), + optionalInputObject = OptionalInput.Defined(BasicInput(OptionalInput.Defined("foo"))), + optionalInputList = OptionalInput.Defined(listOf("a", "b", "c")) + ) + assertEquals("""{"optionalInput":123,"optionalInputObject":{"name":"foo"},"optionalInputList":["a","b","c"]}""", mapper.writeValueAsString(defined)) + } + + @Test + fun `verify inner undefined values are serialized correctly`() { + val nestedUndefined = InputWrapper(optionalInputObject = OptionalInput.Defined(BasicInput(name = OptionalInput.Undefined))) + assertEquals("""{"optionalInputObject":{}}""", mapper.writeValueAsString(nestedUndefined)) + } + + data class InputWrapper( + val optionalInput: OptionalInput = OptionalInput.Undefined, + val optionalInputObject: OptionalInput = OptionalInput.Undefined, + val optionalInputList: OptionalInput> = OptionalInput.Undefined + ) + + data class BasicInput( + val name: OptionalInput = OptionalInput.Undefined + ) +} diff --git a/clients/graphql-kotlin-client-serialization/build.gradle.kts b/clients/graphql-kotlin-client-serialization/build.gradle.kts index e206a099d2..c28ee1a518 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.73".toBigDecimal() + minimum = "0.77".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 3d9b1192cd..060877f76a 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 @@ -41,6 +41,7 @@ class GraphQLClientKotlinxSerializer(private val jsonBuilder: JsonBuilder.() -> apply(jsonBuilder) classDiscriminator = "__typename" coerceInputValues = true + // encodeDefaults = false // need this for optional encodeDefaults = true } diff --git a/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/serializers/AnyKSerializer.kt b/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/serializers/AnyKSerializer.kt index 297fa97c9c..6628986b02 100644 --- a/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/serializers/AnyKSerializer.kt +++ b/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/serializers/AnyKSerializer.kt @@ -16,6 +16,7 @@ package com.expediagroup.graphql.client.serialization.serializers +import com.expediagroup.graphql.client.serialization.types.OptionalInput import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.buildClassSerialDescriptor @@ -47,30 +48,37 @@ object AnyKSerializer : KSerializer { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Any") override fun serialize(encoder: Encoder, value: Any?) { - val jsonEncoder = encoder as JsonEncoder - val jsonElement = serializeAny(value) - jsonEncoder.encodeJsonElement(jsonElement) + serializeAny(value)?.let { + val jsonEncoder = encoder as JsonEncoder + jsonEncoder.encodeJsonElement(it) + } } - private fun serializeAny(value: Any?): JsonElement = when (value) { + private fun serializeAny(value: Any?): JsonElement? = when (value) { null -> JsonNull + is OptionalInput.Undefined -> null + is OptionalInput.Defined<*> -> serializeAny(value.value) is Map<*, *> -> { - val mapContents = value.entries.associate { mapEntry -> - mapEntry.key.toString() to serializeAny(mapEntry.value) - } + val mapContents = value.mapNotNull { (key, value) -> + serializeAny(value)?.let { + key.toString() to it + } + }.toMap() JsonObject(mapContents) } is List<*> -> { - val arrayContents = value.map { listEntry -> serializeAny(listEntry) } + val arrayContents = value.mapNotNull { listEntry -> serializeAny(listEntry) } JsonArray(arrayContents) } is Number -> JsonPrimitive(value) is Boolean -> JsonPrimitive(value) is String -> JsonPrimitive(value) else -> { - val contents = value::class.memberProperties.associate { property -> - property.name to serializeAny(property.getter.call(value)) - } + val contents = value::class.memberProperties.mapNotNull { property -> + serializeAny(property.getter.call(value))?.let { + property.name to it + } + }.toMap() JsonObject(contents) } } diff --git a/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/serializers/OptionalInputSerializer.kt b/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/serializers/OptionalInputSerializer.kt new file mode 100644 index 0000000000..311f12e91d --- /dev/null +++ b/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/serializers/OptionalInputSerializer.kt @@ -0,0 +1,44 @@ +/* + * 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.serializers + +import com.expediagroup.graphql.client.serialization.types.OptionalInput +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +/** + * KSerializer that supports serialization of optional input. Delegates to `AnyKSerializer` serializer logic for serializing the contents. + */ +object OptionalInputSerializer : KSerializer> { + + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("OptionalInput") + + override fun serialize(encoder: Encoder, value: OptionalInput) { + when (value) { + is OptionalInput.Undefined -> { return } + is OptionalInput.Defined<*> -> { + AnyKSerializer.serialize(encoder, value.value) + } + } + } + + // undefined is only supported during client serialization + override fun deserialize(decoder: Decoder): OptionalInput = OptionalInput.Defined(AnyKSerializer.deserialize(decoder)) +} diff --git a/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/types/OptionalInput.kt b/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/types/OptionalInput.kt new file mode 100644 index 0000000000..d0345bfb77 --- /dev/null +++ b/clients/graphql-kotlin-client-serialization/src/main/kotlin/com/expediagroup/graphql/client/serialization/types/OptionalInput.kt @@ -0,0 +1,39 @@ +/* + * 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.types + +import com.expediagroup.graphql.client.serialization.serializers.OptionalInputSerializer +import kotlinx.serialization.Serializable + +@Serializable(with = OptionalInputSerializer::class) +sealed class OptionalInput { + /** + * Represents missing/undefined value. + */ + @Serializable(with = OptionalInputSerializer::class) + object Undefined : OptionalInput() { + override fun toString() = "UNDEFINED" + } + + /** + * Wrapper holding explicitly specified value including NULL. + */ + @Serializable(with = OptionalInputSerializer::class) + data class Defined(val value: U?) : OptionalInput() { + override fun toString(): String = "Defined(value=$value)" + } +} diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinXSerializerTest.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinXSerializerTest.kt index b9724e1666..241dae3745 100644 --- a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinXSerializerTest.kt +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/GraphQLClientKotlinXSerializerTest.kt @@ -18,6 +18,7 @@ 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.InputQuery import com.expediagroup.graphql.client.serialization.data.OtherQuery import com.expediagroup.graphql.client.serialization.data.PolymorphicQuery import com.expediagroup.graphql.client.serialization.data.ScalarQuery @@ -26,10 +27,12 @@ import com.expediagroup.graphql.client.serialization.data.polymorphicquery.Secon 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 com.expediagroup.graphql.client.serialization.types.OptionalInput import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import org.junit.jupiter.api.Test import java.util.UUID +import kotlin.test.Ignore import kotlin.test.assertEquals class GraphQLClientKotlinXSerializerTest { @@ -214,4 +217,30 @@ class GraphQLClientKotlinXSerializerTest { val deserialized = serializer.deserialize(rawResponse, EnumQuery(EnumQuery.Variables()).responseType()) assertEquals(TestEnum.THREE, deserialized.data?.enumResult) } + + @Test + @Ignore("disabled until https://github.com/Kotlin/kotlinx.serialization/issues/1091 is resolved") + fun `verify we can serialize optional inputs`() { + val query = InputQuery( + variables = InputQuery.Variables( + requiredInput = 123, + optionalIntInput = OptionalInput.Defined(123), + optionalStringInput = OptionalInput.Defined(null) + ) + ) + val rawQuery = + """{ + | "query": "INPUT_QUERY", + | "operationName": "InputQuery", + | "variables": { + | "requiredInput": 123, + | "optionalIntInput": 123, + | "optionalStringInput": null + | } + |} + """.trimMargin() + + val serialized = serializer.serialize(query) + assertEquals(rawQuery, serialized) + } } diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/InputQuery.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/InputQuery.kt new file mode 100644 index 0000000000..9c0a2faa10 --- /dev/null +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/data/InputQuery.kt @@ -0,0 +1,46 @@ +/* + * 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 + +import com.expediagroup.graphql.client.serialization.types.OptionalInput +import com.expediagroup.graphql.client.types.GraphQLClientRequest +import kotlinx.serialization.Serializable +import kotlin.reflect.KClass + +@Serializable +class InputQuery( + override val variables: Variables +) : GraphQLClientRequest { + override val query: String = "INPUT_QUERY" + + override val operationName: String = "InputQuery" + + override fun responseType(): KClass = Result::class + + @Serializable + data class Variables( + val requiredInput: Int, + val optionalIntInput: OptionalInput = OptionalInput.Undefined, + val optionalStringInput: OptionalInput = OptionalInput.Undefined, + val optionalBooleanInput: OptionalInput = OptionalInput.Undefined + ) + + @Serializable + data class Result( + val stringResult: String + ) +} diff --git a/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/serializers/OptionalInputSerializerTest.kt b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/serializers/OptionalInputSerializerTest.kt new file mode 100644 index 0000000000..c102f569ab --- /dev/null +++ b/clients/graphql-kotlin-client-serialization/src/test/kotlin/com/expediagroup/graphql/client/serialization/types/serializers/OptionalInputSerializerTest.kt @@ -0,0 +1,75 @@ +/* + * 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.types.serializers + +import com.expediagroup.graphql.client.serialization.types.OptionalInput +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class OptionalInputSerializerTest { + + private val json = Json { + encodeDefaults = false + } + + @Test + fun `verify undefined value is serialized to empty JSON`() { + val undefined = InputWrapper() + assertEquals("{}", json.encodeToString(undefined)) + } + + @Test + fun `verify null value is serialized correctly`() { + val explicitNull = InputWrapper( + optionalInput = OptionalInput.Defined(null), + optionalInputObject = OptionalInput.Defined(null), + optionalInputList = OptionalInput.Defined(null) + ) + assertEquals("""{"optionalInput":null,"optionalInputObject":null,"optionalInputList":null}""", json.encodeToString(explicitNull)) + } + + @Test + fun `verify defined values are serialized correctly`() { + val defined = InputWrapper( + optionalInput = OptionalInput.Defined(123), + optionalInputObject = OptionalInput.Defined(BasicInput(OptionalInput.Defined("foo"))), + optionalInputList = OptionalInput.Defined(listOf("a", "b", "c")) + ) + assertEquals("""{"optionalInput":123,"optionalInputObject":{"name":"foo"},"optionalInputList":["a","b","c"]}""", json.encodeToString(defined)) + } + + @Test + fun `verify inner undefined values are serialized correctly`() { + val nestedUndefined = InputWrapper(optionalInputObject = OptionalInput.Defined(BasicInput(name = OptionalInput.Undefined))) + assertEquals("""{"optionalInputObject":{}}""", json.encodeToString(nestedUndefined)) + } + + @Serializable + data class InputWrapper( + val optionalInput: OptionalInput = OptionalInput.Undefined, + val optionalInputObject: OptionalInput = OptionalInput.Undefined, + val optionalInputList: OptionalInput> = OptionalInput.Undefined + ) + + @Serializable + data class BasicInput( + val name: OptionalInput = OptionalInput.Undefined + ) +} diff --git a/examples/client/maven-client/pom.xml b/examples/client/maven-client/pom.xml index 1f5aee3920..6f6b955860 100755 --- a/examples/client/maven-client/pom.xml +++ b/examples/client/maven-client/pom.xml @@ -81,6 +81,7 @@ JACKSON + true diff --git a/examples/client/maven-client/src/main/kotlin/com/expediagroup/graphql/examples/client/maven/Application.kt b/examples/client/maven-client/src/main/kotlin/com/expediagroup/graphql/examples/client/maven/Application.kt index 4492d57a23..5eac135592 100644 --- a/examples/client/maven-client/src/main/kotlin/com/expediagroup/graphql/examples/client/maven/Application.kt +++ b/examples/client/maven-client/src/main/kotlin/com/expediagroup/graphql/examples/client/maven/Application.kt @@ -16,6 +16,7 @@ package com.expediagroup.graphql.examples.client.maven +import com.expediagroup.graphql.client.jackson.types.OptionalInput import com.expediagroup.graphql.client.spring.GraphQLWebClient import com.expediagroup.graphql.generated.AddObjectMutation import com.expediagroup.graphql.generated.ExampleQuery @@ -49,8 +50,8 @@ fun main() { runBlocking { val results = client.execute( listOf( - HelloWorldQuery(variables = HelloWorldQuery.Variables(name = null)), - HelloWorldQuery(variables = HelloWorldQuery.Variables(name = "Dariusz")) + HelloWorldQuery(variables = HelloWorldQuery.Variables()), + HelloWorldQuery(variables = HelloWorldQuery.Variables(name = OptionalInput.Defined("Dariusz"))) ) ) @@ -75,7 +76,7 @@ fun main() { println("additional examples") runBlocking { - val exampleData = client.execute(ExampleQuery(variables = ExampleQuery.Variables(simpleCriteria = SimpleArgumentInput(max = 1.0f)))) + val exampleData = client.execute(ExampleQuery(variables = ExampleQuery.Variables(simpleCriteria = OptionalInput.Defined(SimpleArgumentInput(max = OptionalInput.Defined(1.0f)))))) println("\tretrieved interface: ${exampleData.data?.interfaceQuery} ") println("\tretrieved union: ${exampleData.data?.unionQuery} ") println("\tretrieved enum: ${exampleData.data?.enumQuery} ") diff --git a/plugins/client/graphql-kotlin-client-generator/build.gradle.kts b/plugins/client/graphql-kotlin-client-generator/build.gradle.kts index 59e4c4350b..e7d857e1b0 100644 --- a/plugins/client/graphql-kotlin-client-generator/build.gradle.kts +++ b/plugins/client/graphql-kotlin-client-generator/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { api("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion") implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("io.ktor:ktor-client-jackson:$ktorVersion") + testImplementation(project(path = ":graphql-kotlin-client-jackson")) testImplementation("com.github.tomakehurst:wiremock-jre8:$wireMockVersion") testImplementation("com.github.tschuchortdev:kotlin-compile-testing:$compileTestingVersion") testImplementation("org.junit.jupiter:junit-jupiter-params:$junitVersion") diff --git a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generateClient.kt b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generateClient.kt index f569f633ac..db844f975b 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generateClient.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generateClient.kt @@ -32,14 +32,16 @@ fun generateClient( customScalarsMap: List = emptyList(), serializer: GraphQLSerializer = GraphQLSerializer.JACKSON, schemaPath: String, - queries: List + queries: List, + useOptionalInputWrapper: Boolean = false ): List { val customScalars = customScalarsMap.associateBy { it.scalar } val config = GraphQLClientGeneratorConfig( packageName = packageName, allowDeprecated = allowDeprecated, customScalarMap = customScalars, - serializer = serializer + serializer = serializer, + useOptionalInputWrapper = useOptionalInputWrapper ) val generator = GraphQLClientGenerator(schemaPath, config) return generator.generate(queries) diff --git a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGenerator.kt b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGenerator.kt index e091ff5b7b..cb9d5b8571 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGenerator.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGenerator.kt @@ -104,7 +104,8 @@ class GraphQLClientGenerator( queryDocument = queryDocument, allowDeprecated = config.allowDeprecated, customScalarMap = config.customScalarMap, - serializer = config.serializer + serializer = config.serializer, + useOptionalInputWrapper = config.useOptionalInputWrapper ) val queryConstName = operationTypeName.toUpperUnderscore() val queryConstProp = PropertySpec.builder(queryConstName, STRING) diff --git a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorConfig.kt b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorConfig.kt index 699fc36708..f6b1dd79b2 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorConfig.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorConfig.kt @@ -27,7 +27,9 @@ data class GraphQLClientGeneratorConfig( /** Custom scalar type to converter mapping. */ val customScalarMap: Map = emptyMap(), /** Type of JSON serializer to be used. */ - val serializer: GraphQLSerializer = GraphQLSerializer.JACKSON + val serializer: GraphQLSerializer = GraphQLSerializer.JACKSON, + /** Explicit opt-in flag to enable support for optional inputs, only available for JACKSON serializer. */ + val useOptionalInputWrapper: Boolean = false ) /** diff --git a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorContext.kt b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorContext.kt index 8380c84112..ff40646e95 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorContext.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorContext.kt @@ -34,7 +34,8 @@ data class GraphQLClientGeneratorContext( val queryDocument: Document, val allowDeprecated: Boolean = false, val customScalarMap: Map = mapOf(), - val serializer: GraphQLSerializer = GraphQLSerializer.JACKSON + val serializer: GraphQLSerializer = GraphQLSerializer.JACKSON, + val useOptionalInputWrapper: Boolean = false ) { val typeSpecs: MutableMap = mutableMapOf() val polymorphicTypes: MutableMap> = mutableMapOf() @@ -49,3 +50,5 @@ data class GraphQLClientGeneratorContext( val classNameCache: MutableMap> = mutableMapOf() val typeToSelectionSetMap: MutableMap> = mutableMapOf() } + +internal fun GraphQLClientGeneratorContext.isOptionalInputSupported() = useOptionalInputWrapper && serializer == GraphQLSerializer.JACKSON diff --git a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateGraphQLInputObjectTypeSpec.kt b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateGraphQLInputObjectTypeSpec.kt index 25341d0a15..2a0c6485e8 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateGraphQLInputObjectTypeSpec.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateGraphQLInputObjectTypeSpec.kt @@ -18,9 +18,13 @@ 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.expediagroup.graphql.plugin.client.generator.isOptionalInputSupported +import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.MemberName import com.squareup.kotlinpoet.ParameterSpec +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec import graphql.language.InputObjectTypeDefinition @@ -45,7 +49,14 @@ internal fun generateGraphQLInputObjectTypeSpec(context: GraphQLClientGeneratorC val kotlinFieldType = generateTypeName(context, fieldDefinition.type) val fieldName = fieldDefinition.name - val inputPropertySpecBuilder = PropertySpec.builder(fieldName, kotlinFieldType) + val inputFieldType = if (kotlinFieldType.isNullable && context.isOptionalInputSupported()) { + ClassName("com.expediagroup.graphql.client.jackson.types", "OptionalInput") + .parameterizedBy(kotlinFieldType.copy(nullable = false)) + } else { + kotlinFieldType + } + + val inputPropertySpecBuilder = PropertySpec.builder(fieldName, inputFieldType) .initializer(fieldName) fieldDefinition.description?.content?.let { kdoc -> inputPropertySpecBuilder.addKdoc("%L", kdoc) @@ -55,8 +66,12 @@ internal fun generateGraphQLInputObjectTypeSpec(context: GraphQLClientGeneratorC inputObjectTypeSpecBuilder.addProperty(inputPropertySpec) val inputParameterSpec = ParameterSpec.builder(inputPropertySpec.name, inputPropertySpec.type) - if (inputPropertySpec.type.isNullable) { - inputParameterSpec.defaultValue("null") + if (kotlinFieldType.isNullable) { + if (context.isOptionalInputSupported()) { + inputParameterSpec.defaultValue("%M", MemberName("com.expediagroup.graphql.client.jackson.types", "OptionalInput.Undefined")) + } else { + inputParameterSpec.defaultValue("null") + } } constructorBuilder.addParameter(inputParameterSpec.build()) } diff --git a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateVariableTypeSpec.kt b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateVariableTypeSpec.kt index 935f5e6ba7..3a48309c39 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateVariableTypeSpec.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateVariableTypeSpec.kt @@ -18,9 +18,13 @@ 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.expediagroup.graphql.plugin.client.generator.isOptionalInputSupported +import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.MemberName import com.squareup.kotlinpoet.ParameterSpec +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec import graphql.language.VariableDefinition @@ -39,14 +43,24 @@ internal fun generateVariableTypeSpec(context: GraphQLClientGeneratorContext, va val constructorSpec = FunSpec.constructorBuilder() variableDefinitions.forEach { variableDef -> val kotlinTypeName = generateTypeName(context, variableDef.type) + val variableTypeName = if (kotlinTypeName.isNullable && context.isOptionalInputSupported()) { + ClassName("com.expediagroup.graphql.client.jackson.types", "OptionalInput") + .parameterizedBy(kotlinTypeName.copy(nullable = false)) + } else { + kotlinTypeName + } - val parameterBuilder = ParameterSpec.builder(variableDef.name, kotlinTypeName) + val parameterBuilder = ParameterSpec.builder(variableDef.name, variableTypeName) if (kotlinTypeName.isNullable) { - parameterBuilder.defaultValue("null") + if (context.isOptionalInputSupported()) { + parameterBuilder.defaultValue("%M", MemberName("com.expediagroup.graphql.client.jackson.types", "OptionalInput.Undefined")) + } else { + parameterBuilder.defaultValue("null") + } } constructorSpec.addParameter(parameterBuilder.build()) variableTypeSpec.addProperty( - PropertySpec.builder(variableDef.name, kotlinTypeName) + PropertySpec.builder(variableDef.name, variableTypeName) .initializer(variableDef.name) .build() ) diff --git a/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/variables/JacksonInputQuery.kt b/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/variables/JacksonInputQuery.kt index db293267d9..8d66f02cf8 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/variables/JacksonInputQuery.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/variables/JacksonInputQuery.kt @@ -1,5 +1,7 @@ package com.expediagroup.graphql.generated +import com.expediagroup.graphql.client.jackson.types.OptionalInput +import com.expediagroup.graphql.client.jackson.types.OptionalInput.Undefined import com.expediagroup.graphql.client.types.GraphQLClientRequest import com.expediagroup.graphql.generated.inputs.SimpleArgumentInput import kotlin.Boolean @@ -20,7 +22,7 @@ public class JacksonInputQuery( JacksonInputQuery.Result::class public data class Variables( - public val input: SimpleArgumentInput? = null + public val input: OptionalInput = OptionalInput.Undefined ) public data class Result( diff --git a/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/variables/inputs/SimpleArgumentInput.kt b/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/variables/inputs/SimpleArgumentInput.kt index 092181ceb9..9205ed67e3 100644 --- a/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/variables/inputs/SimpleArgumentInput.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/test/data/jackson/variables/inputs/SimpleArgumentInput.kt @@ -1,5 +1,7 @@ package com.expediagroup.graphql.generated.inputs +import com.expediagroup.graphql.client.jackson.types.OptionalInput +import com.expediagroup.graphql.client.jackson.types.OptionalInput.Undefined import kotlin.Float import kotlin.String @@ -10,13 +12,13 @@ public data class SimpleArgumentInput( /** * Maximum value for test criteria */ - public val max: Float? = null, + public val max: OptionalInput = OptionalInput.Undefined, /** * Minimum value for test criteria */ - public val min: Float? = null, + public val min: OptionalInput = OptionalInput.Undefined, /** * New value to be set */ - public val newName: String? = null + public val newName: OptionalInput = OptionalInput.Undefined ) diff --git a/plugins/client/graphql-kotlin-client-generator/src/test/kotlin/com/expediagroup/graphql/plugin/client/generator/GenerateJacksonClientIT.kt b/plugins/client/graphql-kotlin-client-generator/src/test/kotlin/com/expediagroup/graphql/plugin/client/generator/GenerateJacksonClientIT.kt index 3cf6b0b97c..56ae20854b 100755 --- a/plugins/client/graphql-kotlin-client-generator/src/test/kotlin/com/expediagroup/graphql/plugin/client/generator/GenerateJacksonClientIT.kt +++ b/plugins/client/graphql-kotlin-client-generator/src/test/kotlin/com/expediagroup/graphql/plugin/client/generator/GenerateJacksonClientIT.kt @@ -28,7 +28,8 @@ class GenerateJacksonClientIT { fun `verify generation of client code using jackson`(testDirectory: File) { val config = defaultConfig.copy( serializer = GraphQLSerializer.JACKSON, - customScalarMap = mapOf("UUID" to GraphQLScalar("UUID", "java.util.UUID", "com.expediagroup.graphql.plugin.client.generator.UUIDScalarConverter")) + customScalarMap = mapOf("UUID" to GraphQLScalar("UUID", "java.util.UUID", "com.expediagroup.graphql.plugin.client.generator.UUIDScalarConverter")), + useOptionalInputWrapper = true ) verifyClientGeneration(config, testDirectory) } diff --git a/plugins/graphql-kotlin-gradle-plugin/README.md b/plugins/graphql-kotlin-gradle-plugin/README.md index d80e57e97d..0e7ea9316d 100755 --- a/plugins/graphql-kotlin-gradle-plugin/README.md +++ b/plugins/graphql-kotlin-gradle-plugin/README.md @@ -53,6 +53,9 @@ graphql { // Read timeout in milliseconds read = 15_000 } + // Opt-in flag to wrap nullable arguments in OptionalInput that distinguish between null and undefined value. + // Only supported for JACKSON serializer + useOptionalInputWrapper = false } schema { // List of supported packages that can contain GraphQL schema type definitions @@ -103,6 +106,7 @@ resulting generated code will be automatically added to the project main source | `serializer` | GraphQLSerializer | | JSON serializer that will be used to generate the data classes.
**Default value is:** `GraphQLSerializer.JACKSON`. | | `schemaFile` | File | `schemaFileName` or `schemaFile` has to be provided | GraphQL schema file that will be used to generate client code. | | `schemaFileName` | String | `schemaFileName` or `schemaFile` has to be provided | Path to GraphQL schema file that will be used to generate client code.
**Command line property is**: `schemaFileName`. | +| `useOptionalInputWrapper` | Boolean | | Boolean opt-in flag to wrap nullable arguments in `OptionalInput` that distinguish between `null` and undefined/omitted value. Only supported for JACKSON serializer.
**Default value is:** `false`.
**Command line property is**: `useOptionalInputWrapper` | ### graphqlGenerateSDL @@ -150,6 +154,7 @@ test source set. | `serializer` | GraphQLSerializer | | JSON serializer that will be used to generate the data classes.
**Default value is:** `GraphQLSerializer.JACKSON`. | | `schemaFile` | File | `schemaFileName` or `schemaFile` has to be provided | GraphQL schema file that will be used to generate client code. | | `schemaFileName` | String | `schemaFileName` or `schemaFile` has to be provided | Path to GraphQL schema file that will be used to generate client code.
**Command line property is**: `schemaFileName`. | +| `useOptionalInputWrapper` | Boolean | | Boolean opt-in flag to wrap nullable arguments in `OptionalInput` that distinguish between `null` and undefined/omitted value. Only supported for JACKSON serializer.
**Default value is:** `false`.
**Command line property is**: `useOptionalInputWrapper` | ### graphqlIntrospectSchema diff --git a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGradlePlugin.kt b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGradlePlugin.kt index f69a599b79..399f2b23f8 100644 --- a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGradlePlugin.kt +++ b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGradlePlugin.kt @@ -96,6 +96,7 @@ class GraphQLGradlePlugin : Plugin { } generateClientTask.queryFiles.setFrom(extension.clientExtension.queryFiles) generateClientTask.serializer.convention(extension.clientExtension.serializer) + generateClientTask.useOptionalInputWrapper.convention(extension.clientExtension.useOptionalInputWrapper) when { extension.clientExtension.endpoint != null -> { diff --git a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLPluginExtension.kt b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLPluginExtension.kt index 247ea22a68..c5e7ec9ee7 100644 --- a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLPluginExtension.kt +++ b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLPluginExtension.kt @@ -73,6 +73,8 @@ open class GraphQLPluginClientExtension { var queryFileDirectory: String? = null /** JSON serializer that will be used to generate the data classes. */ var serializer: GraphQLSerializer = GraphQLSerializer.JACKSON + /** Opt-in flag to wrap nullable arguments in OptionalInput that supports both null and undefined. Only supported for JACKSON serializer. */ + var useOptionalInputWrapper: Boolean = false /** Connect and read timeout configuration for executing introspection query/download schema */ internal val timeoutConfig: TimeoutConfiguration = TimeoutConfiguration() diff --git a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/actions/GenerateClientAction.kt b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/actions/GenerateClientAction.kt index 38a0857460..5dcc99f12b 100644 --- a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/actions/GenerateClientAction.kt +++ b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/actions/GenerateClientAction.kt @@ -38,8 +38,9 @@ abstract class GenerateClientAction : WorkAction { val schemaPath = parameters.schemaPath.get() val queryFiles = parameters.queryFiles.get() val targetDirectory = parameters.targetDirectory.get() + val useOptionalInputWrapper = parameters.useOptionalInputWrapper.get() - generateClient(targetPackage, allowDeprecated, customScalarMap, serializer, schemaPath, queryFiles).forEach { + generateClient(targetPackage, allowDeprecated, customScalarMap, serializer, schemaPath, queryFiles, useOptionalInputWrapper).forEach { it.writeTo(targetDirectory) } } diff --git a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/parameters/GenerateClientParameters.kt b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/parameters/GenerateClientParameters.kt index 4f377eee11..acf2db08b1 100644 --- a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/parameters/GenerateClientParameters.kt +++ b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/parameters/GenerateClientParameters.kt @@ -42,4 +42,9 @@ interface GenerateClientParameters : WorkParameters { val queryFiles: ListProperty /** Directory where to save the generated source files. */ val targetDirectory: Property + /** + * Explicit opt-in flag to wrap nullable arguments in OptionalInput that supports both null and undefined values. + * Only supported for JACKSON serializer. + */ + val useOptionalInputWrapper: Property } diff --git a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/tasks/AbstractGenerateClientTask.kt b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/tasks/AbstractGenerateClientTask.kt index 08d0056d34..9a52880289 100755 --- a/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/tasks/AbstractGenerateClientTask.kt +++ b/plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/tasks/AbstractGenerateClientTask.kt @@ -124,6 +124,11 @@ abstract class AbstractGenerateClientTask : DefaultTask() { @Option(option = "serializer", description = "JSON serializer that will be used to generate the data classes.") val serializer: Property = project.objects.property(GraphQLSerializer::class.java) + @Input + @Optional + @Option(option = "useOptionalInputWrapper", description = "Opt-in flag to wrap nullable arguments in OptionalInput that supports both null and undefined.") + val useOptionalInputWrapper: Property = project.objects.property(Boolean::class.java) + @OutputDirectory val outputDirectory: DirectoryProperty = project.objects.directoryProperty() @@ -139,6 +144,7 @@ abstract class AbstractGenerateClientTask : DefaultTask() { serializer.convention(GraphQLSerializer.JACKSON) queryFileDirectory.convention("${project.projectDir}/src/main/resources") outputDirectory.convention(project.layout.buildDirectory.dir("generated/source/graphql/main")) + useOptionalInputWrapper.convention(false) } @Suppress("EXPERIMENTAL_API_USAGE") @@ -185,6 +191,7 @@ abstract class AbstractGenerateClientTask : DefaultTask() { parameters.schemaPath.set(graphQLSchemaPath) parameters.queryFiles.set(targetQueryFiles) parameters.targetDirectory.set(targetDirectory) + parameters.useOptionalInputWrapper.set(useOptionalInputWrapper) } workQueue.await() logger.debug("successfully generated GraphQL HTTP client") diff --git a/plugins/graphql-kotlin-gradle-plugin/src/test/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGradlePluginIT.kt b/plugins/graphql-kotlin-gradle-plugin/src/test/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGradlePluginIT.kt index 562dc2514d..0f274c2ec5 100755 --- a/plugins/graphql-kotlin-gradle-plugin/src/test/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGradlePluginIT.kt +++ b/plugins/graphql-kotlin-gradle-plugin/src/test/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGradlePluginIT.kt @@ -182,6 +182,7 @@ class GraphQLGradlePluginIT : GraphQLGradlePluginAbstractIT() { | sdlEndpoint = "${wireMockServer.baseUrl()}/sdl" | packageName = "com.example.generated" | serializer = GraphQLSerializer.$serializer + | useOptionalInputWrapper = true | } |} """.trimMargin() @@ -219,7 +220,8 @@ class GraphQLGradlePluginIT : GraphQLGradlePluginAbstractIT() { mapOf( "ktorClient" to useKtorClient, "defaultHeader" to mapOf("name" to defaultHeaderName, "value" to defaultHeaderValue), - "requestHeader" to mapOf("name" to customHeaderName, "value" to customHeaderValue) + "requestHeader" to mapOf("name" to customHeaderName, "value" to customHeaderValue), + "optionalInputWrapper" to (serializer == GraphQLSerializer.JACKSON) ) ) ) diff --git a/plugins/graphql-kotlin-gradle-plugin/src/test/resources/templates/Application.mustache b/plugins/graphql-kotlin-gradle-plugin/src/test/resources/templates/Application.mustache index 47e5ac3b9c..465eb9e832 100644 --- a/plugins/graphql-kotlin-gradle-plugin/src/test/resources/templates/Application.mustache +++ b/plugins/graphql-kotlin-gradle-plugin/src/test/resources/templates/Application.mustache @@ -7,6 +7,7 @@ import com.example.generated.inputs.SimpleArgumentInput import com.example.generated.junitquery.BasicObject2 import com.example.generated.junitquery.ScalarWrapper import com.example.generated.junitquery.SecondInterfaceImplementation +{{#optionalInputWrapper}}import com.expediagroup.graphql.client.jackson.types.OptionalInput{{/optionalInputWrapper}} {{#ktorClient}} import com.expediagroup.graphql.client.ktor.GraphQLKtorClient {{#defaultHeader}} @@ -48,7 +49,13 @@ fun main() { ) {{/ktorClient}} {{/defaultHeader}} - val variables = JUnitQuery.Variables(SimpleArgumentInput(min = null, max = null, newName = "blah")) + + {{#optionalInputWrapper}} + val variables = JUnitQuery.Variables(SimpleArgumentInput(newName = OptionalInput.Defined("blah"))) + {{/optionalInputWrapper}} + {{^optionalInputWrapper}} + val variables = JUnitQuery.Variables(SimpleArgumentInput(newName = "blah")) + {{/optionalInputWrapper}} val query = JUnitQuery(variables) runBlocking { diff --git a/plugins/graphql-kotlin-gradle-plugin/src/test/resources/templates/JUnit.mustache b/plugins/graphql-kotlin-gradle-plugin/src/test/resources/templates/JUnit.mustache index d8ea9128d9..7d1dccafcf 100644 --- a/plugins/graphql-kotlin-gradle-plugin/src/test/resources/templates/JUnit.mustache +++ b/plugins/graphql-kotlin-gradle-plugin/src/test/resources/templates/JUnit.mustache @@ -31,7 +31,7 @@ class GraphQLMavenPluginTest { val graphQLEndpoint = System.getProperty("graphQLEndpoint") val client = GraphQLWebClient(graphQLEndpoint) - val variables = JUnitQuery.Variables(simpleCriteria = SimpleArgumentInput(min = null, max = null, newName = "blah")) + val variables = JUnitQuery.Variables(simpleCriteria = SimpleArgumentInput(newName = "blah")) val query = JUnitQuery(variables) assertDoesNotThrow { diff --git a/plugins/graphql-kotlin-maven-plugin/README.md b/plugins/graphql-kotlin-maven-plugin/README.md index 0995de0922..afd9af8898 100755 --- a/plugins/graphql-kotlin-maven-plugin/README.md +++ b/plugins/graphql-kotlin-maven-plugin/README.md @@ -46,6 +46,9 @@ Plugin should be configured as part of your `pom.xml` build file. 1000 30000 + + false ${project.basedir}/src/main/resources/queries/MyQuery.graphql @@ -111,6 +114,7 @@ Generate GraphQL client code based on the provided GraphQL schema and target que | `queryFiles` | List | | List of query files to be processed. Instead of a list of files to be processed you can also specify `queryFileDirectory` directory containing all the files. If this property is specified it will take precedence over the corresponding directory property. | | `serializer` | GraphQLSerializer | | JSON serializer that will be used to generate the data classes.
**Default value is:** `GraphQLSerializer.JACKSON`. | | `schemaFile` | String | | GraphQL schema file that will be used to generate client code.
**Default value is**: `${project.build.directory}/schema.graphql`
**User property is**: `graphql.schemaFile`. | +| `useOptionalInputWrapper` | Boolean | | Boolean opt-in flag to wrap nullable arguments in `OptionalInput` that distinguish between `null` and undefined/omitted value. Only supported for JACKSON serializer.
**Default value is:** `false`.
**User property is**: `graphql.useOptionalInputWrapper` | **Parameter Details** @@ -131,6 +135,31 @@ Generate GraphQL client code based on the provided GraphQL schema and target que ``` +### generate-sdl + +Generates GraphQL schema in SDL format from your source code using reflections. Utilizes `graphql-kotlin-schema-generator` +to generate the schema from classes implementing `graphql-kotlin-server` marker `Query`, `Mutation` and `Subscription` interfaces. +In order to limit the amount of packages to scan, this mojo requires users to provide a list of `packages` that can contain +GraphQL types. + +This MOJO utilizes [ServiceLoader](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html) +mechanism to dynamically load available `SchemaGeneratorHooksProvider` service providers from the classpath. Service provider +can be provided as part of your project, included in one of your project dependencies or through explicitly provided artifact. +See [Schema Generator Hooks Provider](./hooks-provider.mdx) for additional details on how to create custom hooks service +provider. Configuration below shows how to configure GraphQL Kotlin plugin with explicitly provided artifact. + +**Attributes** + +* *Default Lifecycle Phase*: `process-classes` +* *Requires Maven Project* + +**Parameters** + +| Property | Type | Required | Description | +| -------- | ---- | -------- | ----------- | +| `packages` | `List` | yes | List of supported packages that can be scanned to generate SDL. | +| `schemaFile` | File | | Target GraphQL schema file to be generated.
**Default value is:** `${project.buildDir}/schema.graphql` | + ### generate-test-client Generate GraphQL test client code based on the provided GraphQL schema and target queries. @@ -153,6 +182,7 @@ Generate GraphQL test client code based on the provided GraphQL schema and targe | `queryFiles` | List | | List of query files to be processed. Instead of a list of files to be processed you can also specify `queryFileDirectory` directory containing all the files. If this property is specified it will take precedence over the corresponding directory property. | | `serializer` | GraphQLSerializer | | JSON serializer that will be used to generate the data classes.
**Default value is:** `GraphQLSerializer.JACKSON`. | | `schemaFile` | String | | GraphQL schema file that will be used to generate client code.
**Default value is**: `${project.build.directory}/schema.graphql`
**User property is**: `graphql.schemaFile`. | +| `useOptionalInputWrapper` | Boolean | | Boolean opt-in flag to wrap nullable arguments in `OptionalInput` that distinguish between `null` and undefined/omitted value. Only supported for JACKSON serializer.
**Default value is:** `false`.
**User property is**: `graphql.useOptionalInputWrapper` | **Parameter Details** diff --git a/plugins/graphql-kotlin-maven-plugin/src/integration/basic-setup/src/test/kotlin/com/expediagroup/graphql/plugin/maven/GraphQLMavenPluginTest.kt b/plugins/graphql-kotlin-maven-plugin/src/integration/basic-setup/src/test/kotlin/com/expediagroup/graphql/plugin/maven/GraphQLMavenPluginTest.kt index cb0d35abe0..0ae1063723 100755 --- a/plugins/graphql-kotlin-maven-plugin/src/integration/basic-setup/src/test/kotlin/com/expediagroup/graphql/plugin/maven/GraphQLMavenPluginTest.kt +++ b/plugins/graphql-kotlin-maven-plugin/src/integration/basic-setup/src/test/kotlin/com/expediagroup/graphql/plugin/maven/GraphQLMavenPluginTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 Expedia, Inc + * 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. @@ -53,7 +53,7 @@ class GraphQLMavenPluginTest { val graphQLEndpoint = System.getProperty("graphQLEndpoint") val client = GraphQLWebClient(graphQLEndpoint) - val variables = ExampleQuery.Variables(simpleCriteria = SimpleArgumentInput(newName = "whatever", min = null, max = null)) + val variables = ExampleQuery.Variables(simpleCriteria = SimpleArgumentInput(newName = "whatever")) val query = ExampleQuery(variables) assertDoesNotThrow { runBlocking { diff --git a/plugins/graphql-kotlin-maven-plugin/src/integration/complete-setup-jackson/pom.xml b/plugins/graphql-kotlin-maven-plugin/src/integration/complete-setup-jackson/pom.xml index 1657659295..ecdd2a6720 100755 --- a/plugins/graphql-kotlin-maven-plugin/src/integration/complete-setup-jackson/pom.xml +++ b/plugins/graphql-kotlin-maven-plugin/src/integration/complete-setup-jackson/pom.xml @@ -100,6 +100,7 @@ ${project.basedir}/src/main/resources/DeprecatedQuery.graphql JACKSON + true diff --git a/plugins/graphql-kotlin-maven-plugin/src/integration/complete-setup-jackson/src/test/kotlin/com/expediagroup/graphql/plugin/maven/GraphQLMavenPluginTest.kt b/plugins/graphql-kotlin-maven-plugin/src/integration/complete-setup-jackson/src/test/kotlin/com/expediagroup/graphql/plugin/maven/GraphQLMavenPluginTest.kt index 95e890aae5..c8e95a6942 100755 --- a/plugins/graphql-kotlin-maven-plugin/src/integration/complete-setup-jackson/src/test/kotlin/com/expediagroup/graphql/plugin/maven/GraphQLMavenPluginTest.kt +++ b/plugins/graphql-kotlin-maven-plugin/src/integration/complete-setup-jackson/src/test/kotlin/com/expediagroup/graphql/plugin/maven/GraphQLMavenPluginTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 Expedia, Inc + * 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. @@ -23,6 +23,7 @@ import com.expediagroup.graphql.plugin.generated.examplequery.SecondInterfaceImp import com.expediagroup.graphql.plugin.generated.examplequery.ScalarWrapper import com.expediagroup.graphql.plugin.generated.inputs.SimpleArgumentInput import com.expediagroup.graphql.plugin.generated.scalars.UUID +import com.expediagroup.graphql.client.jackson.types.OptionalInput import com.expediagroup.graphql.client.spring.GraphQLWebClient import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions.assertEquals @@ -55,7 +56,7 @@ class GraphQLMavenPluginTest { val graphQLEndpoint = System.getProperty("graphQLEndpoint") val client = GraphQLWebClient(graphQLEndpoint) - val variables = ExampleQuery.Variables(simpleCriteria = SimpleArgumentInput(newName = "whatever", min = null, max = null)) + val variables = ExampleQuery.Variables(simpleCriteria = SimpleArgumentInput(newName = OptionalInput.Defined("whatever"))) val query = ExampleQuery(variables) assertDoesNotThrow { runBlocking { diff --git a/plugins/graphql-kotlin-maven-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/maven/GenerateClientAbstractMojo.kt b/plugins/graphql-kotlin-maven-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/maven/GenerateClientAbstractMojo.kt index f2420e532d..c5be2987ba 100644 --- a/plugins/graphql-kotlin-maven-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/maven/GenerateClientAbstractMojo.kt +++ b/plugins/graphql-kotlin-maven-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/maven/GenerateClientAbstractMojo.kt @@ -92,6 +92,13 @@ abstract class GenerateClientAbstractMojo : AbstractMojo() { @Parameter(name = "serializer") private var serializer: GraphQLSerializer = GraphQLSerializer.JACKSON + /** + * Explicit opt-in flag to wrap nullable arguments in OptionalInput that supports both null and undefined values. + * Only supported for JACKSON serializer. + */ + @Parameter(defaultValue = "\${graphql.useOptionalInputWrapper}", name = "useOptionalInputWrapper") + private var useOptionalInputWrapper: Boolean = false + /** * Target directory where to store generated files. */ @@ -110,7 +117,7 @@ abstract class GenerateClientAbstractMojo : AbstractMojo() { logConfiguration(schemaPath, targetQueryFiles) val customGraphQLScalars = customScalars.map { GraphQLScalar(it.scalar, it.type, it.converter) } - generateClient(packageName, allowDeprecatedFields, customGraphQLScalars, serializer, schemaPath, targetQueryFiles).forEach { + generateClient(packageName, allowDeprecatedFields, customGraphQLScalars, serializer, schemaPath, targetQueryFiles, useOptionalInputWrapper).forEach { it.writeTo(outputDirectory) } diff --git a/website/docs/plugins/gradle-plugin-tasks.mdx b/website/docs/plugins/gradle-plugin-tasks.mdx index c7953ecadd..0614639cb2 100644 --- a/website/docs/plugins/gradle-plugin-tasks.mdx +++ b/website/docs/plugins/gradle-plugin-tasks.mdx @@ -139,6 +139,9 @@ graphql { // Read timeout in milliseconds read = 15_000 } + // Opt-in flag to wrap nullable arguments in OptionalInput that distinguish between null and undefined value. + // Only supported for JACKSON serializer + useOptionalInputWrapper = false } schema { // List of supported packages that can contain GraphQL schema type definitions @@ -181,6 +184,9 @@ graphql { t.connect = 5000 t.read = 15000 } + // Opt-in flag to wrap nullable arguments in OptionalInput that distinguish between null and undefined value. + // Only supported for JACKSON serializer + useOptionalInputWrapper = false } schema { packages = ["com.example"] @@ -249,6 +255,7 @@ resulting generated code will be automatically added to the project main source | `serializer` | GraphQLSerializer | | JSON serializer that will be used to generate the data classes.
**Default value is:** `GraphQLSerializer.JACKSON`. | | `schemaFile` | File | `schemaFileName` or `schemaFile` has to be provided | GraphQL schema file that will be used to generate client code. | | `schemaFileName` | String | `schemaFileName` or `schemaFile` has to be provided | Path to GraphQL schema file that will be used to generate client code.
**Command line property is**: `schemaFileName`. | | +| `useOptionalInputWrapper` | Boolean | | Boolean opt-in flag to wrap nullable arguments in `OptionalInput` that distinguish between `null` and undefined/omitted value. Only supported for JACKSON serializer.
**Default value is:** `false`.
**Command line property is**: `useOptionalInputWrapper` | By default, this task will generate Jackson compatible data models. See [client serialization documentation](../client/client-serialization.mdx) for details on how to update this process to use `kotlinx.serialization` instead. @@ -298,6 +305,7 @@ test source set. | `serializer` | GraphQLSerializer | | JSON serializer that will be used to generate the data classes.
**Default value is:** `GraphQLSerializer.JACKSON`. | | `schemaFile` | File | `schemaFileName` or `schemaFile` has to be provided | GraphQL schema file that will be used to generate client code. | | `schemaFileName` | String | `schemaFileName` or `schemaFile` has to be provided | Path to GraphQL schema file that will be used to generate client code.
**Command line property is**: `schemaFileName`. | +| `useOptionalInputWrapper` | Boolean | | Boolean opt-in flag to wrap nullable arguments in `OptionalInput` that distinguish between `null` and undefined/omitted value. Only supported for JACKSON serializer.
**Default value is:** `false`.
**Command line property is**: `useOptionalInputWrapper` | By default, this task will generate Jackson compatible data models. See [client serialization documentation](../client/client-serialization.mdx) for details on how to update this process to use `kotlinx.serialization` instead. diff --git a/website/docs/plugins/maven-plugin-goals.md b/website/docs/plugins/maven-plugin-goals.md index 8c622cfcdc..c7ab50752c 100644 --- a/website/docs/plugins/maven-plugin-goals.md +++ b/website/docs/plugins/maven-plugin-goals.md @@ -72,6 +72,7 @@ Generate GraphQL client code based on the provided GraphQL schema and target que | `queryFiles` | `List` | | List of query files to be processed. Instead of a list of files to be processed you can also specify `queryFileDirectory` directory containing all the files. If this property is specified it will take precedence over the corresponding directory property. | | `serializer` | GraphQLSerializer | | JSON serializer that will be used to generate the data classes.
**Default value is:** `GraphQLSerializer.JACKSON`. | | `schemaFile` | String | | GraphQL schema file that will be used to generate client code.
**Default value is**: `${project.build.directory}/schema.graphql`
**User property is**: `graphql.schemaFile`. | +| `useOptionalInputWrapper` | Boolean | | Boolean opt-in flag to wrap nullable arguments in `OptionalInput` that distinguish between `null` and undefined/omitted value. Only supported for JACKSON serializer.
**Default value is:** `false`.
**User property is**: `graphql.useOptionalInputWrapper` | **Parameter Details** @@ -139,6 +140,7 @@ Generate GraphQL test client code based on the provided GraphQL schema and targe | `queryFiles` | `List` | | List of query files to be processed. Instead of a list of files to be processed you can also specify `queryFileDirectory` directory containing all the files. If this property is specified it will take precedence over the corresponding directory property. | | `serializer` | GraphQLSerializer | | JSON serializer that will be used to generate the data classes.
**Default value is:** `GraphQLSerializer.JACKSON`. | | `schemaFile` | String | | GraphQL schema file that will be used to generate client code.
**Default value is**: `${project.build.directory}/schema.graphql`
**User property is**: `graphql.schemaFile`. | +| `useOptionalInputWrapper` | Boolean | | Boolean opt-in flag to wrap nullable arguments in `OptionalInput` that distinguish between `null` and undefined/omitted value. Only supported for JACKSON serializer.
**Default value is:** `false`.
**User property is**: `graphql.useOptionalInputWrapper` | **Parameter Details** diff --git a/website/docs/plugins/maven-plugin-usage.md b/website/docs/plugins/maven-plugin-usage.md index 6f209fdfdf..42eba00ae1 100644 --- a/website/docs/plugins/maven-plugin-usage.md +++ b/website/docs/plugins/maven-plugin-usage.md @@ -313,7 +313,7 @@ Mojos will be executed in the order they are defined in your `pom.xml` build fil ## Complete Configuration Example Following is a configuration example that downloads schema SDL from a target GraphQL server that is then used to generate -the GraphQL client data modles using `kotlinx.serialization` that are based on the provided query. +the GraphQL client data models using `kotlinx.serialization` that are based on the provided query. ```xml