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

Are Generics not supported? #281

Closed
marceloverdijk opened this issue Jun 5, 2020 · 33 comments · Fixed by #385
Closed

Are Generics not supported? #281

marceloverdijk opened this issue Jun 5, 2020 · 33 comments · Fixed by #385
Labels
Server This issue applies to the Server side
Milestone

Comments

@marceloverdijk
Copy link
Contributor

I'm having:

public interface PageType<T> {
    List<T> getItems();
    boolean isHasNextPage();
    boolean isHasPreviousPage();
    long getTotalCount();
}

@Type("ContinentPage")
public class ContinentPageType implements PageType<ContinentType> {

    private final List<ContinentType> items;
    private final boolean hasNextPage;
    private final boolean hasPreviousPage;
    private final long totalCount;

    // ctor, getters, setters omitted to keep issue text shorter
}

@GraphQLApi
public class MyGraphQLApi {

    @Query("continents")
    @Description("Get a page of continents")
    public ContinentPageType getContinentPage() {
        return new ContinentPageType(..);
    }
}

But this gives:

Caused by: io.smallrye.graphql.schema.SchemaBuilderException: Don't know what to do with [T] of kind [TYPE_VARIABLE]
        at io.smallrye.graphql.schema.creator.ReferenceCreator.getReference(ReferenceCreator.java:214)
        at io.smallrye.graphql.schema.creator.ReferenceCreator.getReference(ReferenceCreator.java:202)
        at io.smallrye.graphql.schema.creator.ReferenceCreator.createReferenceForInterfaceField(ReferenceCreator.java:118)
        at io.smallrye.graphql.schema.creator.FieldCreator.createFieldForInterface(FieldCreator.java:55)
        at io.smallrye.graphql.schema.creator.type.InterfaceCreator.addFields(InterfaceCreator.java:78)
        at io.smallrye.graphql.schema.creator.type.InterfaceCreator.create(InterfaceCreator.java:57)
        at io.smallrye.graphql.schema.creator.type.InterfaceCreator.create(InterfaceCreator.java:31)
        at io.smallrye.graphql.schema.SchemaBuilder.createAndAddToSchema(SchemaBuilder.java:148)
        at io.smallrye.graphql.schema.SchemaBuilder.addTypesToSchema(SchemaBuilder.java:107)
        at io.smallrye.graphql.schema.SchemaBuilder.generateSchema(SchemaBuilder.java:88)
        at io.smallrye.graphql.schema.SchemaBuilder.build(SchemaBuilder.java:60)

Note that in the @GraphQLApi I don't directly refer to the PageType<T> interface.

It would be nice to have some sort of Page contract in my Java classes.

@phillip-kruger
Copy link
Member

How to handle generics is not yet discussed in the spec. We can support it in SmallRye but need to discuss how... You are welcome to get this going :)

@marceloverdijk
Copy link
Contributor Author

OK, so it is expected behavior.
I will have a look in the weekend to see how the internals are working...

@marceloverdijk
Copy link
Contributor Author

This is a bit weird.
I thought to debug my project to follow the internals and wanted to put a breakpoint in the SchemaBuilder class but it was not in my project.
I would assume it is in the smallrye-graphql-schema-builder artifiact, right?

Caused by: io.smallrye.graphql.schema.SchemaBuilderException: Don't know what to do with [T] of kind [TYPE_VARIABLE]
        at io.smallrye.graphql.schema.creator.ReferenceCreator.getReference(ReferenceCreator.java:214)
        at io.smallrye.graphql.schema.creator.ReferenceCreator.getReference(ReferenceCreator.java:202)
        at io.smallrye.graphql.schema.creator.ReferenceCreator.createReferenceForInterfaceField(ReferenceCreator.java:118)
        at io.smallrye.graphql.schema.creator.FieldCreator.createFieldForInterface(FieldCreator.java:55)
        at io.smallrye.graphql.schema.creator.type.InterfaceCreator.addFields(InterfaceCreator.java:78)
        at io.smallrye.graphql.schema.creator.type.InterfaceCreator.create(InterfaceCreator.java:57)
        at io.smallrye.graphql.schema.creator.type.InterfaceCreator.create(InterfaceCreator.java:31)
        at io.smallrye.graphql.schema.SchemaBuilder.createAndAddToSchema(SchemaBuilder.java:148)
        at io.smallrye.graphql.schema.SchemaBuilder.addTypesToSchema(SchemaBuilder.java:107)
        at io.smallrye.graphql.schema.SchemaBuilder.generateSchema(SchemaBuilder.java:88)
        at io.smallrye.graphql.schema.SchemaBuilder.build(SchemaBuilder.java:60)
        at io.quarkus.smallrye.graphql.deployment.SmallRyeGraphQLProcessor.buildExecutionService(SmallRyeGraphQLProcessor.java:143)
        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:566)
        at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:932)
        at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
        at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
        at java.base/java.lang.Thread.run(Thread.java:834)
        at org.jboss.threads.JBossThread.run(JBossThread.java:479)

