Skip to content

Commit 055900c

Browse files
tinnoubbakerman
authored andcommitted
Fix UnresolvedTypeException not being propagated as GraphQLError
1 parent 14c9022 commit 055900c

4 files changed

Lines changed: 420 additions & 7 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package graphql;
2+
3+
import graphql.execution.ExecutionPath;
4+
import graphql.execution.ExecutionTypeInfo;
5+
import graphql.execution.UnresolvedTypeException;
6+
import graphql.language.SourceLocation;
7+
8+
import java.util.List;
9+
10+
import static graphql.Assert.assertNotNull;
11+
import static java.lang.String.format;
12+
13+
@PublicApi
14+
public class UnresolvedTypeError implements GraphQLError {
15+
16+
private final String message;
17+
private final List<Object> path;
18+
private final UnresolvedTypeException exception;
19+
20+
public UnresolvedTypeError(ExecutionPath path, ExecutionTypeInfo info,
21+
UnresolvedTypeException exception) {
22+
this.path = assertNotNull(path).toList();
23+
this.exception = assertNotNull(exception);
24+
this.message = mkMessage(path, exception, assertNotNull(info));
25+
}
26+
27+
private String mkMessage(ExecutionPath path, UnresolvedTypeException exception, ExecutionTypeInfo info) {
28+
return format("Can't resolve '%s'. Abstract type '%s' must resolve to an Object type at runtime for field '%s.%s'. %s",
29+
path,
30+
exception.getInterfaceOrUnionType().getName(),
31+
info.getParentTypeInfo().getType().getName(),
32+
info.getFieldDefinition().getName(),
33+
exception.getMessage());
34+
}
35+
36+
public UnresolvedTypeException getException() {
37+
return exception;
38+
}
39+
40+
41+
@Override
42+
public String getMessage() {
43+
return message;
44+
}
45+
46+
@Override
47+
public List<SourceLocation> getLocations() {
48+
return null;
49+
}
50+
51+
@Override
52+
public ErrorType getErrorType() {
53+
return ErrorType.DataFetchingException;
54+
}
55+
56+
public List<Object> getPath() {
57+
return path;
58+
}
59+
60+
@Override
61+
public String toString() {
62+
return "UnresolvedTypeError{" +
63+
"path=" + path +
64+
"exception=" + exception +
65+
'}';
66+
}
67+
68+
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
69+
@Override
70+
public boolean equals(Object o) {
71+
return GraphqlErrorHelper.equals(this, o);
72+
}
73+
74+
@Override
75+
public int hashCode() {
76+
return GraphqlErrorHelper.hashCode(this);
77+
}
78+
}

‎src/main/java/graphql/execution/ExecutionStrategy.java‎

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import graphql.SerializationError;
77
import graphql.TypeMismatchError;
88
import graphql.TypeResolutionEnvironment;
9+
import graphql.UnresolvedTypeError;
910
import graphql.execution.instrumentation.Instrumentation;
1011
import graphql.execution.instrumentation.InstrumentationContext;
1112
import graphql.execution.instrumentation.parameters.InstrumentationFieldCompleteParameters;
@@ -366,15 +367,34 @@ protected CompletableFuture<ExecutionResult> completeValue(ExecutionContext exec
366367
} else if (fieldType instanceof GraphQLEnumType) {
367368
return completeValueForEnum(executionContext, parameters, (GraphQLEnumType) fieldType, result);
368369
}
370+
369371
//
370372
// when we are here, we have a complex type: Interface, Union or Object
371373
// and we must go deeper
372374
//
373-
GraphQLObjectType resolvedObjectType = resolveType(executionContext, parameters, fieldType);
375+
GraphQLObjectType resolvedObjectType;
376+
try {
377+
resolvedObjectType = resolveType(executionContext, parameters, fieldType);
378+
} catch (UnresolvedTypeException ex) {
379+
// consider the result to be null and add the error on the context
380+
handleUnresolvedTypeProblem(executionContext, parameters, ex);
381+
// and validate the field is nullable, if non-nullable throw exception
382+
parameters.getNonNullFieldValidator().validate(parameters.getPath(), null);
383+
// complete the field
384+
return completedFuture(new ExecutionResultImpl(null, null));
385+
}
374386

