diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt index 490344129..b7c23b221 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt @@ -394,6 +394,22 @@ class CodeGen(private val config: CodeGenConfig) { return CodeGenResult() } + val interfaceCodeGen = if (config.generateInterfaces) { + definitions.asSequence() + .filterIsInstance() + .excludeSchemaTypeExtension() + .filter { it.name != "Query" && it.name != "Mutation" && it.name != "RelayPageInfo" } + .map { definition -> + val interfaceGenerator = KotlinInterfaceTypeGenerator(config, document) + val interfaceTypeDefinition = + InterfaceTypeDefinition.newInterfaceTypeDefinition().name("I" + definition.name) + .definitions(definition.fieldDefinitions()).build() + interfaceGenerator.generate(interfaceTypeDefinition, emptyList()) + } + .fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) } + } + else CodeGenResult() + return definitions.asSequence() .filterIsInstance() .excludeSchemaTypeExtension() @@ -401,7 +417,7 @@ class CodeGen(private val config: CodeGenConfig) { val extensions = findInterfaceExtensions(it.name, definitions) KotlinInterfaceTypeGenerator(config, document).generate(it, extensions) } - .fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) } + .fold(interfaceCodeGen) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) } } } 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 86d364a8d..a8273cd44 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 @@ -65,7 +65,11 @@ class KotlinDataTypeGenerator(config: CodeGenConfig, document: Document) : extensions.flatMap { it.fieldDefinitions } .filterSkipped() .map { Field(it.name, typeUtils.findReturnType(it.type), typeUtils.isNullable(it.type), null, it.description) } - val interfaces = definition.implements + val interfaces = when (config.generateInterfaces) { + true -> definition.implements + TypeName.newTypeName("I" + definition.name).build() + else -> definition.implements + } + return generate(definition.name, fields, interfaces, document, definition.description) } @@ -211,7 +215,7 @@ abstract class AbstractKotlinDataTypeGenerator( .map { it.name } .toSet() - if (field.name in interfaceFields) { + if (field.name in interfaceFields || config.generateInterfaces) { // Properties are the syntactical element that will allow us to override things, they are the spec on // which we should add the override modifier. propertySpecBuilder.addModifiers(KModifier.OVERRIDE) 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 0ad75b5e1..ab95dac49 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 @@ -38,6 +38,7 @@ class CodeGenTest { @Test fun generateDataClassWithStringProperties() { + val schema = """ type Query { people: [Person] @@ -52,7 +53,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -63,7 +64,8 @@ class CodeGenTest { assertThat(typeSpec.fieldSpecs.size).isEqualTo(2) assertThat(typeSpec.fieldSpecs).extracting("name").contains("firstname", "lastname") - assertThat(typeSpec.methodSpecs).flatExtracting("parameters").extracting("name").contains("firstname", "lastname") + assertThat(typeSpec.methodSpecs).flatExtracting("parameters").extracting("name") + .contains("firstname", "lastname") dataTypes[0].writeTo(System.out) assertCompilesJava(dataTypes) } @@ -81,7 +83,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() val typeSpec = dataTypes[0].typeSpec @@ -103,7 +105,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() val typeSpec = dataTypes[0].typeSpec @@ -122,7 +124,13 @@ class CodeGenTest { } """.trimIndent() - val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, generateBoxedTypes = true)).generate() + val (dataTypes) = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateBoxedTypes = true + ) + ).generate() val typeSpec = dataTypes[0].typeSpec assertThat(typeSpec.fieldSpecs[0].type.toString()).isEqualTo("java.lang.Integer") assertThat(typeSpec.fieldSpecs[1].type.toString()).isEqualTo("java.lang.Boolean") @@ -142,7 +150,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() val typeSpec = dataTypes[0].typeSpec @@ -167,7 +175,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -195,7 +203,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -220,7 +228,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -233,6 +241,7 @@ class CodeGenTest { @Test fun generateDataClassWithBuilder() { + val schema = """ type Query { people: [Person] @@ -247,7 +256,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -257,8 +266,10 @@ class CodeGenTest { val builderType = dataTypes[0].typeSpec.typeSpecs[0] assertThat(builderType.name).isEqualTo("Builder") assertThat(builderType.methodSpecs).extracting("name").contains("firstname", "lastname", "build") - assertThat(builderType.methodSpecs).filteredOn("name", "firstname").extracting("returnType.simpleName").contains("com.netflix.graphql.dgs.codegen.tests.generated.types.Person.Builder") - assertThat(builderType.methodSpecs).filteredOn("name", "build").extracting("returnType.simpleName").contains("Person") + assertThat(builderType.methodSpecs).filteredOn("name", "firstname").extracting("returnType.simpleName") + .contains("com.netflix.graphql.dgs.codegen.tests.generated.types.Person.Builder") + assertThat(builderType.methodSpecs).filteredOn("name", "build").extracting("returnType.simpleName") + .contains("Person") assertCompilesJava(dataTypes) } @@ -278,7 +289,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -332,7 +343,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -363,7 +374,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -400,7 +411,7 @@ class CodeGenTest { val (dataTypes, interfaces) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -549,7 +560,7 @@ class CodeGenTest { val (dataTypes, interfaces) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -601,7 +612,7 @@ class CodeGenTest { val (dataTypes, interfaces) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -664,7 +675,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -675,7 +686,8 @@ class CodeGenTest { assertThat(dataTypes[0].typeSpec.fieldSpecs).extracting("name").contains("firstname", "lastname", "friends") // Check type of friends field - val parameterizedType = ParameterizedTypeName.get(ClassName.get(List::class.java), ClassName.get(typesPackageName, "Person")) + val parameterizedType = + ParameterizedTypeName.get(ClassName.get(List::class.java), ClassName.get(typesPackageName, "Person")) assertThat(dataTypes[0].typeSpec.fieldSpecs) .withFailMessage("Incorrect type for friends field. List expected.") .filteredOn { it.name == "friends" } @@ -714,7 +726,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -730,55 +742,6 @@ class CodeGenTest { assertCompilesJava(dataTypes) } - @Test - fun generateDataClassWithNoAllConstructor() { - val schema = """ - type Query { - cars: [Car] - } - - type Car { - make: String - model: String - } - """.trimIndent() - - val (dataTypes) = CodeGen( - CodeGenConfig( - schemas = setOf(schema), - packageName = basePackageName, - javaGenerateAllConstructor = false - ) - ).generate() - - assertThat(dataTypes[0].typeSpec.methodSpecs).filteredOn { it.name.equals("") && it.parameters.size > 0 }.hasSize(0) - assertCompilesJava(dataTypes) - } - - @Test - fun generateDataClassWithAllConstructor() { - val schema = """ - type Query { - cars: [Car] - } - - type Car { - make: String - model: String - } - """.trimIndent() - - val (dataTypes) = CodeGen( - CodeGenConfig( - schemas = setOf(schema), - packageName = basePackageName - ) - ).generate() - - assertThat(dataTypes[0].typeSpec.methodSpecs).filteredOn { it.name.equals("") && it.parameters.size > 0 }.hasSize(1) - assertCompilesJava(dataTypes) - } - @Test fun generateEnum() { val schema = """ @@ -796,7 +759,7 @@ class CodeGenTest { val codeGenResult = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -804,7 +767,11 @@ class CodeGenTest { assertThat(codeGenResult.javaEnumTypes.size).isEqualTo(1) assertThat(codeGenResult.javaEnumTypes[0].typeSpec.name).isEqualTo("EmployeeTypes") assertThat(codeGenResult.javaEnumTypes[0].typeSpec.enumConstants.size).isEqualTo(3) - assertThat(codeGenResult.javaEnumTypes[0].typeSpec.enumConstants).containsKeys("ENGINEER", "MANAGER", "DIRECTOR") + assertThat(codeGenResult.javaEnumTypes[0].typeSpec.enumConstants).containsKeys( + "ENGINEER", + "MANAGER", + "DIRECTOR" + ) assertCompilesJava(codeGenResult.javaEnumTypes) } @@ -830,7 +797,7 @@ class CodeGenTest { val codeGenResult = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -838,7 +805,12 @@ class CodeGenTest { assertThat(codeGenResult.javaEnumTypes.size).isEqualTo(1) assertThat(codeGenResult.javaEnumTypes[0].typeSpec.name).isEqualTo("EmployeeTypes") assertThat(codeGenResult.javaEnumTypes[0].typeSpec.enumConstants.size).isEqualTo(4) - assertThat(codeGenResult.javaEnumTypes[0].typeSpec.enumConstants).containsKeys("ENGINEER", "MANAGER", "DIRECTOR", "QA") + assertThat(codeGenResult.javaEnumTypes[0].typeSpec.enumConstants).containsKeys( + "ENGINEER", + "MANAGER", + "DIRECTOR", + "QA" + ) assertCompilesJava(codeGenResult.javaEnumTypes) } @@ -859,7 +831,7 @@ class CodeGenTest { val codeGenResult = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() val dataFetchers = codeGenResult.javaDataFetchers @@ -884,9 +856,18 @@ class CodeGenTest { arguments("Map", "java.util.Map"), arguments("ArrayList", "java.util.ArrayList"), arguments("ArrayList", "java.util.ArrayList"), - arguments("Map", "java.util.Map"), - arguments("ArrayList>>>", "java.util.ArrayList>>>"), - arguments("Map, Map>", "java.util.Map, java.util.Map>"), + arguments( + "Map", + "java.util.Map" + ), + arguments( + "ArrayList>>>", + "java.util.ArrayList>>>" + ), + arguments( + "Map, Map>", + "java.util.Map, java.util.Map>" + ), arguments("ArrayList", "java.util.ArrayList"), arguments("Map", "java.util.Map") ) @@ -916,7 +897,7 @@ class CodeGenTest { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - typeMapping = mapOf("JSON" to mappedTypeAsString) + typeMapping = mapOf("JSON" to mappedTypeAsString), ) ).generate() assertThat(dataTypes.size).isEqualTo(1) @@ -948,7 +929,7 @@ class CodeGenTest { "java.util.Map", "java.util.Map", "java.util.Map", - "?" + "?", ] ) fun testWrongCase(mappedTypeAsString: String) { @@ -957,7 +938,7 @@ class CodeGenTest { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - typeMapping = mapOf("JSON" to mappedTypeAsString) + typeMapping = mapOf("JSON" to mappedTypeAsString), ) ).generate() } @@ -967,6 +948,7 @@ class CodeGenTest { @Test fun `Skip generating a data class when the type is mapped`() { + val schema = """ type Query { person: Person @@ -983,7 +965,7 @@ class CodeGenTest { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - typeMapping = mapOf("Person" to "mypackage.Person") + typeMapping = mapOf("Person" to "mypackage.Person"), ) ).generate() @@ -1012,7 +994,7 @@ class CodeGenTest { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - typeMapping = mapOf("Person" to "mypackage.Person") + typeMapping = mapOf("Person" to "mypackage.Person"), ) ).generate() @@ -1022,6 +1004,7 @@ class CodeGenTest { @Test fun `Use mapped type name when the type is mapped for interface`() { + val schema = """ type Query { search: SearchResult @@ -1046,8 +1029,8 @@ class CodeGenTest { packageName = basePackageName, typeMapping = mapOf( "SomethingWithAName" to "mypackage.SomethingWithAName", - "Person" to "mypackage.Person" - ) + "Person" to "mypackage.Person", + ), ) ).generate() @@ -1059,6 +1042,7 @@ class CodeGenTest { @Test fun `Use mapped type name when the type is mapped for union`() { + val schema = """ type Query { search: SearchResult @@ -1082,8 +1066,8 @@ class CodeGenTest { typeMapping = mapOf( "SearchResult" to "mypackage.SearchResult", "Movie" to "mypackage.Movie", - "Actor" to "mypackage.Actor" - ) + "Actor" to "mypackage.Actor", + ), ) ).generate() @@ -1093,6 +1077,7 @@ class CodeGenTest { @Test fun `Use mapped type name when a concrete type of a union is mapped`() { + val schema = """ type Query { search: SearchResult @@ -1114,8 +1099,8 @@ class CodeGenTest { schemas = setOf(schema), packageName = basePackageName, typeMapping = mapOf( - "Actor" to "mypackage.Actor" - ) + "Actor" to "mypackage.Actor", + ), ) ).generate() @@ -1141,8 +1126,8 @@ class CodeGenTest { schemas = setOf(schema), packageName = basePackageName, typeMapping = mapOf( - "SearchInput" to "mypackage.SearchInput" - ) + "SearchInput" to "mypackage.SearchInput", + ), ) ).generate() @@ -1167,8 +1152,8 @@ class CodeGenTest { schemas = setOf(schema), packageName = basePackageName, typeMapping = mapOf( - "State" to "mypackage.State" - ) + "State" to "mypackage.State", + ), ) ).generate() @@ -1191,7 +1176,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -1217,7 +1202,12 @@ class CodeGenTest { } """.trimIndent() - val (dataTypes, _, enumTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName)).generate() + val (dataTypes, _, enumTypes) = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName + ) + ).generate() assertThat(dataTypes).hasSize(1) val data = dataTypes[0] @@ -1331,7 +1321,12 @@ class CodeGenTest { } """.trimIndent() - val (dataTypes, _, enumTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName)).generate() + val (dataTypes, _, enumTypes) = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName + ) + ).generate() assertThat(dataTypes).hasSize(1) val data = dataTypes[0] @@ -1352,6 +1347,7 @@ class CodeGenTest { @Test fun generateExtendedInputTypes() { + val schema = """ type Query { movies(filter: MovieFilter) @@ -1369,7 +1365,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -1384,6 +1380,7 @@ class CodeGenTest { @Test fun generateToStringMethodForTypes() { + val schema = """ type Query { people: [Person] @@ -1398,7 +1395,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -1406,13 +1403,15 @@ class CodeGenTest { val expectedString = """ return "Person{" + "firstname='" + firstname + "'," +"lastname='" + lastname + "'" +"}"; """.trimIndent() - val generatedString = dataTypes[0].typeSpec.methodSpecs.single { it.name == "toString" }.code.toString().trimIndent() + val generatedString = + dataTypes[0].typeSpec.methodSpecs.single { it.name == "toString" }.code.toString().trimIndent() assertThat(expectedString).isEqualTo(generatedString) assertCompilesJava(dataTypes) } @Test fun generateToStringMethodForSensitiveType() { + val schema = """ type Query { people: [Person] @@ -1429,7 +1428,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -1437,13 +1436,15 @@ class CodeGenTest { val expectedString = """ return "Person{" + "firstname='" + firstname + "'," +"lastname='" + lastname + "'," +"password='" + "*****" + "'" +"}"; """.trimIndent() - val generatedString = dataTypes[0].typeSpec.methodSpecs.single { it.name == "toString" }.code.toString().trimIndent() + val generatedString = + dataTypes[0].typeSpec.methodSpecs.single { it.name == "toString" }.code.toString().trimIndent() assertThat(expectedString).isEqualTo(generatedString) assertCompilesJava(dataTypes) } @Test fun generateToStringMethodForSensitiveInputType() { + val schema = """ type Query { people(filter: PersonFilter): [Person] @@ -1457,7 +1458,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -1465,7 +1466,8 @@ class CodeGenTest { val expectedString = """ return "PersonFilter{" + "email='" + "*****" + "'" +"}"; """.trimIndent() - val generatedString = dataTypes[0].typeSpec.methodSpecs.single { it.name == "toString" }.code.toString().trimIndent() + val generatedString = + dataTypes[0].typeSpec.methodSpecs.single { it.name == "toString" }.code.toString().trimIndent() assertThat(expectedString).isEqualTo(generatedString) assertCompilesJava(dataTypes) } @@ -1474,7 +1476,7 @@ class CodeGenTest { @MethodSource("generateConstantsArguments") fun `Generates constants from Type names available via the DgsConstants class`( snakeCaseEnabled: Boolean, - constantNames: List + constantNames: List, ) { val schema = """ type Query { @@ -1497,7 +1499,7 @@ class CodeGenTest { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - snakeCaseConstantNames = snakeCaseEnabled + snakeCaseConstantNames = snakeCaseEnabled, ) ).generate() val type = result.javaConstants[0].typeSpec @@ -1526,7 +1528,7 @@ class CodeGenTest { val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() val type = result.javaConstants[0].typeSpec @@ -1559,7 +1561,7 @@ class CodeGenTest { val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() val type = result.javaConstants[0].typeSpec @@ -1587,12 +1589,13 @@ class CodeGenTest { val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() val type = result.javaConstants[0].typeSpec assertThat(type.typeSpecs).extracting("name").containsExactly("QUERY", "PERSON") - assertThat(type.typeSpecs[1].fieldSpecs).extracting("name").containsExactly("TYPE_NAME", "Firstname", "Lastname", "Email") + assertThat(type.typeSpecs[1].fieldSpecs).extracting("name") + .containsExactly("TYPE_NAME", "Firstname", "Lastname", "Email") } @Test @@ -1615,7 +1618,7 @@ class CodeGenTest { val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() val type = result.javaConstants[0].typeSpec @@ -1623,38 +1626,6 @@ class CodeGenTest { assertThat(type.typeSpecs[0].fieldSpecs).extracting("name").containsExactly("TYPE_NAME", "People", "Friends") } - @Test - fun generateConstantsForQueryInputArguments() { - val schema = """ - type Query { - shows(titleFilter: String,moveFilter: MovieFilter): [Show] - } - - type Show { - name: String - } - - input MovieFilter { - title: String - genre: Genre - language: Language - tags: [String] - } - """.trimIndent() - - val result = CodeGen( - CodeGenConfig( - schemas = setOf(schema), - packageName = basePackageName - ) - ).generate() - val type = result.javaConstants[0].typeSpec - assertThat(type.typeSpecs).extracting("name").containsExactly("QUERY", "SHOW", "MOVIEFILTER") - assertThat(type.typeSpecs[0].typeSpecs).extracting("name").containsExactly("SHOWS_INPUT_ARGUMENT") - assertThat(type.typeSpecs[0].typeSpecs[0].fieldSpecs).extracting("name") - .containsExactly("TitleFilter", "MoveFilter") - } - @Test fun generateUnion() { val schema = """ @@ -1676,7 +1647,7 @@ class CodeGenTest { val (dataTypes, interfaces) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() assertThat(dataTypes[0].typeSpec.name).isEqualTo("Movie") @@ -1684,7 +1655,12 @@ class CodeGenTest { assertThat(interfaces[0].typeSpec.name).isEqualTo("SearchResult") val typeSpec = dataTypes[0] - assertThat(typeSpec.typeSpec.superinterfaces[0]).isEqualTo(ClassName.get("com.netflix.graphql.dgs.codegen.tests.generated.types", "SearchResult")) + assertThat(typeSpec.typeSpec.superinterfaces[0]).isEqualTo( + ClassName.get( + "com.netflix.graphql.dgs.codegen.tests.generated.types", + "SearchResult" + ) + ) } @Test @@ -1714,7 +1690,7 @@ class CodeGenTest { val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() assertThat(result.javaDataTypes[0].typeSpec.name).isEqualTo("Movie") @@ -1741,8 +1717,7 @@ class CodeGenTest { |}) |public interface SearchResult { |} - | - """.trimMargin() + |""".trimMargin() ) assertCompilesJava(result.javaDataTypes + result.javaInterfaces) @@ -1763,7 +1738,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() assertThat(dataTypes.size).isEqualTo(1) @@ -1782,7 +1757,7 @@ class CodeGenTest { val (dataTypes) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() assertThat(dataTypes[0].typeSpec.name).isEqualTo("Person") @@ -1801,7 +1776,7 @@ class CodeGenTest { val (_, interfaces) = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() assertThat(interfaces[0].typeSpec.name).isEqualTo("Person") @@ -1810,6 +1785,7 @@ class CodeGenTest { @Test fun generateWithCustomSubPackageName() { + val schema = """ type Person { firstname: String @@ -1817,7 +1793,13 @@ class CodeGenTest { } """.trimIndent() - val (dataTypes) = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, subPackageNameTypes = "mytypes")).generate() + val (dataTypes) = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + subPackageNameTypes = "mytypes" + ) + ).generate() assertThat(dataTypes.size).isEqualTo(1) val typeSpec = dataTypes[0].typeSpec @@ -1828,6 +1810,7 @@ class CodeGenTest { @Test fun generateDataClassWithInterfaceInheritance() { + val schema = """ type Query { people: [Person] @@ -1891,8 +1874,7 @@ class CodeGenTest { | | void setLastname(String lastname); |} - | - """.trimMargin() + |""".trimMargin() ) val employee = interfaces[1] @@ -1923,8 +1905,7 @@ class CodeGenTest { | | void setCompany(String company); |} - | - """.trimMargin() + |""".trimMargin() ) assertThat(JavaFile.builder("$basePackageName.types", talent).build().toString()).isEqualTo( @@ -2058,8 +2039,7 @@ class CodeGenTest { | } | } |} - | - """.trimMargin() + |""".trimMargin() ) assertCompilesJava(dataTypes + interfaces) @@ -2067,6 +2047,7 @@ class CodeGenTest { @Test fun generateInterfacesWithoutSetters() { + val schema = """ type Query { people: [Person] @@ -2084,7 +2065,7 @@ class CodeGenTest { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateInterfaceSetters = false + generateInterfaceSetters = false, ) ).generate() @@ -2102,8 +2083,7 @@ class CodeGenTest { | | String getLastname(); |} - | - """.trimMargin() + |""".trimMargin() ) assertCompilesJava(dataTypes + interfaces) } @@ -2128,7 +2108,7 @@ class CodeGenTest { val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -2152,7 +2132,14 @@ class CodeGenTest { scalar MovieID @javaType(name : "java.lang.String") """.trimIndent() - val codeGenResult = CodeGen(CodeGenConfig(schemas = setOf(schema), packageName = basePackageName, generateClientApi = true, typeMapping = mapOf())).generate() + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + typeMapping = mapOf() + ) + ).generate() assertCompilesJava(codeGenResult.javaFiles) } @@ -2327,7 +2314,8 @@ class CodeGenTest { val standing = interfaces[2] assertThat(standing.typeSpec.name).isEqualTo("Standing") - assertThat(standing.typeSpec.methodSpecs).extracting("name").containsExactly("getPosition", "setPosition", "getTeam", "setTeam") + assertThat(standing.typeSpec.methodSpecs).extracting("name") + .containsExactly("getPosition", "setPosition", "getTeam", "setTeam") assertThat(standing.typeSpec.methodSpecs[0].returnType.toString()).contains("int") assertThat(standing.typeSpec.methodSpecs[2].returnType).extracting("simpleName").isEqualTo("ITeam") @@ -2398,7 +2386,8 @@ class CodeGenTest { val iMovie = interfaces[0] assertThat(iMovie.typeSpec.name).isEqualTo("IMovie") - assertThat(iMovie.typeSpec.methodSpecs).extracting("name").containsExactly("getId", "getTitle", "getGenre", "getLanguage", "getTags", "getRating") + assertThat(iMovie.typeSpec.methodSpecs).extracting("name") + .containsExactly("getId", "getTitle", "getGenre", "getLanguage", "getTags", "getRating") assertThat(iMovie.typeSpec.methodSpecs[0].returnType).extracting("simpleName").isEqualTo("String") assertThat(iMovie.typeSpec.methodSpecs[1].returnType).extracting("simpleName").isEqualTo("String") assertThat(iMovie.typeSpec.methodSpecs[2].returnType).extracting("simpleName").isEqualTo("IGenre") @@ -2427,7 +2416,8 @@ class CodeGenTest { val movie = dataTypes[0] assertThat(movie.typeSpec.name).isEqualTo("Movie") assertThat(movie.typeSpec.superinterfaces).extracting("simpleName").containsExactly("IMovie") - assertThat(movie.typeSpec.fieldSpecs).extracting("name").containsExactly("id", "title", "genre", "language", "tags", "rating") + assertThat(movie.typeSpec.fieldSpecs).extracting("name") + .containsExactly("id", "title", "genre", "language", "tags", "rating") assertThat(movie.typeSpec.fieldSpecs[0].type).extracting("simpleName").isEqualTo("String") assertThat(movie.typeSpec.fieldSpecs[1].type).extracting("simpleName").isEqualTo("String") assertThat(movie.typeSpec.fieldSpecs[2].type).extracting("simpleName").isEqualTo("IGenre") @@ -2458,7 +2448,8 @@ class CodeGenTest { val movieFilter = dataTypes[4] assertThat(movieFilter.typeSpec.name).isEqualTo("MovieFilter") assertThat(movieFilter.typeSpec.superinterfaces.size).isEqualTo(0) - assertThat(movieFilter.typeSpec.fieldSpecs).extracting("name").containsExactly("title", "genre", "language", "tags", "rating") + assertThat(movieFilter.typeSpec.fieldSpecs).extracting("name") + .containsExactly("title", "genre", "language", "tags", "rating") assertThat(movieFilter.typeSpec.fieldSpecs[0].type).extracting("simpleName").isEqualTo("String") assertThat(movieFilter.typeSpec.fieldSpecs[1].type).extracting("simpleName").isEqualTo("Genre") assertThat(movieFilter.typeSpec.fieldSpecs[2].type).extracting("simpleName").isEqualTo("Language") @@ -2546,7 +2537,8 @@ class CodeGenTest { val iMovie = interfaces[0] assertThat(iMovie.typeSpec.name).isEqualTo("IMovie") - assertThat(iMovie.typeSpec.methodSpecs).extracting("name").containsExactly("getId", "getTitle", "getGenre", "getLanguage", "getTags", "getRating") + assertThat(iMovie.typeSpec.methodSpecs).extracting("name") + .containsExactly("getId", "getTitle", "getGenre", "getLanguage", "getTags", "getRating") assertThat(iMovie.typeSpec.methodSpecs[0].returnType).extracting("simpleName").isEqualTo("String") assertThat(iMovie.typeSpec.methodSpecs[1].returnType).extracting("simpleName").isEqualTo("String") assertThat(iMovie.typeSpec.methodSpecs[2].returnType).extracting("simpleName").isEqualTo("Genre") @@ -2643,13 +2635,22 @@ class CodeGenTest { assertThat(interfaces[4].typeSpec.name).isEqualTo("Character") assertThat(dataTypes[0].typeSpec.name).isEqualTo("Human") - assertThat(dataTypes[0].typeSpec.superinterfaces).extracting("simpleName").containsExactly("SearchResult", "IHuman", "com.netflix.graphql.dgs.codegen.tests.generated.types.Character") + assertThat(dataTypes[0].typeSpec.superinterfaces).extracting("simpleName").containsExactly( + "SearchResult", + "IHuman", + "com.netflix.graphql.dgs.codegen.tests.generated.types.Character" + ) assertThat(dataTypes[1].typeSpec.name).isEqualTo("Droid") - assertThat(dataTypes[1].typeSpec.superinterfaces).extracting("simpleName").containsExactly("SearchResult", "IDroid", "com.netflix.graphql.dgs.codegen.tests.generated.types.Character") + assertThat(dataTypes[1].typeSpec.superinterfaces).extracting("simpleName").containsExactly( + "SearchResult", + "IDroid", + "com.netflix.graphql.dgs.codegen.tests.generated.types.Character" + ) val searchResultPage = dataTypes[2] assertThat(searchResultPage.typeSpec.name).isEqualTo("SearchResultPage") - assertThat(searchResultPage.typeSpec.superinterfaces).extracting("simpleName").containsExactly("ISearchResultPage") + assertThat(searchResultPage.typeSpec.superinterfaces).extracting("simpleName") + .containsExactly("ISearchResultPage") assertThat(searchResultPage.typeSpec.fieldSpecs).extracting("name").containsExactly("items") parameterizedTypeName = searchResultPage.typeSpec.fieldSpecs[0].type as ParameterizedTypeName assertThat(parameterizedTypeName.rawType).extracting("simpleName").isEqualTo("List") @@ -2687,7 +2688,7 @@ class CodeGenTest { val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -2731,7 +2732,7 @@ It takes a title and such. val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -2760,7 +2761,7 @@ It takes a title and such. val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -2784,7 +2785,7 @@ It takes a title and such. val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -2808,7 +2809,7 @@ It takes a title and such. val result = CodeGen( CodeGenConfig( schemas = setOf(schema), - packageName = basePackageName + packageName = basePackageName, ) ).generate() @@ -2818,28 +2819,6 @@ It takes a title and such. ) } - @Test - fun generateSerializableDataClass() { - val schema = """ - type Person { - firstname: String - lastname: String - } - """.trimIndent() - - val (dataTypes) = CodeGen( - CodeGenConfig( - schemas = setOf(schema), - packageName = basePackageName, - implementSerializable = true - ) - ).generate() - - assertThat(dataTypes.size).isEqualTo(1) - assertThat(dataTypes[0].packageName).isEqualTo(typesPackageName) - assertThat(dataTypes[0].typeSpec.superinterfaces).contains(ClassName.get(Serializable::class.java)) - } - private val CodeGenResult.javaFiles: Collection get() = javaDataTypes + javaInterfaces + javaEnumTypes + javaDataFetchers + javaQueryTypes + clientProjections + javaConstants @@ -2849,18 +2828,127 @@ It takes a title and such. return Stream.of( arguments( true, - listOf("QUERY", "PERSON", "PERSON_META_DATA", "V_PERSON_META_DATA", "V_1_PERSON_META_DATA", "URL_META_DATA") + listOf( + "QUERY", + "PERSON", + "PERSON_META_DATA", + "V_PERSON_META_DATA", + "V_1_PERSON_META_DATA", + "URL_META_DATA" + ) ), arguments( false, listOf("QUERY", "PERSON", "PERSONMETADATA", "VPERSONMETADATA", "V1PERSONMETADATA", "URLMETADATA") - ) + ), ) } @JvmStatic fun generateDataClassesWithParameterizedMappedTypesWrongCases(): Stream = Stream.of( - arguments("java.util.Map") + arguments("java.util.Map"), ) } + + @Test + fun generateKotlinDataType() { + val schema = """ + type Query { + employees: [Employee] + } + + type Employee { + firstname: String! + lastname: String + company: String + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN, + ) + ).generate().kotlinDataTypes + + assertThat(dataTypes.size).isEqualTo(1) + val employee = dataTypes.single() + // Check data class + assertThat(employee.name).isEqualTo("Employee") + assertThat(employee.toString()).isEqualTo( + """ + package com.netflix.graphql.dgs.codegen.tests.generated.types + + import com.fasterxml.jackson.`annotation`.JsonProperty + import kotlin.String + + public data class Employee( + @JsonProperty("firstname") + public val firstname: String, + @JsonProperty("lastname") + public val lastname: String? = null, + @JsonProperty("company") + public val company: String? = null, + ) { + public companion object + } + + """.trimIndent() + ) + + assertCompilesKotlin(dataTypes) + } + + @Test + fun generateKotlinDataTypeWithAdditionalInterface() { + val schema = """ + type Query { + employees: [Employee] + } + + type Employee { + firstname: String! + lastname: String + company: String + } + """.trimIndent() + + val result = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN, + generateInterfaces = true, + ) + ).generate() + + val kotlinDataTypes = result.kotlinDataTypes + val kotlinInterfaces = result.kotlinInterfaces + assertThat(kotlinDataTypes.size).isEqualTo(1) + assertThat(kotlinInterfaces.size).isEqualTo(1) + val iemployee = kotlinInterfaces.single() + // Check data class + assertThat(iemployee.name).isEqualTo("IEmployee") + assertThat(iemployee.toString()).isEqualTo( + """ + package com.netflix.graphql.dgs.codegen.tests.generated.types + + import kotlin.String + + public interface IEmployee { + public val firstname: String + + public val lastname: String? + + public val company: String? + + public companion object + } + + """.trimIndent() + ) + + assertCompilesKotlin(kotlinInterfaces + kotlinDataTypes) + } } 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 16b15c106..f16ef35f6 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 @@ -18,8 +18,16 @@ package com.netflix.graphql.dgs.codegen -import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.LIST +import com.squareup.kotlinpoet.ParameterSpec import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.PropertySpec +import com.squareup.kotlinpoet.STRING +import com.squareup.kotlinpoet.TypeSpec +import com.squareup.kotlinpoet.asTypeName import org.assertj.core.api.Assertions.assertThat import org.assertj.core.data.Index import org.junit.jupiter.api.Nested @@ -27,10 +35,15 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.* +import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.Arguments.arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import org.junit.jupiter.params.provider.ArgumentsSource +import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource import java.util.stream.Stream import java.util.stream.Stream.of +import kotlin.math.max class KotlinCodeGenTest { @@ -2702,4 +2715,123 @@ It takes a title and such. assertCompilesJava(codeGenResult.javaQueryTypes) } + + private fun compare(s1: String, s2: String) { + var diff = -1 + for (i in 0..s1.length) { + if (s1[i] != s2[i]) { + diff = i + break + } + } + if (diff >= 0) { + println("Found diff at $diff:") + println("${s1.substring(max(0, diff - 5), max(s1.length - 1, diff))}") + println("${s2.substring(max(0, diff - 5), max(s2.length - 1, diff))}") + for(i in 0 until diff) print(" ") + println("^") + } + } + + @Test + fun generateKotlinDataType() { + val schema = """ + type Query { + employees: [Employee] + } + + type Employee { + firstname: String! + lastname: String + company: String + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN, + ) + ).generate().kotlinDataTypes + + assertThat(dataTypes.size).isEqualTo(1) + val employee = dataTypes.single() + // Check data class + assertThat(employee.name).isEqualTo("Employee") + assertThat(employee.toString()).isEqualTo( + """ + package com.netflix.graphql.dgs.codegen.tests.generated.types + + import com.fasterxml.jackson.`annotation`.JsonProperty + import kotlin.String + + public data class Employee( + @JsonProperty("firstname") + public val firstname: String, + @JsonProperty("lastname") + public val lastname: String? = null, + @JsonProperty("company") + public val company: String? = null, + ) { + public companion object + } + + """.trimIndent() + ) + + assertCompilesKotlin(dataTypes) + } + + @Test + fun generateKotlinDataTypeWithAdditionalInterface() { + val schema = """ + type Query { + employees: [Employee] + } + + type Employee { + firstname: String! + lastname: String + company: String + } + """.trimIndent() + + val result = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN, + generateInterfaces = true, + ) + ).generate() + + val kotlinDataTypes = result.kotlinDataTypes + val kotlinInterfaces = result.kotlinInterfaces + assertThat(kotlinDataTypes.size).isEqualTo(1) + assertThat(kotlinInterfaces.size).isEqualTo(1) + val iemployee = kotlinInterfaces.single() + // Check data class + assertThat(iemployee.name).isEqualTo("IEmployee") + assertThat(iemployee.toString()).isEqualTo( + """ + package com.netflix.graphql.dgs.codegen.tests.generated.types + + import kotlin.String + + public interface IEmployee { + public val firstname: String + + public val lastname: String? + + public val company: String? + + public companion object + } + + """.trimIndent() + ) + + assertCompilesKotlin(kotlinInterfaces + kotlinDataTypes) + } }