2020-06-10 09:44:13,089 INFO  [io.qua.dep.dev.DevModeMain] (main) Attempting to start hot replacement endpoint to recover from previous Quarkus startup failure
<=========----> 75% EXECUTING [9s]
> :quarkusDev
^C%
my-project on  master [!?] via ☕ v11.0.2 took 12s
❯ ./gradlew dependencies | grep smallrye-graphql-schema-builder

my-project on  master [!?] via ☕ v11.0.2 took 2s
❯ ./gradlew dependencies | grep smallrye-graphql
|    +--- io.quarkus:quarkus-smallrye-graphql:1.5.0.Final (c)
|    +--- io.smallrye:smallrye-graphql-cdi:1.0.3 (c)

Now Gradle is telling smallrye-graphql-schema-builder is not a dependency... but running quarkusDev also gives no NoClassDefFoundError.
I need to dive deeper what's going on...

@phillip-kruger
Copy link
Member

Not sure if this will help, but, in Quarkus SchemaBuilder is only used on build (and not on runtime).
(That is why we structured the project like that).
So we build the model (schema-model) using schema-builder during build. Then we do not care about Schema Builder anymore, just pass the model into the runtime.
That is also why only Schema Builder depends on Jandex.

@marceloverdijk
Copy link
Contributor Author

Yes that will explain it and makes sense.
Hmm, need to see if I can "debug" the build process and get a hold on these classes then :-)

@marceloverdijk
Copy link
Contributor Author

The more I think about it the Generic used for a page are important.
I think most users will need something like that as with GraphQL most people won't return just a list but more likely a Connection or a Page.

And maybe it's not that difficult (just guessing though) as SmallRye already supports List<T> so why not a custom Page<T>.

@phillip-kruger
Copy link
Member

Yea I don't think it's difficult. Just need to do it, and make sure that we boil it up to the spec.

@velias
Copy link
Collaborator

velias commented Jul 8, 2020

I just hit the same problem, trying to implement the same thing, paged result :-D

It is a bit funny, because if graphql @query method returns generic class this way (in my case PageType is not interface but class with some implementations, not sure if it is important):

 @Query("continents")
 @Description("Get a page of continents")
 public PageType<ContinentType> getContinentPage() {
      ...
 }

then quarkus starts, but graphql schema says that this method returns array of ContinentType objects, so looks like current smallrye implementation somehow expects that generic is always a collection :-)

When I created subclass of the generic class with concrete type in it, then I finished with the same exception, which is even stranger.

I'm not sure if graphql schema supports generics somehow, I think that no. So I'd say that if generics part is set to exact class in Java (as in our cases, sorry , not sure correct words for this, hope it is clear) then graphql implementation should be able to take and use it without any problem in the graphql schema.
If class is not known then graphql schema generation should fail as it can't handle it.

I think support for generics is really important to provide convenient functionality for java developers as generics are used a lot.

@phillip-kruger
Copy link
Member

Yea like we said, generics is not supported. Mostly because we have not discussed what the expected behaviour is. I think there is more than one use case, so we need to flesh this out. There is an issues on the Spec (MP) side, but we can try and implement this in SmallRye first and then move it to the spec.

@velias
Copy link
Collaborator

velias commented Aug 3, 2020

@marceloverdijk are you working on this? I'd like to look at it if you are not working on it.

@marceloverdijk
Copy link
Contributor Author

@velias No I'm currently not working on it.

@velias
Copy link
Collaborator

velias commented Aug 10, 2020