375387
return completeValueForObject(executionContext, parameters, resolvedObjectType, result);
376388
}
377389

390+
private void handleUnresolvedTypeProblem(ExecutionContext context, ExecutionStrategyParameters parameters, UnresolvedTypeException e) {
391+
UnresolvedTypeError error = new UnresolvedTypeError(parameters.getPath(), parameters.getTypeInfo(), e);
392+
log.warn(error.getMessage(), e);
393+
context.addError(error);
394+
395+
parameters.deferredErrorSupport().onError(error);
396+
}
397+
378398
private CompletableFuture<ExecutionResult> completeValueForNull(ExecutionStrategyParameters parameters) {
379399
Object nullValue = parameters.getNonNullFieldValidator().validate(parameters.getPath(), null);
380400
return completedFuture(new ExecutionResultImpl(nullValue, null));
@@ -659,9 +679,10 @@ protected GraphQLObjectType resolveType(ExecutionContext executionContext, Execu
659679
*/
660680
protected GraphQLObjectType resolveTypeForInterface(TypeResolutionParameters params) {
661681
TypeResolutionEnvironment env = new TypeResolutionEnvironment(params.getValue(), params.getArgumentValues(), params.getField(), params.getGraphQLInterfaceType(), params.getSchema(), params.getContext());
662-
GraphQLObjectType result = params.getGraphQLInterfaceType().getTypeResolver().getType(env);
682+
GraphQLInterfaceType abstractType = params.getGraphQLInterfaceType();
683+
GraphQLObjectType result = abstractType.getTypeResolver().getType(env);
663684
if (result == null) {
664-
throw new UnresolvedTypeException(params.getGraphQLInterfaceType());
685+
throw new UnresolvedTypeException(abstractType);
665686
}
666687
return result;
667688
}
@@ -675,9 +696,10 @@ protected GraphQLObjectType resolveTypeForInterface(TypeResolutionParameters par
675696
*/
676697
protected GraphQLObjectType resolveTypeForUnion(TypeResolutionParameters params) {
677698
TypeResolutionEnvironment env = new TypeResolutionEnvironment(params.getValue(), params.getArgumentValues(), params.getField(), params.getGraphQLUnionType(), params.getSchema(), params.getContext());
678-
GraphQLObjectType result = params.getGraphQLUnionType().getTypeResolver().getType(env);
699+
GraphQLUnionType abstractType = params.getGraphQLUnionType();
700+
GraphQLObjectType result = abstractType.getTypeResolver().getType(env);
679701
if (result == null) {
680-
throw new UnresolvedTypeException(params.getGraphQLUnionType());
702+
throw new UnresolvedTypeException(abstractType);
681703
}
682704
return result;
683705
}

‎src/main/java/graphql/execution/UnresolvedTypeException.java‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,24 @@ public class UnresolvedTypeException extends GraphQLException {
1313

1414
private final GraphQLOutputType interfaceOrUnionType;
1515

16-
public UnresolvedTypeException(GraphQLOutputType interfaceOrUnionType) {
17-
super("Could not determine the exact type of '" + interfaceOrUnionType.getName() + "'");
16+
/**
17+
* Constructor to use a custom error message
18+
* for an error that happened during type resolution.
19+
*
20+
* @param message custom error message.
21+
* @param interfaceOrUnionType expected type.
22+
*/
23+
public UnresolvedTypeException(String message, GraphQLOutputType interfaceOrUnionType) {
24+
super(message);
1825
this.interfaceOrUnionType = interfaceOrUnionType;
1926
}
2027

28+
public UnresolvedTypeException(GraphQLOutputType interfaceOrUnionType) {
29+
this("Could not determine the exact type of '" + interfaceOrUnionType.getName() + "'", interfaceOrUnionType);
30+
}
31+
2132
public GraphQLOutputType getInterfaceOrUnionType() {
2233
return interfaceOrUnionType;
2334
}
35+
2436
}

0 commit comments

Comments
 (0)