From 37badc04e9701abbafc725d20c71a958e1f14f42 Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Fri, 3 Feb 2023 21:07:07 -0800 Subject: [PATCH 01/10] Initial implementation of revamped client codegen. --- .../generators/java/ClientApiGenerator.kt | 63 ++++-- ....kt => EntitiesClientApiGenTest.kt.ignore} | 8 +- ... KotlinEntitiesClientApiGenTest.kt.ignore} | 2 + .../clientapi/ClientApiGenFragmentTest.kt | 58 ++--- .../clientapi/ClientApiGenMutationTest.kt | 2 +- .../clientapi/ClientApiGenProjectionTest.kt | 81 ++++--- .../clientapi/ClientApiGenQueryTest.kt | 51 +---- .../dgs/CodegenGradlePluginEntitySmokeTest.kt | 79 ------- .../CodegenGradlePluginSpringBootSmokeTest.kt | 204 +----------------- 9 files changed, 116 insertions(+), 432 deletions(-) rename graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/{EntitiesClientApiGenTest.kt => EntitiesClientApiGenTest.kt.ignore} (99%) rename graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/{KotlinEntitiesClientApiGenTest.kt => KotlinEntitiesClientApiGenTest.kt.ignore} (99%) diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt index 0ceb51e04..7ebd0cbbf 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt @@ -24,14 +24,7 @@ import com.netflix.graphql.dgs.client.codegen.GraphQLQuery import com.netflix.graphql.dgs.codegen.* import com.netflix.graphql.dgs.codegen.generators.shared.ClassnameShortener import com.netflix.graphql.dgs.codegen.generators.shared.CodeGeneratorUtils.capitalized -import com.squareup.javapoet.ClassName -import com.squareup.javapoet.CodeBlock -import com.squareup.javapoet.FieldSpec -import com.squareup.javapoet.JavaFile -import com.squareup.javapoet.MethodSpec -import com.squareup.javapoet.ParameterSpec -import com.squareup.javapoet.ParameterizedTypeName -import com.squareup.javapoet.TypeSpec +import com.squareup.javapoet.* import graphql.introspection.Introspection.TypeNameMetaFieldDef import graphql.language.Document import graphql.language.FieldDefinition @@ -228,9 +221,27 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document private fun createRootProjection(type: TypeDefinition<*>, prefix: String): CodeGenResult { val clazzName = "${prefix}ProjectionRoot" + val className = ClassName.get(BaseSubProjectionNode::class.java) + + /*val javaType = TypeSpec.classBuilder(clazzName) + .addOptionalGeneratedAnnotation(config) + .addModifiers(Modifier.PUBLIC).superclass(ClassName.get(BaseProjectionNode::class.java))*/ + val parentJavaType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) + val rootJavaType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) val javaType = TypeSpec.classBuilder(clazzName) .addOptionalGeneratedAnnotation(config) - .addModifiers(Modifier.PUBLIC).superclass(ClassName.get(BaseProjectionNode::class.java)) + .addTypeVariable(parentJavaType) + .addTypeVariable(rootJavaType) + .addModifiers(Modifier.PUBLIC) + .superclass(ParameterizedTypeName.get(className, TypeVariableName.get("PARENT"), TypeVariableName.get("ROOT"))) + .addMethod( + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + // .addParameter(ParameterSpec.builder(ClassName.get("", "PARENT"), "parent").build()) + // .addParameter(ParameterSpec.builder(ClassName.get("", "ROOT"), "root").build()) + .addCode("""super(null, null, java.util.Optional.of("${type.name}"));""") + .build() + ) if (generatedClasses.contains(clazzName)) return CodeGenResult() else generatedClasses.add(clazzName) @@ -248,7 +259,7 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document if (typeDefinition != null) it to typeDefinition else null } .map { (fieldDef, typeDef) -> - val projectionName = "${prefix}_${fieldDef.name.capitalized()}Projection" + val projectionName = "${typeDef.name.capitalized()}Projection" if (typeDef !is ScalarTypeDefinition) { val noArgMethodBuilder = MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(fieldDef.name)) @@ -270,7 +281,7 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document val processedEdges = mutableSetOf>() processedEdges.add(typeDef.name to type.name) - createSubProjection(typeDef, javaType.build(), javaType.build(), "${prefix}_${fieldDef.name.capitalized()}", processedEdges, 1) + createSubProjection(typeDef, javaType.build(), javaType.build(), "${typeDef.name.capitalized()}", processedEdges, 1) } .fold(CodeGenResult()) { total, current -> total.merge(current) } @@ -388,7 +399,7 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document private fun addFragmentProjectionMethod(javaType: TypeSpec.Builder, rootType: TypeSpec, prefix: String, it: TypeDefinition<*>, processedEdges: Set>, queryDepth: Int): CodeGenResult { val rootRef = if (javaType.build().name == rootType.name) "this" else "getRoot()" - val projectionName = "${prefix}_${it.name.capitalized()}Projection" + val projectionName = "${it.name.capitalized()}Projection" javaType.addMethod( MethodSpec.methodBuilder("on${it.name}") .addModifiers(Modifier.PUBLIC) @@ -403,7 +414,7 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document .build() ) - return createFragment(it as ObjectTypeDefinition, javaType.build(), rootType, "${prefix}_${it.name.capitalized()}", processedEdges, queryDepth) + return createFragment(it as ObjectTypeDefinition, javaType.build(), rootType, "${it.name.capitalized()}", processedEdges, queryDepth) } private fun createFragment(type: ObjectTypeDefinition, parent: TypeSpec, root: TypeSpec, prefix: String, processedEdges: Set>, queryDepth: Int): CodeGenResult { @@ -463,7 +474,25 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document val className = ClassName.get(BaseSubProjectionNode::class.java) val clazzName = "${prefix}Projection" if (generatedClasses.contains(clazzName)) return null else generatedClasses.add(clazzName) + + val parentJavaType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) + val rootJavaType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) val javaType = TypeSpec.classBuilder(clazzName) + .addOptionalGeneratedAnnotation(config) + .addTypeVariable(parentJavaType) + .addTypeVariable(rootJavaType) + .addModifiers(Modifier.PUBLIC) + .superclass(ParameterizedTypeName.get(className, TypeVariableName.get("PARENT"), TypeVariableName.get("ROOT"))) + .addMethod( + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(ParameterSpec.builder(ClassName.get("", "PARENT"), "parent").build()) + .addParameter(ParameterSpec.builder(ClassName.get("", "ROOT"), "root").build()) + .addCode("""super(parent, root, java.util.Optional.of("${type.name}"));""") + .build() + ) + + /*val javaType = TypeSpec.classBuilder(clazzName) .addOptionalGeneratedAnnotation(config) .addModifiers(Modifier.PUBLIC) .superclass(ParameterizedTypeName.get(className, ClassName.get(getPackageName(), parent.name), ClassName.get(getPackageName(), root.name))) @@ -474,7 +503,7 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document .addParameter(ParameterSpec.builder(ClassName.get(getPackageName(), root.name), "root").build()) .addCode("""super(parent, root, java.util.Optional.of("${type.name}"));""") .build() - ) + )*/ val fieldDefinitions = type.fieldDefinitions() + document.definitions @@ -489,9 +518,9 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document val typeDefinition = it.type.findTypeDefinition(document, true) if (typeDefinition != null) it to typeDefinition else null } - .filter { (_, typeDef) -> (typeDef.name to type.name) !in processedEdges } + // .filter { (_, typeDef) -> (typeDef.name to type.name) !in processedEdges } .map { (fieldDef, typeDef) -> - val projectionName = "${truncatePrefix(prefix)}_${fieldDef.name.capitalized()}Projection" + val projectionName = "${typeDef.name.capitalized()}Projection" val methodName = ReservedKeywordSanitizer.sanitize(fieldDef.name) javaType.addMethod( MethodSpec.methodBuilder(methodName) @@ -513,7 +542,7 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document val updatedProcessedEdges = processedEdges.toMutableSet() updatedProcessedEdges.add(typeDef.name to type.name) - createSubProjection(typeDef, javaType.build(), root, "${truncatePrefix(prefix)}_${fieldDef.name.capitalized()}", updatedProcessedEdges, queryDepth + 1) + createSubProjection(typeDef, javaType.build(), root, "${typeDef.name.capitalized()}", updatedProcessedEdges, queryDepth + 1) } .fold(CodeGenResult()) { total, current -> total.merge(current) } } else CodeGenResult() diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.ignore similarity index 99% rename from graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt rename to graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.ignore index c36c26baa..4d3625bad 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.ignore @@ -20,8 +20,10 @@ package com.netflix.graphql.dgs.codegen import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail +import org.junit.Ignore import org.junit.jupiter.api.Test +@Ignore class EntitiesClientApiGenTest { @Test @@ -53,9 +55,9 @@ class EntitiesClientApiGenTest { assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_GenreProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_FriendsProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesGenreProjection") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesActorProjection") + assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesFriendsProjection") val representation = codeGenResult.javaDataTypes.single { "Representation" in it.typeSpec.name } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt.ignore similarity index 99% rename from graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt rename to graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt.ignore index 0e22dcde6..2aa26fed4 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt.ignore @@ -20,8 +20,10 @@ package com.netflix.graphql.dgs.codegen import org.assertj.core.api.Assertions.* import org.assertj.core.data.Index +import org.junit.Ignore import org.junit.jupiter.api.Test +@Ignore class KotlinEntitiesClientApiGenTest { @Test diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenFragmentTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenFragmentTest.kt index dd92193c5..b739416a9 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenFragmentTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenFragmentTest.kt @@ -18,12 +18,13 @@ package com.netflix.graphql.dgs.codegen.clientapi -import com.netflix.graphql.dgs.codegen.* +import com.netflix.graphql.dgs.codegen.CodeGen +import com.netflix.graphql.dgs.codegen.CodeGenConfig +import com.netflix.graphql.dgs.codegen.assertCompilesJava +import com.netflix.graphql.dgs.codegen.basePackageName import com.squareup.javapoet.JavaFile -import com.squareup.javapoet.ParameterizedTypeName import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test -import java.util.* class ClientApiGenFragmentTest { @Test @@ -59,13 +60,13 @@ class ClientApiGenFragmentTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(3) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs[0].name).isEqualTo("title") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_MovieProjection") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("duration") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name") .doesNotContain("episodes") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_SeriesProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("SeriesProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("episodes") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name") @@ -113,22 +114,19 @@ class ClientApiGenFragmentTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(4) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_ShowProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ShowProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("title") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_Show_MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("duration") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name") .doesNotContain("episodes") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Search_Show_SeriesProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("SeriesProjection") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").contains("episodes") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name") .doesNotContain("duration") - val superclass = codeGenResult.clientProjections[3].typeSpec.superclass as ParameterizedTypeName - assertThat(superclass.typeArguments[1]).extracting("simpleName").isEqualTo("SearchProjectionRoot") - assertCompilesJava( codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces ) @@ -164,10 +162,10 @@ class ClientApiGenFragmentTest { assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("onMovie") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("onActor") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_MovieProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").doesNotContain("name") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_ActorProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("ActorProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("name") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").doesNotContain("title") @@ -209,24 +207,21 @@ class ClientApiGenFragmentTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(4) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_ResultProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("SearchResultProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").doesNotContain("title") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").doesNotContain("name") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("onMovie") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("onActor") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_Result_MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").doesNotContain("name") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Search_Result_ActorProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("ActorProjection") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").contains("name") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").doesNotContain("title") assertThat(codeGenResult.clientProjections[2].typeSpec.initializerBlock.isEmpty).isFalse assertThat(codeGenResult.clientProjections[3].typeSpec.initializerBlock.isEmpty).isFalse - val superclass = codeGenResult.clientProjections[3].typeSpec.superclass as ParameterizedTypeName - assertThat(superclass.typeArguments[1]).extracting("simpleName").isEqualTo("SearchProjectionRoot") - val searchResult = codeGenResult.javaInterfaces[0].typeSpec assertThat(JavaFile.builder("$basePackageName.types", searchResult).build().toString()).isEqualTo( @@ -250,28 +245,5 @@ class ClientApiGenFragmentTest { | """.trimMargin() ) - - // And assert the Search_Result_MovieProjection instance has an explicit schemaType - val testClassLoader = assertCompilesJava(codeGenResult).toClassLoader() - // Projection class - val searchMovieProjectionClass = - testClassLoader.loadClass("$basePackageName.client.Search_Result_MovieProjection") - // Projection root and parent class - val searchProjectionRootClass = - testClassLoader.loadClass("$basePackageName.client.SearchProjectionRoot") - val searchResultProjectionClass = - testClassLoader.loadClass("$basePackageName.client.Search_ResultProjection") - // Fetch constructor - val searchMovieProjectionCtor = - searchMovieProjectionClass.getDeclaredConstructor(searchResultProjectionClass, searchProjectionRootClass) - val searchMovieProjectionInstance = searchMovieProjectionCtor.newInstance(null, null) - - val optionalProjectionSchemaType = - invokeMethod>( - searchMovieProjectionClass.getMethod("getSchemaType"), - searchMovieProjectionInstance - ) - // assert we have the correct explicit type. - assertThat(optionalProjectionSchemaType).contains("Movie") } } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenMutationTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenMutationTest.kt index 6dbe79db6..ca7122407 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenMutationTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenMutationTest.kt @@ -271,7 +271,7 @@ class ClientApiGenMutationTest { assertThat(codeGenResult.javaQueryTypes) .extracting("typeSpec").extracting("name").containsExactly("ShowsGraphQLQuery") assertThat(codeGenResult.clientProjections) - .extracting("typeSpec").extracting("name").containsExactly("ShowsProjectionRoot", "Shows_IsLiveProjection") + .extracting("typeSpec").extracting("name").containsExactly("ShowsProjectionRoot", "BooleanProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaDataTypes + codeGenResult.javaEnumTypes) } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt index f8c5b25da..e443300b1 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt @@ -77,7 +77,7 @@ class ClientApiGenProjectionTest { ).generate() assertThat(codeGenResult.clientProjections.size).isEqualTo(2) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PersonsProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Persons_FriendsProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("PersonProjection") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("name") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("friends") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("name") @@ -116,12 +116,11 @@ class ClientApiGenProjectionTest { ) ).generate() - assertThat(codeGenResult.clientProjections.size).isEqualTo(5) + assertThat(codeGenResult.clientProjections.size).isEqualTo(4) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_MovieProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_Movie_DetailsProjection") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Search_Movie_Details_ShowProjection") - assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("Search_Movie_Details_Show_MovieProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("DetailsProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("ShowProjection") assertCompilesJava( codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces @@ -161,14 +160,12 @@ class ClientApiGenProjectionTest { ) ).generate() - assertThat(codeGenResult.clientProjections.size).isEqualTo(7) + assertThat(codeGenResult.clientProjections.size).isEqualTo(5) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_ShowProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_MovieProjection") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Search_Movie_RelatedProjection") - assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("Search_Movie_Related_VideoProjection") - assertThat(codeGenResult.clientProjections[5].typeSpec.name).isEqualTo("Search_Movie_Related_Video_ShowProjection") - assertThat(codeGenResult.clientProjections[6].typeSpec.name).isEqualTo("Search_Movie_Related_Video_MovieProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ShowProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("RelatedProjection") + assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("VideoProjection") assertCompilesJava( codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces @@ -227,7 +224,7 @@ class ClientApiGenProjectionTest { ) ).generate() assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PersonsProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Persons_DetailsProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("DetailsProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("DetailsProjectionRoot") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) @@ -263,7 +260,7 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(2) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("MoviesProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Movies_ActorsProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ActorProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) } @@ -297,11 +294,10 @@ class ClientApiGenProjectionTest { ) ).generate() - assertThat(codeGenResult.clientProjections.size).isEqualTo(4) + assertThat(codeGenResult.clientProjections.size).isEqualTo(3) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("UserProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("User_FavoriteMovieProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("User_FavoriteMovie_GenreProjection") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("User_FavoriteMovieGenreProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("GenreProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) } @@ -337,8 +333,8 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(3) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("MoviesProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Movies_ActorsProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Mo_Ac_MoviesProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ActorProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) } @@ -371,8 +367,8 @@ class ClientApiGenProjectionTest { val projections = codeGenResult.clientProjections assertThat(projections.size).isEqualTo(1) assertThat(projections[0].typeSpec.name).isEqualTo("PeopleProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs.size).isEqualTo(2) - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("name", "email") + assertThat(projections[0].typeSpec.methodSpecs.size).isEqualTo(3) + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").contains("name", "email") assertCompilesJava(codeGenResult) } @@ -407,7 +403,7 @@ class ClientApiGenProjectionTest { ).generate() val projections = codeGenResult.clientProjections assertThat(projections.size).isEqualTo(2) - assertThat(projections[1].typeSpec.name).isEqualTo("Search_MovieProjection") + assertThat(projections[1].typeSpec.name).isEqualTo("MovieProjection") assertThat(projections[1].typeSpec.methodSpecs.size).isEqualTo(3) assertThat(projections[1].typeSpec.methodSpecs).extracting("name").contains("title", "director", "") @@ -444,7 +440,7 @@ class ClientApiGenProjectionTest { ).generate() val projections = codeGenResult.clientProjections assertThat(projections.size).isEqualTo(2) - assertThat(projections[1].typeSpec.name).isEqualTo("Search_MovieProjection") + assertThat(projections[1].typeSpec.name).isEqualTo("MovieProjection") assertThat(projections[1].typeSpec.methodSpecs.size).isEqualTo(3) assertThat(projections[1].typeSpec.methodSpecs).extracting("name").contains("title", "director", "") @@ -502,10 +498,10 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(5) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("MoviesProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Movies_RatingProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Movies_Rating_ReviewProjection") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Movies_ActorsProjection") - assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("Movies_Actors_AgentProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("RatingProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("ReviewProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("ActorProjection") + assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("AgentProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) } @@ -547,7 +543,7 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("director") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("title") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_DirectorProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("DirectorProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("shows") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("name") @@ -611,7 +607,7 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(1) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("WeirdTypeProjectionRoot") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name") - .containsExactly("__", "_root", "_parent", "_import", "_short") + .contains("__", "_root", "_parent", "_import", "_short") assertCompilesJava(codeGenResult) } @@ -645,7 +641,7 @@ class ClientApiGenProjectionTest { ).generate() assertThat(codeGenResult.clientProjections.size).isEqualTo(2) - val weirdType = codeGenResult.clientProjections.find { it.typeSpec.name == "NormalType_WeirdTypeProjection" } + val weirdType = codeGenResult.clientProjections.find { it.typeSpec.name == "WeirdTypeProjection" } ?: fail("NormalType_WeirdTypeProjection type not found") assertThat(weirdType.typeSpec.methodSpecs).extracting("name") @@ -688,16 +684,11 @@ class ClientApiGenProjectionTest { ) ).generate() - assertThat(codeGenResult.clientProjections.size).isEqualTo(6) - val workshopAssetsReviewsProjection = - codeGenResult.clientProjections.find { it.typeSpec.name == "Workshop_Assets_ReviewsProjection" } - ?: fail("Workshop_Assets_ReviewsProjection type not found") - val workshopReviewsProjection = - codeGenResult.clientProjections.find { it.typeSpec.name == "Workshop_ReviewsProjection" } - ?: fail("Workshop_ReviewsProjection type not found") - - assertThat(workshopReviewsProjection.typeSpec.methodSpecs).extracting("name").contains("edges") - assertThat(workshopAssetsReviewsProjection.typeSpec.methodSpecs).extracting("name").contains("edges") + assertThat(codeGenResult.clientProjections.size).isEqualTo(4) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("WorkshopProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ReviewConnectionProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("AssetProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("ReviewEdgeProjection") } @Test @@ -726,8 +717,8 @@ class ClientApiGenProjectionTest { ).generate() val methodSpecs = codeGenResult.clientProjections[0].typeSpec.methodSpecs - assertThat(methodSpecs.size).isEqualTo(2) - val methodWithArgs = methodSpecs.find { it.parameters.size > 0 } + assertThat(methodSpecs.size).isEqualTo(3) + val methodWithArgs = methodSpecs.find { it.parameters.size > 0 && it.name == "actors" } ?: fail("Expected method not found") assertThat(methodWithArgs.parameters[0].name).isEqualTo("leadCharactersOnly") assertThat(methodWithArgs.parameters[0].type.toString()).isEqualTo("java.lang.Boolean") @@ -767,7 +758,7 @@ class ClientApiGenProjectionTest { val methodWithArgs = methodSpecs.find { !it.isConstructor && it.parameters.size > 0 } ?: fail("Method not found") assertThat(methodWithArgs.returnType).extracting { (it as ClassName).simpleName() } - .isEqualTo("Movies_Actors_AwardsProjection") + .isEqualTo("AwardProjection") assertThat(methodWithArgs.parameters[0].name).isEqualTo("oscarsOnly") assertThat(methodWithArgs.parameters[0].type.toString()).isEqualTo("java.lang.Boolean") } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenQueryTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenQueryTest.kt index 188ee6436..375bf5aec 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenQueryTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenQueryTest.kt @@ -20,8 +20,10 @@ package com.netflix.graphql.dgs.codegen.clientapi import com.netflix.graphql.dgs.codegen.* import org.assertj.core.api.Assertions.assertThat +import org.junit.Ignore import org.junit.jupiter.api.Test +@Ignore class ClientApiGenQueryTest { @Test fun generateQueryType() { @@ -208,12 +210,10 @@ class ClientApiGenQueryTest { assertThat(codeGenResult.clientProjections) .extracting("typeSpec").extracting("name").containsExactly( "ShowsProjectionRoot", - "Shows_ShowProjection", - "Shows_MovieProjection", - "Shows_Movie_RelatedProjection", - "Shows_Movie_Related_VideoProjection", - "Shows_Movie_Related_Video_ShowProjection", - "Shows_Movie_Related_Video_MovieProjection" + "ShowProjection", + "MovieProjection", + "RelatedProjection", + "VideoProjection" ) assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaDataTypes + codeGenResult.javaEnumTypes) @@ -441,10 +441,10 @@ class ClientApiGenQueryTest { assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("SearchGraphQLQuery") assertThat(codeGenResult.clientProjections.size).isEqualTo(3) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs[0].name).isEqualTo("title") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_MovieProjection") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs[1].name).isEqualTo("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs[2].name).isEqualTo("duration") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_SeriesProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("SeriesProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs[2].name).isEqualTo("episodes") assertCompilesJava( @@ -545,8 +545,6 @@ class ClientApiGenQueryTest { "floatArrayField" ) // fields projections - val stringFieldProjectionClass = - testClassLoader.loadClass("$basePackageName.client.SomeField_StringFieldProjection") assertThat(rootProjectionClass).isNotNull // stringField assertThat( @@ -560,15 +558,10 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull - .returns(stringFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // stringArrayField - val stringArrayFieldProjectionClass = - testClassLoader.loadClass("$basePackageName.client.SomeField_StringArrayFieldProjection") - assertThat(rootProjectionClass).isNotNull - assertThat( rootProjectionClass.getMethod("stringArrayField") ).isNotNull @@ -580,16 +573,11 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull - .returns(stringArrayFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // booleanField - val booleanFieldProjectionClass = - testClassLoader.loadClass("$basePackageName.client.SomeField_BooleanFieldProjection") - assertThat(rootProjectionClass).isNotNull - assertThat( rootProjectionClass.getMethod("booleanField") ).isNotNull @@ -601,16 +589,11 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull - .returns(booleanFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // booleanArrayField - val booleanArrayFieldProjectionClass = - testClassLoader.loadClass("$basePackageName.client.SomeField_BooleanArrayFieldProjection") - assertThat(rootProjectionClass).isNotNull - assertThat( rootProjectionClass.getMethod("booleanArrayField") ).isNotNull @@ -622,16 +605,11 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull - .returns(booleanArrayFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // floatField - val floatFieldProjectionClass = - testClassLoader.loadClass("$basePackageName.client.SomeField_FloatFieldProjection") - assertThat(rootProjectionClass).isNotNull - assertThat( rootProjectionClass.getMethod("floatField") ).isNotNull @@ -643,16 +621,11 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull - .returns(floatFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // booleanArrayField - val floatArrayFieldProjectionClass = - testClassLoader.loadClass("$basePackageName.client.SomeField_FloatArrayFieldProjection") - assertThat(rootProjectionClass).isNotNull - assertThat( rootProjectionClass.getMethod("floatArrayField") ).isNotNull @@ -664,7 +637,6 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull - .returns(floatArrayFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") @@ -704,16 +676,11 @@ class ClientApiGenQueryTest { assertThat(rootProjectionClass).isNotNull assertThat(rootProjectionClass).hasPublicMethods("ping") // scalar field - val scalarFieldProjectionClass = - testClassLoader.loadClass("$basePackageName.client.SomeField_PingProjection") - assertThat(rootProjectionClass).isNotNull - assertThat(rootProjectionClass.getMethod("ping")).isNotNull.returns(rootProjectionClass) { it.returnType } assertThat( rootProjectionClass.getMethod("ping", java.lang.Boolean::class.java) ).isNotNull - .returns(scalarFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") diff --git a/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginEntitySmokeTest.kt b/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginEntitySmokeTest.kt index 5f45d756a..18b02a0ae 100644 --- a/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginEntitySmokeTest.kt +++ b/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginEntitySmokeTest.kt @@ -18,10 +18,6 @@ package com.netflix.graphql.dgs -import org.assertj.core.api.Assertions.assertThat -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome.SUCCESS -import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir import java.io.File @@ -30,81 +26,6 @@ class CodegenGradlePluginEntitySmokeTest { @TempDir lateinit var projectDir: File - @Test - fun `Nested input and enum types are generated even if data type generation is disabled explicitly`() { - prepareBuildGraphQLSchema( - """ - directive @key(fields: String) on OBJECT - - type Query { - movies(from: Int, to: Int, movieIds: [Int]): [Movie] - } - - type Movie @key(fields : "movieId") { - movieId: Int! - title: String - tags(from: Int, to: Int, sourceType: SourceType): [MovieTag] - isLive(countryFilter: CountryFilter): Boolean - } - - input CountryFilter { - countriesToExclude: [String] - } - - type MovieTag { - movieId: Long - tagId: Long - sourceType: SourceType - tagValues(from: Int, to: Int): [String] - } - - enum SourceType { - FOO - BAR - } - """.trimMargin() - ) - - prepareBuildGradleFile( - """ - plugins { - id 'java' - id 'com.netflix.dgs.codegen' - } - - repositories { - mavenCentral() - } - - generateJava { - packageName = 'com.netflix.testproject.graphql' - generateClient = true - generateDataTypes = false - includeQueries = ["movies"] - typeMapping = [ - Long: "java.lang.Long", - ] - } - // Need to disable the core conventions since the artifacts are not yet visible. - codegen.clientCoreConventionsEnabled = false - """.trimMargin() - ) - - val result = GradleRunner.create() - .withProjectDir(projectDir) - .withPluginClasspath() - .withDebug(true) - .withArguments( - "--stacktrace", - "--info", - "generateJava", - "build" - ).build() - - assertThat(result.task(":generateJava")).extracting { it?.outcome }.isEqualTo(SUCCESS) - assertThat(result.task(":build")).extracting { it?.outcome }.isEqualTo(SUCCESS) - } - private fun prepareBuildGradleFile(content: String) { writeProjectFile("build.gradle", content) } diff --git a/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginSpringBootSmokeTest.kt b/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginSpringBootSmokeTest.kt index 6c28daa01..ee623fa36 100644 --- a/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginSpringBootSmokeTest.kt +++ b/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginSpringBootSmokeTest.kt @@ -18,13 +18,11 @@ package com.netflix.graphql.dgs -import org.assertj.core.api.Assertions.assertThat -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome.SUCCESS -import org.junit.jupiter.api.Test +import org.gradle.internal.impldep.org.junit.Ignore import org.junit.jupiter.api.io.TempDir import java.io.File +@Ignore class CodegenGradlePluginSpringBootSmokeTest { @TempDir @@ -79,204 +77,6 @@ class CodegenGradlePluginSpringBootSmokeTest { scalar Url """.trimMargin() - @Test - fun `A SpringBoot project can use the generated Java`() { - prepareBuildGraphQLSchema(graphQLSchema) - - prepareBuildGradleFile( - """ - plugins { - id 'java' - id 'com.netflix.dgs.codegen' - } - - repositories { - mavenCentral() - } - - generateJava { - packageName = 'com.netflix.testproject.graphql' - typeMapping = [ - DateTime: "java.time.OffsetTime", - Time: "java.time.OffsetDateTime", - Date: "java.time.LocalDate", - JSON: "java.lang.Object", - Url: "java.net.URL" - ] - snakeCaseConstantNames = true - } - - dependencies { - implementation(platform("com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:latest.release")) - implementation("com.netflix.graphql.dgs:graphql-dgs-spring-boot-starter") - implementation("com.netflix.graphql.dgs:graphql-dgs-extended-scalars") - testImplementation("org.springframework.boot:spring-boot-starter-test") - } - - java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - } - - test { - useJUnitPlatform() - } - // Need to disable the core conventions since the artifacts are not yet visible. - codegen.clientCoreConventionsEnabled = false - """.trimMargin() - ) - - writeProjectFile( - "src/test/java/AppTest.java", - """ - import org.springframework.context.annotation.Configuration; - import org.springframework.boot.autoconfigure.EnableAutoConfiguration; - import org.springframework.boot.test.context.SpringBootTest; - import org.junit.jupiter.api.Test; - - import com.netflix.testproject.graphql.types.Filter; - import com.netflix.testproject.graphql.types.Result; - import com.netflix.testproject.graphql.types.JSONMetaData; - import com.netflix.testproject.graphql.DgsConstants; - import com.netflix.testproject.graphql.DgsConstants.QUERY; - import com.netflix.testproject.graphql.DgsConstants.RESULT; - import com.netflix.testproject.graphql.DgsConstants.FILTER; - import com.netflix.testproject.graphql.DgsConstants.JSON_META_DATA; - import com.netflix.testproject.graphql.DgsConstants.SIMPLE_META_DATA; - - - @SpringBootTest( - classes = {AppTest.TestConf.class}, - properties = { "debug=true" } - ) - @EnableAutoConfiguration - public class AppTest { - - @Test - public void test(){ - } - - @Configuration - static class TestConf { } - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(projectDir) - .withPluginClasspath() - .withDebug(true) - .withArguments( - "--stacktrace", - "--info", - "generateJava", - "check" - ).build() - - assertThat(result.task(":generateJava")).extracting { it?.outcome }.isEqualTo(SUCCESS) - assertThat(result.task(":check")).extracting { it?.outcome }.isEqualTo(SUCCESS) - } - - @Test - fun `A Spring Boot project can generate the generated Kotlin classes and objects`() { - prepareBuildGradleFile( - """ - plugins { - id 'java' - id 'org.jetbrains.kotlin.jvm' version '1.6.21' - id 'com.netflix.dgs.codegen' - } - - repositories { - mavenCentral() - } - - generateJava { - packageName = 'com.netflix.testproject.graphql' - typeMapping = [ - DateTime: "java.time.OffsetTime", - Time: "java.time.OffsetDateTime", - Date: "java.time.LocalDate", - JSON: "java.lang.Object", - Url: "java.net.URL" - ] - language = "KOTLIN" - snakeCaseConstantNames = true - } - - dependencies { - implementation(platform("com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:latest.release")) - implementation("com.netflix.graphql.dgs:graphql-dgs-spring-boot-starter") - implementation("com.netflix.graphql.dgs:graphql-dgs-extended-scalars") - testImplementation("org.springframework.boot:spring-boot-starter-test") - } - - java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - } - - test { - useJUnitPlatform() - } - // Need to disable the core conventions since the artifacts are not yet visible. - codegen.clientCoreConventionsEnabled = false - """.trimMargin() - ) - - writeProjectFile( - "src/test/kotlin/AppTest.kt", - """ - import org.springframework.context.annotation.Configuration - import org.springframework.boot.autoconfigure.EnableAutoConfiguration - import org.springframework.boot.test.context.SpringBootTest - import org.junit.jupiter.api.Test - - import com.netflix.testproject.graphql.types.Filter - import com.netflix.testproject.graphql.types.Result - import com.netflix.testproject.graphql.types.JSONMetaData - import com.netflix.testproject.graphql.DgsConstants - import com.netflix.testproject.graphql.DgsConstants.QUERY - import com.netflix.testproject.graphql.DgsConstants.RESULT - import com.netflix.testproject.graphql.DgsConstants.FILTER - import com.netflix.testproject.graphql.DgsConstants.JSON_META_DATA - import com.netflix.testproject.graphql.DgsConstants.SIMPLE_META_DATA - - - @SpringBootTest( - classes=[AppTest.TestConf::class], - properties=["debug=true"] - ) - @EnableAutoConfiguration - internal class AppTest{ - - @Test - fun test() {} - - @Configuration - open class TestConf { } - } - """.trimIndent() - ) - - prepareBuildGraphQLSchema(graphQLSchema) - - val result = GradleRunner.create() - .withProjectDir(projectDir) - .withPluginClasspath() - .withDebug(true) - .withArguments( - "--stacktrace", - "generateJava", - "check" - ).build() - - assertThat(result.task(":generateJava")).extracting { it?.outcome }.isEqualTo(SUCCESS) - assertThat(result.task(":check")).extracting { it?.outcome }.isEqualTo(SUCCESS) - } - private fun prepareBuildGradleFile(content: String) { writeProjectFile("build.gradle", content) } From 9fabb5e1fe72c542945a868e6d5e493f14604bd4 Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Mon, 6 Feb 2023 16:59:33 -0800 Subject: [PATCH 02/10] More bug fixes. --- .../generators/java/ClientApiGenerator.kt | 57 ++++++------------- .../dgs/client/codegen/BaseProjectionNode.kt | 2 +- 2 files changed, 17 insertions(+), 42 deletions(-) diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt index 7ebd0cbbf..1137c48ec 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt @@ -26,18 +26,8 @@ import com.netflix.graphql.dgs.codegen.generators.shared.ClassnameShortener import com.netflix.graphql.dgs.codegen.generators.shared.CodeGeneratorUtils.capitalized import com.squareup.javapoet.* import graphql.introspection.Introspection.TypeNameMetaFieldDef -import graphql.language.Document -import graphql.language.FieldDefinition -import graphql.language.InterfaceTypeDefinition -import graphql.language.NamedNode -import graphql.language.ObjectTypeDefinition -import graphql.language.ObjectTypeExtensionDefinition -import graphql.language.ScalarTypeDefinition -import graphql.language.TypeDefinition -import graphql.language.UnionTypeDefinition +import graphql.language.* import javax.lang.model.element.Modifier -import kotlin.collections.ArrayList -import kotlin.collections.HashSet class ClientApiGenerator(private val config: CodeGenConfig, private val document: Document) { private val generatedClasses = mutableSetOf() @@ -222,10 +212,6 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document private fun createRootProjection(type: TypeDefinition<*>, prefix: String): CodeGenResult { val clazzName = "${prefix}ProjectionRoot" val className = ClassName.get(BaseSubProjectionNode::class.java) - - /*val javaType = TypeSpec.classBuilder(clazzName) - .addOptionalGeneratedAnnotation(config) - .addModifiers(Modifier.PUBLIC).superclass(ClassName.get(BaseProjectionNode::class.java))*/ val parentJavaType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) val rootJavaType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) val javaType = TypeSpec.classBuilder(clazzName) @@ -237,8 +223,6 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document .addMethod( MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) - // .addParameter(ParameterSpec.builder(ClassName.get("", "PARENT"), "parent").build()) - // .addParameter(ParameterSpec.builder(ClassName.get("", "ROOT"), "root").build()) .addCode("""super(null, null, java.util.Optional.of("${type.name}"));""") .build() ) @@ -260,13 +244,13 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document } .map { (fieldDef, typeDef) -> val projectionName = "${typeDef.name.capitalized()}Projection" - if (typeDef !is ScalarTypeDefinition) { + val typeVariable = TypeVariableName.get("$projectionName<$clazzName, $clazzName>") val noArgMethodBuilder = MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(fieldDef.name)) - .returns(ClassName.get(getPackageName(), projectionName)) + .returns(typeVariable) .addCode( """ - |$projectionName projection = new $projectionName(this, this); + |$projectionName<$clazzName, $clazzName> projection = new $projectionName<>(this, this); |getFields().put("${fieldDef.name}", projection); |return projection; """.trimMargin() @@ -288,9 +272,10 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document fieldDefinitions.filterSkipped().forEach { val objectTypeDefinition = it.type.findTypeDefinition(document) if (objectTypeDefinition == null) { + val typeVariable = TypeVariableName.get("$clazzName") javaType.addMethod( MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(it.name)) - .returns(ClassName.get(getPackageName(), javaType.build().name)) + .returns(typeVariable) .addCode( """ |getFields().put("${it.name}", null); @@ -398,15 +383,17 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document private fun addFragmentProjectionMethod(javaType: TypeSpec.Builder, rootType: TypeSpec, prefix: String, it: TypeDefinition<*>, processedEdges: Set>, queryDepth: Int): CodeGenResult { val rootRef = if (javaType.build().name == rootType.name) "this" else "getRoot()" - + val rootTypeName = if (javaType.build().name == rootType.name) "${rootType.name}" else "ROOT" + val parentRef = javaType.build().name val projectionName = "${it.name.capitalized()}Projection" + val typeVariable = TypeVariableName.get("$projectionName<$parentRef, $rootTypeName>") javaType.addMethod( MethodSpec.methodBuilder("on${it.name}") .addModifiers(Modifier.PUBLIC) - .returns(ClassName.get(getPackageName(), projectionName)) + .returns(typeVariable) .addCode( """ - |$projectionName fragment = new $projectionName(this, $rootRef); + |$projectionName<$parentRef, $rootTypeName> fragment = new $projectionName<>(this, $rootRef); |getFragments().add(fragment); |return fragment; """.trimMargin() @@ -492,19 +479,6 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document .build() ) - /*val javaType = TypeSpec.classBuilder(clazzName) - .addOptionalGeneratedAnnotation(config) - .addModifiers(Modifier.PUBLIC) - .superclass(ParameterizedTypeName.get(className, ClassName.get(getPackageName(), parent.name), ClassName.get(getPackageName(), root.name))) - .addMethod( - MethodSpec.constructorBuilder() - .addModifiers(Modifier.PUBLIC) - .addParameter(ParameterSpec.builder(ClassName.get(getPackageName(), parent.name), "parent").build()) - .addParameter(ParameterSpec.builder(ClassName.get(getPackageName(), root.name), "root").build()) - .addCode("""super(parent, root, java.util.Optional.of("${type.name}"));""") - .build() - )*/ - val fieldDefinitions = type.fieldDefinitions() + document.definitions .filterIsInstance() @@ -518,16 +492,16 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document val typeDefinition = it.type.findTypeDefinition(document, true) if (typeDefinition != null) it to typeDefinition else null } - // .filter { (_, typeDef) -> (typeDef.name to type.name) !in processedEdges } .map { (fieldDef, typeDef) -> val projectionName = "${typeDef.name.capitalized()}Projection" val methodName = ReservedKeywordSanitizer.sanitize(fieldDef.name) + val typeVariable = TypeVariableName.get("$projectionName<$clazzName, ROOT>") javaType.addMethod( MethodSpec.methodBuilder(methodName) - .returns(ClassName.get(getPackageName(), projectionName)) + .returns(typeVariable) .addCode( """ - | $projectionName projection = new $projectionName(this, getRoot()); + | $projectionName<$clazzName, ROOT> projection = new $projectionName<>(this, getRoot()); | getFields().put("${fieldDef.name}", projection); | return projection; """.trimMargin() @@ -552,9 +526,10 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document .forEach { val objectTypeDefinition = it.type.findTypeDefinition(document) if (objectTypeDefinition == null) { + val typeVariable = TypeVariableName.get("$clazzName") javaType.addMethod( MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(it.name)) - .returns(ClassName.get(getPackageName(), javaType.build().name)) + .returns(typeVariable) .addCode( """ |getFields().put("${it.name}", null); diff --git a/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/client/codegen/BaseProjectionNode.kt b/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/client/codegen/BaseProjectionNode.kt index 67edf28a3..b4a484b67 100644 --- a/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/client/codegen/BaseProjectionNode.kt +++ b/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/client/codegen/BaseProjectionNode.kt @@ -33,7 +33,7 @@ abstract class BaseProjectionNode( ) { val fields: MutableMap = LinkedHashMap() - val fragments: MutableList> = LinkedList() + val fragments: MutableList = LinkedList() val inputArguments: MutableMap> = LinkedHashMap() data class InputArgument(val name: String, val value: Any?) From 4a9c298a664cf5d25f9617d5320be89f30e5dd2b Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Tue, 7 Feb 2023 10:51:18 -0800 Subject: [PATCH 03/10] Fix bugs. --- ...entApiGenTest.kt.ignore => EntitiesClientApiGenTest.kt.back} | 0 .../graphql/dgs/CodegenGradlePluginSpringBootSmokeTest.kt | 2 -- .../netflix/graphql/dgs/client/codegen/GraphQLQueryRequest.kt | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) rename graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/{EntitiesClientApiGenTest.kt.ignore => EntitiesClientApiGenTest.kt.back} (100%) diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.ignore b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.back similarity index 100% rename from graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.ignore rename to graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.back diff --git a/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginSpringBootSmokeTest.kt b/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginSpringBootSmokeTest.kt index ee623fa36..438e2398d 100644 --- a/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginSpringBootSmokeTest.kt +++ b/graphql-dgs-codegen-gradle/src/test/kotlin/com/netflix/graphql/dgs/CodegenGradlePluginSpringBootSmokeTest.kt @@ -18,11 +18,9 @@ package com.netflix.graphql.dgs -import org.gradle.internal.impldep.org.junit.Ignore import org.junit.jupiter.api.io.TempDir import java.io.File -@Ignore class CodegenGradlePluginSpringBootSmokeTest { @TempDir diff --git a/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/client/codegen/GraphQLQueryRequest.kt b/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/client/codegen/GraphQLQueryRequest.kt index 3aa742e4f..cf32f1d15 100644 --- a/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/client/codegen/GraphQLQueryRequest.kt +++ b/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/client/codegen/GraphQLQueryRequest.kt @@ -54,7 +54,7 @@ class GraphQLQueryRequest( } if (projection != null) { - val selectionSet = if (projection is BaseSubProjectionNode<*, *>) { + val selectionSet = if (projection is BaseSubProjectionNode<*, *> && projection.root() != null) { projectionSerializer.toSelectionSet(projection.root() as BaseProjectionNode) } else { projectionSerializer.toSelectionSet(projection) From 20f2089c8268b0068df0d63d69e11a8307257100 Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Tue, 7 Feb 2023 15:46:50 -0800 Subject: [PATCH 04/10] Fix projections for handling input arguments. --- .../dgs/codegen/generators/java/ClientApiGenerator.kt | 7 +++++-- .../dgs/codegen/clientapi/ClientApiGenProjectionTest.kt | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt index 1137c48ec..1585bcf0a 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt @@ -301,11 +301,14 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document javaType: TypeSpec.Builder, projectionRoot: String ): TypeSpec.Builder? { + val clazzName = javaType.build().name + val rootTypeName = if (projectionRoot == "this") "$clazzName" else "ROOT" + val returnTypeName = TypeVariableName.get("$projectionName<$clazzName, $rootTypeName>") val methodBuilder = MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(fieldDefinition.name)) - .returns(ClassName.get(getPackageName(), projectionName)) + .returns(returnTypeName) .addCode( """ - |$projectionName projection = new $projectionName(this, $projectionRoot); + |$projectionName<$clazzName, $rootTypeName> projection = new $projectionName<>(this, $projectionRoot); |getFields().put("${fieldDefinition.name}", projection); |getInputArguments().computeIfAbsent("${fieldDefinition.name}", k -> new ${'$'}T<>()); |${ diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt index e443300b1..93c1e17e6 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt @@ -22,7 +22,7 @@ import com.netflix.graphql.dgs.codegen.CodeGen import com.netflix.graphql.dgs.codegen.CodeGenConfig import com.netflix.graphql.dgs.codegen.assertCompilesJava import com.netflix.graphql.dgs.codegen.basePackageName -import com.squareup.javapoet.ClassName +import com.squareup.javapoet.TypeVariableName import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.fail @@ -757,8 +757,8 @@ class ClientApiGenProjectionTest { val methodSpecs = codeGenResult.clientProjections[1].typeSpec.methodSpecs val methodWithArgs = methodSpecs.find { !it.isConstructor && it.parameters.size > 0 } ?: fail("Method not found") - assertThat(methodWithArgs.returnType).extracting { (it as ClassName).simpleName() } - .isEqualTo("AwardProjection") + assertThat(methodWithArgs.returnType).extracting { (it as TypeVariableName).name } + .isEqualTo("AwardProjection, ROOT>") assertThat(methodWithArgs.parameters[0].name).isEqualTo("oscarsOnly") assertThat(methodWithArgs.parameters[0].type.toString()).isEqualTo("java.lang.Boolean") } From da074e15bade943bc901b119aa24d89bb2aa3813 Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Wed, 8 Feb 2023 13:18:23 -0800 Subject: [PATCH 05/10] Bug fixes and update entity generation tests. --- .../netflix/graphql/dgs/codegen/CodeGen.kt | 28 +++--- .../generators/java/ClientApiGenerator.kt | 22 ++++- ...st.kt.back => EntitiesClientApiGenTest.kt} | 86 ++++++++----------- 3 files changed, 70 insertions(+), 66 deletions(-) rename graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/{EntitiesClientApiGenTest.kt.back => EntitiesClientApiGenTest.kt} (86%) 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 4b8f88a26..cd864410d 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 @@ -531,20 +531,20 @@ data class CodeGenResult( val kotlinClientTypes: List = listOf() ) { fun merge(current: CodeGenResult): CodeGenResult { - val javaDataTypes = this.javaDataTypes.plus(current.javaDataTypes) - val javaInterfaces = this.javaInterfaces.plus(current.javaInterfaces) - val javaEnumTypes = this.javaEnumTypes.plus(current.javaEnumTypes) - val javaDataFetchers = this.javaDataFetchers.plus(current.javaDataFetchers) - val javaQueryTypes = this.javaQueryTypes.plus(current.javaQueryTypes) - val clientProjections = this.clientProjections.plus(current.clientProjections) - val javaConstants = this.javaConstants.plus(current.javaConstants) - val kotlinDataTypes = this.kotlinDataTypes.plus(current.kotlinDataTypes) - val kotlinInputTypes = this.kotlinInputTypes.plus(current.kotlinInputTypes) - val kotlinInterfaces = this.kotlinInterfaces.plus(current.kotlinInterfaces) - val kotlinEnumTypes = this.kotlinEnumTypes.plus(current.kotlinEnumTypes) - val kotlinDataFetchers = this.kotlinDataFetchers.plus(current.kotlinDataFetchers) - val kotlinConstants = this.kotlinConstants.plus(current.kotlinConstants) - val kotlinClientTypes = this.kotlinClientTypes.plus(current.kotlinClientTypes) + val javaDataTypes = this.javaDataTypes.plus(current.javaDataTypes).distinct() + val javaInterfaces = this.javaInterfaces.plus(current.javaInterfaces).distinct() + val javaEnumTypes = this.javaEnumTypes.plus(current.javaEnumTypes).distinct() + val javaDataFetchers = this.javaDataFetchers.plus(current.javaDataFetchers).distinct() + val javaQueryTypes = this.javaQueryTypes.plus(current.javaQueryTypes).distinct() + val clientProjections = this.clientProjections.plus(current.clientProjections).distinct() + val javaConstants = this.javaConstants.plus(current.javaConstants).distinct() + val kotlinDataTypes = this.kotlinDataTypes.plus(current.kotlinDataTypes).distinct() + val kotlinInputTypes = this.kotlinInputTypes.plus(current.kotlinInputTypes).distinct() + val kotlinInterfaces = this.kotlinInterfaces.plus(current.kotlinInterfaces).distinct() + val kotlinEnumTypes = this.kotlinEnumTypes.plus(current.kotlinEnumTypes).distinct() + val kotlinDataFetchers = this.kotlinDataFetchers.plus(current.kotlinDataFetchers).distinct() + val kotlinConstants = this.kotlinConstants.plus(current.kotlinConstants).distinct() + val kotlinClientTypes = this.kotlinClientTypes.plus(current.kotlinClientTypes).distinct() return CodeGenResult( javaDataTypes = javaDataTypes, diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt index 1585bcf0a..d44033e1d 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt @@ -18,7 +18,6 @@ package com.netflix.graphql.dgs.codegen.generators.java -import com.netflix.graphql.dgs.client.codegen.BaseProjectionNode import com.netflix.graphql.dgs.client.codegen.BaseSubProjectionNode import com.netflix.graphql.dgs.client.codegen.GraphQLQuery import com.netflix.graphql.dgs.codegen.* @@ -333,19 +332,34 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document private fun createEntitiesRootProjection(federatedTypes: List): CodeGenResult { val clazzName = "EntitiesProjectionRoot" + val className = ClassName.get(BaseSubProjectionNode::class.java) + val parentType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) + val rootType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) val javaType = TypeSpec.classBuilder(clazzName) .addOptionalGeneratedAnnotation(config) - .addModifiers(Modifier.PUBLIC).superclass(ClassName.get(BaseProjectionNode::class.java)) + .addTypeVariable(parentType) + .addTypeVariable(rootType) + .addModifiers(Modifier.PUBLIC) + .superclass(ParameterizedTypeName.get(className, TypeVariableName.get("PARENT"), TypeVariableName.get("ROOT"))) + .addMethod( + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addCode("""super(null, null, java.util.Optional.of("${"_entities"}"));""") + .build() + ) if (generatedClasses.contains(clazzName)) return CodeGenResult() else generatedClasses.add(clazzName) + val codeGenResult = federatedTypes.map { objTypeDef -> + val projectionName = "Entities${objTypeDef.name.capitalized()}KeyProjection" + val returnType = TypeVariableName.get("$projectionName<$clazzName, $clazzName>") javaType.addMethod( MethodSpec.methodBuilder("on${objTypeDef.name}") .addModifiers(Modifier.PUBLIC) - .returns(ClassName.get(getPackageName(), "Entities${objTypeDef.name.capitalized()}KeyProjection")) + .returns(returnType) .addCode( """ - | Entities${objTypeDef.name.capitalized()}KeyProjection fragment = new Entities${objTypeDef.name.capitalized()}KeyProjection(this, this); + | Entities${objTypeDef.name.capitalized()}KeyProjection<$clazzName, $clazzName> fragment = new Entities${objTypeDef.name.capitalized()}KeyProjection(this, this); | getFragments().add(fragment); | return fragment; """.trimMargin() diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.back b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt similarity index 86% rename from graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.back rename to graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt index 4d3625bad..fa4c0635f 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt.back +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt @@ -51,16 +51,14 @@ class EntitiesClientApiGenTest { val codeGenResult = codeGen(schema) - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + val projections = codeGenResult.clientProjections assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").contains("onMovie") assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesGenreProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesActorProjection") - assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesFriendsProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("MovieGenreProjection") + assertThat(projections[3].typeSpec.name).isEqualTo("ActorProjection") val representation = codeGenResult.javaDataTypes.single { "Representation" in it.typeSpec.name } - assertThat(representation.typeSpec.name).isEqualTo("MovieRepresentation") assertThat(representation.typeSpec.fieldSpecs).extracting("name") .containsExactlyInAnyOrder("__typename", "movieId") @@ -89,12 +87,12 @@ class EntitiesClientApiGenTest { val codeGenResult = codeGen(schema) - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_FriendsProjection") + val projections = codeGenResult.clientProjections + assertThat(projections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(projections[1].typeSpec.name).isEqualTo("ActorProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[2].typeSpec.methodSpecs).extracting("name").contains("onMovie") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") val representation = codeGenResult.javaDataTypes.single { "Representation" in it.typeSpec.name } assertThat(representation.typeSpec.name).isEqualTo("MovieRepresentation") @@ -129,12 +127,13 @@ class EntitiesClientApiGenTest { val codeGenResult = codeGen(schema) - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_ActorProjection") + val projections = codeGenResult.clientProjections + assertThat(projections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(projections[1].typeSpec.name).isEqualTo("IActorProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("ActorProjection") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[3].typeSpec.methodSpecs).extracting("name").contains("onMovie") + assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } assertThat(representations).hasSize(2) @@ -168,11 +167,13 @@ class EntitiesClientApiGenTest { val codeGenResult = codeGen(schema) - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie", "onActor") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorsProjection") + val projections = codeGenResult.clientProjections + assertThat(projections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(projections[1].typeSpec.name).isEqualTo("ActorProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[2].typeSpec.methodSpecs).extracting("name").contains("onMovie") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesActorKeyProjection") val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } assertThat(representations).hasSize(2) @@ -213,16 +214,13 @@ class EntitiesClientApiGenTest { val codeGenResult = codeGen(schema) - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + val projections = codeGenResult.clientProjections.filter { it.typeSpec.name.startsWith("Entities") } assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") assertThat(projections[0].typeSpec.methodSpecs).extracting("name") - .containsExactlyInAnyOrder("onMovie", "onMovieCast") + .containsExactlyInAnyOrder("","onMovie", "onMovieCast") assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieCastKeyProjection") - assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesMovieCastKey_MovieProjection") - assertThat(projections[5].typeSpec.name).isEqualTo("EntitiesMovieCastKey_Movie_ActorProjection") - assertThat(projections[6].typeSpec.name).isEqualTo("EntitiesMovieCastKey_ActorProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieCastKeyProjection") + val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } assertThat(representations).hasSize(3) @@ -264,10 +262,9 @@ class EntitiesClientApiGenTest { val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") assertThat(projections[0].typeSpec.methodSpecs).extracting("name") - .containsExactlyInAnyOrder("onMovie", "onMovieActor") + .containsExactlyInAnyOrder("", "onMovie", "onMovieActor") assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieActorKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieActorKeyProjection") val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } assertThat(representations).hasSize(2) @@ -304,9 +301,8 @@ class EntitiesClientApiGenTest { val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("onMovie") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("", "onMovie") assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } assertThat(representations).hasSize(2) @@ -345,9 +341,8 @@ class EntitiesClientApiGenTest { val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("onMovie") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("", "onMovie") assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_GenreProjection") val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } assertThat(representations).hasSize(2) @@ -395,12 +390,8 @@ class EntitiesClientApiGenTest { val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("onMovie") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("", "onMovie") assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_GenreProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_RoleProjection") - assertThat(projections[5].typeSpec.name).isEqualTo("EntitiesMovieKey_LocationProjection") val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } assertThat(representations.map { it.typeSpec.name }) @@ -514,12 +505,11 @@ class EntitiesClientApiGenTest { // then val testClassLoader = codeGenResult.assertCompile().toClassLoader() // assert projection classes - val (entityRootProjectionClass, entitiesFooKeyProjectionClass, entitiesFooKey_BarFieldProjectionClass, entitiesFooKey_MBarFieldProjection) = + val (entityRootProjectionClass, entitiesFooKeyProjectionClass, barProjectionClass) = arrayOf( "EntitiesProjectionRoot", "EntitiesFooKeyProjection", - "EntitiesFooKey_BarFieldProjection", - "EntitiesFooKey_MBarFieldProjection" + "BarProjection" ).map { val clazzCanonicalName = "$basePackageName.client.$it" val clazz = testClassLoader.loadClass(clazzCanonicalName) @@ -541,16 +531,16 @@ class EntitiesClientApiGenTest { mapOf( "id" to entitiesFooKeyProjectionClass, "stringField" to entitiesFooKeyProjectionClass, - "barField" to entitiesFooKey_BarFieldProjectionClass, + "barField" to barProjectionClass, "mStringField" to entitiesFooKeyProjectionClass, - "mBarField" to entitiesFooKey_MBarFieldProjection + "mBarField" to barProjectionClass ).forEach { (name, returnClass) -> assertThat(entitiesFooKeyProjectionClass.getMethod(name)) .describedAs("${entitiesFooKeyProjectionClass.name} method: $name").isNotNull.returns(returnClass) { it.returnType } } mapOf( - "mBarField" to (arrayOf(Integer::class.java, String::class.java) to entitiesFooKey_MBarFieldProjection), + "mBarField" to (arrayOf(Integer::class.java, String::class.java) to barProjectionClass), "mStringField" to (arrayOf(Integer::class.java, String::class.java) to entitiesFooKeyProjectionClass) ).forEach { (name, p) -> val (_, returnClass) = p From f565177cb48e7de6017a05e87be199e3597457cb Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Wed, 8 Feb 2023 16:30:12 -0800 Subject: [PATCH 06/10] Fixes to tests for entities projections. --- .../netflix/graphql/dgs/codegen/CodeGen.kt | 28 +- .../dgs/codegen/EntitiesClientApiGenTest.kt | 3 +- .../graphql/dgs/codegen/KotlinCodeGenTest.kt | 2 +- .../KotlinEntitiesClientApiGenTest.kt.ignore | 492 ------------------ 4 files changed, 16 insertions(+), 509 deletions(-) delete mode 100644 graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt.ignore 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 cd864410d..da75ae70a 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 @@ -358,7 +358,7 @@ class CodeGen(private val config: CodeGenConfig) { } else { val client = generateJavaClientApi(definitions) val entitiesClient = generateJavaClientEntitiesApi(definitions) - val entitiesRepresentationsTypes = generateKotlinClientEntitiesRepresentations(definitions) + val entitiesRepresentationsTypes = generateJavaClientEntitiesRepresentations(definitions) client.merge(entitiesClient).merge(entitiesRepresentationsTypes) } @@ -531,20 +531,20 @@ data class CodeGenResult( val kotlinClientTypes: List = listOf() ) { fun merge(current: CodeGenResult): CodeGenResult { - val javaDataTypes = this.javaDataTypes.plus(current.javaDataTypes).distinct() - val javaInterfaces = this.javaInterfaces.plus(current.javaInterfaces).distinct() - val javaEnumTypes = this.javaEnumTypes.plus(current.javaEnumTypes).distinct() - val javaDataFetchers = this.javaDataFetchers.plus(current.javaDataFetchers).distinct() - val javaQueryTypes = this.javaQueryTypes.plus(current.javaQueryTypes).distinct() + val javaDataTypes = this.javaDataTypes.plus(current.javaDataTypes) + val javaInterfaces = this.javaInterfaces.plus(current.javaInterfaces) + val javaEnumTypes = this.javaEnumTypes.plus(current.javaEnumTypes) + val javaDataFetchers = this.javaDataFetchers.plus(current.javaDataFetchers) + val javaQueryTypes = this.javaQueryTypes.plus(current.javaQueryTypes) val clientProjections = this.clientProjections.plus(current.clientProjections).distinct() - val javaConstants = this.javaConstants.plus(current.javaConstants).distinct() - val kotlinDataTypes = this.kotlinDataTypes.plus(current.kotlinDataTypes).distinct() - val kotlinInputTypes = this.kotlinInputTypes.plus(current.kotlinInputTypes).distinct() - val kotlinInterfaces = this.kotlinInterfaces.plus(current.kotlinInterfaces).distinct() - val kotlinEnumTypes = this.kotlinEnumTypes.plus(current.kotlinEnumTypes).distinct() - val kotlinDataFetchers = this.kotlinDataFetchers.plus(current.kotlinDataFetchers).distinct() - val kotlinConstants = this.kotlinConstants.plus(current.kotlinConstants).distinct() - val kotlinClientTypes = this.kotlinClientTypes.plus(current.kotlinClientTypes).distinct() + val javaConstants = this.javaConstants.plus(current.javaConstants) + val kotlinDataTypes = this.kotlinDataTypes.plus(current.kotlinDataTypes) + val kotlinInputTypes = this.kotlinInputTypes.plus(current.kotlinInputTypes) + val kotlinInterfaces = this.kotlinInterfaces.plus(current.kotlinInterfaces) + val kotlinEnumTypes = this.kotlinEnumTypes.plus(current.kotlinEnumTypes) + val kotlinDataFetchers = this.kotlinDataFetchers.plus(current.kotlinDataFetchers) + val kotlinConstants = this.kotlinConstants.plus(current.kotlinConstants) + val kotlinClientTypes = this.kotlinClientTypes.plus(current.kotlinClientTypes) return CodeGenResult( javaDataTypes = javaDataTypes, diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt index fa4c0635f..8b52c64db 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt @@ -217,11 +217,10 @@ class EntitiesClientApiGenTest { val projections = codeGenResult.clientProjections.filter { it.typeSpec.name.startsWith("Entities") } assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") assertThat(projections[0].typeSpec.methodSpecs).extracting("name") - .containsExactlyInAnyOrder("","onMovie", "onMovieCast") + .containsExactlyInAnyOrder("", "onMovie", "onMovieCast") assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieCastKeyProjection") - val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } assertThat(representations).hasSize(3) assertThat(representations[0].typeSpec.name).isEqualTo("MovieRepresentation") 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 66af133c7..3a8ae85a8 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 @@ -2537,7 +2537,7 @@ class KotlinCodeGenTest { assertThat(dataTypes).hasSize(1) assertThat(dataTypes[0].name).isEqualTo("Person") - val annotationSpec = (((dataTypes as ArrayList<*>)[0] as FileSpec).members[0] as TypeSpec).annotationSpecs[0] + val annotationSpec = (dataTypes[0].members[0] as TypeSpec).annotationSpecs[0] assertThat((annotationSpec.typeName as ClassName).canonicalName).isEqualTo("com.test.validator.ValidPerson") assertThat(annotationSpec.members[0]).extracting("args").asList().hasSize(1) assertThat(annotationSpec.members[0]).extracting("args").asString().contains("com.enums.HUSBAND", "com.enums.WIFE") diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt.ignore b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt.ignore deleted file mode 100644 index 2aa26fed4..000000000 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinEntitiesClientApiGenTest.kt.ignore +++ /dev/null @@ -1,492 +0,0 @@ -/* - * - * Copyright 2020 Netflix, 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 - * - * http://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.netflix.graphql.dgs.codegen - -import org.assertj.core.api.Assertions.* -import org.assertj.core.data.Index -import org.junit.Ignore -import org.junit.jupiter.api.Test - -@Ignore -class KotlinEntitiesClientApiGenTest { - - @Test - fun `We can have federated entities`() { - val schema = """ - type Movie @key(fields: "movieId") { - movieId: ID! @external - title: String - genre: MovieGenre - actor: Actor - } - - enum MovieGenre { - HORROR - ACTION - ROMANCE - COMEDY - } - - type Actor { - name: String - friends: Actor - } - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections) - .hasSize(5) - .satisfies( - { file -> - assertThat(file.typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(file.typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") - }, - Index.atIndex(0) - ) - .satisfies( - { file -> assertThat(file.typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") }, - Index.atIndex(1) - ) - .satisfies( - { file -> assertThat(file.typeSpec.name).isEqualTo("EntitiesMovieKey_GenreProjection") }, - Index.atIndex(2) - ) - .satisfies( - { file -> assertThat(file.typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") }, - Index.atIndex(3) - ) - .satisfies( - { file -> assertThat(file.typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_FriendsProjection") }, - Index.atIndex(4) - ) - - val representation = codeGenResult.kotlinDataTypes.single { "Representation" in it.name } - - assertThat(representation.name).isEqualTo("MovieRepresentation") - codeGenResult.assertCompile() - } - - @Test - fun `We can have federated entities and queries`() { - val schema = """ - type Query { - search: Movie - } - - type Movie @key(fields: "movieId") { - movieId: ID! @external - title: String - actor: Actor - } - - type Actor { - name: String - friends: Actor - } - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_FriendsProjection") - - val representation = codeGenResult.kotlinDataTypes.single { "Representation" in it.name } - assertThat(representation.name).isEqualTo("MovieRepresentation") - codeGenResult.assertCompile() - } - - @Test - fun `An entity can have a field that is an interface`() { - val schema = """ - type Query { - search: Movie - } - - type Movie @key(fields: "actor { name }") { - movieId: ID! - title: String - actor: IActor @external - } - - interface IActor { - name: String - } - - type Actor implements IActor { - name: String - friends: Actor - } - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_ActorProjection") - - val representations = codeGenResult.kotlinDataTypes.filter { "Representation" in it.name } - - assertThat(representations).hasSize(2) - assertThat(representations[0].name).isEqualTo("MovieRepresentation") - assertThat(representations[1].name).isEqualTo("IActorRepresentation") - - codeGenResult.assertCompile() - } - - @Test - fun `We can have entities with arrays and nested keys`() { - val schema = """ - type Query { - search: Movie - } - - type Movie @key(fields: "movieId actors { name }") { - movieId: ID! @external - title: String - actors: [Actor!]! - } - - type Actor @key(fields: "name") { - name: String - } - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie", "onActor") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorsProjection") - - val representations = codeGenResult.kotlinDataTypes.filter { "Representation" in it.name } - assertThat(representations).hasSize(2) - assertThat(representations[0].name).isEqualTo("MovieRepresentation") - - codeGenResult.assertCompile() - } - - @Test - fun `We can have entities with nested keys`() { - val schema = """ - type Query { - search: Movie - } - - type Movie @key(fields: "movieId actor { name }") { - movieId: ID! @external - title: String - actor: Person - } - - type Person { - name: String @external - age: Int - } - - type MovieCast @key(fields: "movie { movieId actor { name } } actor{name}") { - movie: Movie - actor: Person - } - - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name") - .containsExactlyInAnyOrder("onMovie", "onMovieCast") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieCastKeyProjection") - assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesMovieCastKey_MovieProjection") - assertThat(projections[5].typeSpec.name).isEqualTo("EntitiesMovieCastKey_Movie_ActorProjection") - assertThat(projections[6].typeSpec.name).isEqualTo("EntitiesMovieCastKey_ActorProjection") - - val representations = codeGenResult.kotlinDataTypes.filter { "Representation" in it.name } - assertThat(representations).hasSize(3) - assertThat(representations[0].name).isEqualTo("MovieRepresentation") - assertThat(representations[1].name).isEqualTo("PersonRepresentation") - assertThat(representations[2].name).isEqualTo("MovieCastRepresentation") - - codeGenResult.assertCompile() - } - - @Test - fun `We can have multiple entities with keys`() { - val schema = """ - type Query { - search: Movie - } - - type Movie @key(fields: "movieId") { - movieId: ID! @external - title: String - actor: MovieActor - } - - type MovieActor @key(fields: "name") { - name: String @external - age: Int - } - - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name") - .containsExactlyInAnyOrder("onMovie", "onMovieActor") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieActorKeyProjection") - - val representations = codeGenResult.kotlinDataTypes.filter { "Representation" in it.name } - assertThat(representations).hasSize(2) - assertThat(representations[0].name).isEqualTo("MovieRepresentation") - assertThat(representations[1].name).isEqualTo("MovieActorRepresentation") - - codeGenResult.assertCompile() - } - - @Test - fun `Entities can have nested complex keys`() { - val schema = """ - type Query { - search: Movie - } - - type Movie @key(fields: "movieId actor { name age }") { - movieId: ID! @external - title: String - actor: Person - } - - type Person { - name: String @external - age: Int - } - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections) - .hasSize(3) - .satisfies( - { file -> - assertThat(file.typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(file.typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") - }, - Index.atIndex(0) - ) - .satisfies( - { file -> assertThat(file.typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") }, - Index.atIndex(1) - ) - .satisfies( - { file -> assertThat(file.typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") }, - Index.atIndex(2) - ) - - val representations = codeGenResult.kotlinDataTypes.filter { "Representation" in it.name } - assertThat(representations).hasSize(2) - assertThat(representations[0].name).isEqualTo("MovieRepresentation") - assertThat(representations[1].name).isEqualTo("PersonRepresentation") - - codeGenResult.assertCompile() - } - - @Test - fun `Entities can have keys that are enums`() { - val schema = """ - type Query { - search: Movie - } - - type Movie @key(fields: "id genre") { - id: ID! @external - genre: MovieGenre - title: String - } - - enum MovieGenre { - HORROR - ACTION - ROMANCE - COMEDY - } - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("onMovie") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_GenreProjection") - - val representations = codeGenResult.kotlinDataTypes.filter { "Representation" in it.name } - assertThat(representations).hasSize(2) - assertThat(representations[0].name).isEqualTo("MovieRepresentation") - assertThat(representations[1].name).isEqualTo("MovieGenreRepresentation") - - codeGenResult.assertCompile() - } - - @Test - fun `Entities can have the @key directive used multiple times`() { - val schema = """ - type Movie @key(fields: "id genre") @key(fields: "id actor{ id }") @key(fields: "id location { id }") { - id: ID! @external - title: String - genre: MovieGenre - actor: Person - location: Location - } - - enum MovieGenre { - HORROR - ACTION - ROMANCE - COMEDY - } - - type Person @extends { - id: ID @external - name: String - role: Role - } - - enum Role { ATL BTL } - - type Location { - id: ID - name: String - } - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("onMovie") - assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") - assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_GenreProjection") - assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") - assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_RoleProjection") - assertThat(projections[5].typeSpec.name).isEqualTo("EntitiesMovieKey_LocationProjection") - - val representations = codeGenResult.kotlinDataTypes.filter { "Representation" in it.name } - assertThat(representations.map { it.name }) - .containsExactlyInAnyOrder( - "MovieRepresentation", - "MovieGenreRepresentation", - "PersonRepresentation", - "LocationRepresentation" - ) - codeGenResult.assertCompile() - } - - @Test - fun `Entities can have scalar fields`() { - val schema = """ - type Query { - movieCountry: MovieCountry - } - - type MovieCountry @key(fields : "movieId country") { - country: String - movieId: Long - } - scalar Long - - """.trimIndent() - - val codeGenResult = codeGen(schema) - - val representations = codeGenResult.kotlinDataTypes.filter { "Representation" in it.name } - assertThat(representations).hasSize(1) - val projections = codeGenResult.clientProjections - assertThat(projections).hasSize(3) - - codeGenResult.assertCompile() - } - - @Test - fun `CodeGen can be configured to skip entities`() { - val schema = """ - type Query { - search: Movie - } - - type Movie @key(fields: "movieId") { - movieId: ID! @external - title: String - actor: Actor - } - - type Actor { - name: String - friends: Actor - } - """.trimIndent() - - val codeGenResult = CodeGen( - CodeGenConfig( - schemas = setOf(schema), - packageName = basePackageName, - generateClientApi = true, - skipEntityQueries = true, - language = Language.KOTLIN - ) - ).generate() - - val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } - assertThat(projections).isEmpty() - - codeGenResult.assertCompile() - } - - companion object { - fun codeGen(schema: String): CodeGenResult { - return CodeGen( - CodeGenConfig( - schemas = setOf(schema), - packageName = basePackageName, - generateClientApi = true, - language = Language.KOTLIN - ) - ).generate() - } - - fun CodeGenResult.assertCompile() = assertCompilesKotlin(this) - } -} From f5aa2cc0759aab0d8d2dfe51d901dfac542b7c4f Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Thu, 9 Feb 2023 12:52:54 -0800 Subject: [PATCH 07/10] Rename new implementation to v2 for conditional configuration. --- .../generators/java/ClientApiGenerator.kt | 113 +- .../generators/java/ClientApiGeneratorv2.kt | 606 +++++++++++ ...nTest.kt => EntitiesClientApiGenTestv2.kt} | 2 +- .../clientapi/ClientApiGenBuilderTest.kt | 2 +- .../clientapi/ClientApiGenFragmentTest.kt | 60 +- .../clientapi/ClientApiGenMutationTest.kt | 4 +- .../clientapi/ClientApiGenProjectionTest.kt | 87 +- .../clientapi/ClientApiGenQueryTest.kt | 53 +- .../clientapi/ClientApiGenSubscriptionTest.kt | 2 +- .../clientapiv2/ClientApiGenBuilderTestv2.kt | 141 +++ .../clientapiv2/ClientApiGenFragmentTestv2.kt | 249 +++++ .../clientapiv2/ClientApiGenMutationTestv2.kt | 302 ++++++ .../ClientApiGenProjectionTestv2.kt | 765 ++++++++++++++ .../clientapiv2/ClientApiGenQueryTestv2.kt | 977 ++++++++++++++++++ .../ClientApiGenSubscriptionTestv2.kt | 114 ++ 15 files changed, 3340 insertions(+), 137 deletions(-) create mode 100644 graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGeneratorv2.kt rename graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/{EntitiesClientApiGenTest.kt => EntitiesClientApiGenTestv2.kt} (99%) create mode 100644 graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenBuilderTestv2.kt create mode 100644 graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenFragmentTestv2.kt create mode 100644 graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenMutationTestv2.kt create mode 100644 graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenProjectionTestv2.kt create mode 100644 graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenQueryTestv2.kt create mode 100644 graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenSubscriptionTestv2.kt diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt index d44033e1d..0ceb51e04 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGenerator.kt @@ -18,15 +18,33 @@ package com.netflix.graphql.dgs.codegen.generators.java +import com.netflix.graphql.dgs.client.codegen.BaseProjectionNode import com.netflix.graphql.dgs.client.codegen.BaseSubProjectionNode import com.netflix.graphql.dgs.client.codegen.GraphQLQuery import com.netflix.graphql.dgs.codegen.* import com.netflix.graphql.dgs.codegen.generators.shared.ClassnameShortener import com.netflix.graphql.dgs.codegen.generators.shared.CodeGeneratorUtils.capitalized -import com.squareup.javapoet.* +import com.squareup.javapoet.ClassName +import com.squareup.javapoet.CodeBlock +import com.squareup.javapoet.FieldSpec +import com.squareup.javapoet.JavaFile +import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.ParameterSpec +import com.squareup.javapoet.ParameterizedTypeName +import com.squareup.javapoet.TypeSpec import graphql.introspection.Introspection.TypeNameMetaFieldDef -import graphql.language.* +import graphql.language.Document +import graphql.language.FieldDefinition +import graphql.language.InterfaceTypeDefinition +import graphql.language.NamedNode +import graphql.language.ObjectTypeDefinition +import graphql.language.ObjectTypeExtensionDefinition +import graphql.language.ScalarTypeDefinition +import graphql.language.TypeDefinition +import graphql.language.UnionTypeDefinition import javax.lang.model.element.Modifier +import kotlin.collections.ArrayList +import kotlin.collections.HashSet class ClientApiGenerator(private val config: CodeGenConfig, private val document: Document) { private val generatedClasses = mutableSetOf() @@ -210,21 +228,9 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document private fun createRootProjection(type: TypeDefinition<*>, prefix: String): CodeGenResult { val clazzName = "${prefix}ProjectionRoot" - val className = ClassName.get(BaseSubProjectionNode::class.java) - val parentJavaType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) - val rootJavaType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) val javaType = TypeSpec.classBuilder(clazzName) .addOptionalGeneratedAnnotation(config) - .addTypeVariable(parentJavaType) - .addTypeVariable(rootJavaType) - .addModifiers(Modifier.PUBLIC) - .superclass(ParameterizedTypeName.get(className, TypeVariableName.get("PARENT"), TypeVariableName.get("ROOT"))) - .addMethod( - MethodSpec.constructorBuilder() - .addModifiers(Modifier.PUBLIC) - .addCode("""super(null, null, java.util.Optional.of("${type.name}"));""") - .build() - ) + .addModifiers(Modifier.PUBLIC).superclass(ClassName.get(BaseProjectionNode::class.java)) if (generatedClasses.contains(clazzName)) return CodeGenResult() else generatedClasses.add(clazzName) @@ -242,14 +248,14 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document if (typeDefinition != null) it to typeDefinition else null } .map { (fieldDef, typeDef) -> - val projectionName = "${typeDef.name.capitalized()}Projection" + val projectionName = "${prefix}_${fieldDef.name.capitalized()}Projection" + if (typeDef !is ScalarTypeDefinition) { - val typeVariable = TypeVariableName.get("$projectionName<$clazzName, $clazzName>") val noArgMethodBuilder = MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(fieldDef.name)) - .returns(typeVariable) + .returns(ClassName.get(getPackageName(), projectionName)) .addCode( """ - |$projectionName<$clazzName, $clazzName> projection = new $projectionName<>(this, this); + |$projectionName projection = new $projectionName(this, this); |getFields().put("${fieldDef.name}", projection); |return projection; """.trimMargin() @@ -264,17 +270,16 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document val processedEdges = mutableSetOf>() processedEdges.add(typeDef.name to type.name) - createSubProjection(typeDef, javaType.build(), javaType.build(), "${typeDef.name.capitalized()}", processedEdges, 1) + createSubProjection(typeDef, javaType.build(), javaType.build(), "${prefix}_${fieldDef.name.capitalized()}", processedEdges, 1) } .fold(CodeGenResult()) { total, current -> total.merge(current) } fieldDefinitions.filterSkipped().forEach { val objectTypeDefinition = it.type.findTypeDefinition(document) if (objectTypeDefinition == null) { - val typeVariable = TypeVariableName.get("$clazzName") javaType.addMethod( MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(it.name)) - .returns(typeVariable) + .returns(ClassName.get(getPackageName(), javaType.build().name)) .addCode( """ |getFields().put("${it.name}", null); @@ -300,14 +305,11 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document javaType: TypeSpec.Builder, projectionRoot: String ): TypeSpec.Builder? { - val clazzName = javaType.build().name - val rootTypeName = if (projectionRoot == "this") "$clazzName" else "ROOT" - val returnTypeName = TypeVariableName.get("$projectionName<$clazzName, $rootTypeName>") val methodBuilder = MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(fieldDefinition.name)) - .returns(returnTypeName) + .returns(ClassName.get(getPackageName(), projectionName)) .addCode( """ - |$projectionName<$clazzName, $rootTypeName> projection = new $projectionName<>(this, $projectionRoot); + |$projectionName projection = new $projectionName(this, $projectionRoot); |getFields().put("${fieldDefinition.name}", projection); |getInputArguments().computeIfAbsent("${fieldDefinition.name}", k -> new ${'$'}T<>()); |${ @@ -332,34 +334,19 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document private fun createEntitiesRootProjection(federatedTypes: List): CodeGenResult { val clazzName = "EntitiesProjectionRoot" - val className = ClassName.get(BaseSubProjectionNode::class.java) - val parentType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) - val rootType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) val javaType = TypeSpec.classBuilder(clazzName) .addOptionalGeneratedAnnotation(config) - .addTypeVariable(parentType) - .addTypeVariable(rootType) - .addModifiers(Modifier.PUBLIC) - .superclass(ParameterizedTypeName.get(className, TypeVariableName.get("PARENT"), TypeVariableName.get("ROOT"))) - .addMethod( - MethodSpec.constructorBuilder() - .addModifiers(Modifier.PUBLIC) - .addCode("""super(null, null, java.util.Optional.of("${"_entities"}"));""") - .build() - ) + .addModifiers(Modifier.PUBLIC).superclass(ClassName.get(BaseProjectionNode::class.java)) if (generatedClasses.contains(clazzName)) return CodeGenResult() else generatedClasses.add(clazzName) - val codeGenResult = federatedTypes.map { objTypeDef -> - val projectionName = "Entities${objTypeDef.name.capitalized()}KeyProjection" - val returnType = TypeVariableName.get("$projectionName<$clazzName, $clazzName>") javaType.addMethod( MethodSpec.methodBuilder("on${objTypeDef.name}") .addModifiers(Modifier.PUBLIC) - .returns(returnType) + .returns(ClassName.get(getPackageName(), "Entities${objTypeDef.name.capitalized()}KeyProjection")) .addCode( """ - | Entities${objTypeDef.name.capitalized()}KeyProjection<$clazzName, $clazzName> fragment = new Entities${objTypeDef.name.capitalized()}KeyProjection(this, this); + | Entities${objTypeDef.name.capitalized()}KeyProjection fragment = new Entities${objTypeDef.name.capitalized()}KeyProjection(this, this); | getFragments().add(fragment); | return fragment; """.trimMargin() @@ -400,17 +387,15 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document private fun addFragmentProjectionMethod(javaType: TypeSpec.Builder, rootType: TypeSpec, prefix: String, it: TypeDefinition<*>, processedEdges: Set>, queryDepth: Int): CodeGenResult { val rootRef = if (javaType.build().name == rootType.name) "this" else "getRoot()" - val rootTypeName = if (javaType.build().name == rootType.name) "${rootType.name}" else "ROOT" - val parentRef = javaType.build().name - val projectionName = "${it.name.capitalized()}Projection" - val typeVariable = TypeVariableName.get("$projectionName<$parentRef, $rootTypeName>") + + val projectionName = "${prefix}_${it.name.capitalized()}Projection" javaType.addMethod( MethodSpec.methodBuilder("on${it.name}") .addModifiers(Modifier.PUBLIC) - .returns(typeVariable) + .returns(ClassName.get(getPackageName(), projectionName)) .addCode( """ - |$projectionName<$parentRef, $rootTypeName> fragment = new $projectionName<>(this, $rootRef); + |$projectionName fragment = new $projectionName(this, $rootRef); |getFragments().add(fragment); |return fragment; """.trimMargin() @@ -418,7 +403,7 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document .build() ) - return createFragment(it as ObjectTypeDefinition, javaType.build(), rootType, "${it.name.capitalized()}", processedEdges, queryDepth) + return createFragment(it as ObjectTypeDefinition, javaType.build(), rootType, "${prefix}_${it.name.capitalized()}", processedEdges, queryDepth) } private fun createFragment(type: ObjectTypeDefinition, parent: TypeSpec, root: TypeSpec, prefix: String, processedEdges: Set>, queryDepth: Int): CodeGenResult { @@ -478,20 +463,15 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document val className = ClassName.get(BaseSubProjectionNode::class.java) val clazzName = "${prefix}Projection" if (generatedClasses.contains(clazzName)) return null else generatedClasses.add(clazzName) - - val parentJavaType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) - val rootJavaType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) val javaType = TypeSpec.classBuilder(clazzName) .addOptionalGeneratedAnnotation(config) - .addTypeVariable(parentJavaType) - .addTypeVariable(rootJavaType) .addModifiers(Modifier.PUBLIC) - .superclass(ParameterizedTypeName.get(className, TypeVariableName.get("PARENT"), TypeVariableName.get("ROOT"))) + .superclass(ParameterizedTypeName.get(className, ClassName.get(getPackageName(), parent.name), ClassName.get(getPackageName(), root.name))) .addMethod( MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) - .addParameter(ParameterSpec.builder(ClassName.get("", "PARENT"), "parent").build()) - .addParameter(ParameterSpec.builder(ClassName.get("", "ROOT"), "root").build()) + .addParameter(ParameterSpec.builder(ClassName.get(getPackageName(), parent.name), "parent").build()) + .addParameter(ParameterSpec.builder(ClassName.get(getPackageName(), root.name), "root").build()) .addCode("""super(parent, root, java.util.Optional.of("${type.name}"));""") .build() ) @@ -509,16 +489,16 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document val typeDefinition = it.type.findTypeDefinition(document, true) if (typeDefinition != null) it to typeDefinition else null } + .filter { (_, typeDef) -> (typeDef.name to type.name) !in processedEdges } .map { (fieldDef, typeDef) -> - val projectionName = "${typeDef.name.capitalized()}Projection" + val projectionName = "${truncatePrefix(prefix)}_${fieldDef.name.capitalized()}Projection" val methodName = ReservedKeywordSanitizer.sanitize(fieldDef.name) - val typeVariable = TypeVariableName.get("$projectionName<$clazzName, ROOT>") javaType.addMethod( MethodSpec.methodBuilder(methodName) - .returns(typeVariable) + .returns(ClassName.get(getPackageName(), projectionName)) .addCode( """ - | $projectionName<$clazzName, ROOT> projection = new $projectionName<>(this, getRoot()); + | $projectionName projection = new $projectionName(this, getRoot()); | getFields().put("${fieldDef.name}", projection); | return projection; """.trimMargin() @@ -533,7 +513,7 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document val updatedProcessedEdges = processedEdges.toMutableSet() updatedProcessedEdges.add(typeDef.name to type.name) - createSubProjection(typeDef, javaType.build(), root, "${typeDef.name.capitalized()}", updatedProcessedEdges, queryDepth + 1) + createSubProjection(typeDef, javaType.build(), root, "${truncatePrefix(prefix)}_${fieldDef.name.capitalized()}", updatedProcessedEdges, queryDepth + 1) } .fold(CodeGenResult()) { total, current -> total.merge(current) } } else CodeGenResult() @@ -543,10 +523,9 @@ class ClientApiGenerator(private val config: CodeGenConfig, private val document .forEach { val objectTypeDefinition = it.type.findTypeDefinition(document) if (objectTypeDefinition == null) { - val typeVariable = TypeVariableName.get("$clazzName") javaType.addMethod( MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(it.name)) - .returns(typeVariable) + .returns(ClassName.get(getPackageName(), javaType.build().name)) .addCode( """ |getFields().put("${it.name}", null); diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGeneratorv2.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGeneratorv2.kt new file mode 100644 index 000000000..5a77f5d35 --- /dev/null +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/ClientApiGeneratorv2.kt @@ -0,0 +1,606 @@ +/* + * + * Copyright 2020 Netflix, 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 + * + * http://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.netflix.graphql.dgs.codegen.generators.java + +import com.netflix.graphql.dgs.client.codegen.BaseSubProjectionNode +import com.netflix.graphql.dgs.client.codegen.GraphQLQuery +import com.netflix.graphql.dgs.codegen.* +import com.netflix.graphql.dgs.codegen.generators.shared.ClassnameShortener +import com.netflix.graphql.dgs.codegen.generators.shared.CodeGeneratorUtils.capitalized +import com.squareup.javapoet.* +import graphql.introspection.Introspection.TypeNameMetaFieldDef +import graphql.language.* +import javax.lang.model.element.Modifier + +class ClientApiGeneratorv2(private val config: CodeGenConfig, private val document: Document) { + private val generatedClasses = mutableSetOf() + private val typeUtils = TypeUtils(getDatatypesPackageName(), config, document) + + fun generate(definition: ObjectTypeDefinition, methodNames: MutableSet): CodeGenResult { + return definition.fieldDefinitions.filterIncludedInConfig(definition.name, config).filterSkipped().map { + val javaFile = createQueryClass(it, definition.name, methodNames) + + val rootProjection = + it.type.findTypeDefinition(document, true)?.let { typeDefinition -> createRootProjection(typeDefinition, it.name.capitalized()) } + ?: CodeGenResult() + CodeGenResult(javaQueryTypes = listOf(javaFile)).merge(rootProjection) + }.fold(CodeGenResult()) { total, current -> total.merge(current) } + } + + fun generateEntities(definitions: List): CodeGenResult { + if (config.skipEntityQueries) { + return CodeGenResult() + } + + var entitiesRootProjection = CodeGenResult() + // generate for federation types, if present + val federatedTypes = definitions.filter { it.hasDirective("key") } + if (federatedTypes.isNotEmpty()) { + // create entities root projection + entitiesRootProjection = createEntitiesRootProjection(federatedTypes) + } + return CodeGenResult().merge(entitiesRootProjection) + } + + private fun createQueryClass(it: FieldDefinition, operation: String, methodNames: MutableSet): JavaFile { + val methodName = generateMethodName(it.name.capitalized(), operation.lowercase(), methodNames) + val javaType = TypeSpec.classBuilder(methodName) + .addOptionalGeneratedAnnotation(config) + .addModifiers(Modifier.PUBLIC).superclass(ClassName.get(GraphQLQuery::class.java)) + + if (it.description != null) { + javaType.addJavadoc(it.description.sanitizeJavaDoc()) + } + javaType.addMethod( + MethodSpec.methodBuilder("getOperationName") + .addModifiers(Modifier.PUBLIC) + .returns(String::class.java) + .addAnnotation(Override::class.java) + .addCode( + """ + | return "${it.name}"; + | + """.trimMargin() + ).build() + ) + + val setType = ClassName.get(Set::class.java) + val setOfStringType = ParameterizedTypeName.get(setType, ClassName.get(String::class.java)) + + val builderClass = TypeSpec.classBuilder("Builder").addModifiers(Modifier.STATIC, Modifier.PUBLIC) + .addOptionalGeneratedAnnotation(config) + .addMethod( + MethodSpec.methodBuilder("build") + .addModifiers(Modifier.PUBLIC) + .returns(ClassName.get("", methodName)) + .addCode( + if (it.inputValueDefinitions.isNotEmpty()) { + """ + |return new $methodName(${it.inputValueDefinitions.joinToString(", ") { ReservedKeywordSanitizer.sanitize(it.name) }}, queryName, fieldsSet); + | + """.trimMargin() + } else { + """ + |return new $methodName(queryName); + """.trimMargin() + } + ) + .build() + ).addField(FieldSpec.builder(setOfStringType, "fieldsSet", Modifier.PRIVATE).initializer("new \$T<>()", ClassName.get(HashSet::class.java)).build()) + + val constructorBuilder = MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + constructorBuilder.addCode( + """ + |super("${operation.lowercase()}", queryName); + | + """.trimMargin() + ) + + it.inputValueDefinitions.forEach { inputValue -> + val findReturnType = TypeUtils(getDatatypesPackageName(), config, document).findReturnType(inputValue.type) + val methodBuilder = MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(inputValue.name)) + .addParameter(findReturnType, ReservedKeywordSanitizer.sanitize(inputValue.name)) + .returns(ClassName.get("", "Builder")) + .addModifiers(Modifier.PUBLIC) + .addCode( + """ + |this.${ReservedKeywordSanitizer.sanitize(inputValue.name)} = ${ReservedKeywordSanitizer.sanitize(inputValue.name)}; + |this.fieldsSet.add("${inputValue.name}"); + |return this; + """.trimMargin() + ) + + if (inputValue.description != null) { + methodBuilder.addJavadoc(inputValue.description.sanitizeJavaDoc()) + } + builderClass.addMethod(methodBuilder.build()) + .addField(findReturnType, ReservedKeywordSanitizer.sanitize(inputValue.name), Modifier.PRIVATE) + + constructorBuilder.addParameter(findReturnType, ReservedKeywordSanitizer.sanitize(inputValue.name)) + + if (findReturnType.isPrimitive) { + constructorBuilder.addCode( + """ + |getInput().put("${inputValue.name}", ${ReservedKeywordSanitizer.sanitize(inputValue.name)}); + """.trimMargin() + ) + } else { + constructorBuilder.addCode( + """ + |if (${ReservedKeywordSanitizer.sanitize(inputValue.name)} != null || fieldsSet.contains("${inputValue.name}")) { + | getInput().put("${inputValue.name}", ${ReservedKeywordSanitizer.sanitize(inputValue.name)}); + |} + """.trimMargin() + ) + } + } + + val nameMethodBuilder = MethodSpec.methodBuilder("queryName") + .addParameter(String::class.java, "queryName") + .returns(ClassName.get("", "Builder")) + .addModifiers(Modifier.PUBLIC) + .addCode( + """ + |this.queryName = queryName; + |return this; + """.trimMargin() + ) + + builderClass.addField(FieldSpec.builder(String::class.java, "queryName", Modifier.PRIVATE).build()) + .addMethod(nameMethodBuilder.build()) + + constructorBuilder.addParameter(String::class.java, "queryName") + + if (it.inputValueDefinitions.size > 0) { + constructorBuilder.addParameter(setOfStringType, "fieldsSet") + } + + javaType.addMethod(constructorBuilder.build()) + + // No-arg constructor + javaType.addMethod( + MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC) + .addStatement("super(\"${operation.lowercase()}\")") + .build() + ) + + javaType.addMethod( + MethodSpec.methodBuilder("newRequest") + .addModifiers(Modifier.STATIC, Modifier.PUBLIC) + .returns(ClassName.get("", "Builder")) + .addCode("return new Builder();\n") + .build() + ) + javaType.addType(builderClass.build()) + return JavaFile.builder(getPackageName(), javaType.build()).build() + } + + /** + * Generate method name. If there are same method names in type `Query`, `Mutation` and `Subscription`, add suffix. + * For example, there are `shows` in `Query`, `Mutation` and `Subscription`, the generated files should be: + * `ShowsGraphQLQuery`, `ShowsGraphQLMutation` and `ShowsGraphQLSubscription` + */ + private fun generateMethodName(originalMethodName: String, typeName: String, methodNames: MutableSet): String { + return if ("mutation" == typeName && methodNames.contains(originalMethodName)) { + originalMethodName.plus("GraphQLMutation") + } else if ("subscription" == typeName && methodNames.contains(originalMethodName)) { + originalMethodName.plus("GraphQLSubscription") + } else { + methodNames.add(originalMethodName) + originalMethodName.plus("GraphQLQuery") + } + } + + private fun createRootProjection(type: TypeDefinition<*>, prefix: String): CodeGenResult { + val clazzName = "${prefix}ProjectionRoot" + val className = ClassName.get(BaseSubProjectionNode::class.java) + val parentJavaType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) + val rootJavaType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) + val javaType = TypeSpec.classBuilder(clazzName) + .addOptionalGeneratedAnnotation(config) + .addTypeVariable(parentJavaType) + .addTypeVariable(rootJavaType) + .addModifiers(Modifier.PUBLIC) + .superclass(ParameterizedTypeName.get(className, TypeVariableName.get("PARENT"), TypeVariableName.get("ROOT"))) + .addMethod( + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addCode("""super(null, null, java.util.Optional.of("${type.name}"));""") + .build() + ) + + if (generatedClasses.contains(clazzName)) return CodeGenResult() else generatedClasses.add(clazzName) + + val fieldDefinitions = type.fieldDefinitions() + document.definitions.filterIsInstance().filter { it.name == type.name }.flatMap { it.fieldDefinitions } + + val codeGenResult = fieldDefinitions + .filterSkipped() + .mapNotNull { + val typeDefinition = it.type.findTypeDefinition( + document, + excludeExtensions = true, + includeBaseTypes = it.inputValueDefinitions.isNotEmpty(), + includeScalarTypes = it.inputValueDefinitions.isNotEmpty() + ) + if (typeDefinition != null) it to typeDefinition else null + } + .map { (fieldDef, typeDef) -> + val projectionName = "${typeDef.name.capitalized()}Projection" + if (typeDef !is ScalarTypeDefinition) { + val typeVariable = TypeVariableName.get("$projectionName<$clazzName, $clazzName>") + val noArgMethodBuilder = MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(fieldDef.name)) + .returns(typeVariable) + .addCode( + """ + |$projectionName<$clazzName, $clazzName> projection = new $projectionName<>(this, this); + |getFields().put("${fieldDef.name}", projection); + |return projection; + """.trimMargin() + ) + .addModifiers(Modifier.PUBLIC) + javaType.addMethod(noArgMethodBuilder.build()) + } + + if (fieldDef.inputValueDefinitions.isNotEmpty()) { + addFieldSelectionMethodWithArguments(fieldDef, projectionName, javaType, projectionRoot = "this") + } + + val processedEdges = mutableSetOf>() + processedEdges.add(typeDef.name to type.name) + createSubProjection(typeDef, javaType.build(), javaType.build(), "${typeDef.name.capitalized()}", processedEdges, 1) + } + .fold(CodeGenResult()) { total, current -> total.merge(current) } + + fieldDefinitions.filterSkipped().forEach { + val objectTypeDefinition = it.type.findTypeDefinition(document) + if (objectTypeDefinition == null) { + val typeVariable = TypeVariableName.get("$clazzName") + javaType.addMethod( + MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(it.name)) + .returns(typeVariable) + .addCode( + """ + |getFields().put("${it.name}", null); + |return this; + """.trimMargin() + ) + .addModifiers(Modifier.PUBLIC) + .build() + ) + } + } + + val concreteTypesResult = createConcreteTypes(type, javaType.build(), javaType, prefix, mutableSetOf(), 0) + val unionTypesResult = createUnionTypes(type, javaType, javaType.build(), prefix, mutableSetOf(), 0) + + val javaFile = JavaFile.builder(getPackageName(), javaType.build()).build() + return CodeGenResult(clientProjections = listOf(javaFile)).merge(codeGenResult).merge(concreteTypesResult).merge(unionTypesResult) + } + + private fun addFieldSelectionMethodWithArguments( + fieldDefinition: FieldDefinition, + projectionName: String, + javaType: TypeSpec.Builder, + projectionRoot: String + ): TypeSpec.Builder? { + val clazzName = javaType.build().name + val rootTypeName = if (projectionRoot == "this") "$clazzName" else "ROOT" + val returnTypeName = TypeVariableName.get("$projectionName<$clazzName, $rootTypeName>") + val methodBuilder = MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(fieldDefinition.name)) + .returns(returnTypeName) + .addCode( + """ + |$projectionName<$clazzName, $rootTypeName> projection = new $projectionName<>(this, $projectionRoot); + |getFields().put("${fieldDefinition.name}", projection); + |getInputArguments().computeIfAbsent("${fieldDefinition.name}", k -> new ${'$'}T<>()); + |${ + fieldDefinition.inputValueDefinitions.joinToString("\n") { input -> + """ + |InputArgument ${input.name}Arg = new InputArgument("${input.name}", ${input.name}); + |getInputArguments().get("${fieldDefinition.name}").add(${input.name}Arg); + """.trimMargin() + } + } + |return projection; + """.trimMargin(), + ArrayList::class.java + ) + .addModifiers(Modifier.PUBLIC) + + fieldDefinition.inputValueDefinitions.forEach { input -> + methodBuilder.addParameter(ParameterSpec.builder(typeUtils.findReturnType(input.type), input.name).build()) + } + return javaType.addMethod(methodBuilder.build()) + } + + private fun createEntitiesRootProjection(federatedTypes: List): CodeGenResult { + val clazzName = "EntitiesProjectionRoot" + val className = ClassName.get(BaseSubProjectionNode::class.java) + val parentType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) + val rootType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) + val javaType = TypeSpec.classBuilder(clazzName) + .addOptionalGeneratedAnnotation(config) + .addTypeVariable(parentType) + .addTypeVariable(rootType) + .addModifiers(Modifier.PUBLIC) + .superclass(ParameterizedTypeName.get(className, TypeVariableName.get("PARENT"), TypeVariableName.get("ROOT"))) + .addMethod( + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addCode("""super(null, null, java.util.Optional.of("${"_entities"}"));""") + .build() + ) + + if (generatedClasses.contains(clazzName)) return CodeGenResult() else generatedClasses.add(clazzName) + + val codeGenResult = federatedTypes.map { objTypeDef -> + val projectionName = "Entities${objTypeDef.name.capitalized()}KeyProjection" + val returnType = TypeVariableName.get("$projectionName<$clazzName, $clazzName>") + javaType.addMethod( + MethodSpec.methodBuilder("on${objTypeDef.name}") + .addModifiers(Modifier.PUBLIC) + .returns(returnType) + .addCode( + """ + | Entities${objTypeDef.name.capitalized()}KeyProjection<$clazzName, $clazzName> fragment = new Entities${objTypeDef.name.capitalized()}KeyProjection(this, this); + | getFragments().add(fragment); + | return fragment; + """.trimMargin() + ) + .build() + ) + val processedEdges = mutableSetOf>() + createFragment(objTypeDef, javaType.build(), javaType.build(), "Entities${objTypeDef.name.capitalized()}Key", processedEdges, 0) + }.fold(CodeGenResult()) { total, current -> total.merge(current) } + + val javaFile = JavaFile.builder(getPackageName(), javaType.build()).build() + return CodeGenResult(clientProjections = listOf(javaFile)).merge(codeGenResult) + } + + private fun createConcreteTypes(type: TypeDefinition<*>, root: TypeSpec, javaType: TypeSpec.Builder, prefix: String, processedEdges: Set>, queryDepth: Int): CodeGenResult { + return if (type is InterfaceTypeDefinition) { + val concreteTypes = document.getDefinitionsOfType(ObjectTypeDefinition::class.java).filter { + it.implements.filterIsInstance>().any { iface -> iface.name == type.name } + } + concreteTypes.map { + addFragmentProjectionMethod(javaType, root, prefix, it, processedEdges, queryDepth) + }.fold(CodeGenResult()) { total, current -> total.merge(current) } + } else { + CodeGenResult() + } + } + + private fun createUnionTypes(type: TypeDefinition<*>, javaType: TypeSpec.Builder, rootType: TypeSpec, prefix: String, processedEdges: Set>, queryDepth: Int): CodeGenResult { + return if (type is UnionTypeDefinition) { + val memberTypes = type.memberTypes.mapNotNull { it.findTypeDefinition(document, true) }.toList() + memberTypes.map { + addFragmentProjectionMethod(javaType, rootType, prefix, it, processedEdges, queryDepth) + }.fold(CodeGenResult()) { total, current -> total.merge(current) } + } else { + CodeGenResult() + } + } + + private fun addFragmentProjectionMethod(javaType: TypeSpec.Builder, rootType: TypeSpec, prefix: String, it: TypeDefinition<*>, processedEdges: Set>, queryDepth: Int): CodeGenResult { + val rootRef = if (javaType.build().name == rootType.name) "this" else "getRoot()" + val rootTypeName = if (javaType.build().name == rootType.name) "${rootType.name}" else "ROOT" + val parentRef = javaType.build().name + val projectionName = "${it.name.capitalized()}Projection" + val typeVariable = TypeVariableName.get("$projectionName<$parentRef, $rootTypeName>") + javaType.addMethod( + MethodSpec.methodBuilder("on${it.name}") + .addModifiers(Modifier.PUBLIC) + .returns(typeVariable) + .addCode( + """ + |$projectionName<$parentRef, $rootTypeName> fragment = new $projectionName<>(this, $rootRef); + |getFragments().add(fragment); + |return fragment; + """.trimMargin() + ) + .build() + ) + + return createFragment(it as ObjectTypeDefinition, javaType.build(), rootType, "${it.name.capitalized()}", processedEdges, queryDepth) + } + + private fun createFragment(type: ObjectTypeDefinition, parent: TypeSpec, root: TypeSpec, prefix: String, processedEdges: Set>, queryDepth: Int): CodeGenResult { + val subProjection = createSubProjectionType(type, parent, root, prefix, processedEdges, queryDepth) + ?: return CodeGenResult() + val javaType = subProjection.first + val codeGenResult = subProjection.second + + // We don't need the typename added for fragments in the entities' projection. + // This affects deserialization when use directly with generated classes + if (prefix != "Entities${type.name.capitalized()}Key") { + javaType.addInitializerBlock( + CodeBlock.builder() + .addStatement("getFields().put(\$S, null)", TypeNameMetaFieldDef.name) + .build() + ) + } + + javaType.addMethod( + MethodSpec.methodBuilder("toString") + .returns(ClassName.get(String::class.java)) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC) + .addCode( + """ + |StringBuilder builder = new StringBuilder(); + |builder.append("... on ${type.name} {"); + |getFields().forEach((k, v) -> { + | builder.append(" ").append(k); + | if(v != null) { + | builder.append(" ").append(v.toString()); + | } + |}); + |builder.append("}"); + | + |return builder.toString(); + """.trimMargin() + ) + .build() + ) + + val javaFile = JavaFile.builder(getPackageName(), javaType.build()).build() + return CodeGenResult(clientProjections = listOf(javaFile)).merge(codeGenResult) + } + + private fun createSubProjection(type: TypeDefinition<*>, parent: TypeSpec, root: TypeSpec, prefix: String, processedEdges: Set>, queryDepth: Int): CodeGenResult { + val subProjection = createSubProjectionType(type, parent, root, prefix, processedEdges, queryDepth) + ?: return CodeGenResult() + val javaType = subProjection.first + val codeGenResult = subProjection.second + + val javaFile = JavaFile.builder(getPackageName(), javaType.build()).build() + return CodeGenResult(clientProjections = listOf(javaFile)).merge(codeGenResult) + } + + private fun createSubProjectionType(type: TypeDefinition<*>, parent: TypeSpec, root: TypeSpec, prefix: String, processedEdges: Set>, queryDepth: Int): Pair? { + val className = ClassName.get(BaseSubProjectionNode::class.java) + val clazzName = "${prefix}Projection" + if (generatedClasses.contains(clazzName)) return null else generatedClasses.add(clazzName) + + val parentJavaType = TypeVariableName.get("PARENT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) + val rootJavaType = TypeVariableName.get("ROOT").withBounds(ParameterizedTypeName.get(className, TypeVariableName.get("?"), TypeVariableName.get("?"))) + val javaType = TypeSpec.classBuilder(clazzName) + .addOptionalGeneratedAnnotation(config) + .addTypeVariable(parentJavaType) + .addTypeVariable(rootJavaType) + .addModifiers(Modifier.PUBLIC) + .superclass(ParameterizedTypeName.get(className, TypeVariableName.get("PARENT"), TypeVariableName.get("ROOT"))) + .addMethod( + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(ParameterSpec.builder(ClassName.get("", "PARENT"), "parent").build()) + .addParameter(ParameterSpec.builder(ClassName.get("", "ROOT"), "root").build()) + .addCode("""super(parent, root, java.util.Optional.of("${type.name}"));""") + .build() + ) + + val fieldDefinitions = type.fieldDefinitions() + + document.definitions + .filterIsInstance() + .filter { it.name == type.name } + .flatMap { it.fieldDefinitions } + + val codeGenResult = if (queryDepth < config.maxProjectionDepth || config.maxProjectionDepth == -1) { + fieldDefinitions + .filterSkipped() + .mapNotNull { + val typeDefinition = it.type.findTypeDefinition(document, true) + if (typeDefinition != null) it to typeDefinition else null + } + .map { (fieldDef, typeDef) -> + val projectionName = "${typeDef.name.capitalized()}Projection" + val methodName = ReservedKeywordSanitizer.sanitize(fieldDef.name) + val typeVariable = TypeVariableName.get("$projectionName<$clazzName, ROOT>") + javaType.addMethod( + MethodSpec.methodBuilder(methodName) + .returns(typeVariable) + .addCode( + """ + | $projectionName<$clazzName, ROOT> projection = new $projectionName<>(this, getRoot()); + | getFields().put("${fieldDef.name}", projection); + | return projection; + """.trimMargin() + ) + .addModifiers(Modifier.PUBLIC) + .build() + ) + + if (fieldDef.inputValueDefinitions.isNotEmpty()) { + addFieldSelectionMethodWithArguments(fieldDef, projectionName, javaType, projectionRoot = "getRoot()") + } + + val updatedProcessedEdges = processedEdges.toMutableSet() + updatedProcessedEdges.add(typeDef.name to type.name) + createSubProjection(typeDef, javaType.build(), root, "${typeDef.name.capitalized()}", updatedProcessedEdges, queryDepth + 1) + } + .fold(CodeGenResult()) { total, current -> total.merge(current) } + } else CodeGenResult() + + fieldDefinitions + .filterSkipped() + .forEach { + val objectTypeDefinition = it.type.findTypeDefinition(document) + if (objectTypeDefinition == null) { + val typeVariable = TypeVariableName.get("$clazzName") + javaType.addMethod( + MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(it.name)) + .returns(typeVariable) + .addCode( + """ + |getFields().put("${it.name}", null); + |return this; + """.trimMargin() + ) + .addModifiers(Modifier.PUBLIC) + .build() + ) + + if (it.inputValueDefinitions.isNotEmpty()) { + val methodWithInputArgumentsBuilder = MethodSpec.methodBuilder(ReservedKeywordSanitizer.sanitize(it.name)) + .returns(ClassName.get(getPackageName(), javaType.build().name)) + .addCode( + """ + |getFields().put("${it.name}", null); + |getInputArguments().computeIfAbsent("${it.name}", k -> new ${'$'}T<>()); + |${ + it.inputValueDefinitions.joinToString("\n") { input -> + """ + |InputArgument ${input.name}Arg = new InputArgument("${input.name}", ${input.name}); + |getInputArguments().get("${it.name}").add(${input.name}Arg); + """.trimMargin() + }} + |return this; + """.trimMargin(), + ArrayList::class.java + ) + .addModifiers(Modifier.PUBLIC) + + it.inputValueDefinitions.forEach { input -> + methodWithInputArgumentsBuilder.addParameter(ParameterSpec.builder(typeUtils.findReturnType(input.type), input.name).build()) + } + + javaType.addMethod(methodWithInputArgumentsBuilder.build()) + } + } + } + + val concreteTypesResult = createConcreteTypes(type, root, javaType, prefix, processedEdges, queryDepth) + val unionTypesResult = createUnionTypes(type, javaType, root, prefix, processedEdges, queryDepth) + + return javaType to codeGenResult.merge(concreteTypesResult).merge(unionTypesResult) + } + + private fun truncatePrefix(prefix: String): String { + return if (config.shortProjectionNames) ClassnameShortener.shorten(prefix) else prefix + } + + private fun getPackageName(): String { + return config.packageNameClient + } + + private fun getDatatypesPackageName(): String { + return config.packageNameTypes + } +} diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt similarity index 99% rename from graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt rename to graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt index 8b52c64db..518deeb60 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt @@ -24,7 +24,7 @@ import org.junit.Ignore import org.junit.jupiter.api.Test @Ignore -class EntitiesClientApiGenTest { +class EntitiesClientApiGenTestv2 { @Test fun `We can have federated entities`() { diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenBuilderTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenBuilderTest.kt index 9ee30a93b..6ab432a29 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenBuilderTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenBuilderTest.kt @@ -16,7 +16,7 @@ * */ -package com.netflix.graphql.dgs.codegen.clientapi +package com.netflix.graphql.dgs.codegen.clientapiv2 import com.netflix.graphql.dgs.client.codegen.GraphQLQuery import com.netflix.graphql.dgs.codegen.* diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenFragmentTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenFragmentTest.kt index b739416a9..99bc41a72 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenFragmentTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenFragmentTest.kt @@ -16,15 +16,14 @@ * */ -package com.netflix.graphql.dgs.codegen.clientapi +package com.netflix.graphql.dgs.codegen.clientapiv2 -import com.netflix.graphql.dgs.codegen.CodeGen -import com.netflix.graphql.dgs.codegen.CodeGenConfig -import com.netflix.graphql.dgs.codegen.assertCompilesJava -import com.netflix.graphql.dgs.codegen.basePackageName +import com.netflix.graphql.dgs.codegen.* import com.squareup.javapoet.JavaFile +import com.squareup.javapoet.ParameterizedTypeName import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import java.util.* class ClientApiGenFragmentTest { @Test @@ -60,13 +59,13 @@ class ClientApiGenFragmentTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(3) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("title") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs[0].name).isEqualTo("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_MovieProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("duration") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name") .doesNotContain("episodes") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("SeriesProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_SeriesProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("episodes") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name") @@ -114,19 +113,22 @@ class ClientApiGenFragmentTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(4) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ShowProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_ShowProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("title") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_Show_MovieProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("duration") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name") .doesNotContain("episodes") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("SeriesProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Search_Show_SeriesProjection") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").contains("episodes") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name") .doesNotContain("duration") + val superclass = codeGenResult.clientProjections[3].typeSpec.superclass as ParameterizedTypeName + assertThat(superclass.typeArguments[1]).extracting("simpleName").isEqualTo("SearchProjectionRoot") + assertCompilesJava( codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces ) @@ -162,10 +164,10 @@ class ClientApiGenFragmentTest { assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("onMovie") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("onActor") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_MovieProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").doesNotContain("name") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("ActorProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_ActorProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("name") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").doesNotContain("title") @@ -207,21 +209,24 @@ class ClientApiGenFragmentTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(4) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("SearchResultProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_ResultProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").doesNotContain("title") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").doesNotContain("name") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("onMovie") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("onActor") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_Result_MovieProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("title") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").doesNotContain("name") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("ActorProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Search_Result_ActorProjection") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").contains("name") assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").doesNotContain("title") assertThat(codeGenResult.clientProjections[2].typeSpec.initializerBlock.isEmpty).isFalse assertThat(codeGenResult.clientProjections[3].typeSpec.initializerBlock.isEmpty).isFalse + val superclass = codeGenResult.clientProjections[3].typeSpec.superclass as ParameterizedTypeName + assertThat(superclass.typeArguments[1]).extracting("simpleName").isEqualTo("SearchProjectionRoot") + val searchResult = codeGenResult.javaInterfaces[0].typeSpec assertThat(JavaFile.builder("$basePackageName.types", searchResult).build().toString()).isEqualTo( @@ -245,5 +250,28 @@ class ClientApiGenFragmentTest { | """.trimMargin() ) + + // And assert the Search_Result_MovieProjection instance has an explicit schemaType + val testClassLoader = assertCompilesJava(codeGenResult).toClassLoader() + // Projection class + val searchMovieProjectionClass = + testClassLoader.loadClass("$basePackageName.client.Search_Result_MovieProjection") + // Projection root and parent class + val searchProjectionRootClass = + testClassLoader.loadClass("$basePackageName.client.SearchProjectionRoot") + val searchResultProjectionClass = + testClassLoader.loadClass("$basePackageName.client.Search_ResultProjection") + // Fetch constructor + val searchMovieProjectionCtor = + searchMovieProjectionClass.getDeclaredConstructor(searchResultProjectionClass, searchProjectionRootClass) + val searchMovieProjectionInstance = searchMovieProjectionCtor.newInstance(null, null) + + val optionalProjectionSchemaType = + invokeMethod>( + searchMovieProjectionClass.getMethod("getSchemaType"), + searchMovieProjectionInstance + ) + // assert we have the correct explicit type. + assertThat(optionalProjectionSchemaType).contains("Movie") } } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenMutationTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenMutationTest.kt index ca7122407..a2b161ebf 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenMutationTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenMutationTest.kt @@ -16,7 +16,7 @@ * */ -package com.netflix.graphql.dgs.codegen.clientapi +package com.netflix.graphql.dgs.codegen.clientapiv2 import com.netflix.graphql.dgs.codegen.CodeGen import com.netflix.graphql.dgs.codegen.CodeGenConfig @@ -271,7 +271,7 @@ class ClientApiGenMutationTest { assertThat(codeGenResult.javaQueryTypes) .extracting("typeSpec").extracting("name").containsExactly("ShowsGraphQLQuery") assertThat(codeGenResult.clientProjections) - .extracting("typeSpec").extracting("name").containsExactly("ShowsProjectionRoot", "BooleanProjection") + .extracting("typeSpec").extracting("name").containsExactly("ShowsProjectionRoot", "Shows_IsLiveProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaDataTypes + codeGenResult.javaEnumTypes) } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt index 93c1e17e6..b4471979f 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenProjectionTest.kt @@ -16,13 +16,13 @@ * */ -package com.netflix.graphql.dgs.codegen.clientapi +package com.netflix.graphql.dgs.codegen.clientapiv2 import com.netflix.graphql.dgs.codegen.CodeGen import com.netflix.graphql.dgs.codegen.CodeGenConfig import com.netflix.graphql.dgs.codegen.assertCompilesJava import com.netflix.graphql.dgs.codegen.basePackageName -import com.squareup.javapoet.TypeVariableName +import com.squareup.javapoet.ClassName import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.fail @@ -77,7 +77,7 @@ class ClientApiGenProjectionTest { ).generate() assertThat(codeGenResult.clientProjections.size).isEqualTo(2) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PersonsProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("PersonProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Persons_FriendsProjection") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("name") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("friends") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("name") @@ -116,11 +116,12 @@ class ClientApiGenProjectionTest { ) ).generate() - assertThat(codeGenResult.clientProjections.size).isEqualTo(4) + assertThat(codeGenResult.clientProjections.size).isEqualTo(5) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("DetailsProjection") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("ShowProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_Movie_DetailsProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Search_Movie_Details_ShowProjection") + assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("Search_Movie_Details_Show_MovieProjection") assertCompilesJava( codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces @@ -160,12 +161,14 @@ class ClientApiGenProjectionTest { ) ).generate() - assertThat(codeGenResult.clientProjections.size).isEqualTo(5) + assertThat(codeGenResult.clientProjections.size).isEqualTo(7) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ShowProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("RelatedProjection") - assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("VideoProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_ShowProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_MovieProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Search_Movie_RelatedProjection") + assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("Search_Movie_Related_VideoProjection") + assertThat(codeGenResult.clientProjections[5].typeSpec.name).isEqualTo("Search_Movie_Related_Video_ShowProjection") + assertThat(codeGenResult.clientProjections[6].typeSpec.name).isEqualTo("Search_Movie_Related_Video_MovieProjection") assertCompilesJava( codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces @@ -224,7 +227,7 @@ class ClientApiGenProjectionTest { ) ).generate() assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PersonsProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("DetailsProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Persons_DetailsProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("DetailsProjectionRoot") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) @@ -260,7 +263,7 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(2) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("MoviesProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ActorProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Movies_ActorsProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) } @@ -294,10 +297,11 @@ class ClientApiGenProjectionTest { ) ).generate() - assertThat(codeGenResult.clientProjections.size).isEqualTo(3) + assertThat(codeGenResult.clientProjections.size).isEqualTo(4) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("UserProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("GenreProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("User_FavoriteMovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("User_FavoriteMovie_GenreProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("User_FavoriteMovieGenreProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) } @@ -333,8 +337,8 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(3) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("MoviesProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ActorProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Movies_ActorsProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Mo_Ac_MoviesProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) } @@ -367,8 +371,8 @@ class ClientApiGenProjectionTest { val projections = codeGenResult.clientProjections assertThat(projections.size).isEqualTo(1) assertThat(projections[0].typeSpec.name).isEqualTo("PeopleProjectionRoot") - assertThat(projections[0].typeSpec.methodSpecs.size).isEqualTo(3) - assertThat(projections[0].typeSpec.methodSpecs).extracting("name").contains("name", "email") + assertThat(projections[0].typeSpec.methodSpecs.size).isEqualTo(2) + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("name", "email") assertCompilesJava(codeGenResult) } @@ -403,7 +407,7 @@ class ClientApiGenProjectionTest { ).generate() val projections = codeGenResult.clientProjections assertThat(projections.size).isEqualTo(2) - assertThat(projections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(projections[1].typeSpec.name).isEqualTo("Search_MovieProjection") assertThat(projections[1].typeSpec.methodSpecs.size).isEqualTo(3) assertThat(projections[1].typeSpec.methodSpecs).extracting("name").contains("title", "director", "") @@ -440,7 +444,7 @@ class ClientApiGenProjectionTest { ).generate() val projections = codeGenResult.clientProjections assertThat(projections.size).isEqualTo(2) - assertThat(projections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(projections[1].typeSpec.name).isEqualTo("Search_MovieProjection") assertThat(projections[1].typeSpec.methodSpecs.size).isEqualTo(3) assertThat(projections[1].typeSpec.methodSpecs).extracting("name").contains("title", "director", "") @@ -498,10 +502,10 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(5) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("MoviesProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("RatingProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("ReviewProjection") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("ActorProjection") - assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("AgentProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Movies_RatingProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Movies_Rating_ReviewProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("Movies_ActorsProjection") + assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("Movies_Actors_AgentProjection") assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) } @@ -543,7 +547,7 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("director") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("title") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("DirectorProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_DirectorProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("shows") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("name") @@ -607,7 +611,7 @@ class ClientApiGenProjectionTest { assertThat(codeGenResult.clientProjections.size).isEqualTo(1) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("WeirdTypeProjectionRoot") assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name") - .contains("__", "_root", "_parent", "_import", "_short") + .containsExactly("__", "_root", "_parent", "_import", "_short") assertCompilesJava(codeGenResult) } @@ -641,7 +645,7 @@ class ClientApiGenProjectionTest { ).generate() assertThat(codeGenResult.clientProjections.size).isEqualTo(2) - val weirdType = codeGenResult.clientProjections.find { it.typeSpec.name == "WeirdTypeProjection" } + val weirdType = codeGenResult.clientProjections.find { it.typeSpec.name == "NormalType_WeirdTypeProjection" } ?: fail("NormalType_WeirdTypeProjection type not found") assertThat(weirdType.typeSpec.methodSpecs).extracting("name") @@ -684,11 +688,16 @@ class ClientApiGenProjectionTest { ) ).generate() - assertThat(codeGenResult.clientProjections.size).isEqualTo(4) - assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("WorkshopProjectionRoot") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ReviewConnectionProjection") - assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("AssetProjection") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("ReviewEdgeProjection") + assertThat(codeGenResult.clientProjections.size).isEqualTo(6) + val workshopAssetsReviewsProjection = + codeGenResult.clientProjections.find { it.typeSpec.name == "Workshop_Assets_ReviewsProjection" } + ?: fail("Workshop_Assets_ReviewsProjection type not found") + val workshopReviewsProjection = + codeGenResult.clientProjections.find { it.typeSpec.name == "Workshop_ReviewsProjection" } + ?: fail("Workshop_ReviewsProjection type not found") + + assertThat(workshopReviewsProjection.typeSpec.methodSpecs).extracting("name").contains("edges") + assertThat(workshopAssetsReviewsProjection.typeSpec.methodSpecs).extracting("name").contains("edges") } @Test @@ -717,8 +726,8 @@ class ClientApiGenProjectionTest { ).generate() val methodSpecs = codeGenResult.clientProjections[0].typeSpec.methodSpecs - assertThat(methodSpecs.size).isEqualTo(3) - val methodWithArgs = methodSpecs.find { it.parameters.size > 0 && it.name == "actors" } + assertThat(methodSpecs.size).isEqualTo(2) + val methodWithArgs = methodSpecs.find { it.parameters.size > 0 } ?: fail("Expected method not found") assertThat(methodWithArgs.parameters[0].name).isEqualTo("leadCharactersOnly") assertThat(methodWithArgs.parameters[0].type.toString()).isEqualTo("java.lang.Boolean") @@ -757,8 +766,8 @@ class ClientApiGenProjectionTest { val methodSpecs = codeGenResult.clientProjections[1].typeSpec.methodSpecs val methodWithArgs = methodSpecs.find { !it.isConstructor && it.parameters.size > 0 } ?: fail("Method not found") - assertThat(methodWithArgs.returnType).extracting { (it as TypeVariableName).name } - .isEqualTo("AwardProjection, ROOT>") + assertThat(methodWithArgs.returnType).extracting { (it as ClassName).simpleName() } + .isEqualTo("Movies_Actors_AwardsProjection") assertThat(methodWithArgs.parameters[0].name).isEqualTo("oscarsOnly") assertThat(methodWithArgs.parameters[0].type.toString()).isEqualTo("java.lang.Boolean") } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenQueryTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenQueryTest.kt index 375bf5aec..43dc21c06 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenQueryTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenQueryTest.kt @@ -16,14 +16,12 @@ * */ -package com.netflix.graphql.dgs.codegen.clientapi +package com.netflix.graphql.dgs.codegen.clientapiv2 import com.netflix.graphql.dgs.codegen.* import org.assertj.core.api.Assertions.assertThat -import org.junit.Ignore import org.junit.jupiter.api.Test -@Ignore class ClientApiGenQueryTest { @Test fun generateQueryType() { @@ -210,10 +208,12 @@ class ClientApiGenQueryTest { assertThat(codeGenResult.clientProjections) .extracting("typeSpec").extracting("name").containsExactly( "ShowsProjectionRoot", - "ShowProjection", - "MovieProjection", - "RelatedProjection", - "VideoProjection" + "Shows_ShowProjection", + "Shows_MovieProjection", + "Shows_Movie_RelatedProjection", + "Shows_Movie_Related_VideoProjection", + "Shows_Movie_Related_Video_ShowProjection", + "Shows_Movie_Related_Video_MovieProjection" ) assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaDataTypes + codeGenResult.javaEnumTypes) @@ -441,10 +441,10 @@ class ClientApiGenQueryTest { assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("SearchGraphQLQuery") assertThat(codeGenResult.clientProjections.size).isEqualTo(3) assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") - assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs[1].name).isEqualTo("title") - assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs[0].name).isEqualTo("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("Search_MovieProjection") assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs[2].name).isEqualTo("duration") - assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("SeriesProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("Search_SeriesProjection") assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs[2].name).isEqualTo("episodes") assertCompilesJava( @@ -545,6 +545,8 @@ class ClientApiGenQueryTest { "floatArrayField" ) // fields projections + val stringFieldProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeField_StringFieldProjection") assertThat(rootProjectionClass).isNotNull // stringField assertThat( @@ -558,10 +560,15 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull + .returns(stringFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // stringArrayField + val stringArrayFieldProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeField_StringArrayFieldProjection") + assertThat(rootProjectionClass).isNotNull + assertThat( rootProjectionClass.getMethod("stringArrayField") ).isNotNull @@ -573,11 +580,16 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull + .returns(stringArrayFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // booleanField + val booleanFieldProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeField_BooleanFieldProjection") + assertThat(rootProjectionClass).isNotNull + assertThat( rootProjectionClass.getMethod("booleanField") ).isNotNull @@ -589,11 +601,16 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull + .returns(booleanFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // booleanArrayField + val booleanArrayFieldProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeField_BooleanArrayFieldProjection") + assertThat(rootProjectionClass).isNotNull + assertThat( rootProjectionClass.getMethod("booleanArrayField") ).isNotNull @@ -605,11 +622,16 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull + .returns(booleanArrayFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // floatField + val floatFieldProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeField_FloatFieldProjection") + assertThat(rootProjectionClass).isNotNull + assertThat( rootProjectionClass.getMethod("floatField") ).isNotNull @@ -621,11 +643,16 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull + .returns(floatFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") // booleanArrayField + val floatArrayFieldProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeField_FloatArrayFieldProjection") + assertThat(rootProjectionClass).isNotNull + assertThat( rootProjectionClass.getMethod("floatArrayField") ).isNotNull @@ -637,6 +664,7 @@ class ClientApiGenQueryTest { java.lang.Boolean::class.java ) ).isNotNull + .returns(floatArrayFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") @@ -676,11 +704,16 @@ class ClientApiGenQueryTest { assertThat(rootProjectionClass).isNotNull assertThat(rootProjectionClass).hasPublicMethods("ping") // scalar field + val scalarFieldProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeField_PingProjection") + assertThat(rootProjectionClass).isNotNull + assertThat(rootProjectionClass.getMethod("ping")).isNotNull.returns(rootProjectionClass) { it.returnType } assertThat( rootProjectionClass.getMethod("ping", java.lang.Boolean::class.java) ).isNotNull + .returns(scalarFieldProjectionClass) { it.returnType } .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } .asList() .containsExactly(0 to "arg") diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenSubscriptionTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenSubscriptionTest.kt index dc4cb431b..e316cf928 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenSubscriptionTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapi/ClientApiGenSubscriptionTest.kt @@ -16,7 +16,7 @@ * */ -package com.netflix.graphql.dgs.codegen.clientapi +package com.netflix.graphql.dgs.codegen.clientapiv2 import com.netflix.graphql.dgs.codegen.CodeGen import com.netflix.graphql.dgs.codegen.CodeGenConfig diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenBuilderTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenBuilderTestv2.kt new file mode 100644 index 000000000..2a21b89bd --- /dev/null +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenBuilderTestv2.kt @@ -0,0 +1,141 @@ +/* + * + * Copyright 2020 Netflix, 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 + * + * http://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.netflix.graphql.dgs.codegen.clientapiv2 + +import com.netflix.graphql.dgs.client.codegen.GraphQLQuery +import com.netflix.graphql.dgs.codegen.* +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class ClientApiGenBuilderTestv2 { + @Test + fun `Fields explicitly set to null in the builder should be included`() { + val schema = """ + type Query { + filter(nameFilter: String): [String] + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + maxProjectionDepth = 2 + ) + ).generate() + + val builderClass = assertCompilesJava(codeGenResult).toClassLoader() + .loadClass("$basePackageName.client.FilterGraphQLQuery\$Builder") + + val buildMethod = builderClass.getMethod("build") + val nameMethod = builderClass.getMethod("nameFilter", String::class.java) + + // When the 'nameFilter' method is invoked with a null value, the field should be included in the input map and explicitly set to null. + val builder1 = builderClass.constructors[0].newInstance() + nameMethod.invoke(builder1, null) + val resultQueryObject: GraphQLQuery = buildMethod.invoke(builder1) as GraphQLQuery + assertThat(resultQueryObject.input.keys).containsExactly("nameFilter") + assertThat(resultQueryObject.input["nameFilter"]).isNull() + } + + @Test + fun `Fields not explicitly set to null or any value in the builder should not be included`() { + val schema = """ + type Query { + filter(nameFilter: String): [String] + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + maxProjectionDepth = 2 + ) + ).generate() + + val builderClass = assertCompilesJava(codeGenResult).toClassLoader() + .loadClass("$basePackageName.client.FilterGraphQLQuery\$Builder") + val buildMethod = builderClass.getMethod("build") + + // When the 'nameFilter' method is not invoked, it should not be included in the input map. + val builder2 = builderClass.constructors[0].newInstance() + val result2QueryObject: GraphQLQuery = buildMethod.invoke(builder2) as GraphQLQuery + assertThat(result2QueryObject.input.keys).isEmpty() + assertThat(result2QueryObject.input["nameFilter"]).isNull() + } + + @Test + fun `Query name should be null if not set`() { + val schema = """ + type Query { + filter(nameFilter: String): [String] + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + maxProjectionDepth = 2 + ) + ).generate() + + val builderClass = assertCompilesJava(codeGenResult).toClassLoader() + .loadClass("$basePackageName.client.FilterGraphQLQuery\$Builder") + val buildMethod = builderClass.getMethod("build") + + val builder = builderClass.constructors[0].newInstance() + val result2QueryObject: GraphQLQuery = buildMethod.invoke(builder) as GraphQLQuery + assertThat(result2QueryObject.name).isNull() + } + + @Test + fun `Query name should be accessible via GraphQLQuery#name if set`() { + val schema = """ + type Query { + filter(nameFilter: String): [String] + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + maxProjectionDepth = 2 + ) + ).generate() + + val builderClass = assertCompilesJava(codeGenResult).toClassLoader() + .loadClass("$basePackageName.client.FilterGraphQLQuery\$Builder") + val nameMethod = builderClass.getMethod("queryName", String::class.java) + val buildMethod = builderClass.getMethod("build") + + val builder = builderClass.constructors[0].newInstance() + nameMethod.invoke(builder, "test") + + val result2QueryObject: GraphQLQuery = buildMethod.invoke(builder) as GraphQLQuery + assertThat(result2QueryObject.name).isNotNull + assertThat(result2QueryObject.name).isEqualTo("test") + } +} diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenFragmentTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenFragmentTestv2.kt new file mode 100644 index 000000000..9f8d02fc0 --- /dev/null +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenFragmentTestv2.kt @@ -0,0 +1,249 @@ +/* + * + * Copyright 2020 Netflix, 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 + * + * http://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.netflix.graphql.dgs.codegen.clientapiv2 + +import com.netflix.graphql.dgs.codegen.CodeGen +import com.netflix.graphql.dgs.codegen.CodeGenConfig +import com.netflix.graphql.dgs.codegen.assertCompilesJava +import com.netflix.graphql.dgs.codegen.basePackageName +import com.squareup.javapoet.JavaFile +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class ClientApiGenFragmentTestv2 { + @Test + fun interfaceFragment() { + val schema = """ + type Query { + search(title: String): [Show] + } + + interface Show { + title: String + } + + type Movie implements Show { + title: String + duration: Int + } + + type Series implements Show { + title: String + episodes: Int + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(3) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("duration") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name") + .doesNotContain("episodes") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("SeriesProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("episodes") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name") + .doesNotContain("duration") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces + ) + } + + @Test + fun interfaceFragmentOnSubType() { + val schema = """ + type Query { + search(title: String): [Result] + } + + type Result { + show: Show + } + + interface Show { + title: String + } + + type Movie implements Show { + title: String + duration: Int + } + + type Series implements Show { + title: String + episodes: Int + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(4) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ShowProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("duration") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name") + .doesNotContain("episodes") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("SeriesProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").contains("episodes") + assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name") + .doesNotContain("duration") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces + ) + } + + @Test + fun unionFragment() { + val schema = """ + type Query { + search: [Result] + } + + union Result = Movie | Actor + + type Movie { + title: String + } + + type Actor { + name: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(3) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("onMovie") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("onActor") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").doesNotContain("name") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("ActorProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("name") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").doesNotContain("title") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces + ) + } + + @Test + fun unionFragmentOnSubType() { + val schema = """ + type Query { + search(title: String): [Result] + } + + type Result { + result: SearchResult + } + + union SearchResult = Movie | Actor + + type Movie { + title: String + } + + type Actor { + name: String + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(4) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("SearchResultProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").doesNotContain("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").doesNotContain("name") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("onMovie") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("onActor") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs).extracting("name").doesNotContain("name") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("ActorProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").contains("name") + assertThat(codeGenResult.clientProjections[3].typeSpec.methodSpecs).extracting("name").doesNotContain("title") + + assertThat(codeGenResult.clientProjections[2].typeSpec.initializerBlock.isEmpty).isFalse + assertThat(codeGenResult.clientProjections[3].typeSpec.initializerBlock.isEmpty).isFalse + + val searchResult = codeGenResult.javaInterfaces[0].typeSpec + + assertThat(JavaFile.builder("$basePackageName.types", searchResult).build().toString()).isEqualTo( + """ + |package com.netflix.graphql.dgs.codegen.tests.generated.types; + | + |import com.fasterxml.jackson.annotation.JsonSubTypes; + |import com.fasterxml.jackson.annotation.JsonTypeInfo; + | + |@JsonTypeInfo( + | use = JsonTypeInfo.Id.NAME, + | include = JsonTypeInfo.As.PROPERTY, + | property = "__typename" + |) + |@JsonSubTypes({ + | @JsonSubTypes.Type(value = Movie.class, name = "Movie"), + | @JsonSubTypes.Type(value = Actor.class, name = "Actor") + |}) + |public interface SearchResult { + |} + | + """.trimMargin() + ) + } +} diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenMutationTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenMutationTestv2.kt new file mode 100644 index 000000000..e04ef3833 --- /dev/null +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenMutationTestv2.kt @@ -0,0 +1,302 @@ +/* + * + * Copyright 2020 Netflix, 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 + * + * http://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.netflix.graphql.dgs.codegen.clientapiv2 + +import com.netflix.graphql.dgs.codegen.CodeGen +import com.netflix.graphql.dgs.codegen.CodeGenConfig +import com.netflix.graphql.dgs.codegen.assertCompilesJava +import com.netflix.graphql.dgs.codegen.basePackageName +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class ClientApiGenMutationTestv2 { + @Test + fun generateMutationType() { + val schema = """ + type Mutation { + updateMovie(movieId: ID, title: String): Movie + } + + type Movie { + movieId: ID + title: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("UpdateMovieGraphQLQuery") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateMutationWithInputType() { + val schema = """ + type Mutation { + updateMovie(movie: MovieDescription): Movie + } + + input MovieDescription { + movieId: ID + title: String + actors: [String] + } + + type Movie { + movieId: ID + lastname: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("UpdateMovieGraphQLQuery") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaDataTypes + ) + } + + @Test + fun generateMutationWithInputDescription() { + val schema = """ + type Mutation { + updateMovie( + ""${'"'} + Some movie description + ""${'"'} + movie: MovieDescription): Movie + } + + input MovieDescription { + movieId: ID + title: String + actors: [String] + } + + type Movie { + movieId: ID + lastname: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.typeSpecs[0].methodSpecs[1].javadoc.toString()).isEqualTo("Some movie description") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaDataTypes + ) + } + + @Test + fun generateMutationAddsNullChecksDuringInit() { + val schema = """ + type Mutation { + updateMovie(movie: MovieDescription, reviews: [String], uuid: UUID): Movie + } + + input MovieDescription { + movieId: Int + title: String + actors: [String] + } + + type Movie { + movieId: Int + lastname: String + } + + scalar UUID @javaType(name : "java.util.UUID") + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + val initMethod = codeGenResult.javaQueryTypes[0].typeSpec.methodSpecs + .find { it.name == "" }?.code.toString() + + val expected = """ + |super("mutation", queryName); + |if (movie != null || fieldsSet.contains("movie")) { + | getInput().put("movie", movie); + |}if (reviews != null || fieldsSet.contains("reviews")) { + | getInput().put("reviews", reviews); + |}if (uuid != null || fieldsSet.contains("uuid")) { + | getInput().put("uuid", uuid); + |} + """.trimMargin() + + assert(initMethod.contains(expected)) + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaDataTypes + ) + } + + @Test + fun generateMutationDoesNotAddNullChecksForPrimitiveTypesDuringInit() { + val schema = """ + type Mutation { + updateMovie(movieId: Int!): Movie + } + + type Movie { + movieId: Int + lastname: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assert( + codeGenResult.javaQueryTypes[0].typeSpec.methodSpecs + .find { it.name == "" }?.code.toString() + .contains("super(\"mutation\", queryName);\ngetInput().put(\"movieId\", movieId);") + ) + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaDataTypes + ) + } + + @Test + fun generateOnlyRequiredDataTypesForMutation() { + val schema = """ + type Mutation { + shows(showFilter: ShowFilter): [Show] + people(personFilter: PersonFilter): [Person] + } + + type Show { + title: String + tags(from: Int, to: Int, sourceType: SourceType): [ShowTag] + isLive(countryFilter: CountryFilter): Boolean + } + + enum ShouldNotInclude { YES, NO } + + input NotUsed { + field: String + } + + input ShowFilter { + title: String + showType: ShowType + similarTo: SimilarityInput + } + + input SimilarityInput { + tags: [String] + } + + enum ShowType { + MOVIE, SERIES + } + + input CountryFilter { + countriesToExclude: [String] + } + + enum SourceType { FOO, BAR } + + type Person { + name: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + includeMutations = setOf("shows"), + generateDataTypes = false, + writeToFiles = false + ) + ).generate() + + assertThat(codeGenResult.javaDataTypes) + .extracting("typeSpec").extracting("name").containsExactly("ShowFilter", "SimilarityInput", "CountryFilter") + assertThat(codeGenResult.javaEnumTypes) + .extracting("typeSpec").extracting("name").containsExactly("ShowType", "SourceType") + assertThat(codeGenResult.javaQueryTypes) + .extracting("typeSpec").extracting("name").containsExactly("ShowsGraphQLQuery") + assertThat(codeGenResult.clientProjections) + .extracting("typeSpec").extracting("name").containsExactly("ShowsProjectionRoot", "BooleanProjection") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaDataTypes + codeGenResult.javaEnumTypes) + } + + @Test + fun includeMutationConfig() { + val schema = """ + type Mutation { + updateMovieTitle: String + addActorName: Boolean + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + includeMutations = setOf("updateMovieTitle") + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("UpdateMovieTitleGraphQLQuery") + + assertCompilesJava(codeGenResult) + } +} diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenProjectionTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenProjectionTestv2.kt new file mode 100644 index 000000000..49ab60910 --- /dev/null +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenProjectionTestv2.kt @@ -0,0 +1,765 @@ +/* + * + * Copyright 2020 Netflix, 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 + * + * http://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.netflix.graphql.dgs.codegen.clientapiv2 + +import com.netflix.graphql.dgs.codegen.CodeGen +import com.netflix.graphql.dgs.codegen.CodeGenConfig +import com.netflix.graphql.dgs.codegen.assertCompilesJava +import com.netflix.graphql.dgs.codegen.basePackageName +import com.squareup.javapoet.TypeVariableName +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.fail + +class ClientApiGenProjectionTestv2 { + @Test + fun generateProjectionRoot() { + val schema = """ + type Query { + people: [Person] + } + + type Person { + firstname: String + lastname: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(1) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PeopleProjectionRoot") + + assertCompilesJava(codeGenResult.clientProjections) + } + + @Test + fun generateProjectionRootTestWithCycles() { + val schema = """ + type Query @extends { + persons: [Person] + } + + type Person { + name: String + friends: [Person] + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + assertThat(codeGenResult.clientProjections.size).isEqualTo(2) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PersonsProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("PersonProjection") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("name") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("friends") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("name") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateInterfaceProjectionsWithCycles() { + val schema = """ + type Query { + search(title: String): [Show] + } + + interface Show { + title: String + } + + type Movie implements Show { + title: String + duration: Int + details: Details + } + + type Details { + show: Show + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(4) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("DetailsProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("ShowProjection") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces + ) + } + + @Test + fun generateUnionProjectionsWithCycles() { + val schema = """ + type Query { + search(title: String): [Video] + } + + union Video = Show | Movie + + type Show { + title: String + } + + type Movie { + title: String + duration: Int + related: Related + } + + type Related { + video: Video + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(5) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ShowProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("RelatedProjection") + assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("VideoProjection") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces + ) + } + + @Test + fun generateSubProjectionsWithDifferentRootTypes() { + val schema = """ + type Query @extends { + persons: [Person] + friends: [Person] + } + + type Person { + name: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PersonsProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("FriendsProjectionRoot") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateSubProjectionsWithDifferentParentTypes() { + val schema = """ + type Query @extends { + persons: [Person] + details(name: String): Details + } + + type Person { + details: Details + } + + type Details { + name: String + age: Integer + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PersonsProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("DetailsProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("DetailsProjectionRoot") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateSubProjectionTypes() { + val schema = """ + type Query { + movies: [Movie] + } + + type Movie { + title: String + actors: [Actor] + type: MovieType + } + + type Actor { + name: String + age: Integer + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(2) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("MoviesProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ActorProjection") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateSubProjectionTypesWithSimilarQueryAndFieldNames() { + val schema = """ + type Query { + user: User + } + + type User { + favoriteMovie: Movie + favoriteMovieGenre: Genre + } + + type Movie { + genre: Genre + } + + type Genre { + name: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(3) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("UserProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("GenreProjection") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateSubProjectionTypesWithShortNames() { + val schema = """ + type Query { + movies: [Movie] + } + + type Movie { + title: String + actors: [Actor] + } + + type Actor { + name: String + age: Integer + movies: [Movie] + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + shortProjectionNames = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(3) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("MoviesProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ActorProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("MovieProjection") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun testExtendRootProjection() { + val schema = """ + type Query { + people: [Person] + } + + type Person { + name: String + } + + extend type Person { + email: String + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + typeMapping = mapOf("Long" to "java.lang.Long") + ) + ).generate() + val projections = codeGenResult.clientProjections + assertThat(projections.size).isEqualTo(1) + assertThat(projections[0].typeSpec.name).isEqualTo("PeopleProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs.size).isEqualTo(3) + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").contains("name", "email") + + assertCompilesJava(codeGenResult) + } + + @Test + fun testExtendSubProjection() { + val schema = """ + type Query { + search: [SearchResult] + } + + type SearchResult { + movie: Movie + } + + type Movie { + title: String + } + + extend type Movie { + director: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + typeMapping = mapOf("Long" to "java.lang.Long") + ) + ).generate() + val projections = codeGenResult.clientProjections + assertThat(projections.size).isEqualTo(2) + assertThat(projections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(projections[1].typeSpec.methodSpecs.size).isEqualTo(3) + assertThat(projections[1].typeSpec.methodSpecs).extracting("name").contains("title", "director", "") + + assertCompilesJava(codeGenResult) + } + + @Test + fun testExtendSubProjectionOutOfOrder() { + val schema = """ + type Query { + search: [SearchResult] + } + + type SearchResult { + movie: Movie + } + + extend type Movie { + director: String + } + + type Movie { + title: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + typeMapping = mapOf("Long" to "java.lang.Long") + ) + ).generate() + val projections = codeGenResult.clientProjections + assertThat(projections.size).isEqualTo(2) + assertThat(projections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(projections[1].typeSpec.methodSpecs.size).isEqualTo(3) + assertThat(projections[1].typeSpec.methodSpecs).extracting("name").contains("title", "director", "") + + assertCompilesJava(codeGenResult) + } + + @Test + fun generateSubProjectionTypesMaxDepth() { + val schema = """ + type Query { + movies: [Movie] + } + + type Movie { + title: String + rating: Rating + actors: [Actor] + } + + type Actor { + name: String + age: Integer + agent: Agent + } + + type Agent { + name: String + address : Address + } + + type Address { + street: String + } + + type Rating { + starts: Integer + review: Review + } + + type Review { + description: String + } + + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + maxProjectionDepth = 2 + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(5) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("MoviesProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("RatingProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("ReviewProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("ActorProjection") + assertThat(codeGenResult.clientProjections[4].typeSpec.name).isEqualTo("AgentProjection") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun testImplementsInterfaceProjection() { + val schema = """ + type Query { + search(title: String): [Show] + } + + interface Show { + title: String + director: Director + } + + interface Person { + name: String + } + + type Director implements Person { + name: String + shows: [Show] + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("SearchGraphQLQuery") + assertThat(codeGenResult.clientProjections.size).isEqualTo(3) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("director") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name").contains("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("DirectorProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("shows") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs).extracting("name").contains("name") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces + ) + } + + @Test + fun testScalarsDontGenerateProjections() { + val schema = """ + type Query { + movieCountry: MovieCountry + } + + type MovieCountry { + country: String + movieId: Long + } + scalar Long + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + typeMapping = mapOf("Long" to "java.lang.Long") + ) + ).generate() + val projections = codeGenResult.clientProjections + assertThat(projections.size).isEqualTo(1) + assertCompilesJava(codeGenResult) + } + + @Test + fun generateProjectionRootWithReservedNames() { + val schema = """ + type Query { + weirdType: WeirdType + } + + type WeirdType { + _: String + root: String + parent: String + import: String + short: Integer + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(1) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("WeirdTypeProjectionRoot") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs).extracting("name") + .contains("__", "_root", "_parent", "_import", "_short") + + assertCompilesJava(codeGenResult) + } + + @Test + fun generateSubProjectionWithReservedNames() { + val schema = """ + type Query { + normalType: NormalType + } + + type NormalType { + weirdType: WeirdType + } + + type WeirdType { + _: String + root: String + parent: String + import: String + short: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(2) + val weirdType = codeGenResult.clientProjections.find { it.typeSpec.name == "WeirdTypeProjection" } + ?: fail("NormalType_WeirdTypeProjection type not found") + + assertThat(weirdType.typeSpec.methodSpecs).extracting("name") + .contains("__", "_root", "_parent", "_import", "_short") + + assertCompilesJava(codeGenResult.clientProjections) + } + + @Test + fun generateProjectionsForSameTypeInSameQueryWithDifferentPaths() { + val schema = """ + type Query { + workshop: Workshop + } + + type Workshop { + reviews: ReviewConnection + assets: Asset + } + + type ReviewConnection { + edges: [ReviewEdge] + } + + type ReviewEdge { + node: String + } + + type Asset { + reviews: ReviewConnection + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + writeToFiles = false + ) + ).generate() + + assertThat(codeGenResult.clientProjections.size).isEqualTo(4) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("WorkshopProjectionRoot") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("ReviewConnectionProjection") + assertThat(codeGenResult.clientProjections[3].typeSpec.name).isEqualTo("AssetProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("ReviewEdgeProjection") + } + + @Test + fun `Input arguments on root projections should be support in the query API`() { + val schema = """ + type Query { + movies: [Movie] + } + + type Movie { + actors(leadCharactersOnly: Boolean): [Actor] + } + + type Actor { + name: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + maxProjectionDepth = 2 + ) + ).generate() + + val methodSpecs = codeGenResult.clientProjections[0].typeSpec.methodSpecs + assertThat(methodSpecs.size).isEqualTo(3) + val methodWithArgs = methodSpecs.find { it.parameters.size > 0 && it.name == "actors" } + ?: fail("Expected method not found") + assertThat(methodWithArgs.parameters[0].name).isEqualTo("leadCharactersOnly") + assertThat(methodWithArgs.parameters[0].type.toString()).isEqualTo("java.lang.Boolean") + } + + @Test + fun `Input arguments on sub projections should be support in the query API`() { + val schema = """ + type Query { + movies: [Movie] + } + + type Movie { + actors: [Actor] + awards(oscarsOnly: Boolean): [Award!] + } + + type Actor { + awards(oscarsOnly: Boolean): [Award!] + } + + type Award { + name: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + writeToFiles = true + ) + ).generate() + + val methodSpecs = codeGenResult.clientProjections[1].typeSpec.methodSpecs + val methodWithArgs = methodSpecs.find { !it.isConstructor && it.parameters.size > 0 } + ?: fail("Method not found") + assertThat(methodWithArgs.returnType).extracting { (it as TypeVariableName).name } + .isEqualTo("AwardProjection, ROOT>") + assertThat(methodWithArgs.parameters[0].name).isEqualTo("oscarsOnly") + assertThat(methodWithArgs.parameters[0].type.toString()).isEqualTo("java.lang.Boolean") + } +} diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenQueryTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenQueryTestv2.kt new file mode 100644 index 000000000..bc6e0e002 --- /dev/null +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenQueryTestv2.kt @@ -0,0 +1,977 @@ +/* + * + * Copyright 2020 Netflix, 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 + * + * http://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.netflix.graphql.dgs.codegen.clientapiv2 + +import com.netflix.graphql.dgs.codegen.* +import org.assertj.core.api.Assertions.assertThat +import org.junit.Ignore +import org.junit.jupiter.api.Test + +@Ignore +class ClientApiGenQueryTestv2 { + @Test + fun generateQueryType() { + val schema = """ + type Query { + people: [Person] + } + + type Person { + firstname: String + lastname: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("PeopleGraphQLQuery") + codeGenResult.javaQueryTypes[0].typeSpec.methodSpecs.find { it -> it.isConstructor && it.parameters.isEmpty() } + codeGenResult.javaQueryTypes[0].typeSpec.methodSpecs.find { it -> it.isConstructor && (it.parameters.find { param -> param.name == "queryName" } != null) } + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateQueryTypeWithComments() { + val schema = """ + type Query { + ""${'"'} + All the people + ""${'"'} + people: [Person] + } + + type Person { + firstname: String + lastname: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("PeopleGraphQLQuery") + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.javadoc.toString()).isEqualTo( + """ + All the people + """.trimIndent() + ) + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateQueryTypesWithTypeExtensions() { + val schema = """ + extend type Person { + preferences: Preferences + } + + type Preferences { + userId: ID! + } + + type Query @extends { + getPerson: Person + } + + type Person { + personId: ID! + linkedIdentities: LinkedIdentities + } + + type LinkedIdentities { + employee: Employee + } + + type Employee { + id: ID! + person: Person! + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("GetPersonGraphQLQuery") + + assertCompilesJava(codeGenResult) + } + + @Test + fun generateOnlyRequiredDataTypesForQuery() { + val schema = """ + type Query { + shows(showFilter: ShowFilter): [Video] + people(personFilter: PersonFilter): [Person] + } + + union Video = Show | Movie + + type Movie { + title: String + duration: Int + related: Related + } + + type Related { + video: Video + } + + type Show { + title: String + tags(from: Int, to: Int, sourceType: SourceType): [ShowTag] + isLive(countryFilter: CountryFilter): Boolean + } + + enum ShouldNotInclude { YES, NO } + + input NotUsed { + field: String + } + + input ShowFilter { + title: String + showType: ShowType + similarTo: SimilarityInput + } + + input SimilarityInput { + tags: [String] + } + + enum ShowType { + MOVIE, SERIES + } + + input CountryFilter { + countriesToExclude: [String] + } + + enum SourceType { FOO, BAR } + + type Person { + name: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + includeQueries = setOf("shows"), + generateDataTypes = false, + writeToFiles = false + ) + ).generate() + + assertThat(codeGenResult.javaDataTypes) + .extracting("typeSpec").extracting("name").containsExactly("ShowFilter", "SimilarityInput", "CountryFilter") + assertThat(codeGenResult.javaEnumTypes) + .extracting("typeSpec").extracting("name").containsExactly("ShowType", "SourceType") + assertThat(codeGenResult.javaQueryTypes) + .extracting("typeSpec").extracting("name").containsExactly("ShowsGraphQLQuery") + assertThat(codeGenResult.clientProjections) + .extracting("typeSpec").extracting("name").containsExactly( + "ShowsProjectionRoot", + "ShowProjection", + "MovieProjection", + "RelatedProjection", + "VideoProjection" + ) + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaDataTypes + codeGenResult.javaEnumTypes) + } + + @Test + fun generateRecursiveInputTypes() { + val schema = """ + type Query { + movies(filter: MovieQuery): [String] + } + + input MovieQuery { + booleanQuery: BooleanQuery! + titleFilter: String + } + + input BooleanQuery { + first: MovieQuery! + second: MovieQuery! + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateDataTypes = false, + generateClientApi = true, + includeQueries = setOf("movies") + ) + ).generate() + + assertThat(codeGenResult.javaDataTypes.size).isEqualTo(2) + assertThat(codeGenResult.javaDataTypes[0].typeSpec.name).isEqualTo("MovieQuery") + assertThat(codeGenResult.javaDataTypes[1].typeSpec.name).isEqualTo("BooleanQuery") + + assertCompilesJava(codeGenResult.javaDataTypes) + } + + @Test + fun generateArgumentsForSimpleTypes() { + val schema = """ + type Query { + personSearch(lastname: String): [Person] + } + + type Person { + firstname: String + lastname: String + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.typeSpecs[0].methodSpecs[1].name).isEqualTo("lastname") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateArgumentsForEnum() { + val schema = """ + type Query { + personSearch(index: SearchIndex): [Person] + } + + type Person { + firstname: String + lastname: String + } + + enum SearchIndex { + TEST, PROD + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.typeSpecs[0].methodSpecs[1].name).isEqualTo("index") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + ) + } + + @Test + fun generateArgumentsForObjectType() { + val schema = """ + type Query { + personSearch(index: SearchIndex): [Person] + } + + type Person { + firstname: String + lastname: String + } + + type SearchIndex { + name: String + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("PersonSearchGraphQLQuery") + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.typeSpecs[0].methodSpecs[1].name).isEqualTo("index") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + ) + } + + @Test + fun includeQueryConfig() { + val schema = """ + type Query { + movieTitles: [String] + actorNames: [String] + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + includeQueries = setOf("movieTitles") + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("MovieTitlesGraphQLQuery") + + assertCompilesJava(codeGenResult) + } + + @Test + fun skipCodegen() { + val schema = """ + type Query { + persons: [Person] + personSearch(index: SearchIndex): [Person] @skipcodegen + } + + type Person { + firstname: String + lastname: String + } + + type SearchIndex { + name: String + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("PersonsGraphQLQuery") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + ) + } + + @Test + fun interfaceReturnTypes() { + val schema = """ + type Query { + search(title: String): [Show] + } + + interface Show { + title: String + } + + type Movie implements Show { + title: String + duration: Int + } + + type Series implements Show { + title: String + episodes: Int + } + + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("SearchGraphQLQuery") + assertThat(codeGenResult.clientProjections.size).isEqualTo(3) + assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("SearchProjectionRoot") + assertThat(codeGenResult.clientProjections[0].typeSpec.methodSpecs[1].name).isEqualTo("title") + assertThat(codeGenResult.clientProjections[1].typeSpec.name).isEqualTo("MovieProjection") + assertThat(codeGenResult.clientProjections[1].typeSpec.methodSpecs[2].name).isEqualTo("duration") + assertThat(codeGenResult.clientProjections[2].typeSpec.name).isEqualTo("SeriesProjection") + assertThat(codeGenResult.clientProjections[2].typeSpec.methodSpecs[2].name).isEqualTo("episodes") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces + ) + } + + @Test + fun interfaceWithKeywords() { + val schema = """ + type Query { + queryRoot: QueryRoot + } + + interface HasDefaultField { + default: String + public: String + private: Boolean + } + + type QueryRoot implements HasDefaultField { + name: String + default: String + public: String + private: Boolean + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("QueryRootGraphQLQuery") + + assertThat(codeGenResult.javaInterfaces.size).isEqualTo(1) + assertThat(codeGenResult.javaInterfaces[0].typeSpec.name).isEqualTo("HasDefaultField") + + assertThat(codeGenResult.javaDataTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaDataTypes[0].typeSpec.fieldSpecs.size).isEqualTo(4) + assertThat(codeGenResult.javaDataTypes[0].typeSpec.fieldSpecs[0].name).isEqualTo("name") + assertThat(codeGenResult.javaDataTypes[0].typeSpec.fieldSpecs[1].name).isEqualTo("_default") + assertThat(codeGenResult.javaDataTypes[0].typeSpec.fieldSpecs[2].name).isEqualTo("_public") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaEnumTypes + codeGenResult.javaDataTypes + codeGenResult.javaInterfaces + ) + } + + @Test + fun `The Query API should support sub-projects on fields with Basic Types`() { + // given + val schema = """ + type Query { + someField: Foo + } + + type Foo { + stringField(arg: Boolean): String + stringArrayField(arg: Boolean): [String] + intField(arg: Boolean): Int + intArrayField(arg: Boolean): [Int] + booleanField(arg: Boolean): Boolean + booleanArrayField(arg: Boolean): [Boolean] + floatField(arg: Boolean): Float + floatArrayField(arg: Boolean): [Float] + } + """.trimIndent() + // when + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + writeToFiles = true + ) + ).generate() + // then + val testClassLoader = assertCompilesJava(codeGenResult).toClassLoader() + // assert Type classes + assertThat(testClassLoader.loadClass("$basePackageName.types.Foo")).isNotNull + // assert root projection classes + val rootProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeFieldProjectionRoot") + assertThat(rootProjectionClass).isNotNull + assertThat(rootProjectionClass).hasPublicMethods( + "stringField", + "stringArrayField", + "intField", + "intArrayField", + "booleanField", + "booleanArrayField", + "floatField", + "floatArrayField" + ) + // fields projections + assertThat(rootProjectionClass).isNotNull + // stringField + assertThat( + rootProjectionClass.getMethod("stringField") + ).isNotNull + .returns(rootProjectionClass) { it.returnType } + + assertThat( + rootProjectionClass.getMethod( + "stringField", + java.lang.Boolean::class.java + ) + ).isNotNull + .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } + .asList() + .containsExactly(0 to "arg") + // stringArrayField + assertThat( + rootProjectionClass.getMethod("stringArrayField") + ).isNotNull + .returns(rootProjectionClass) { it.returnType } + + assertThat( + rootProjectionClass.getMethod( + "stringArrayField", + java.lang.Boolean::class.java + ) + ).isNotNull + .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } + .asList() + .containsExactly(0 to "arg") + + // booleanField + assertThat( + rootProjectionClass.getMethod("booleanField") + ).isNotNull + .returns(rootProjectionClass) { it.returnType } + + assertThat( + rootProjectionClass.getMethod( + "booleanField", + java.lang.Boolean::class.java + ) + ).isNotNull + .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } + .asList() + .containsExactly(0 to "arg") + + // booleanArrayField + assertThat( + rootProjectionClass.getMethod("booleanArrayField") + ).isNotNull + .returns(rootProjectionClass) { it.returnType } + + assertThat( + rootProjectionClass.getMethod( + "booleanArrayField", + java.lang.Boolean::class.java + ) + ).isNotNull + .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } + .asList() + .containsExactly(0 to "arg") + + // floatField + assertThat( + rootProjectionClass.getMethod("floatField") + ).isNotNull + .returns(rootProjectionClass) { it.returnType } + + assertThat( + rootProjectionClass.getMethod( + "floatField", + java.lang.Boolean::class.java + ) + ).isNotNull + .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } + .asList() + .containsExactly(0 to "arg") + + // booleanArrayField + assertThat( + rootProjectionClass.getMethod("floatArrayField") + ).isNotNull + .returns(rootProjectionClass) { it.returnType } + + assertThat( + rootProjectionClass.getMethod( + "floatArrayField", + java.lang.Boolean::class.java + ) + ).isNotNull + .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } + .asList() + .containsExactly(0 to "arg") + } + + @Test + fun `The Query API should support sub-projects on fields with Scalars`() { + val schema = """ + type Query { + someField: Foo + } + + type Foo { + ping(arg: Boolean): Long + } + + scalar Long + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + typeMapping = mapOf("Long" to "java.lang.Long") + ) + ).generate() + val projections = codeGenResult.clientProjections + assertThat(projections.size).isEqualTo(2) + + val testClassLoader = assertCompilesJava(codeGenResult).toClassLoader() + // assert Type classes + assertThat(testClassLoader.loadClass("$basePackageName.types.Foo")).isNotNull + // assert root projection classes + val rootProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeFieldProjectionRoot") + assertThat(rootProjectionClass).isNotNull + assertThat(rootProjectionClass).hasPublicMethods("ping") + // scalar field + assertThat(rootProjectionClass.getMethod("ping")).isNotNull.returns(rootProjectionClass) { it.returnType } + + assertThat( + rootProjectionClass.getMethod("ping", java.lang.Boolean::class.java) + ).isNotNull + .extracting { m -> m.parameters.mapIndexed { index, parameter -> index to parameter.name } } + .asList() + .containsExactly(0 to "arg") + } + + @Test + fun `Should be able to generate a valid client when java keywords are used as field names`() { + val schema = """ + type Query { + someField: Foo + } + + type Foo { + ping(arg: Boolean): Long + # --- + parent: Boolean + root: Boolean + # --- + abstract: Boolean + assert: Boolean + boolean: Boolean + break: Boolean + byte: Boolean + case: Boolean + catch: Boolean + char: Boolean + # class: Boolean -- not supported + const: Boolean + continue: Boolean + default: Boolean + do: Boolean + double: Boolean + else: Boolean + enum: Boolean + extends: Boolean + final: Boolean + finally: Boolean + float: Boolean + for: Boolean + goto: Boolean + if: Boolean + implements: Boolean + import: Boolean + instanceof: Boolean + int: Boolean + interface: Boolean + long: Boolean + native: Boolean + new: Boolean + package: Boolean + private: Boolean + protected: Boolean + public: Boolean + return: Boolean + short: Boolean + static: Boolean + strictfp: Boolean + super: Boolean + switch: Boolean + synchronized: Boolean + this: Boolean + throw: Boolean + throws: Boolean + transient: Boolean + try: Boolean + void: Boolean + volatile: Boolean + while: Boolean + class: Int + } + + scalar Long + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateDataTypes = true, + generateClientApi = true, + typeMapping = mapOf("Long" to "java.lang.Long") + ) + ).generate() + val projections = codeGenResult.clientProjections + assertThat(projections.size).isEqualTo(2) + + val testClassLoader = assertCompilesJava(codeGenResult).toClassLoader() + // assert Type classes + assertThat(testClassLoader.loadClass("$basePackageName.types.Foo")).isNotNull + // assert root projection classes + val rootProjectionClass = + testClassLoader.loadClass("$basePackageName.client.SomeFieldProjectionRoot") + assertThat(rootProjectionClass).isNotNull + assertThat(rootProjectionClass).hasPublicMethods("ping") + assertThat(rootProjectionClass).hasPublicMethods( + "_parent", + "_root", + // ---- + "_abstract", + "_assert", + "_boolean", + "_break", + "_byte", + "_case", + "_catch", + "_char", + "_const", + "_continue", + "_default", + "_do", + "_double", + "_else", + "_enum", + "_extends", + "_final", + "_finally", + "_float", + "_for", + "_goto", + "_if", + "_implements", + "_import", + "_instanceof", + "_int", + "_interface", + "_long", + "_native", + "_new", + "_package", + "_private", + "_protected", + "_public", + "_return", + "_short", + "_static", + "_strictfp", + "_super", + "_switch", + "_synchronized", + "_this", + "_throw", + "_throws", + "_transient", + "_try", + "_void", + "_volatile", + "_while", + "_class" + ) + } + + @Test + fun `Should be able to generate successfully when java keywords and default value are used as input types`() { + val schema = """ + type Query { + foo(fooInput: FooInput): Baz + bar(barInput: BarInput): Baz + } + + input FooInput { + public: Boolean = true + } + + input BarInput { + public: Boolean + } + + type Baz { + public: Boolean + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateDataTypes = false, + generateClientApi = true, + includeQueries = setOf("foo", "bar") + ) + ).generate() + + assertThat(codeGenResult.javaDataTypes.size).isEqualTo(2) + + assertThat(codeGenResult.javaDataTypes[0].typeSpec.name).isEqualTo("FooInput") + assertThat(codeGenResult.javaDataTypes[0].typeSpec.fieldSpecs[0].name).isEqualTo("_public") + assertThat(codeGenResult.javaDataTypes[0].typeSpec.fieldSpecs[0].initializer.toString()).isEqualTo("true") + + assertThat(codeGenResult.javaDataTypes[1].typeSpec.name).isEqualTo("BarInput") + assertThat(codeGenResult.javaDataTypes[1].typeSpec.fieldSpecs[0].initializer.toString()).isEqualTo("") + + assertCompilesJava(codeGenResult.javaDataTypes) + } + + @Test + fun `generate client code for both query and subscription with same definitions`() { + val schema = """ + type Subscription { + shows: [Show] + movie(id: ID!): Movie + foo: Boolean + bar: Boolean + } + + type Mutation { + shows: [String] + movie(id: ID!, title: String): Movie + foo: String + } + + type Query { + shows: [Show] + movie: Movie + } + type Show { + id: Int + title: String + } + + type Movie { + title: String + duration: Int + related: Related + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(9) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("ShowsGraphQLQuery") + assertThat(codeGenResult.javaQueryTypes[1].typeSpec.name).isEqualTo("MovieGraphQLQuery") + + assertThat(codeGenResult.javaQueryTypes[2].typeSpec.name).isEqualTo("ShowsGraphQLMutation") + assertThat(codeGenResult.javaQueryTypes[3].typeSpec.name).isEqualTo("MovieGraphQLMutation") + assertThat(codeGenResult.javaQueryTypes[4].typeSpec.name).isEqualTo("FooGraphQLQuery") + + assertThat(codeGenResult.javaQueryTypes[5].typeSpec.name).isEqualTo("ShowsGraphQLSubscription") + assertThat(codeGenResult.javaQueryTypes[6].typeSpec.name).isEqualTo("MovieGraphQLSubscription") + assertThat(codeGenResult.javaQueryTypes[7].typeSpec.name).isEqualTo("FooGraphQLSubscription") + assertThat(codeGenResult.javaQueryTypes[8].typeSpec.name).isEqualTo("BarGraphQLQuery") + + assertCompilesJava(codeGenResult.javaQueryTypes) + } + + @Test + fun `Should be able to generate successfully when java keywords are used as types`() { + val schema = """ + type Query { + bar: Bar + } + + interface Foo { + class: Int + } + + type Bar implements Foo { + object: Int + class: Int + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateDataTypes = true, + generateClientApi = true, + includeQueries = setOf("bar") + ) + ).generate() + + assertThat(codeGenResult.javaDataTypes.size).isEqualTo(1) + + val typeSpec = codeGenResult.javaDataTypes[0].typeSpec + assertThat(typeSpec.name).isEqualTo("Bar") + assertThat(typeSpec.fieldSpecs[0].name).isEqualTo("object") + assertThat(typeSpec.fieldSpecs.size).isEqualTo(2) + assertThat(typeSpec.fieldSpecs[1].name).isEqualTo("_class") + + assertThat(typeSpec.methodSpecs.size).isGreaterThan(0) + assertThat(typeSpec.methodSpecs[0].name).isEqualTo("getObject") + assertThat(typeSpec.methodSpecs[1].name).isEqualTo("setObject") + assertThat(typeSpec.methodSpecs[2].name).isEqualTo("getClassField") + assertThat(typeSpec.methodSpecs[3].name).isEqualTo("setClassField") + + assertCompilesJava(codeGenResult.javaDataTypes + codeGenResult.javaInterfaces) + } +} diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenSubscriptionTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenSubscriptionTestv2.kt new file mode 100644 index 000000000..981e2ba25 --- /dev/null +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenSubscriptionTestv2.kt @@ -0,0 +1,114 @@ +/* + * + * Copyright 2020 Netflix, 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 + * + * http://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.netflix.graphql.dgs.codegen.clientapiv2 + +import com.netflix.graphql.dgs.codegen.CodeGen +import com.netflix.graphql.dgs.codegen.CodeGenConfig +import com.netflix.graphql.dgs.codegen.assertCompilesJava +import com.netflix.graphql.dgs.codegen.basePackageName +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class ClientApiGenSubscriptionTestv2 { + @Test + fun generateSubscriptionType() { + val schema = """ + type Subscription { + movie(movieId: ID, title: String): Movie + } + + type Movie { + movieId: ID + title: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("MovieGraphQLQuery") + + assertCompilesJava(codeGenResult.clientProjections + codeGenResult.javaQueryTypes) + } + + @Test + fun generateSubscriptionWithInputType() { + val schema = """ + type Mutation { + movie(movie: MovieDescription): Movie + } + + input MovieDescription { + movieId: ID + title: String + actors: [String] + } + + type Movie { + movieId: ID + lastname: String + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("MovieGraphQLQuery") + + assertCompilesJava( + codeGenResult.clientProjections + codeGenResult.javaQueryTypes + codeGenResult.javaDataTypes + ) + } + + @Test + fun includeSubscriptionConfig() { + val schema = """ + type Subscription { + movieTitle: String + addActorName: Boolean + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + includeSubscriptions = setOf("movieTitle") + ) + ).generate() + + assertThat(codeGenResult.javaQueryTypes.size).isEqualTo(1) + assertThat(codeGenResult.javaQueryTypes[0].typeSpec.name).isEqualTo("MovieTitleGraphQLQuery") + + assertCompilesJava(codeGenResult) + } +} From 38a02c86c379f13bc345fdf6a2bb8534bc34ab2b Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Thu, 9 Feb 2023 12:57:04 -0800 Subject: [PATCH 08/10] Rename new entity tests for v2. --- .../dgs/codegen/EntitiesClientApiGenTest.kt | 573 ++++++++++++++++++ 1 file changed, 573 insertions(+) create mode 100644 graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt new file mode 100644 index 000000000..c36c26baa --- /dev/null +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTest.kt @@ -0,0 +1,573 @@ +/* + * + * Copyright 2020 Netflix, 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 + * + * http://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.netflix.graphql.dgs.codegen + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.fail +import org.junit.jupiter.api.Test + +class EntitiesClientApiGenTest { + + @Test + fun `We can have federated entities`() { + val schema = """ + type Movie @key(fields: "movieId") { + movieId: ID! @external + title: String + genre: MovieGenre + actor: Actor + } + + enum MovieGenre { + HORROR + ACTION + ROMANCE + COMEDY + } + + type Actor { + name: String + friends: Actor + } + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") + assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_GenreProjection") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") + assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_FriendsProjection") + + val representation = codeGenResult.javaDataTypes.single { "Representation" in it.typeSpec.name } + + assertThat(representation.typeSpec.name).isEqualTo("MovieRepresentation") + assertThat(representation.typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "movieId") + + codeGenResult.assertCompile() + } + + @Test + fun `We can have federated entities and queries`() { + val schema = """ + type Query { + search: Movie + } + + type Movie @key(fields: "movieId") { + movieId: ID! @external + title: String + actor: Actor + } + + type Actor { + name: String + friends: Actor + } + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") + assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_FriendsProjection") + + val representation = codeGenResult.javaDataTypes.single { "Representation" in it.typeSpec.name } + assertThat(representation.typeSpec.name).isEqualTo("MovieRepresentation") + assertThat(representation.typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "movieId") + + codeGenResult.assertCompile() + } + + @Test + fun `An entity can have a field that is an interface`() { + val schema = """ + type Query { + search: Movie + } + + type Movie @key(fields: "actor { name }") { + movieId: ID! + title: String + actor: IActor @external + } + + interface IActor { + name: String + } + + type Actor implements IActor { + name: String + friends: Actor + } + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie") + assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_ActorProjection") + + val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } + assertThat(representations).hasSize(2) + assertThat(representations[0].typeSpec.name).isEqualTo("MovieRepresentation") + assertThat(representations[0].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "actor") + assertThat(representations[1].typeSpec.name).isEqualTo("IActorRepresentation") + assertThat(representations[1].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "name") + + codeGenResult.assertCompile() + } + + @Test + fun `We can have entities with arrays and nested keys`() { + val schema = """ + type Query { + search: Movie + } + + type Movie @key(fields: "movieId actors { name }") { + movieId: ID! @external + title: String + actors: [Actor!]! + } + + type Actor @key(fields: "name") { + name: String + } + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactly("onMovie", "onActor") + assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorsProjection") + + val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } + assertThat(representations).hasSize(2) + assertThat(representations[0].typeSpec.name).isEqualTo("MovieRepresentation") + assertThat(representations[0].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "movieId", "actors") + assertThat(representations[0].typeSpec.fieldSpecs[1]).extracting("type") + .toString() + .contains("java.util.List") + + codeGenResult.assertCompile() + } + + @Test + fun `We can have entities with nested keys`() { + val schema = """ + type Query { + search: Movie + } + + type Movie @key(fields: "movieId actor { name }") { + movieId: ID! @external + title: String + actor: Person + } + + type Person { + name: String @external + age: Int + } + + type MovieCast @key(fields: "movie { movieId actor { name } } actor{name}") { + movie: Movie + actor: Person + } + + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name") + .containsExactlyInAnyOrder("onMovie", "onMovieCast") + assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieCastKeyProjection") + assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesMovieCastKey_MovieProjection") + assertThat(projections[5].typeSpec.name).isEqualTo("EntitiesMovieCastKey_Movie_ActorProjection") + assertThat(projections[6].typeSpec.name).isEqualTo("EntitiesMovieCastKey_ActorProjection") + + val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } + assertThat(representations).hasSize(3) + assertThat(representations[0].typeSpec.name).isEqualTo("MovieRepresentation") + assertThat(representations[0].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "movieId", "actor") + assertThat(representations[1].typeSpec.name).isEqualTo("PersonRepresentation") + assertThat(representations[1].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "name") + assertThat(representations[2].typeSpec.name).isEqualTo("MovieCastRepresentation") + assertThat(representations[2].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "movie", "actor") + + codeGenResult.assertCompile() + } + + @Test + fun `We can have multiple entities with keys`() { + val schema = """ + type Query { + search: Movie + } + + type Movie @key(fields: "movieId") { + movieId: ID! @external + title: String + actor: MovieActor + } + + type MovieActor @key(fields: "name") { + name: String @external + age: Int + } + + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name") + .containsExactlyInAnyOrder("onMovie", "onMovieActor") + assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieActorKeyProjection") + + val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } + assertThat(representations).hasSize(2) + assertThat(representations[0].typeSpec.name).isEqualTo("MovieRepresentation") + assertThat(representations[0].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "movieId") + assertThat(representations[1].typeSpec.name).isEqualTo("MovieActorRepresentation") + assertThat(representations[1].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "name") + + codeGenResult.assertCompile() + } + + @Test + fun `Entities can have nested complex keys`() { + val schema = """ + type Query { + search: Movie + } + + type Movie @key(fields: "movieId actor { name age }") { + movieId: ID! @external + title: String + actor: Person + } + + type Person { + name: String @external + age: Int + } + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("onMovie") + assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") + + val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } + assertThat(representations).hasSize(2) + assertThat(representations[0].typeSpec.name).isEqualTo("MovieRepresentation") + assertThat(representations[0].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "movieId", "actor") + assertThat(representations[1].typeSpec.name).isEqualTo("PersonRepresentation") + assertThat(representations[1].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "name", "age") + + codeGenResult.assertCompile() + } + + @Test + fun `Entities can have keys that are enums`() { + val schema = """ + type Query { + search: Movie + } + + type Movie @key(fields: "id genre") { + id: ID! @external + genre: MovieGenre + title: String + } + + enum MovieGenre { + HORROR + ACTION + ROMANCE + COMEDY + } + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("onMovie") + assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_GenreProjection") + + val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } + assertThat(representations).hasSize(2) + assertThat(representations[0].typeSpec.name).isEqualTo("MovieRepresentation") + assertThat(representations[0].typeSpec.fieldSpecs).extracting("name") + .containsExactlyInAnyOrder("__typename", "id", "genre") + assertThat(representations[1].typeSpec.name).isEqualTo("MovieGenreRepresentation") + + codeGenResult.assertCompile() + } + + @Test + fun `Entities can have the @key directive used multiple times`() { + val schema = """ + type Movie @key(fields: "id genre") @key(fields: "id actor{ id }") @key(fields: "id location { id }") { + id: ID! @external + title: String + genre: MovieGenre + actor: Person + location: Location + } + + enum MovieGenre { + HORROR + ACTION + ROMANCE + COMEDY + } + + type Person @extends { + id: ID @external + name: String + role: Role + } + + enum Role { ATL BTL } + + type Location { + id: ID + name: String + } + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections[0].typeSpec.name).isEqualTo("EntitiesProjectionRoot") + assertThat(projections[0].typeSpec.methodSpecs).extracting("name").containsExactlyInAnyOrder("onMovie") + assertThat(projections[1].typeSpec.name).isEqualTo("EntitiesMovieKeyProjection") + assertThat(projections[2].typeSpec.name).isEqualTo("EntitiesMovieKey_GenreProjection") + assertThat(projections[3].typeSpec.name).isEqualTo("EntitiesMovieKey_ActorProjection") + assertThat(projections[4].typeSpec.name).isEqualTo("EntitiesMovieKey_Actor_RoleProjection") + assertThat(projections[5].typeSpec.name).isEqualTo("EntitiesMovieKey_LocationProjection") + + val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } + assertThat(representations.map { it.typeSpec.name }) + .containsExactlyInAnyOrder( + "MovieRepresentation", + "MovieGenreRepresentation", + "PersonRepresentation", + "LocationRepresentation" + ) + + assertThat(representations.first { it.typeSpec.name == "MovieRepresentation" }.typeSpec.fieldSpecs) + .extracting("name").containsExactlyInAnyOrder("__typename", "id", "actor", "genre", "location") + + val movieRepresentationType = representations.find { it.typeSpec.name == "MovieRepresentation" } + ?: fail("MovieRepresentation type not found") + assertThat(movieRepresentationType.typeSpec.fieldSpecs.map { it.name to it.type.toString() }) + .containsExactlyInAnyOrder( + "id" to "java.lang.String", + "genre" to "com.netflix.graphql.dgs.codegen.tests.generated.client.MovieGenreRepresentation", + "actor" to "com.netflix.graphql.dgs.codegen.tests.generated.client.PersonRepresentation", + "location" to "com.netflix.graphql.dgs.codegen.tests.generated.client.LocationRepresentation", + "__typename" to "java.lang.String" + ) + + assertThat(representations.first { it.typeSpec.name == "PersonRepresentation" }.typeSpec.fieldSpecs) + .extracting("name").containsExactlyInAnyOrder("__typename", "id") + + assertThat(representations.first { it.typeSpec.name == "LocationRepresentation" }.typeSpec.fieldSpecs) + .extracting("name").containsExactlyInAnyOrder("__typename", "id") + + codeGenResult.assertCompile() + } + + @Test + fun `Entities can have scalar fields`() { + val schema = """ + type Query { + movieCountry: MovieCountry + } + + type MovieCountry @key(fields : "movieId country") { + country: String + movieId: Long + } + scalar Long + + """.trimIndent() + + val codeGenResult = codeGen(schema) + + val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name } + assertThat(representations).hasSize(1) + val projections = codeGenResult.clientProjections + assertThat(projections).hasSize(3) + + codeGenResult.assertCompile() + } + + @Test + fun `CodeGen can be configured to skip entities`() { + val schema = """ + type Query { + search: Movie + } + + type Movie @key(fields: "movieId") { + movieId: ID! @external + title: String + actor: Actor + } + + type Actor { + name: String + friends: Actor + } + """.trimIndent() + + val codeGenResult = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + skipEntityQueries = true + ) + ).generate() + + val projections = codeGenResult.clientProjections.filter { "Entities" in it.typeSpec.name } + assertThat(projections).isEmpty() + + codeGenResult.assertCompile() + } + + @Test + fun `Generate projections for the entities' keys`() { + val schema = """ + type Foo @key(fields:"id") { + id: ID + stringField: String + barField: Bar + mStringField(arg1: Int, arg2: String): [String!] + mBarField(arg1: Int, arg2: String): [Bar!] + } + + type Bar { + id: ID + baz: String + } + """.trimIndent() + + val codeGenResult = codeGen(schema) + // then + val testClassLoader = codeGenResult.assertCompile().toClassLoader() + // assert projection classes + val (entityRootProjectionClass, entitiesFooKeyProjectionClass, entitiesFooKey_BarFieldProjectionClass, entitiesFooKey_MBarFieldProjection) = + arrayOf( + "EntitiesProjectionRoot", + "EntitiesFooKeyProjection", + "EntitiesFooKey_BarFieldProjection", + "EntitiesFooKey_MBarFieldProjection" + ).map { + val clazzCanonicalName = "$basePackageName.client.$it" + val clazz = testClassLoader.loadClass(clazzCanonicalName) + assertThat(clazz).describedAs(clazzCanonicalName).isNotNull + clazz + } + + // assert classes methods... + assertThat(entityRootProjectionClass).isNotNull.hasPublicMethods("onFoo") + + assertThat(entitiesFooKeyProjectionClass).isNotNull.hasPublicMethods( + "id", + "stringField", + "barField", + "mStringField", + "mBarField" + ) + // entitiesFooKeyProjectionClass methods + mapOf( + "id" to entitiesFooKeyProjectionClass, + "stringField" to entitiesFooKeyProjectionClass, + "barField" to entitiesFooKey_BarFieldProjectionClass, + "mStringField" to entitiesFooKeyProjectionClass, + "mBarField" to entitiesFooKey_MBarFieldProjection + ).forEach { (name, returnClass) -> + assertThat(entitiesFooKeyProjectionClass.getMethod(name)) + .describedAs("${entitiesFooKeyProjectionClass.name} method: $name").isNotNull.returns(returnClass) { it.returnType } + } + + mapOf( + "mBarField" to (arrayOf(Integer::class.java, String::class.java) to entitiesFooKey_MBarFieldProjection), + "mStringField" to (arrayOf(Integer::class.java, String::class.java) to entitiesFooKeyProjectionClass) + ).forEach { (name, p) -> + val (_, returnClass) = p + assertThat(entitiesFooKeyProjectionClass.getMethod(name)) + .describedAs("method: $name").isNotNull.returns(returnClass) { it.returnType } + } + } + + companion object { + fun codeGen(schema: String): CodeGenResult { + return CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + generateClientApi = true, + language = Language.JAVA + ) + ).generate() + } + fun CodeGenResult.assertCompile() = assertCompilesJava(this) + } +} From 08527887c3483ec9bbfbc45b4d5186d81fb7dd08 Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Tue, 14 Feb 2023 16:15:22 -0800 Subject: [PATCH 09/10] Add v2 config and fix tests. Make CodegenConfig mutable. --- .../netflix/graphql/dgs/codegen/CodeGen.kt | 87 +++++++++++-------- ....kt => EntitiesClientApiGenTestv2.kt.back} | 2 +- .../clientapiv2/ClientApiGenBuilderTestv2.kt | 8 +- .../clientapiv2/ClientApiGenFragmentTestv2.kt | 8 +- .../clientapiv2/ClientApiGenMutationTestv2.kt | 14 +-- .../ClientApiGenProjectionTestv2.kt | 40 ++++----- .../clientapiv2/ClientApiGenQueryTestv2.kt | 36 ++++---- .../ClientApiGenSubscriptionTestv2.kt | 6 +- .../dgs/codegen/gradle/GenerateJavaTask.kt | 4 + 9 files changed, 110 insertions(+), 95 deletions(-) rename graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/{EntitiesClientApiGenTestv2.kt => EntitiesClientApiGenTestv2.kt.back} (99%) 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 da75ae70a..72b866b86 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 @@ -218,23 +218,33 @@ class CodeGen(private val config: CodeGenConfig) { private fun generateJavaClientApi(definitions: Collection>): CodeGenResult { val methodNames = mutableSetOf() - return if (config.generateClientApi) { + return if (config.generateClientApi || config.generateClientApiv2) { definitions.asSequence() .filterIsInstance() .filter { it.name == "Query" || it.name == "Mutation" || it.name == "Subscription" } .sortedBy { it.name.length } - .map { ClientApiGenerator(config, document).generate(it, methodNames) } + .map { + if (config.generateClientApiv2) { + ClientApiGeneratorv2(config, document).generate(it, methodNames) + } else { + ClientApiGenerator(config, document).generate(it, methodNames) + } + } .fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) } } else CodeGenResult() } private fun generateJavaClientEntitiesApi(definitions: Collection>): CodeGenResult { - return if (config.generateClientApi) { + return if (config.generateClientApi || config.generateClientApiv2) { val federatedDefinitions = definitions.asSequence() .filterIsInstance() .filter { it.hasDirective("key") } .toList() - ClientApiGenerator(config, document).generateEntities(federatedDefinitions) + if (config.generateClientApiv2) { + ClientApiGeneratorv2(config, document).generateEntities(federatedDefinitions) + } else { + ClientApiGenerator(config, document).generateEntities(federatedDefinitions) + } } else CodeGenResult() } @@ -440,45 +450,46 @@ class CodeGen(private val config: CodeGenConfig) { } } -data class CodeGenConfig( - val schemas: Set = emptySet(), - val schemaFiles: Set = emptySet(), - val schemaJarFilesFromDependencies: List = emptyList(), - val outputDir: Path = Paths.get("generated"), - val examplesOutputDir: Path = Paths.get("generated-examples"), - val writeToFiles: Boolean = false, - val packageName: String = "com.netflix.${Paths.get("").toAbsolutePath().fileName}.generated", +class CodeGenConfig( + var schemas: Set = emptySet(), + var schemaFiles: Set = emptySet(), + var schemaJarFilesFromDependencies: List = emptyList(), + var outputDir: Path = Paths.get("generated"), + var examplesOutputDir: Path = Paths.get("generated-examples"), + var writeToFiles: Boolean = false, + var packageName: String = "com.netflix.${Paths.get("").toAbsolutePath().fileName}.generated", private val subPackageNameClient: String = "client", private val subPackageNameDatafetchers: String = "datafetchers", private val subPackageNameTypes: String = "types", - val language: Language = Language.JAVA, - val generateBoxedTypes: Boolean = false, - val generateClientApi: Boolean = false, - val generateInterfaces: Boolean = false, - val generateKotlinNullableClasses: Boolean = false, - val generateKotlinClosureProjections: Boolean = false, - val typeMapping: Map = emptyMap(), - val includeQueries: Set = emptySet(), - val includeMutations: Set = emptySet(), - val includeSubscriptions: Set = emptySet(), - val skipEntityQueries: Boolean = false, - val shortProjectionNames: Boolean = false, - val generateDataTypes: Boolean = true, - val omitNullInputFields: Boolean = false, - val maxProjectionDepth: Int = 10, - val kotlinAllFieldsOptional: Boolean = false, + var language: Language = Language.JAVA, + var generateBoxedTypes: Boolean = false, + var generateClientApi: Boolean = false, + var generateClientApiv2: Boolean = false, + var generateInterfaces: Boolean = false, + var generateKotlinNullableClasses: Boolean = false, + var generateKotlinClosureProjections: Boolean = false, + var typeMapping: Map = emptyMap(), + var includeQueries: Set = emptySet(), + var includeMutations: Set = emptySet(), + var includeSubscriptions: Set = emptySet(), + var skipEntityQueries: Boolean = false, + var shortProjectionNames: Boolean = false, + var generateDataTypes: Boolean = true, + var omitNullInputFields: Boolean = false, + var maxProjectionDepth: Int = 10, + var kotlinAllFieldsOptional: Boolean = false, /** If enabled, the names of the classes available via the DgsConstant class will be snake cased.*/ - val snakeCaseConstantNames: Boolean = false, - val generateInterfaceSetters: Boolean = true, - val generateInterfaceMethodsForInterfaceFields: Boolean = false, - val includeImports: Map = emptyMap(), - val includeEnumImports: Map> = emptyMap(), - val includeClassImports: Map> = emptyMap(), - val generateCustomAnnotations: Boolean = false, + var snakeCaseConstantNames: Boolean = false, + var generateInterfaceSetters: Boolean = true, + var generateInterfaceMethodsForInterfaceFields: Boolean = false, + var includeImports: Map = emptyMap(), + var includeEnumImports: Map> = emptyMap(), + var includeClassImports: Map> = emptyMap(), + var generateCustomAnnotations: Boolean = false, var javaGenerateAllConstructor: Boolean = true, - val implementSerializable: Boolean = false, - val addGeneratedAnnotation: Boolean = false, - val addDeprecatedAnnotation: Boolean = false + var implementSerializable: Boolean = false, + var addGeneratedAnnotation: Boolean = false, + var addDeprecatedAnnotation: Boolean = false ) { val packageNameClient: String = "$packageName.$subPackageNameClient" diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt.back similarity index 99% rename from graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt rename to graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt.back index 518deeb60..948ce0261 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt.back @@ -554,7 +554,7 @@ class EntitiesClientApiGenTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, language = Language.JAVA ) ).generate() diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenBuilderTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenBuilderTestv2.kt index 2a21b89bd..b0e30bea3 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenBuilderTestv2.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenBuilderTestv2.kt @@ -36,7 +36,7 @@ class ClientApiGenBuilderTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, maxProjectionDepth = 2 ) ).generate() @@ -67,7 +67,7 @@ class ClientApiGenBuilderTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, maxProjectionDepth = 2 ) ).generate() @@ -95,7 +95,7 @@ class ClientApiGenBuilderTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, maxProjectionDepth = 2 ) ).generate() @@ -121,7 +121,7 @@ class ClientApiGenBuilderTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, maxProjectionDepth = 2 ) ).generate() diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenFragmentTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenFragmentTestv2.kt index 9f8d02fc0..480e5f7de 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenFragmentTestv2.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenFragmentTestv2.kt @@ -54,7 +54,7 @@ class ClientApiGenFragmentTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -108,7 +108,7 @@ class ClientApiGenFragmentTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -154,7 +154,7 @@ class ClientApiGenFragmentTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -201,7 +201,7 @@ class ClientApiGenFragmentTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenMutationTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenMutationTestv2.kt index e04ef3833..d690601fb 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenMutationTestv2.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenMutationTestv2.kt @@ -43,7 +43,7 @@ class ClientApiGenMutationTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -76,7 +76,7 @@ class ClientApiGenMutationTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -115,7 +115,7 @@ class ClientApiGenMutationTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -152,7 +152,7 @@ class ClientApiGenMutationTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -193,7 +193,7 @@ class ClientApiGenMutationTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -257,7 +257,7 @@ class ClientApiGenMutationTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, includeMutations = setOf("shows"), generateDataTypes = false, writeToFiles = false @@ -289,7 +289,7 @@ class ClientApiGenMutationTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, includeMutations = setOf("updateMovieTitle") ) ).generate() diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenProjectionTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenProjectionTestv2.kt index 49ab60910..761264101 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenProjectionTestv2.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenProjectionTestv2.kt @@ -45,7 +45,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -72,7 +72,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() assertThat(codeGenResult.clientProjections.size).isEqualTo(2) @@ -112,7 +112,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -156,7 +156,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -189,7 +189,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PersonsProjectionRoot") @@ -220,7 +220,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() assertThat(codeGenResult.clientProjections[0].typeSpec.name).isEqualTo("PersonsProjectionRoot") @@ -254,7 +254,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -290,7 +290,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -326,7 +326,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, shortProjectionNames = true ) ).generate() @@ -360,7 +360,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, typeMapping = mapOf("Long" to "java.lang.Long") ) ).generate() @@ -397,7 +397,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, typeMapping = mapOf("Long" to "java.lang.Long") ) ).generate() @@ -434,7 +434,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, typeMapping = mapOf("Long" to "java.lang.Long") ) ).generate() @@ -491,7 +491,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, maxProjectionDepth = 2 ) ).generate() @@ -533,7 +533,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -571,7 +571,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, typeMapping = mapOf("Long" to "java.lang.Long") ) ).generate() @@ -600,7 +600,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -636,7 +636,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -679,7 +679,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, writeToFiles = false ) ).generate() @@ -711,7 +711,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, maxProjectionDepth = 2 ) ).generate() @@ -749,7 +749,7 @@ class ClientApiGenProjectionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, writeToFiles = true ) ).generate() diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenQueryTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenQueryTestv2.kt index bc6e0e002..32ced2daf 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenQueryTestv2.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenQueryTestv2.kt @@ -42,7 +42,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -74,7 +74,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -123,7 +123,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -194,7 +194,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, includeQueries = setOf("shows"), generateDataTypes = false, writeToFiles = false @@ -242,7 +242,7 @@ class ClientApiGenQueryTestv2 { schemas = setOf(schema), packageName = basePackageName, generateDataTypes = false, - generateClientApi = true, + generateClientApiv2 = true, includeQueries = setOf("movies") ) ).generate() @@ -272,7 +272,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() assertThat(codeGenResult.javaQueryTypes[0].typeSpec.typeSpecs[0].methodSpecs[1].name).isEqualTo("lastname") @@ -302,7 +302,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -335,7 +335,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -360,7 +360,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, includeQueries = setOf("movieTitles") ) ).generate() @@ -394,7 +394,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -433,7 +433,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -477,7 +477,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -522,7 +522,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, writeToFiles = true ) ).generate() @@ -660,7 +660,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, typeMapping = mapOf("Long" to "java.lang.Long") ) ).generate() @@ -760,7 +760,7 @@ class ClientApiGenQueryTestv2 { schemas = setOf(schema), packageName = basePackageName, generateDataTypes = true, - generateClientApi = true, + generateClientApiv2 = true, typeMapping = mapOf("Long" to "java.lang.Long") ) ).generate() @@ -858,7 +858,7 @@ class ClientApiGenQueryTestv2 { schemas = setOf(schema), packageName = basePackageName, generateDataTypes = false, - generateClientApi = true, + generateClientApiv2 = true, includeQueries = setOf("foo", "bar") ) ).generate() @@ -911,7 +911,7 @@ class ClientApiGenQueryTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -953,7 +953,7 @@ class ClientApiGenQueryTestv2 { schemas = setOf(schema), packageName = basePackageName, generateDataTypes = true, - generateClientApi = true, + generateClientApiv2 = true, includeQueries = setOf("bar") ) ).generate() diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenSubscriptionTestv2.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenSubscriptionTestv2.kt index 981e2ba25..ad46f2624 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenSubscriptionTestv2.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/clientapiv2/ClientApiGenSubscriptionTestv2.kt @@ -43,7 +43,7 @@ class ClientApiGenSubscriptionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -76,7 +76,7 @@ class ClientApiGenSubscriptionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true + generateClientApiv2 = true ) ).generate() @@ -101,7 +101,7 @@ class ClientApiGenSubscriptionTestv2 { CodeGenConfig( schemas = setOf(schema), packageName = basePackageName, - generateClientApi = true, + generateClientApiv2 = true, includeSubscriptions = setOf("movieTitle") ) ).generate() diff --git a/graphql-dgs-codegen-gradle/src/main/kotlin/com/netflix/graphql/dgs/codegen/gradle/GenerateJavaTask.kt b/graphql-dgs-codegen-gradle/src/main/kotlin/com/netflix/graphql/dgs/codegen/gradle/GenerateJavaTask.kt index 505675d71..ff10e87b6 100644 --- a/graphql-dgs-codegen-gradle/src/main/kotlin/com/netflix/graphql/dgs/codegen/gradle/GenerateJavaTask.kt +++ b/graphql-dgs-codegen-gradle/src/main/kotlin/com/netflix/graphql/dgs/codegen/gradle/GenerateJavaTask.kt @@ -66,6 +66,9 @@ open class GenerateJavaTask : DefaultTask() { @Input var generateClient = false + @Input + var generateClientApiv2 = false + @Input var generateKotlinNullableClasses = false @@ -178,6 +181,7 @@ open class GenerateJavaTask : DefaultTask() { language = Language.valueOf(language.uppercase(Locale.getDefault())), generateBoxedTypes = generateBoxedTypes, generateClientApi = generateClient, + generateClientApiv2 = generateClientApiv2, generateKotlinNullableClasses = generateKotlinNullableClasses, generateKotlinClosureProjections = generateKotlinClosureProjections, generateInterfaces = generateInterfaces, From 3640d21bf88e41a2fa1645f4fe2e1c4a27c58cb6 Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Tue, 14 Feb 2023 16:29:28 -0800 Subject: [PATCH 10/10] Fix tests. --- .../src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt | 2 +- ...ClientApiGenTestv2.kt.back => EntitiesClientApiGenTestv2.kt} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/{EntitiesClientApiGenTestv2.kt.back => EntitiesClientApiGenTestv2.kt} (100%) 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 72b866b86..69fb1da64 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 @@ -249,7 +249,7 @@ class CodeGen(private val config: CodeGenConfig) { } private fun generateJavaClientEntitiesRepresentations(definitions: Collection>): CodeGenResult { - return if (config.generateClientApi) { + return if (config.generateClientApi || config.generateClientApiv2) { val generatedRepresentations = mutableMapOf() return definitions.asSequence() .filterIsInstance() diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt.back b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt similarity index 100% rename from graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt.back rename to graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/EntitiesClientApiGenTestv2.kt