diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt index b3cb7078..ef82cd33 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt @@ -285,12 +285,25 @@ class InputTypeGenerator internal constructor( is ObjectValue -> { val inputObjectDefinition = - inputTypeDefinitions.first { + inputTypeDefinitions.firstOrNull { val expectedCanonicalClassName = config.typeMapping[it.name] ?: "${config.packageNameTypes}.${it.name}" expectedCanonicalClassName == type.className.canonicalName() } - if (value.objectFields.isEmpty()) { + if (inputObjectDefinition == null) { + val canonical = type.className.canonicalName() + if (canonical == "java.util.Map" || canonical == "java.util.HashMap") { + if (value.objectFields.isEmpty()) { + CodeBlock.of("\$T.emptyMap()", Collections::class.java) + } else { + null + } + } else if (value.objectFields.isEmpty()) { + CodeBlock.of("new \$T()", type) + } else { + null + } + } else if (value.objectFields.isEmpty()) { CodeBlock.of("new \$T()", type) } else { CodeBlock.of( @@ -379,7 +392,12 @@ class InputTypeGenerator internal constructor( get() = when (this) { is ClassName -> this - is ParameterizedTypeName -> typeArguments().first().className + is ParameterizedTypeName -> + if (rawType().canonicalName() == "java.util.List") { + typeArguments().first().className + } else { + rawType() + } else -> throw UnsupportedOperationException("Unknown type: ${this.javaClass}") } } diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/shared/GenerateKotlinCode.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/shared/GenerateKotlinCode.kt index 7811d366..4d07424b 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/shared/GenerateKotlinCode.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/shared/GenerateKotlinCode.kt @@ -61,12 +61,40 @@ fun generateKotlinCode( is ObjectValue -> { val inputObjectDefinition = - inputTypeDefinitions.first { + inputTypeDefinitions.firstOrNull { val expectedCanonicalClassName = config.typeMapping[it.name] ?: "${config.packageNameTypes}.${it.name}" expectedCanonicalClassName == type.className.canonicalName } + if (inputObjectDefinition == null) { + val canonical = type.className.canonicalName + if (canonical == "kotlin.collections.Map" || canonical == "java.util.Map") { + if (value.objectFields.isEmpty()) { + CodeBlock.of("emptyMap()") + } else { + CodeBlock.of( + "mapOf(%L)", + value.objectFields.joinToString { objectProperty -> + "\"${objectProperty.name}\" to ${ + generateKotlinCode( + objectProperty.value, + type, + inputTypeDefinitions, + config, + typeUtils, + ) + }" + }, + ) + } + } else if (value.objectFields.isEmpty()) { + CodeBlock.of("%T()", type) + } else { + CodeBlock.of("null") + } + } else { + CodeBlock.of( type.className.canonicalName + "(%L)", value.objectFields.joinToString { objectProperty -> @@ -85,6 +113,7 @@ fun generateKotlinCode( }" }, ) + } } is NullValue -> CodeBlock.of("null") @@ -147,6 +176,11 @@ private val TypeName.className: ClassName get() = when (this) { is ClassName -> this - is ParameterizedTypeName -> typeArguments[0].className + is ParameterizedTypeName -> + if (rawType.canonicalName == "kotlin.collections.List" || rawType.canonicalName == "java.util.List") { + typeArguments[0].className + } else { + rawType + } else -> TODO() } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/CodeGenTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/CodeGenTest.kt index a001390c..18b09379 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/CodeGenTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/CodeGenTest.kt @@ -2123,6 +2123,29 @@ class CodeGenTest { assertCompilesJava(codeGenResult) } + @Test + fun `Input field with scalar type and object literal default value does not fail in Java`() { + val schema = + """ + scalar JSON + type Query { + search(filter: FilterInput): String + } + input FilterInput { + metadata: JSON = {} + } + """.trimIndent() + val codeGenResult = + CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = BASE_PACKAGE_NAME, + typeMapping = mapOf("JSON" to "java.util.Map"), + ), + ).generate() + assertCompilesJava(codeGenResult) + } + @ParameterizedTest @ValueSource(booleans = [true, false]) fun generateInputWithDefaultValueForEnum(trackInputFieldSet: Boolean) { diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinCodeGenTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinCodeGenTest.kt index 408c7e73..06fee54d 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinCodeGenTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinCodeGenTest.kt @@ -75,6 +75,30 @@ class KotlinCodeGenTest { assertCompilesKotlin(codeGenResult) } + @Test + fun `Input field with scalar type and object literal default value does not fail in Kotlin`() { + val schema = + """ + scalar JSON + type Query { + search(filter: FilterInput): String + } + input FilterInput { + metadata: JSON = {} + } + """.trimIndent() + val codeGenResult = + CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = BASE_PACKAGE_NAME, + language = Language.KOTLIN, + typeMapping = mapOf("JSON" to "Map"), + ), + ).generate() + assertCompilesKotlin(codeGenResult) + } + @Test fun generateDataClassesOutsideMetaInf() { val schema = @@ -5366,3 +5390,4 @@ It takes a title and such. } """.trimIndent() } + diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/TestUtils.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/TestUtils.kt index e947607c..7c5bc4d3 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/TestUtils.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/TestUtils.kt @@ -113,7 +113,7 @@ fun classpath(): String = .split(System.getProperty("path.separator")) .filter { File(it).exists() && File(it).canRead() - }.joinToString(":") + }.joinToString(File.pathSeparator) fun codegenTestClassLoader( compilation: Compilation, @@ -202,3 +202,4 @@ fun KAnnotationSpec.canonicalName() = (typeName as KClassName).canonicalName const val BASE_PACKAGE_NAME = "com.netflix.graphql.dgs.codegen.tests.generated" const val TYPES_PACKAGE_NAME = "$BASE_PACKAGE_NAME.types" const val DATA_FETCHER_PACKAGE_NAME = "$BASE_PACKAGE_NAME.datafetchers" +