I started some investigation around this. This is ticket to discuss Generics directly to GraphQL spec graphql/graphql-spec#190 . They are not there (and some of the spec authors says that intentionally), and it is question if they will ever be. We can still workaround it by allowing only generics with exactly defined types (no any wildchars) in the in/out object trees, and generating GraphQL schema in a way it contains extra class for each exact variant of the generic with given type. I'll try to implement this, I expect correct place is smallrye-graphql-schema-builder subproject.
Second question is how/if serialization/deserialization of Java objects will work correctly for generics. I'm not fully sure if this is implemented somewhere in smallrye-graphql itself or is performed by underlying graphql-java, need to dive deeper to see where and how is this performed.

@velias
Copy link
Collaborator

velias commented Aug 10, 2020

And BTW we have to patch arrays in smallrye-graphql schema generator, as current implementation expects that all generics (excluding few exactly named classes) are an array. I read whole microprofile-graphql 1.0.1 spec and it should be probably extended to mention which java constructs are translated to arrays as it is not mentioned anywhere. It should probably say that Java arrays and all sub-classes of java.util.Collection are translated to GraphQL arrays.

velias added a commit to velias/smallrye-graphql that referenced this issue Aug 13, 2020
velias added a commit to velias/smallrye-graphql that referenced this issue Aug 31, 2020
velias added a commit to velias/smallrye-graphql that referenced this issue Aug 31, 2020
velias added a commit to velias/smallrye-graphql that referenced this issue Aug 31, 2020
velias added a commit to velias/smallrye-graphql that referenced this issue Aug 31, 2020
velias added a commit to velias/smallrye-graphql that referenced this issue Aug 31, 2020
velias added a commit to velias/smallrye-graphql that referenced this issue Aug 31, 2020
@phillip-kruger phillip-kruger added the Server This issue applies to the Server side label Sep 1, 2020
@phillip-kruger phillip-kruger linked a pull request Sep 1, 2020 that will close this issue
phillip-kruger added a commit that referenced this issue Sep 1, 2020
@phillip-kruger phillip-kruger added this to the 1.0.9 milestone Sep 1, 2020
@velias
Copy link
Collaborator

velias commented Sep 1, 2020

Thanks @phillip-kruger for merging my PR. My only concern is that it is not documented anywhere beside this issue. Do you plan any documentation for smallrye-graphql to cover areas not covered by the spec itself? There are extensions in functionality, some config params specific for this impl etc which should be documented.
I also created issue for microprofile-graphql spec to include support of generics, not sure if I can help to add it somehow.
Also created second issue for microprofile-graphql spec to clarify translation from Java types to GraphQL arrays.

@phillip-kruger
Copy link
Member

Thanks @velias , yes we can maybe for now document in the Readme until we know how documentation will work , it's in discussion at the moment on a SmallRye level

@velias
Copy link
Collaborator

velias commented Sep 21, 2020

I just tried to use generics in GraphQL in Quarkus 1.8 and app fails during startup :-( quarkusio/quarkus#12230 created.

@phillip-kruger
Copy link
Member

Hi @velias - that is weird. Quarkus 1.8.1.Final contains SmallRye GraphQL1.0.9 that contains your PR and should work. Are you investigating ?

@velias
Copy link
Collaborator

velias commented Sep 21, 2020

Yep, I'm going to create small reproducer Quarkus app and investigate where exactly the problem is. It is somehow related to introspection to build GraphQL scheme in quarkus deployment step.

@phillip-kruger
Copy link
Member

Thanks :)

@apellizzn
Copy link

Has this issue been resolved? I' am still getting this error

@phillip-kruger
Copy link
Member

What error are you getting, can you share your example ?

@velias
Copy link
Collaborator

velias commented Nov 5, 2020

Maybe some of my subsequent patches is not released yet in quarkus? What do you use @apellizzn ?

@apellizzn
Copy link

apellizzn commented Nov 6, 2020

@velias I am using quarkus 1.8.3.Final
@phillip-kruger Here is my example

package test.graphql;

import test.graphql.dtos.PaginationInfo;

import java.util.List;

public interface Connection<T> {
    List<T> getData();
    PaginationInfo getPagination();
}
package test.graphql;

import test.dtos.PaginationInfo;
import lombok.Builder;
import java.util.List;

@Builder
public class FruitConnection implements Connection<Fruit> {

    private List<Fruit> data;
    private PaginationInfo paginationInfo;

    @Override
    public List<Fruit> getData() {
        return data;
    }

