graphql-java-annotations icon indicating copy to clipboard operation
graphql-java-annotations copied to clipboard

"Duplicate field found in extension" from GraphQLTypeRetriever.getGraphQLType

Open TuomasKiviaho opened this issue 6 years ago • 2 comments

I start getting "Duplicate field found in extension" GraphQLTypeRetriever.getGraphQLType when I try to examine the interface after using extensionsHandler.registerTypeExtension for the implementation.

There seems to be a side impact in the used OutputObjectBuilder that doesn't understand that the annotated interface method exactly the same as what was found previously graphQLObjectInfoRetriever.getOrderedMethods.

       List<String> definedFields = new ArrayList<>();
        for (Method method : graphQLObjectInfoRetriever.getOrderedMethods(object)) {
            if (method.isBridge() || method.isSynthetic()) {
                continue;
            }
            if (methodSearchAlgorithm.isFound(method)) {
                GraphQLFieldDefinition gqlField = graphQLFieldRetriever.getField(method, container);
                definedFields.add(gqlField.getName());
                builder.field(gqlField);
            }
        }

        for (Field field : getAllFields(object).values()) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            if (fieldSearchAlgorithm.isFound(field)) {
                GraphQLFieldDefinition gqlField = graphQLFieldRetriever.getField(field, container);
                definedFields.add(gqlField.getName());
                builder.field(gqlField);
            }
        }
       for (Class<?> iface : object.getInterfaces()) {
            if (iface.getAnnotation(GraphQLTypeResolver.class) != null) {
                String ifaceName = graphQLObjectInfoRetriever.getTypeName(iface);
                if (container.getProcessing().contains(ifaceName)) {
                    builder.withInterface(new GraphQLTypeReference(ifaceName));
                } else {
                    builder.withInterface((GraphQLInterfaceType) graphQLInterfaceRetriever.getInterface(iface, container));
                }
                builder.fields(extensionsHandler.getExtensionFields(iface, definedFields, container));
            }
        }

        builder.fields(extensionsHandler.getExtensionFields(object, definedFields, container));

If the intention is to return extended GraphQLType then I suggest that at least whenever extensions type registry contains the class then extensionshandler is used and otherwise only the parts identical to InputObjectBuilder are used.

    public GraphQLObjectType.Builder getOutputObjectBuilder(Class<?> object, ProcessingElementsContainer container) throws GraphQLAnnotationsException {
        GraphQLObjectType.Builder builder = newObject();
        builder.name(graphQLObjectInfoRetriever.getTypeName(object));
        GraphQLDescription description = object.getAnnotation(GraphQLDescription.class);
        if (description != null) {
            builder.description(description.value());
        }
        List<String> definedFields = new ArrayList<>();
        if (!container.getExtensionsTypeRegistry().containsKey(object)) {
            for (Method method : graphQLObjectInfoRetriever.getOrderedMethods(object)) {
                if (method.isBridge() || method.isSynthetic()) {
                    continue;
                }
                if (methodSearchAlgorithm.isFound(method)) {
                    GraphQLFieldDefinition gqlField = graphQLFieldRetriever.getField(method, container);
                    definedFields.add(gqlField.getName());
                    builder.field(gqlField);
                }
            }

            for (Field field : getAllFields(object).values()) {
                if (Modifier.isStatic(field.getModifiers())) {
                    continue;
                }
                if (fieldSearchAlgorithm.isFound(field)) {
                    GraphQLFieldDefinition gqlField = graphQLFieldRetriever.getField(field, container);
                    definedFields.add(gqlField.getName());
                    builder.field(gqlField);
                }
            }
        } else {
            for (Class<?> iface : object.getInterfaces()) {
                if (iface.getAnnotation(GraphQLTypeResolver.class) != null) {
                    String ifaceName = graphQLObjectInfoRetriever.getTypeName(iface);
                    if (container.getProcessing().contains(ifaceName)) {
                        builder.withInterface(new GraphQLTypeReference(ifaceName));
                    } else {
                        builder.withInterface((GraphQLInterfaceType) graphQLInterfaceRetriever.getInterface(iface, container));
                    }
                    builder.fields(extensionsHandler.getExtensionFields(iface, definedFields, container));
                }
            }

            builder.fields(extensionsHandler.getExtensionFields(object, definedFields, container));
        }

        return builder;
    }

This of course doesn't help the ambiguous situations when there are multiple implementations of which to choose from and some of the share the same class structure yielding to same/overloaded versions of the methods.

I'd at least change also the GraphQLExtensionsHandler so that same/overloaded methods would not be processed twice.

TuomasKiviaho avatar Jan 07 '19 12:01 TuomasKiviaho

@yarinvak and I will have a look at it. If you know how to solve it. we'd be thankful if you sent a PR

guy120494 avatar Jan 11 '19 16:01 guy120494

can you please show us an example of your usage? and what is the expected behaviour on your side? Also, you can PR this if you know how to solve the problem

yarinvak avatar Feb 03 '19 11:02 yarinvak

Hi @TuomasKiviaho,

We're helping with project maintenance and reviewing the list of opened PRs and Issues.

This issue was created quite a while ago, we were wondering if you were still interested in the outcome, please let us know if this is the case.

Without an answer by July 1st, 2023, this issue will be closed as "inactive" (and can always be re-opened later on if needed).

Thanks,

Fgerthoffert avatar Jun 02 '23 08:06 Fgerthoffert