diff --git a/src/main/java/graphql/execution/ExecutionStepInfo.java b/src/main/java/graphql/execution/ExecutionStepInfo.java index eefa8a81cc..c9f27c38c4 100644 --- a/src/main/java/graphql/execution/ExecutionStepInfo.java +++ b/src/main/java/graphql/execution/ExecutionStepInfo.java @@ -126,6 +126,18 @@ public GraphQLOutputType getUnwrappedNonNullType() { return (GraphQLOutputType) GraphQLTypeUtil.unwrapNonNull(this.type); } + /** + * This returns the type which is unwrapped if it was {@link GraphQLNonNull} wrapped + * and then cast to the target type. + * + * @param for two + * + * @return the graphql type in question + */ + public T getUnwrappedNonNullTypeAs() { + return GraphQLTypeUtil.unwrapNonNullAs(this.type); + } + /** * This returns the field definition that is in play when this type info was created or null * if the type is a root query type diff --git a/src/main/java/graphql/execution/ExecutionStepInfoFactory.java b/src/main/java/graphql/execution/ExecutionStepInfoFactory.java index ec2716aec3..286106a7dc 100644 --- a/src/main/java/graphql/execution/ExecutionStepInfoFactory.java +++ b/src/main/java/graphql/execution/ExecutionStepInfoFactory.java @@ -25,7 +25,7 @@ public class ExecutionStepInfoFactory { public ExecutionStepInfo newExecutionStepInfoForListElement(ExecutionStepInfo executionInfo, ResultPath indexedPath) { - GraphQLList fieldType = (GraphQLList) executionInfo.getUnwrappedNonNullType(); + GraphQLList fieldType = executionInfo.getUnwrappedNonNullTypeAs(); GraphQLOutputType typeInList = (GraphQLOutputType) fieldType.getWrappedType(); return executionInfo.transform(typeInList, executionInfo, indexedPath); } diff --git a/src/main/java/graphql/execution/ExecutionStrategy.java b/src/main/java/graphql/execution/ExecutionStrategy.java index a16f0f80f1..feb831b6d6 100644 --- a/src/main/java/graphql/execution/ExecutionStrategy.java +++ b/src/main/java/graphql/execution/ExecutionStrategy.java @@ -394,7 +394,7 @@ protected Object resolveFieldWithInfo(ExecutionContext executionContext, Executi @DuckTyped(shape = "CompletableFuture | ") protected Object fetchField(ExecutionContext executionContext, ExecutionStrategyParameters parameters) { MergedField field = parameters.getField(); - GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType(); + GraphQLObjectType parentType = parameters.getExecutionStepInfo().getUnwrappedNonNullTypeAs(); GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, field.getSingleField()); return fetchField(fieldDef, executionContext, parameters); } @@ -408,7 +408,7 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec } MergedField field = parameters.getField(); - GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType(); + GraphQLObjectType parentType = parameters.getExecutionStepInfo().getUnwrappedNonNullTypeAs(); // if the DF (like PropertyDataFetcher) does not use the arguments or execution step info then dont build any @@ -615,13 +615,13 @@ protected FieldValueInfo completeField(ExecutionContext executionContext, executionContext.throwIfCancelled(); Field field = parameters.getField().getSingleField(); - GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType(); + GraphQLObjectType parentType = parameters.getExecutionStepInfo().getUnwrappedNonNullTypeAs(); GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, field); return completeField(fieldDef, executionContext, parameters, fetchedValue); } private FieldValueInfo completeField(GraphQLFieldDefinition fieldDef, ExecutionContext executionContext, ExecutionStrategyParameters parameters, Object fetchedValue) { - GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType(); + GraphQLObjectType parentType = parameters.getExecutionStepInfo().getUnwrappedNonNullTypeAs(); ExecutionStepInfo executionStepInfo = createExecutionStepInfo(executionContext, parameters, fieldDef, parentType); Instrumentation instrumentation = executionContext.getInstrumentation(); @@ -1008,7 +1008,7 @@ private boolean incrementAndCheckMaxNodesExceeded(ExecutionContext executionCont * @return a {@link GraphQLFieldDefinition} */ protected GraphQLFieldDefinition getFieldDef(ExecutionContext executionContext, ExecutionStrategyParameters parameters, Field field) { - GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType(); + GraphQLObjectType parentType = parameters.getExecutionStepInfo().getUnwrappedNonNullTypeAs(); return getFieldDef(executionContext.getGraphQLSchema(), parentType, field); } diff --git a/src/main/java/graphql/execution/SubscriptionExecutionStrategy.java b/src/main/java/graphql/execution/SubscriptionExecutionStrategy.java index 45e2192248..d2da978471 100644 --- a/src/main/java/graphql/execution/SubscriptionExecutionStrategy.java +++ b/src/main/java/graphql/execution/SubscriptionExecutionStrategy.java @@ -226,7 +226,7 @@ private ExecutionStrategyParameters firstFieldOfSubscriptionSelection(ExecutionC private ExecutionStepInfo createSubscribedFieldStepInfo(ExecutionContext executionContext, ExecutionStrategyParameters parameters) { Field field = parameters.getField().getSingleField(); - GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType(); + GraphQLObjectType parentType = parameters.getExecutionStepInfo().getUnwrappedNonNullTypeAs(); GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, field); return createExecutionStepInfo(executionContext, parameters, fieldDef, parentType); } diff --git a/src/main/java/graphql/schema/GraphQLNonNull.java b/src/main/java/graphql/schema/GraphQLNonNull.java index 12f131b428..44762d1017 100644 --- a/src/main/java/graphql/schema/GraphQLNonNull.java +++ b/src/main/java/graphql/schema/GraphQLNonNull.java @@ -46,8 +46,8 @@ public GraphQLNonNull(GraphQLType wrappedType) { } private void assertNonNullWrapping(GraphQLType wrappedType) { - assertTrue(!GraphQLTypeUtil.isNonNull(wrappedType), - "A non null type cannot wrap an existing non null type '%s'", GraphQLTypeUtil.simplePrint(wrappedType)); + assertTrue(!GraphQLTypeUtil.isNonNull(wrappedType), () -> + "A non null type cannot wrap an existing non null type"); } @Override diff --git a/src/main/java/graphql/schema/GraphQLTypeUtil.java b/src/main/java/graphql/schema/GraphQLTypeUtil.java index c2a44d641c..ef933eb291 100644 --- a/src/main/java/graphql/schema/GraphQLTypeUtil.java +++ b/src/main/java/graphql/schema/GraphQLTypeUtil.java @@ -219,22 +219,26 @@ private static GraphQLType unwrapAllImpl(GraphQLType type) { /** - * Unwraps all non nullable layers of the type until it reaches a type that is not {@link GraphQLNonNull} + * Unwraps a single non-nullable layer of the type if its present. Note there can + * only ever be one non-nullable wrapping of a type and this is enforced by {@link GraphQLNonNull} * * @param type the type to unwrap * * @return the underlying type that is not {@link GraphQLNonNull} */ public static GraphQLType unwrapNonNull(GraphQLType type) { - while (isNonNull(type)) { - type = unwrapOne(type); + // its illegal to have a type that is a non-null wrapping a non-null type + // and GraphQLNonNull has code that prevents it so we can just check once during the unwrapping + if (isNonNull(type)) { + // is cheaper doing this direct rather than calling #unwrapOne + type = ((GraphQLNonNull) type).getWrappedType(); } return type; } /** - * Unwraps all non nullable layers of the type until it reaches a type that is not {@link GraphQLNonNull} - * and then cast to the target type. + * Unwraps a single non-nullable layer of the type if its present and then cast to the target type. Note there can + * only ever be one non-nullable wrapping of a type and this is enforced by {@link GraphQLNonNull} * * @param type the type to unwrap * @param for two diff --git a/src/test/groovy/graphql/schema/GraphQLTypeUtilTest.groovy b/src/test/groovy/graphql/schema/GraphQLTypeUtilTest.groovy index 39763a0b0d..bc8a08aff1 100644 --- a/src/test/groovy/graphql/schema/GraphQLTypeUtilTest.groovy +++ b/src/test/groovy/graphql/schema/GraphQLTypeUtilTest.groovy @@ -6,7 +6,7 @@ import static graphql.Scalars.GraphQLString import static graphql.schema.GraphQLList.list import static graphql.schema.GraphQLNonNull.nonNull import static graphql.schema.GraphQLObjectType.newObject -import static graphql.schema.GraphQLTypeReference.* +import static graphql.schema.GraphQLTypeReference.typeRef class GraphQLTypeUtilTest extends Specification { @@ -205,4 +205,32 @@ class GraphQLTypeUtilTest extends Specification { then: !GraphQLTypeUtil.isInput(type) } + + def "can unwrap non null-ness"() { + + when: + def type = GraphQLTypeUtil.unwrapNonNull(nonNull(GraphQLString)) + + then: + (type as GraphQLNamedType).getName() == "String" + + when: + type = GraphQLTypeUtil.unwrapNonNull(nonNull(list(GraphQLString))) + + then: + type instanceof GraphQLList + + when: + type = GraphQLTypeUtil.unwrapNonNull(list(GraphQLString)) + + then: + type instanceof GraphQLList + + when: + type = GraphQLTypeUtil.unwrapNonNull(GraphQLString) + + then: + (type as GraphQLNamedType).getName() == "String" + + } }