From 9ae58cbf3628716ba9a9da633a34c509de9fbaf8 Mon Sep 17 00:00:00 2001 From: Paul Bakker Date: Tue, 16 Feb 2021 20:31:33 -0800 Subject: [PATCH 1/6] Added support for default array values --- .../generators/java/DataTypeGenerator.kt | 1 + .../kotlin/KotlinDataTypeGenerator.kt | 10 + .../graphql/dgs/codegen/CodeGenTest.kt | 27 +++ .../graphql/dgs/codegen/KotlinCodeGenTest.kt | 190 ++++++++++++++++++ 4 files changed, 228 insertions(+) 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 43dd87a92..32f3c69f4 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 @@ -61,6 +61,7 @@ class InputTypeGenerator(config: CodeGenConfig) : BaseDataTypeGenerator(config.p is StringValue -> CodeBlock.of("\$S", defVal.value) is FloatValue -> CodeBlock.of("\$L", defVal.value) is EnumValue -> CodeBlock.of("\$T.\$N", typeUtils.findReturnType(it.type), defVal.name) + is ArrayValue -> CodeBlock.of("java.util.Collections.emptyList()") else -> CodeBlock.of("\$L", defVal) } } diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt index 5276aa6b7..aab7089ee 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt @@ -63,6 +63,16 @@ class KotlinInputTypeGenerator(private val config: CodeGenConfig, private val do is StringValue -> CodeBlock.of("%S", defVal.value) is FloatValue -> CodeBlock.of("%L", defVal.value) is EnumValue -> CodeBlock.of("%M", MemberName(type as ClassName, defVal.name)) + is ArrayValue -> if(defVal.values.isEmpty()) CodeBlock.of("emptyList()") else CodeBlock.of("listOf(%L)", defVal.values.map { v -> + when(v) { + is BooleanValue -> CodeBlock.of("%L", v.isValue) + is IntValue -> CodeBlock.of("%L", v.value) + is StringValue -> CodeBlock.of("%S", v.value) + is FloatValue -> CodeBlock.of("%Lf", v.value) + is EnumValue -> CodeBlock.of("%M", MemberName((type as ParameterizedTypeName).typeArguments[0] as ClassName, v.name)) + else -> "" + } + }.joinToString()) else -> CodeBlock.of("%L", defVal) } } 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 e7c216437..bdbe02ecc 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 @@ -599,6 +599,33 @@ class CodeGenTest { assertCompiles(enumTypes + dataTypes) } + @Test + fun generateInputWithDefaultValueForArray() { + val schema = """ + input SomeType { + names: [String] = [] + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName)).generate() as CodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val type = data.typeSpec + assertThat(type.name).isEqualTo("SomeType") + + val fields = type.fieldSpecs + assertThat(fields).hasSize(1) + + val colorField = fields[0] + assertThat(colorField.name).isEqualTo("names") + assertThat(colorField.initializer.toString()).isEqualTo("java.util.Collections.emptyList()") + + assertCompiles(dataTypes) + } + @Test fun generateToInputStringMethodForInputTypes() { 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 ceceeec6d..a43dcc192 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 @@ -550,6 +550,196 @@ class KotlinCodeGenTest { assertThat(colorParam.defaultValue.toString()).isEqualTo("$typesPackageName.Color.red") } + @Test + fun generateInputWithEmptyDefaultValueForArray() { + val schema = """ + input SomeType { + names: [String!]! = [] + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, language = Language.KOTLIN)).generate() as KotlinCodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("SomeType") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("names") + assertThat(colorParam.type.toString()).isEqualTo("kotlin.collections.List") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("emptyList()") + } + + @Test + fun generateInputWithDefaultValueForStringArray() { + val schema = """ + input SomeType { + names: [String!]! = ["DGS"] + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, language = Language.KOTLIN)).generate() as KotlinCodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("SomeType") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("names") + assertThat(colorParam.type.toString()).isEqualTo("kotlin.collections.List") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("""listOf("DGS")""") + } + + @Test + fun generateInputWithDefaultValueForNullableStringArray() { + val schema = """ + input SomeType { + names: [String]! = ["DGS"] + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, language = Language.KOTLIN)).generate() as KotlinCodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("SomeType") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("names") + assertThat(colorParam.type.toString()).isEqualTo("kotlin.collections.List") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("""listOf("DGS")""") + } + + @Test + fun generateInputWithDefaultValueForIntArray() { + val schema = """ + input SomeType { + names: [Int!]! = [1,2,3] + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, language = Language.KOTLIN)).generate() as KotlinCodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("SomeType") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("names") + assertThat(colorParam.type.toString()).isEqualTo("kotlin.collections.List") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("""listOf(1, 2, 3)""") + } + + @Test + fun generateInputWithDefaultValueForEnumArray() { + val schema = """ + input SomeType { + colors: [Color!]! = [red] + } + + enum Color { + red, + blue + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, language = Language.KOTLIN)).generate() as KotlinCodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("SomeType") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("colors") + assertThat(colorParam.type.toString()).isEqualTo("kotlin.collections.List") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("""listOf(com.netflix.graphql.dgs.codegen.tests.generated.types.Color.red)""") + } + + @Test + fun generateInputWithDefaultValueForBooleanArray() { + val schema = """ + input SomeType { + booleans: [Bool!]! = [true] + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, language = Language.KOTLIN)).generate() as KotlinCodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("SomeType") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("booleans") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("""listOf(true)""") + } + @Test fun generateToStringMethodForInputTypes() { val schema = """ From d8efe283d1dcf512c050272c7366f8abf82be70e Mon Sep 17 00:00:00 2001 From: Paul Bakker Date: Wed, 17 Feb 2021 10:28:17 -0800 Subject: [PATCH 2/6] Added support for default array values in Java --- .../generators/java/DataTypeGenerator.kt | 11 ++- .../graphql/dgs/codegen/CodeGenTest.kt | 86 +++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) 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 32f3c69f4..eaf2ba8e0 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 @@ -61,7 +61,16 @@ class InputTypeGenerator(config: CodeGenConfig) : BaseDataTypeGenerator(config.p is StringValue -> CodeBlock.of("\$S", defVal.value) is FloatValue -> CodeBlock.of("\$L", defVal.value) is EnumValue -> CodeBlock.of("\$T.\$N", typeUtils.findReturnType(it.type), defVal.name) - is ArrayValue -> CodeBlock.of("java.util.Collections.emptyList()") + is ArrayValue -> if(defVal.values.isEmpty()) CodeBlock.of("java.util.Collections.emptyList()") else CodeBlock.of("java.util.Arrays.asList(\$L)", defVal.values.map { v -> + when(v) { + is BooleanValue -> CodeBlock.of("\$L", v.isValue) + is IntValue -> CodeBlock.of("\$L", v.value) + is StringValue -> CodeBlock.of("\$S", v.value) + is FloatValue -> CodeBlock.of("\$L", v.value) + is EnumValue -> CodeBlock.of("\$L.\$N", ((it.type as ListType).type as TypeName).name, v.name) + else -> "" + } + }.joinToString()) else -> CodeBlock.of("\$L", defVal) } } 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 bdbe02ecc..4802a7e54 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 @@ -626,6 +626,92 @@ class CodeGenTest { assertCompiles(dataTypes) } + @Test + fun generateInputWithDefaultStringValueForArray() { + val schema = """ + input SomeType { + names: [String] = ["A", "B"] + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName)).generate() as CodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val type = data.typeSpec + assertThat(type.name).isEqualTo("SomeType") + + val fields = type.fieldSpecs + assertThat(fields).hasSize(1) + + val colorField = fields[0] + assertThat(colorField.name).isEqualTo("names") + assertThat(colorField.initializer.toString()).isEqualTo("""java.util.Arrays.asList("A", "B")""") + + assertCompiles(dataTypes) + } + + @Test + fun generateInputWithDefaultIntValueForArray() { + val schema = """ + input SomeType { + numbers: [Int] = [1, 2, 3] + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName)).generate() as CodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val type = data.typeSpec + assertThat(type.name).isEqualTo("SomeType") + + val fields = type.fieldSpecs + assertThat(fields).hasSize(1) + + val colorField = fields[0] + assertThat(colorField.name).isEqualTo("numbers") + assertThat(colorField.initializer.toString()).isEqualTo("""java.util.Arrays.asList(1, 2, 3)""") + + assertCompiles(dataTypes) + } + + @Test + fun generateInputWithDefaultEnumValueForArray() { + val schema = """ + input SomeType { + colors: [Color] = [red] + } + + enum Color { + red, + blue + } + """.trimIndent() + + val (dataTypes,_, enumTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, writeToFiles = true)).generate() as CodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val type = data.typeSpec + assertThat(type.name).isEqualTo("SomeType") + + val fields = type.fieldSpecs + assertThat(fields).hasSize(1) + + val colorField = fields[0] + assertThat(colorField.name).isEqualTo("colors") + assertThat(colorField.initializer.toString()).isEqualTo("""java.util.Arrays.asList(Color.red)""") + + assertCompiles(dataTypes + enumTypes) + } + @Test fun generateToInputStringMethodForInputTypes() { From eb059d46787a3b49272bc4113c59bbf4f0387cca Mon Sep 17 00:00:00 2001 From: Bjoern Hiller Date: Sat, 13 Feb 2021 08:21:33 +0100 Subject: [PATCH 3/6] Fix Kotlin code generation for input types with enum default values Use of `CodeBlock`s should make sure that a proper type is referenced which also should take care of needed imports. --- .../kotlin/KotlinDataTypeGenerator.kt | 41 ++++++++----------- ...tlinEntitiesRepresentationTypeGenerator.kt | 3 +- .../graphql/dgs/codegen/KotlinCodeGenTest.kt | 35 ++++++++++++++++ 3 files changed, 54 insertions(+), 25 deletions(-) diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt index 65a7453a0..5276aa6b7 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt @@ -53,23 +53,21 @@ class KotlinInputTypeGenerator(private val config: CodeGenConfig, private val do fun generate(definition: InputObjectTypeDefinition, extensions: List): KotlinCodeGenResult { val fields = definition.inputValueDefinitions - .filter(ReservedKeywordFilter.filterInvalidNames) - .map { - val defaultValue: Any - if (it.defaultValue != null) { - defaultValue = when (it.defaultValue) { - is BooleanValue -> (it.defaultValue as BooleanValue).isValue - is IntValue -> (it.defaultValue as IntValue).value - is StringValue -> (it.defaultValue as StringValue).value - is FloatValue -> (it.defaultValue as FloatValue).value - else -> it.defaultValue - } - - Field(it.name, typeUtils.findReturnType(it.type), typeUtils.isNullable(it.type), defaultValue) - } else { - Field(it.name, typeUtils.findReturnType(it.type), typeUtils.isNullable(it.type)) - } - }.plus(extensions.flatMap { it.inputValueDefinitions }.map { Field(it.name, typeUtils.findReturnType(it.type), typeUtils.isNullable(it.type)) }) + .filter(ReservedKeywordFilter.filterInvalidNames) + .map { + val type = typeUtils.findReturnType(it.type) + val defaultValue = it.defaultValue?.let { defVal -> + when (defVal) { + is BooleanValue -> CodeBlock.of("%L", defVal.isValue) + is IntValue -> CodeBlock.of("%L", defVal.value) + is StringValue -> CodeBlock.of("%S", defVal.value) + is FloatValue -> CodeBlock.of("%L", defVal.value) + is EnumValue -> CodeBlock.of("%M", MemberName(type as ClassName, defVal.name)) + else -> CodeBlock.of("%L", defVal) + } + } + Field(it.name, type, typeUtils.isNullable(it.type), defaultValue) + }.plus(extensions.flatMap { it.inputValueDefinitions }.map { Field(it.name, typeUtils.findReturnType(it.type), typeUtils.isNullable(it.type)) }) val interfaces = emptyList>() return generate(definition.name, fields, interfaces, true, document) } @@ -79,7 +77,7 @@ class KotlinInputTypeGenerator(private val config: CodeGenConfig, private val do } } -internal data class Field(val name: String, val type: com.squareup.kotlinpoet.TypeName, val nullable: Boolean, val default: Any? = null) +internal data class Field(val name: String, val type: com.squareup.kotlinpoet.TypeName, val nullable: Boolean, val default: CodeBlock? = null) abstract class AbstractKotlinDataTypeGenerator(private val packageName: String, private val config: CodeGenConfig) { protected val typeUtils = KotlinTypeUtils(packageName, config) @@ -104,12 +102,7 @@ abstract class AbstractKotlinDataTypeGenerator(private val packageName: String, .addAnnotation(jsonPropertyAnnotation(field.name)) if (field.default != null) { - val initializerBlock = if (field.type.toString().contains("String")) { - "\"${field.default}\"" - } else { - "${field.default}" - } - parameterSpec.defaultValue(initializerBlock) + parameterSpec.defaultValue(field.default) } else { when (returnType) { STRING -> if (field.nullable) parameterSpec.defaultValue("null") diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinEntitiesRepresentationTypeGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinEntitiesRepresentationTypeGenerator.kt index 11f489b6e..9f72943b1 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinEntitiesRepresentationTypeGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinEntitiesRepresentationTypeGenerator.kt @@ -20,6 +20,7 @@ package com.netflix.graphql.dgs.codegen.generators.kotlin import com.netflix.graphql.dgs.codegen.CodeGenConfig import com.netflix.graphql.dgs.codegen.KotlinCodeGenResult +import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.LIST import com.squareup.kotlinpoet.ParameterizedTypeName import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy @@ -47,7 +48,7 @@ class KotlinEntitiesRepresentationTypeGenerator(private val config: CodeGenConfi var result = KotlinCodeGenResult() // generate representations of entity types that have @key, including the __typename field, and the key fields - val typeName = Field("__typename", STRING, false, definition.name) + val typeName = Field("__typename", STRING, false, CodeBlock.of("%S", definition.name)) val fieldDefinitions= definition.fieldDefinitions .filter { keyFields.containsKey(it.name) 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 82019bbfc..ceceeec6d 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 @@ -515,6 +515,41 @@ class KotlinCodeGenTest { assertThat(type.propertySpecs).extracting("name").contains("genre") } + @Test + fun generateInputWithDefaultValueForEnum() { + val schema = """ + enum Color { + red + } + + input ColorFilter { + color: Color = red + } + """.trimIndent() + + val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, language = Language.KOTLIN)).generate() as KotlinCodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("ColorFilter") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("color") + assertThat(colorParam.type.toString()).isEqualTo("$typesPackageName.Color?") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("$typesPackageName.Color.red") + } + @Test fun generateToStringMethodForInputTypes() { val schema = """ From e4aeec7dcfecaa6e3f56a100fdfe88aa35e16ee0 Mon Sep 17 00:00:00 2001 From: Paul Bakker Date: Wed, 17 Feb 2021 10:35:13 -0800 Subject: [PATCH 4/6] fixed float handling in default array values. --- .../dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt index aab7089ee..60dd26445 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt @@ -68,7 +68,7 @@ class KotlinInputTypeGenerator(private val config: CodeGenConfig, private val do is BooleanValue -> CodeBlock.of("%L", v.isValue) is IntValue -> CodeBlock.of("%L", v.value) is StringValue -> CodeBlock.of("%S", v.value) - is FloatValue -> CodeBlock.of("%Lf", v.value) + is FloatValue -> CodeBlock.of("%L", v.value) is EnumValue -> CodeBlock.of("%M", MemberName((type as ParameterizedTypeName).typeArguments[0] as ClassName, v.name)) else -> "" } From 6196f3fa570484fe0e45d3eb68d2860d7f13f18e Mon Sep 17 00:00:00 2001 From: Bjoern Hiller Date: Sat, 13 Feb 2021 08:25:44 +0100 Subject: [PATCH 5/6] Fix Java code generation for input types with enum default values Use of `CodeBlock`s should make sure that a proper type is used. --- .../generators/java/DataTypeGenerator.kt | 45 +++++++------------ .../EntitiesRepresentationTypeGenerator.kt | 3 +- .../graphql/dgs/codegen/CodeGenTest.kt | 32 +++++++++++++ 3 files changed, 49 insertions(+), 31 deletions(-) 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 1c33b070b..43dd87a92 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 @@ -25,10 +25,6 @@ import com.netflix.graphql.dgs.codegen.shouldSkip import com.squareup.javapoet.* import graphql.language.* import graphql.language.TypeName -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.LocalTime -import java.time.OffsetDateTime import javax.lang.model.element.Modifier class DataTypeGenerator(config: CodeGenConfig) : BaseDataTypeGenerator(config.packageNameTypes, config) { @@ -58,26 +54,23 @@ class InputTypeGenerator(config: CodeGenConfig) : BaseDataTypeGenerator(config.p val name = definition.name val fieldDefinitions = definition.inputValueDefinitions.map { - var defaultValue: Any - if (it.defaultValue != null) { - defaultValue = when (it.defaultValue) { - is BooleanValue -> (it.defaultValue as BooleanValue).isValue - is IntValue -> (it.defaultValue as graphql.language.IntValue).value - is StringValue -> (it.defaultValue as graphql.language.StringValue).value - is FloatValue -> (it.defaultValue as graphql.language.FloatValue).value - else -> it.defaultValue - } - Field(it.name, typeUtils.findReturnType(it.type), defaultValue) - } else { - Field(it.name, typeUtils.findReturnType(it.type)) + val defaultValue = it.defaultValue?.let { defVal -> + when (defVal) { + is BooleanValue -> CodeBlock.of("\$L", defVal.isValue) + is IntValue -> CodeBlock.of("\$L", defVal.value) + is StringValue -> CodeBlock.of("\$S", defVal.value) + is FloatValue -> CodeBlock.of("\$L", defVal.value) + is EnumValue -> CodeBlock.of("\$T.\$N", typeUtils.findReturnType(it.type), defVal.name) + else -> CodeBlock.of("\$L", defVal) + } } - + Field(it.name, typeUtils.findReturnType(it.type), defaultValue) }.plus(extensions.flatMap { it.inputValueDefinitions }.map { Field(it.name, typeUtils.findReturnType(it.type)) }) return generate(name, emptyList(), fieldDefinitions, true) } } -internal data class Field(val name: String, val type: com.squareup.javapoet.TypeName, val initialValue: Any? = null) +internal data class Field(val name: String, val type: com.squareup.javapoet.TypeName, val initialValue: CodeBlock? = null) abstract class BaseDataTypeGenerator(internal val packageName: String, config: CodeGenConfig) { internal val typeUtils = TypeUtils(packageName, config) @@ -290,20 +283,12 @@ abstract class BaseDataTypeGenerator(internal val packageName: String, config: C } private fun addFieldWithGetterAndSetter(returnType: com.squareup.javapoet.TypeName?, fieldDefinition: Field, javaType: TypeSpec.Builder) { - if (fieldDefinition.initialValue != null) { - var initializerBlock = if (fieldDefinition.type.toString().contains("String")) { - "\"${fieldDefinition.initialValue}\"" - } else { - "${fieldDefinition.initialValue}" - } - val field = FieldSpec.builder(fieldDefinition.type, fieldDefinition.name).addModifiers(Modifier.PRIVATE) - .initializer(initializerBlock) - .build() - javaType.addField(field) + val field = if (fieldDefinition.initialValue != null) { + FieldSpec.builder(fieldDefinition.type, fieldDefinition.name).addModifiers(Modifier.PRIVATE).initializer(fieldDefinition.initialValue).build() } else { - val field = FieldSpec.builder(returnType, ReservedKeywordSanitizer.sanitize(fieldDefinition.name)).addModifiers(Modifier.PRIVATE).build() - javaType.addField(field) + FieldSpec.builder(returnType, ReservedKeywordSanitizer.sanitize(fieldDefinition.name)).addModifiers(Modifier.PRIVATE).build() } + javaType.addField(field) val getterName = "get${fieldDefinition.name[0].toUpperCase()}${fieldDefinition.name.substring(1)}" javaType.addMethod(MethodSpec.methodBuilder(getterName).addModifiers(Modifier.PUBLIC).returns(returnType).addStatement("return \$N", ReservedKeywordSanitizer.sanitize(fieldDefinition.name)).build()) diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/EntitiesRepresentationTypeGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/EntitiesRepresentationTypeGenerator.kt index fafec0154..7c7501431 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/EntitiesRepresentationTypeGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/EntitiesRepresentationTypeGenerator.kt @@ -21,6 +21,7 @@ package com.netflix.graphql.dgs.codegen.generators.java import com.netflix.graphql.dgs.codegen.CodeGenConfig import com.netflix.graphql.dgs.codegen.CodeGenResult import com.squareup.javapoet.ClassName +import com.squareup.javapoet.CodeBlock import graphql.language.* @@ -48,7 +49,7 @@ class EntitiesRepresentationTypeGenerator(val config: CodeGenConfig): BaseDataTy } var result = CodeGenResult() // generate representations of entity types that have @key, including the __typename field, and the key fields - val typeName = Field("__typename", ClassName.get(String::class.java), definition.name) + val typeName = Field("__typename", ClassName.get(String::class.java), CodeBlock.of("\$S", definition.name)) val fieldDefinitions = definition.fieldDefinitions .filter { keyFields.containsKey(it.name) 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 0806b49d2..e7c216437 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 @@ -567,6 +567,38 @@ class CodeGenTest { assertCompiles(dataTypes) } + @Test + fun generateInputWithDefaultValueForEnum() { + val schema = """ + enum Color { + red + } + + input ColorFilter { + color: Color = red + } + """.trimIndent() + + val (dataTypes, _, enumTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName)).generate() as CodeGenResult + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val type = data.typeSpec + assertThat(type.name).isEqualTo("ColorFilter") + + val fields = type.fieldSpecs + assertThat(fields).hasSize(1) + + val colorField = fields[0] + assertThat(colorField.name).isEqualTo("color") + assertThat(colorField.type.toString()).isEqualTo("$typesPackageName.Color") + assertThat(colorField.initializer.toString()).isEqualTo("$typesPackageName.Color.red") + + assertCompiles(enumTypes + dataTypes) + } + @Test fun generateToInputStringMethodForInputTypes() { From 5abae8a7e89adadc4d56f09a4689345cfdedcb11 Mon Sep 17 00:00:00 2001 From: Bjoern Hiller Date: Wed, 17 Feb 2021 21:23:23 +0100 Subject: [PATCH 6/6] Avoid code duplication --- .../kotlin/KotlinDataTypeGenerator.kt | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt index 60dd26445..587ac00af 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt @@ -23,9 +23,9 @@ import com.netflix.graphql.dgs.codegen.KotlinCodeGenResult import com.netflix.graphql.dgs.codegen.filterSkipped import com.netflix.graphql.dgs.codegen.shouldSkip import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.TypeName import graphql.language.* - class KotlinDataTypeGenerator(private val config: CodeGenConfig, private val document: Document): AbstractKotlinDataTypeGenerator(config.packageNameTypes, config) { fun generate(definition: ObjectTypeDefinition, extensions: List): KotlinCodeGenResult { if(definition.shouldSkip()) { @@ -56,32 +56,33 @@ class KotlinInputTypeGenerator(private val config: CodeGenConfig, private val do .filter(ReservedKeywordFilter.filterInvalidNames) .map { val type = typeUtils.findReturnType(it.type) - val defaultValue = it.defaultValue?.let { defVal -> - when (defVal) { - is BooleanValue -> CodeBlock.of("%L", defVal.isValue) - is IntValue -> CodeBlock.of("%L", defVal.value) - is StringValue -> CodeBlock.of("%S", defVal.value) - is FloatValue -> CodeBlock.of("%L", defVal.value) - is EnumValue -> CodeBlock.of("%M", MemberName(type as ClassName, defVal.name)) - is ArrayValue -> if(defVal.values.isEmpty()) CodeBlock.of("emptyList()") else CodeBlock.of("listOf(%L)", defVal.values.map { v -> - when(v) { - is BooleanValue -> CodeBlock.of("%L", v.isValue) - is IntValue -> CodeBlock.of("%L", v.value) - is StringValue -> CodeBlock.of("%S", v.value) - is FloatValue -> CodeBlock.of("%L", v.value) - is EnumValue -> CodeBlock.of("%M", MemberName((type as ParameterizedTypeName).typeArguments[0] as ClassName, v.name)) - else -> "" - } - }.joinToString()) - else -> CodeBlock.of("%L", defVal) - } - } + val defaultValue = it.defaultValue?.let { value -> generateCode(value, type) } Field(it.name, type, typeUtils.isNullable(it.type), defaultValue) }.plus(extensions.flatMap { it.inputValueDefinitions }.map { Field(it.name, typeUtils.findReturnType(it.type), typeUtils.isNullable(it.type)) }) val interfaces = emptyList>() return generate(definition.name, fields, interfaces, true, document) } + private fun generateCode(value: Value>, type: TypeName): CodeBlock = + when (value) { + is BooleanValue -> CodeBlock.of("%L", value.isValue) + is IntValue -> CodeBlock.of("%L", value.value) + is StringValue -> CodeBlock.of("%S", value.value) + is FloatValue -> CodeBlock.of("%L", value.value) + is EnumValue -> CodeBlock.of("%M", MemberName(type.className, value.name)) + is ArrayValue -> + if (value.values.isEmpty()) CodeBlock.of("emptyList()") + else CodeBlock.of("listOf(%L)", value.values.joinToString { v -> generateCode(v, type).toString() }) + else -> CodeBlock.of("%L", value) + } + + private val TypeName.className: ClassName + get() = when (this) { + is ClassName -> this + is ParameterizedTypeName -> typeArguments[0].className + else -> TODO() + } + override fun getPackageName(): String { return config.packageNameTypes }