    @Override
    public PaginationInfo getPagination() {
        return paginationInfo;
    }
}
package test.graphql;
import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class Fruit {
    private String name;
}
package test.graphql;

import test.dtos.PaginationInfo;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Query;

import java.util.ArrayList;

@GraphQLApi
public class FruitsResource {

    @Query
    public FruitConnection all() {
        return FruitConnection.builder()
                .data(new ArrayList<>() {{ add(new Fruit("apple")); }})
                .paginationInfo(new PaginationInfo())
                .build();
    }
}

I get the following error compiling

Build step io.quarkus.smallrye.graphql.deployment.SmallRyeGraphQLProcessor#buildExecutionService threw an exception: io.smallrye.graphql.schema.SchemaBuilderException: Don't know what to do with [T] of kind [TYPE_VARIABLE] as parent object reference is missing or incomplete

Do you think this error is related to this issue?

@phillip-kruger
Copy link
Member

Can you double check this with the latest (1.9.2) version ?

@apellizzn
Copy link

Tried with 1.9.2.Final and got this error when I start the app

2020-11-09 10:20:46,960 ERROR [io.qua.dep.dev.IsolatedDevModeMain] (main) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
	[error]: Build step io.quarkus.smallrye.graphql.deployment.SmallRyeGraphQLProcessor#buildExecutionService threw an exception: java.lang.IllegalArgumentException: Not a type variable!
at org.jboss.jandex.Type.asTypeVariable(Type.java:241)
at io.smallrye.graphql.schema.creator.ReferenceCreator.createReference(ReferenceCreator.java:203)
at io.smallrye.graphql.schema.creator.type.TypeCreator.addInterfaces(TypeCreator.java:153)
at io.smallrye.graphql.schema.creator.type.TypeCreator.create(TypeCreator.java:78)
at io.smallrye.graphql.schema.creator.type.TypeCreator.create(TypeCreator.java:41)
at io.smallrye.graphql.schema.SchemaBuilder.createAndAddToSchema(SchemaBuilder.java:197)
at io.smallrye.graphql.schema.SchemaBuilder.addTypesToSchema(SchemaBuilder.java:130)

@velias
Copy link
Collaborator

velias commented Nov 9, 2020

@phillip-kruger any plan when smallrye-graphql 1.0.15 will be in Quarkus? It contains some patches around generic interfaces, but I'm not fully sure if it covers this case.
@apellizzn could you provide full reproducer project please (I'm missing some classes from your example here)? I can test it against latest patches and make sure it is really patched.

@velias
Copy link
Collaborator

velias commented Nov 9, 2020

@apellizzn and create new issue for it please, as this one is closed

@velias
Copy link
Collaborator

velias commented Nov 9, 2020

@apellizzn OK, I was able to create testing project and reproduce this error. So please create new issue and I'll try to patch it. Would be good to have patch in smallrye-graphql 1.0.15 and next Quarkus

@phillip-kruger
Copy link
Member

@velias - I am busy pulling 1.0.15 in to Quarkus 1.10.0 (CR release tomorrow). We can still patch after that with 1.0.16

@velias
Copy link
Collaborator

velias commented Nov 9, 2020

I have patch for this problem, any chance to get it into 1.0.15 if I create PR now?

@velias
Copy link
Collaborator

velias commented Nov 9, 2020

To be honest, I forgot to rebase, and just realized during the rebase that the problem had been patched 9 days ago. So it should be patched in 1.0.15 and my PR only adds test to cover this problem (I like to cover all errors with tests not to be reintroduced later ;-)

@phillip-kruger
Copy link
Member

1.0.15 already released and PR open to get it into Q. But We can do a 1.0.16 and still try and get it into Q before the 1.10.0 release.

@phillip-kruger
Copy link
Member

To be honest, I forgot to rebase, and just realized during the rebase that the problem had been patched 9 days ago. So it should be patched in 1.0.15 and my PR only adds test to cover this problem (I like to cover all errors with tests not to be reintroduced later ;-)

Ok great, lets get the tests into master. Please do a PR

velias added a commit to velias/smallrye-graphql that referenced this issue Nov 9, 2020
phillip-kruger added a commit that referenced this issue Nov 9, 2020
Added test for interface problem reported in #281 comments and patched
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Server This issue applies to the Server side
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants