Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic Type Is Not Supported in Input Type #468

Open
dynastywind opened this issue Dec 25, 2020 · 3 comments
Open

Generic Type Is Not Supported in Input Type #468

dynastywind opened this issue Dec 25, 2020 · 3 comments
Labels

Comments

@dynastywind
Copy link

Describe the bug
Trying to add a definition factory that can dynamically generate input types containing generic types as fields. But it turns out to be a crash.

To Reproduce
Have a schema like this:

input LanguageInput {
    id: Long
}
type Mutation {
    test(in: LanguageAudit @audit(for: "LanguageInput")): String
}
scalar Long

And a corresponding Java resolver and return type like this:

@Component
public class Mutation implements GraphQLMutationResolver {

    public String test(AuditWrapper<LanguageInput> in) {
        return "success";
    }

}

public class AuditWrapper<T> {

    private T content;

    private String operator;

}

I have a factory below to create a dynamic input type. Below is the core part:

private InputObjectTypeDefinition createDefinition(DirectiveWithInputType directive) {
		return InputObjectTypeDefinition.newInputObjectDefinition().name(directive.getTypeName())
				.inputValueDefinition(new InputValueDefinition("content", new TypeName(directive.forTypeName()))) // Get name from directive audit's "for" argument
				.inputValueDefinition(new InputValueDefinition("operator", new TypeName("String")))
				.build();
	}

Once you try to build the whole schema, it crashes.

Expected behavior
Should parse schema successfully.

Exception Stacks

Caused by: java.lang.ClassCastException: class sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to class java.lang.Class (sun.reflect.generics.reflectiveObjects.TypeVariableImpl and java.lang.Class are in module java.base of loader 'bootstrap')
	at graphql.kickstart.tools.util.UtilsKt.unwrap(Utils.kt:36)
	at graphql.kickstart.tools.GenericType$RelativeTo.parameterizedDeclaringTypeOrSuperType(GenericType.kt:120)
	at graphql.kickstart.tools.GenericType$RelativeTo.unwrapGenericType(GenericType.kt:102)
	at graphql.kickstart.tools.TypeClassMatcher.match(TypeClassMatcher.kt:28)
	at graphql.kickstart.tools.TypeClassMatcher.match(TypeClassMatcher.kt:23)
	at graphql.kickstart.tools.SchemaClassScanner.handleNewType(SchemaClassScanner.kt:341)
	at graphql.kickstart.tools.SchemaClassScanner.handleFoundType(SchemaClassScanner.kt:321)
	at graphql.kickstart.tools.SchemaClassScanner.handleFoundType(SchemaClassScanner.kt:286)
	at graphql.kickstart.tools.SchemaClassScanner.scanResolverInfoForPotentialMatches(SchemaClassScanner.kt:274)
	at graphql.kickstart.tools.SchemaClassScanner.handleRootType(SchemaClassScanner.kt:129)
	at graphql.kickstart.tools.SchemaClassScanner.scanForClasses(SchemaClassScanner.kt:71)
	at graphql.kickstart.tools.SchemaParserBuilder.scan(SchemaParserBuilder.kt:154)
	at graphql.kickstart.tools.SchemaParserBuilder.build(SchemaParserBuilder.kt:195)
	at com.lyndon.demo.graphql.GraphQLConfig.getGraphQL(GraphQLConfig.java:45)
	at com.lyndon.demo.graphql.GraphQLConfig$$EnhancerBySpringCGLIB$$7db0c7a6.CGLIB$getGraphQL$0(<generated>)
	at com.lyndon.demo.graphql.GraphQLConfig$$EnhancerBySpringCGLIB$$7db0c7a6$$FastClassBySpringCGLIB$$e26d0083.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
	at com.lyndon.demo.graphql.GraphQLConfig$$EnhancerBySpringCGLIB$$7db0c7a6.getGraphQL(<generated>)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
	... 109 more

Possible reason:
I've checked the current implementation and found that in SchemaClassScanner.kt:339, you have this code:

val inputValueJavaType = findInputValueType(inputValueDefinition.name, inputGraphQLType, javaType.unwrap())

This javaType.unwrap() will discard generic type information and then when trying to match type field and its Java type, you will get a "T" as field's type, which results in the final crash. Here we need to get the real type (LanguageInput in my case) behind the generic label.

Actually this happens every time when you try to put a Java type with generic fields as a variable's type.

Hope this issue can be fixed soon. Otherwise I have to manually define types myself both in schema and Java implementation.
Thank you.

@pelletier197
Copy link

pelletier197 commented Oct 18, 2021

Same issue here.

Maybe i'm wrong, but the only thing required seems to modify the Utils function into:

    if (this is ParameterizedType) {
        this.rawType as Class<*>
    } else if (this is TypeVariable<*>) 
        this.bounds[0] as Class<*>
    else {
        this as Class<*>
    }

Is there a reason this issue has been open for almost a year ?

@kaydensubskribe
Copy link

Hey, it's been another half-year. Any reason why this hasn't been fixed, if the solution is indeed that simple?

@nigelsim
Copy link

nigelsim commented Sep 2, 2022

That's the wrong spot, because at that point in Utils.unwrap() it is unwrapping the generic type argument itself, where what it needs to doing is resolving the appropriate bounds on the concrete class.

I believe the issue is solved here. I'll put in a PR if/when I can figure out how to write a test for this. (This is my first foray into Kotlin)

diff --git a/src/main/kotlin/graphql/kickstart/tools/GenericType.kt b/src/main/kotlin/graphql/kickstart/tools/GenericType.kt
index 35b9df7..80d9a64 100644
--- a/src/main/kotlin/graphql/kickstart/tools/GenericType.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/GenericType.kt
@@ -99,7 +99,7 @@ internal open class GenericType(protected val mostSpecificType: JavaType, protec
                     return unwrapGenericType(unwrapsTo)
                 }
                 is TypeVariable<*> -> {
-                    val parameterizedDeclaringType = parameterizedDeclaringTypeOrSuperType(declaringType)
+                    val parameterizedDeclaringType = parameterizedDeclaringTypeOrSuperType(mostSpecificType)
                     if (parameterizedDeclaringType != null) {
                         unwrapGenericType(parameterizedDeclaringType, type)
                     } else {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants