Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/main/java/graphql/TypeResolutionEnvironment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package graphql;

import java.util.Map;

import graphql.language.Field;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLType;

public class TypeResolutionEnvironment {

private final Object object;
private final Map<String, Object> arguments;
private final Field field;
private final GraphQLType fieldType;
private final GraphQLSchema schema;

public TypeResolutionEnvironment(Object object, Map<String, Object> arguments, Field field, GraphQLType fieldType, GraphQLSchema schema) {
this.object = object;
this.arguments = arguments;
this.field = field;
this.fieldType = fieldType;
this.schema = schema;
}

public Object getObject() {
return object;
}

public Map<String, Object> getArguments() {
return arguments;
}

public Field getField() {
return field;
}

public GraphQLType getFieldType() {
return fieldType;
}

public GraphQLSchema getSchema() {
return schema;
}
}
126 changes: 94 additions & 32 deletions src/main/java/graphql/execution/ExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import graphql.ExecutionResult;
import graphql.ExecutionResultImpl;
import graphql.GraphQLException;
import graphql.TypeResolutionEnvironment;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.InstrumentationContext;
import graphql.execution.instrumentation.parameters.FieldFetchParameters;
Expand Down Expand Up @@ -73,74 +74,115 @@ protected ExecutionResult resolveField(ExecutionContext executionContext, GraphQ
fetchCtx.onEnd(e);
}

ExecutionResult result = completeValue(executionContext, fieldDef.getType(), fields, resolvedValue);
ExecutionResult result = completeValue(createCompletionParams(executionContext, fieldDef.getType(), fields, resolvedValue, argumentValues));

fieldCtx.onEnd(result);
return result;
}

