diff --git a/src/main/java/graphql/annotations/processor/util/ConnectionUtil.java b/src/main/java/graphql/annotations/processor/util/ConnectionUtil.java index 387c904b..d95e54ee 100644 --- a/src/main/java/graphql/annotations/processor/util/ConnectionUtil.java +++ b/src/main/java/graphql/annotations/processor/util/ConnectionUtil.java @@ -33,13 +33,24 @@ public class ConnectionUtil { private static final List TYPES_FOR_CONNECTION = Arrays.asList(GraphQLObjectType.class, GraphQLInterfaceType.class, GraphQLUnionType.class, GraphQLTypeReference.class); public static boolean isConnection(AccessibleObject obj, GraphQLType type) { + if (!obj.isAnnotationPresent(GraphQLConnection.class)) { + return false; + } + + if (type instanceof graphql.schema.GraphQLNonNull) { + type = ((GraphQLNonNull) type).getWrappedType(); + } + if (!(type instanceof GraphQLList)) { + return false; + } + + type = ((GraphQLList) type).getWrappedType(); if (type instanceof graphql.schema.GraphQLNonNull) { type = ((GraphQLNonNull) type).getWrappedType(); } - final GraphQLType actualType = type; - boolean isValidGraphQLTypeForConnection = obj.isAnnotationPresent(GraphQLConnection.class) && - actualType instanceof GraphQLList && TYPES_FOR_CONNECTION.stream().anyMatch(aClass -> - aClass.isInstance(((GraphQLList) actualType).getWrappedType())); + + final GraphQLType elementType = type; + boolean isValidGraphQLTypeForConnection = TYPES_FOR_CONNECTION.stream().anyMatch(aClass -> aClass.isInstance(elementType)); if (isValidGraphQLTypeForConnection) { ConnectionValidator validator = newInstance(obj.getAnnotation(GraphQLConnection.class).validator()); diff --git a/src/test/java/graphql/annotations/connection/GraphQLConnectionTest.java b/src/test/java/graphql/annotations/connection/GraphQLConnectionTest.java index 1d11d455..ec1ebd57 100644 --- a/src/test/java/graphql/annotations/connection/GraphQLConnectionTest.java +++ b/src/test/java/graphql/annotations/connection/GraphQLConnectionTest.java @@ -19,10 +19,15 @@ import graphql.annotations.annotationTypes.GraphQLNonNull; import graphql.annotations.connection.exceptions.GraphQLConnectionException; import graphql.annotations.processor.GraphQLAnnotations; +import graphql.annotations.processor.ProcessingElementsContainer; import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; +import graphql.annotations.processor.typeFunctions.TypeFunction; import graphql.annotations.processor.util.CustomRelay; import graphql.relay.Relay; import graphql.schema.*; +import java.lang.reflect.AnnotatedParameterizedType; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.ParameterizedType; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -35,6 +40,7 @@ import static graphql.annotations.AnnotationsSchemaCreator.newAnnotationsSchema; import static graphql.annotations.processor.util.RelayKit.EMPTY_CONNECTION; +import static graphql.schema.GraphQLNonNull.nonNull; import static graphql.schema.GraphQLSchema.newSchema; import static java.util.Collections.emptyList; import static org.testng.Assert.*; @@ -201,6 +207,19 @@ public void methodNonNull() { testResult("getNonNullObjs", result); } + @Test + public void methodDoubleNonNull() { + GraphQLSchema schema = newAnnotationsSchema().query(TestConnections.class).build(); + + GraphQL graphQL = GraphQL.newGraphQL(schema).build(); + ExecutionResult result = graphQL.execute("{ getDoubleNonNullObjs(first: 1) { edges { cursor node { id, val } } } }", + new TestConnections(Arrays.asList(new Obj("1", "test"), new Obj("2", "hello"), new Obj("3", "world")))); + + assertTrue(result.getErrors().isEmpty()); + + testResult("getDoubleNonNullObjs", result); + } + @Test public void methodNull() { GraphQLSchema schema = newAnnotationsSchema().query(TestConnections.class).build(); @@ -387,6 +406,13 @@ public String getCursor(Obj entity) { }; } + @GraphQLField + @GraphQLConnection(name = "doubleNonNullObjs") + @graphql.annotations.annotationTypes.GraphQLType(DoubleNonNullIterableTypeFunction.class) + public PaginatedData getDoubleNonNullObjs(DataFetchingEnvironment environment) { + return getNonNullObjs(environment); + } + @GraphQLField @GraphQLConnection(name = "nullObj") public PaginatedData getNullObj() { @@ -396,4 +422,42 @@ public PaginatedData getNullObj() { } + /* + * double @GraphQLNonNull on @GraphQLList like [ObjectType!]! + */ + public static class DoubleNonNullIterableTypeFunction implements TypeFunction { + + @Override + public boolean canBuildType(Class theClass, AnnotatedType annotatedType) { + return Iterable.class.isAssignableFrom(theClass); + } + + @Override + public GraphQLType buildType( + boolean input, + Class theClass, + AnnotatedType annotatedType, + ProcessingElementsContainer container) { + + if (!(annotatedType instanceof AnnotatedParameterizedType)) { + throw new IllegalArgumentException("List type parameter should be specified"); + } + AnnotatedParameterizedType parameterizedType = (AnnotatedParameterizedType) annotatedType; + AnnotatedType arg = parameterizedType.getAnnotatedActualTypeArguments()[0]; + Class klass; + if (arg.getType() instanceof ParameterizedType) { + klass = (Class) ((ParameterizedType) (arg.getType())).getRawType(); + } else { + klass = (Class) arg.getType(); + } + + TypeFunction typeFunction = container.getDefaultTypeFunction(); + GraphQLType type = typeFunction.buildType(input, klass, arg, container); + + // double @GraphQLNonNull on @GraphQLList like [ObjectType!]! + return nonNull(new GraphQLList(nonNull(type))); + } + } + + }