Enigmatis / graphql-java-annotations

GraphQL Annotations for Java
Other
387 stars 97 forks source link

@GraphQLNonNull is ignored in combination with @GraphQLType #308

Open oos-mxx opened 1 week ago

oos-mxx commented 1 week ago

The following is a simplified version of my problem.

I have an input class MyInput like

class MyInput {

    private final Foo foo;

    ...

    @GraphQLField
    @GraphQLNonNull
    @GraphQLType(Bar.class)
    public Foo foo() {
        foo;
    }
}

class Bar implements TypeFunction {

    private final GraphQLEnumType enumType = buildEnumType("Bar", ...);

    private static GraphQLEnumType buildEnumType(String name, Collection<GraphQLEnumValueDefinition> values) {
        // build the GraphQLEnumType here ...
    }
}

The created schema does not define foo as non-null:

...

input MyInput {
    ...
    foo: Bar
    ...
}

...

If I remove the @GraphQLType annotation the contained input looks like that:

input MyInput {
    ...
    foo: Foo!
    ...
}

Note: Foo is the data type of a field in several classes (say MyInputOne, MyInputTwo, ...) in my application. The GraphQL interface should use specific enums (BarOne or BarTwo ...) as data types for each class.

Fgerthoffert commented 1 week ago

Thanks, we'll look into this and hope to have an answer within a month or so.

Note that you can also submit a PR directly if you have identified where the issue is located.

jayblanc commented 5 days ago

Hello, could you provide a more complete code sample to reproduce your problem ? You may also start a PR, even draft, if you have any test case that reproduce it.

oos-mxx commented 1 day ago

I fixed the problem for my concrete TypeFunction Bar. I had to override TypeFunction#buildType(boolean, Class<?>, AnnotatedType, ProcessingElementsContainer) to handle the presence of a GraphQLNonNull annotation.

I found the GraphQLFieldRetriever having the following method getTypeFunction:

private TypeFunction getTypeFunction(Method method, ProcessingElementsContainer container) {
        graphql.annotations.annotationTypes.GraphQLType annotation = method.getAnnotation(graphql.annotations.annotationTypes.GraphQLType.class);
        TypeFunction typeFunction = container.getDefaultTypeFunction();

        if (annotation != null) {
            typeFunction = newInstance(annotation.value());
        }
        return typeFunction;
    }

For my annotated method foo this method would not use DefaultTypeFunction as annotation != null would apply. Otherwise DefaultTypeFunction's implementation of buildType would be executed as follows:

    @Override
    public GraphQLType buildType(boolean input, Class<?> aClass, AnnotatedType annotatedType, ProcessingElementsContainer container) {
        TypeFunction typeFunction = getTypeFunction(aClass, annotatedType);
        if (typeFunction == null) {
            throw new IllegalArgumentException("unsupported type");
        }

        GraphQLType result = typeFunction.buildType(input, aClass, annotatedType, container);
        if (annotatedType != null && annotatedType.isAnnotationPresent(GraphQLNonNull.class)) {
            result = new graphql.schema.GraphQLNonNull(result);
        }
        return result;
    }

I copied this behaviour in my Bar#buildType method, which resulted in a non-null input field in the schema.

Now I'm not sure if this is the intended way to do for concrete implementations or should be concerned by maybe GraphQLFieldRetriever.