/**
* @deprecated Use {@link #completeValue(ValueCompletionParameters)} instead
*/
@Deprecated
protected ExecutionResult completeValue(ExecutionContext executionContext, GraphQLType fieldType, List<Field> fields, Object result) {
if (fieldType instanceof GraphQLNonNull) {
GraphQLNonNull graphQLNonNull = (GraphQLNonNull) fieldType;
ExecutionResult completed = completeValue(executionContext, graphQLNonNull.getWrappedType(), fields, result);
return completeValue(createCompletionParams(executionContext, fieldType, fields, result, null));
}

protected ExecutionResult completeValue(ValueCompletionParameters params) {
if (params.getFieldType() instanceof GraphQLNonNull) {
ValueCompletionParameters unwrapped = createCompletionParams(params.getExecutionContext(), params.<GraphQLNonNull>getFieldType().getWrappedType(),
params.getFields(), params.getResult(), params.getArgumentValues()
);
ExecutionResult completed = completeValue(unwrapped);
if (completed == null) {
throw new GraphQLException("Cannot return null for non-nullable type: " + fields);
throw new GraphQLException("Cannot return null for non-nullable type: " + params.getFields());
}
return completed;

} else if (result == null) {
} else if (params.getResult() == null) {
return null;
} else if (fieldType instanceof GraphQLList) {
return completeValueForList(executionContext, (GraphQLList) fieldType, fields, result);
} else if (fieldType instanceof GraphQLScalarType) {
return completeValueForScalar((GraphQLScalarType) fieldType, result);
} else if (fieldType instanceof GraphQLEnumType) {
return completeValueForEnum((GraphQLEnumType) fieldType, result);
} else if (params.getFieldType() instanceof GraphQLList) {
return completeValueForListOrArray(params);
} else if (params.getFieldType() instanceof GraphQLScalarType) {
return completeValueForScalar((GraphQLScalarType) params.getFieldType(), params.getResult());
} else if (params.getFieldType() instanceof GraphQLEnumType) {
return completeValueForEnum((GraphQLEnumType) params.getFieldType(), params.getResult());
}


GraphQLObjectType resolvedType;
if (fieldType instanceof GraphQLInterfaceType) {
resolvedType = resolveType((GraphQLInterfaceType) fieldType, result);
} else if (fieldType instanceof GraphQLUnionType) {
resolvedType = resolveType((GraphQLUnionType) fieldType, result);
if (params.getFieldType() instanceof GraphQLInterfaceType) {
TypeResolutionParameters resolutionParams = TypeResolutionParameters.newParameters()
.graphQLInterfaceType(params.<GraphQLInterfaceType>getFieldType())
.field(params.getFields().get(0))
.value(params.getResult())
.argumentValues(params.getArgumentValues())
.schema(params.getExecutionContext().getGraphQLSchema()).build();
resolvedType = resolveTypeForInterface(resolutionParams);
} else if (params.getFieldType() instanceof GraphQLUnionType) {
TypeResolutionParameters resolutionParams = TypeResolutionParameters.newParameters()
.graphQLUnionType(params.<GraphQLUnionType>getFieldType())
.field(params.getFields().get(0))
.value(params.getResult())
.argumentValues(params.getArgumentValues())
.schema(params.getExecutionContext().getGraphQLSchema()).build();
resolvedType = resolveTypeForUnion(resolutionParams);
} else {
resolvedType = (GraphQLObjectType) fieldType;
resolvedType = params.getFieldType();
}

Map<String, List<Field>> subFields = new LinkedHashMap<String, List<Field>>();
List<String> visitedFragments = new ArrayList<String>();
for (Field field : fields) {
for (Field field : params.getFields()) {
if (field.getSelectionSet() == null) continue;
fieldCollector.collectFields(executionContext, resolvedType, field.getSelectionSet(), visitedFragments, subFields);
fieldCollector.collectFields(params.getExecutionContext(), resolvedType, field.getSelectionSet(), visitedFragments, subFields);
}

// Calling this from the executionContext to ensure we shift back from mutation strategy to the query strategy.

return executionContext.getQueryStrategy().execute(executionContext, resolvedType, result, subFields);
return params.getExecutionContext().getQueryStrategy().execute(params.getExecutionContext(), resolvedType, params.getResult(), subFields);
}

private ExecutionResult completeValueForList(ExecutionContext executionContext, GraphQLList fieldType, List<Field> fields, Object result) {
if (result.getClass().isArray()) {
result = Arrays.asList((Object[]) result);
private ExecutionResult completeValueForListOrArray(ValueCompletionParameters params) {
if (params.getResult().getClass().isArray()) {
List<Object> result = Arrays.asList((Object[]) params.getResult());
params = createCompletionParams(params.getExecutionContext(), params.getFieldType(), params.getFields(), result, params.getArgumentValues());
}

//noinspection unchecked
return completeValueForList(executionContext, fieldType, fields, (Iterable<Object>) result);
return completeValueForList(params);
}

/**
* @deprecated Use {@link #resolveTypeForInterface(TypeResolutionParameters)}
*/
@Deprecated
protected GraphQLObjectType resolveType(GraphQLInterfaceType graphQLInterfaceType, Object value) {
GraphQLObjectType result = graphQLInterfaceType.getTypeResolver().getType(value);
return resolveTypeForInterface(TypeResolutionParameters.newParameters().graphQLInterfaceType(graphQLInterfaceType).value(value).build());
}

protected GraphQLObjectType resolveTypeForInterface(TypeResolutionParameters params) {
TypeResolutionEnvironment env = new TypeResolutionEnvironment(params.getValue(), params.getArgumentValues(), params.getField(), params.getGraphQLInterfaceType(), params.getSchema());
GraphQLObjectType result = params.getGraphQLInterfaceType().getTypeResolver().getType(env);
if (result == null) {
throw new GraphQLException("could not determine type");
throw new GraphQLException("Could not determine the exact type of " + params.getGraphQLInterfaceType().getName());
}
return result;
}

/**
* @deprecated Use {@link #resolveTypeForUnion(TypeResolutionParameters)}
*/
@Deprecated
protected GraphQLObjectType resolveType(GraphQLUnionType graphQLUnionType, Object value) {
GraphQLObjectType result = graphQLUnionType.getTypeResolver().getType(value);
return resolveTypeForUnion(TypeResolutionParameters.newParameters().graphQLUnionType(graphQLUnionType).value(value).build());
}

protected GraphQLObjectType resolveTypeForUnion(TypeResolutionParameters params) {
TypeResolutionEnvironment env = new TypeResolutionEnvironment(params.getValue(), params.getArgumentValues(), params.getField(), params.getGraphQLUnionType(), params.getSchema());
GraphQLObjectType result = params.getGraphQLUnionType().getTypeResolver().getType(env);
if (result == null) {
throw new GraphQLException("could not determine type");
throw new GraphQLException("Could not determine the exact type of " + params.getGraphQLUnionType().getName());
}
return result;
}
Expand All @@ -159,10 +201,21 @@ protected ExecutionResult completeValueForScalar(GraphQLScalarType scalarType, O
return new ExecutionResultImpl(serialized, null);
}

/**
* @deprecated Use {@link #completeValueForList(ValueCompletionParameters)}
*/
@Deprecated
protected ExecutionResult completeValueForList(ExecutionContext executionContext, GraphQLList fieldType, List<Field> fields, Iterable<Object> result) {
return completeValueForList(createCompletionParams(executionContext, fieldType, fields, result, null));
}

protected ExecutionResult completeValueForList(ValueCompletionParameters params) {
List<Object> completedResults = new ArrayList<Object>();
for (Object item : result) {
ExecutionResult completedValue = completeValue(executionContext, fieldType.getWrappedType(), fields, item);
for (Object item : params.<Iterable<Object>>getResult()) {
ValueCompletionParameters unwrapped = createCompletionParams(params.getExecutionContext(), params.<GraphQLList>getFieldType().getWrappedType(),
params.getFields(), item, params.getArgumentValues()
);
ExecutionResult completedValue = completeValue(unwrapped);
completedResults.add(completedValue != null ? completedValue.getData() : null);
}
return new ExecutionResultImpl(completedResults, null);
Expand All @@ -183,10 +236,19 @@ protected GraphQLFieldDefinition getFieldDef(GraphQLSchema schema, GraphQLObject

GraphQLFieldDefinition fieldDefinition = parentType.getFieldDefinition(field.getName());
if (fieldDefinition == null) {
throw new GraphQLException("unknown field " + field.getName());
throw new GraphQLException("Unknown field " + field.getName());
}
return fieldDefinition;
}


private ValueCompletionParameters createCompletionParams(ExecutionContext executionContext, GraphQLType fieldType,
List<Field> fields, Object result, Map<String, Object> argumentValues) {
return ValueCompletionParameters.newParameters()
.executionContext(executionContext)
.fieldType(fieldType)
.fields(fields)
.result(result)
.argumentValues(argumentValues)
.build();
}
}
100 changes: 100 additions & 0 deletions src/main/java/graphql/execution/TypeResolutionParameters.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package graphql.execution;

import java.util.Map;

import graphql.language.Field;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLUnionType;

public class TypeResolutionParameters {

private final GraphQLInterfaceType graphQLInterfaceType;
private final GraphQLUnionType graphQLUnionType;
private final Field field;
private final Object value;
private final Map<String, Object> argumentValues;
private final GraphQLSchema schema;

private TypeResolutionParameters(GraphQLInterfaceType graphQLInterfaceType, GraphQLUnionType graphQLUnionType,
Field field, Object value, Map<String, Object> argumentValues, GraphQLSchema schema) {
this.graphQLInterfaceType = graphQLInterfaceType;
this.graphQLUnionType = graphQLUnionType;
this.field = field;
this.value = value;
this.argumentValues = argumentValues;
this.schema = schema;
}

public GraphQLInterfaceType getGraphQLInterfaceType() {
return graphQLInterfaceType;
}

public GraphQLUnionType getGraphQLUnionType() {
return graphQLUnionType;
}

public Field getField() {
return field;
}

public Object getValue() {
return value;
}

public Map<String, Object> getArgumentValues() {
return argumentValues;
}

public GraphQLSchema getSchema() {
return schema;
}

public static Builder newParameters() {
return new Builder();
}

public static class Builder {

private Field field;
private GraphQLInterfaceType graphQLInterfaceType;
private GraphQLUnionType graphQLUnionType;
private Object value;
private Map<String, Object> argumentValues;
private GraphQLSchema schema;

public Builder field(Field field) {
this.field = field;
return this;
}

public Builder graphQLInterfaceType(GraphQLInterfaceType graphQLInterfaceType) {
this.graphQLInterfaceType = graphQLInterfaceType;
return this;
}

public Builder graphQLUnionType(GraphQLUnionType graphQLUnionType) {
this.graphQLUnionType = graphQLUnionType;
return this;
}

public Builder value(Object value) {
this.value = value;
return this;
}

public Builder argumentValues(Map<String, Object> argumentValues) {
this.argumentValues = argumentValues;
return this;
}

public Builder schema(GraphQLSchema schema) {
this.schema = schema;
return this;
}

public TypeResolutionParameters build() {
return new TypeResolutionParameters(graphQLInterfaceType, graphQLUnionType, field, value, argumentValues, schema);
}
}
}
Loading