graphql-java-kickstart / graphql-java-tools

A schema-first tool for graphql-java inspired by graphql-tools for JS
https://www.graphql-java-kickstart.com/tools/
MIT License
812 stars 174 forks source link

Unions of Relay connections fail on `TypeResolverError: Expected object type with name 'DefaultConnection' to exist for union` #186

Open b4eEX opened 6 years ago

b4eEX commented 6 years ago

The issue is relevant for 5.3.1+.

I'm trying to create an interface with a polymorphic connection field, which requires interface to declare it as a union of all implementation types, something like

union AnyConnection = XConnection | YConnection

# connection and edge definitions...

interface WithConnection {
    connection: AnyConnection!
}

type WithXConnection {
    connection: XConnection!
}

type WithYConnection {
    connection: YConnection!
}

However, trying to do something like this fails on com.coxautodev.graphql.tools.TypeResolverError: Expected object type with name 'DefaultConnection' to exist for union 'AnyConnection', but it doesn't!.

My reproducer, based on relay connection test:

public class Test {

    static class QueryResolver implements GraphQLQueryResolver {
        Connection<User> users(int first, String after, DataFetchingEnvironment env) {
            return new SimpleListConnection<User>(List.of(new User(1L, "name"))).get(env);
        }

        Connection<AnotherType> otherTypes(DataFetchingEnvironment env) {
            return new SimpleListConnection<AnotherType>(List.of(new AnotherType("echo"))).get(env);
        }

        Object any(DataFetchingEnvironment env) {
            return new SimpleListConnection<AnotherType>(List.of(new AnotherType("echo"))).get(env);
        }
    }

    static class User {
        Long id;
        String name;

        User(Long id, String name) {
            this.id = id;
            this.name = name;
        }
    }

    static class AnotherType {
        String echo;

        AnotherType(String echo) {
            this.echo = echo;
        }
    }

    public static void main(String[] args) {

                var schemaString = " type Query {\n" +
                "     users(first: Int, after: String): UserConnection\n" +
                "     otherTypes: AnotherTypeConnection\n" +
                "     any: AnyConnection!\n" +
                " }\n" +
                " type UserConnection {\n" +
                "     edges: [UserEdge!]!\n" +
                "     pageInfo: PageInfo!\n" +
                " }\n" +
                " \n" +
                " type UserEdge {\n" +
                "     node: User!\n" +
                " }\n" +
                " \n" +
                " type User {\n" +
                "     id: ID!\n" +
                "     name: String\n" +
                " }\n" +
                " \n" +
                " type PageInfo {\n" +
                " }\n" +
                " \n" +
                " type AnotherTypeConnection {\n" +
                "     edges: [AnotherTypeEdge!]!\n" +
                " }\n" +
                " \n" +
                " type AnotherTypeEdge {\n" +
                "     node: AnotherType!\n" +
                " }\n" +
                " \n" +
                " type AnotherType {\n" +
                "     echo: String\n" +
                " }\n" +
                "\n" +
                " union AnyConnection = UserConnection | AnotherTypeConnection";

        var schema = SchemaParser.newParser().schemaString(schemaString)
                .resolvers(new QueryResolver())
                .build()
                .makeExecutableSchema();

        var gql = GraphQL.newGraphQL(schema)
                .queryExecutionStrategy(new AsyncExecutionStrategy())
                .build();

        var query = "query {\n" +
                "     any {\n" +
                "         ...on AnotherTypeConnection {\n" +
                "             edges {\n" +
                "                 node {\n" +
                "                     echo\n" +
                "                 }\n" +
                "             }\n" +
                "         }\n" +
                "     }\n" +
                " }";

        System.out.println(gql.execute(query));
    }
}

The culprit appears to be https://github.com/graphql-java-kickstart/graphql-java-tools/blob/24b42b0ba048bc148754946c8b5c31f25a6b0be8/src/main/kotlin/com/coxautodev/graphql/tools/DictionaryTypeResolver.kt#L26

It seems to try to do all the autowiring magic, but Connection types have class names that are different from GraphQL types. Adding dictionaries won't help also, since they're all DefaultConnection.

marazt commented 6 years ago

Hi. Is there any progress with this issue? Thanks.

oliemansm commented 6 years ago

@b4eEX Is this issue still relevant now with the new @connection directive?

b4eEX commented 6 years ago

I guess it still should, but I didn’t even manage to make a “standalone” connection type to use in a union.

Something like this:

type XConnection @connection(for: “X”) {}
type YConnection @connection(for: “Y”) {}
union AnyConnection = XConnection | YConnection

XConnection and YConnection don’t seem to get generated, so I can’t even get to union resolution phase.

On 24 Nov 2018, at 10:48, Michiel Oliemans notifications@github.com wrote:

@b4eEX https://github.com/b4eEX Is this issue still relevant now with the new @connection directive?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/graphql-java-kickstart/graphql-java-tools/issues/186#issuecomment-441355817, or mute the thread https://github.com/notifications/unsubscribe-auth/AmI6gZXQYoxYgV4O9TNTQ84UT6bgom9sks5uyRXrgaJpZM4XDptq.

oliemansm commented 6 years ago

@b4eEX The @connection directive only works for fields, not for types. I'll leave this issue open then to take a look at when I have some more time.

mxmlnglt commented 5 years ago

@b4eEX The @connection directive only works for fields, not for types. I'll leave this issue open then to take a look at when I have some more time.

@oliemansm what do you mean? Your documentation actually showcases that: https://www.graphql-java-kickstart.com/tools/relay/#using-the-type-definition-factory

vojtapol commented 4 years ago

@b4eEX The @connection directive only works for fields, not for types. I'll leave this issue open then to take a look at when I have some more time.

@oliemansm what do you mean? Your documentation actually showcases that: https://www.graphql-java-kickstart.com/tools/relay/#using-the-type-definition-factory

The @connection directive must be on a field (usually within the Query root object). In your example it is on a type definition which is not supported.