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
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
org.gradle.daemon=true

bintray.user=DUMMY_USER
bintray.key=DUMMY_KEY
bintray.key=DUMMY_KEY

11 changes: 11 additions & 0 deletions src/main/java/graphql/ExceptionWhileDataFetching.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package graphql;


import graphql.execution.Path;
import graphql.language.SourceLocation;

import java.util.List;

public class ExceptionWhileDataFetching implements GraphQLError {

private final Path path;
private final Throwable exception;

public ExceptionWhileDataFetching(Throwable exception) {
this(null, exception);
}

public ExceptionWhileDataFetching(Path path, Throwable exception) {
this.path = path;
this.exception = exception;
}

Expand All @@ -28,6 +35,10 @@ public List<SourceLocation> getLocations() {
return null;
}

public String getPath() {
return path == null ? null : path.toString();
}

@Override
public ErrorType getErrorType() {
return ErrorType.DataFetchingException;
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/graphql/execution/Execution.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ private ExecutionResult executeOperation(
fieldCollector.collectFields(executionContext, operationRootType, operationDefinition.getSelectionSet(), new ArrayList<String>(), fields);

if (operationDefinition.getOperation() == OperationDefinition.Operation.MUTATION) {
return mutationStrategy.execute(executionContext, operationRootType, root, fields);
return mutationStrategy.execute(executionContext, Path.root, operationRootType, root, fields);
} else {
return queryStrategy.execute(executionContext, operationRootType, root, fields);
return queryStrategy.execute(executionContext, Path.root, operationRootType, root, fields);
}
}
}
28 changes: 14 additions & 14 deletions src/main/java/graphql/execution/ExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ public abstract class ExecutionStrategy {
protected final ValuesResolver valuesResolver = new ValuesResolver();
protected final FieldCollector fieldCollector = new FieldCollector();

public abstract ExecutionResult execute(ExecutionContext executionContext, GraphQLObjectType parentType, Object source, Map<String, List<Field>> fields);
public abstract ExecutionResult execute(ExecutionContext executionContext, Path currentPath, GraphQLObjectType parentType, Object source, Map<String, List<Field>> fields);

protected ExecutionResult resolveField(ExecutionContext executionContext, GraphQLObjectType parentType, Object source, List<Field> fields) {
protected ExecutionResult resolveField(ExecutionContext executionContext, Path currentPath, GraphQLObjectType parentType, Object source, List<Field> fields) {
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, fields.get(0));

Map<String, Object> argumentValues = valuesResolver.getArgumentValues(fieldDef.getArguments(), fields.get(0).getArguments(), executionContext.getVariables());
Expand All @@ -40,16 +40,16 @@ protected ExecutionResult resolveField(ExecutionContext executionContext, GraphQ
resolvedValue = fieldDef.getDataFetcher().get(environment);
} catch (Exception e) {
log.warn("Exception while fetching data", e);
executionContext.addError(new ExceptionWhileDataFetching(e));
executionContext.addError(new ExceptionWhileDataFetching(currentPath, e));
}

return completeValue(executionContext, fieldDef.getType(), fields, resolvedValue);
return completeValue(executionContext, currentPath, fieldDef.getType(), fields, resolvedValue);
}

protected ExecutionResult completeValue(ExecutionContext executionContext, GraphQLType fieldType, List<Field> fields, Object result) {
protected ExecutionResult completeValue(ExecutionContext executionContext, Path currentPath, GraphQLType fieldType, List<Field> fields, Object result) {
if (fieldType instanceof GraphQLNonNull) {
GraphQLNonNull graphQLNonNull = (GraphQLNonNull) fieldType;
ExecutionResult completed = completeValue(executionContext, graphQLNonNull.getWrappedType(), fields, result);
ExecutionResult completed = completeValue(executionContext, currentPath, graphQLNonNull.getWrappedType(), fields, result);
if (completed == null) {
throw new GraphQLException("Cannot return null for non-nullable type: " + fields);
}
Expand All @@ -58,7 +58,7 @@ protected ExecutionResult completeValue(ExecutionContext executionContext, Graph
} else if (result == null) {
return null;
} else if (fieldType instanceof GraphQLList) {
return completeValueForList(executionContext, (GraphQLList) fieldType, fields, result);
return completeValueForList(executionContext, currentPath, (GraphQLList) fieldType, fields, result);
} else if (fieldType instanceof GraphQLScalarType) {
return completeValueForScalar((GraphQLScalarType) fieldType, result);
} else if (fieldType instanceof GraphQLEnumType) {
Expand All @@ -84,16 +84,16 @@ protected ExecutionResult completeValue(ExecutionContext executionContext, Graph

// 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 executionContext.getQueryStrategy().execute(executionContext, currentPath, resolvedType, result, subFields);
}

private ExecutionResult completeValueForList(ExecutionContext executionContext, GraphQLList fieldType, List<Field> fields, Object result) {
private ExecutionResult completeValueForList(ExecutionContext executionContext, Path currentPath, GraphQLList fieldType, List<Field> fields, Object result) {
if (result.getClass().isArray()) {
result = Arrays.asList((Object[]) result);
}

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

protected GraphQLObjectType resolveType(GraphQLInterfaceType graphQLInterfaceType, Object value) {
Expand Down Expand Up @@ -126,10 +126,12 @@ protected ExecutionResult completeValueForScalar(GraphQLScalarType scalarType, O
return new ExecutionResultImpl(serialized, null);
}

protected ExecutionResult completeValueForList(ExecutionContext executionContext, GraphQLList fieldType, List<Field> fields, Iterable<Object> result) {
protected ExecutionResult completeValueForList(ExecutionContext executionContext, Path currentPath, GraphQLList fieldType, List<Field> fields, Iterable<Object> result) {
List<Object> completedResults = new ArrayList<Object>();
int idx = -1;
for (Object item : result) {
ExecutionResult completedValue = completeValue(executionContext, fieldType.getWrappedType(), fields, item);
++idx;
ExecutionResult completedValue = completeValue(executionContext, currentPath.withTrailing(idx), fieldType.getWrappedType(), fields, item);
completedResults.add(completedValue != null ? completedValue.getData() : null);
}
return new ExecutionResultImpl(completedResults, null);
Expand All @@ -154,6 +156,4 @@ protected GraphQLFieldDefinition getFieldDef(GraphQLSchema schema, GraphQLObject
}
return fieldDefinition;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
/**
* <p>ExecutorServiceExecutionStrategy uses an {@link ExecutorService} to parallelize the resolve.</p>
*
* Due to the nature of {@link #execute(ExecutionContext, GraphQLObjectType, Object, Map)} implementation, {@link ExecutorService}
* Due to the nature of {@link #execute(ExecutionContext, Path, GraphQLObjectType, Object, Map)} implementation, {@link ExecutorService}
* MUST have the following 2 characteristics:
* <ul>
* <li>1. The underlying {@link java.util.concurrent.ThreadPoolExecutor} MUST have a reasonable {@code maximumPoolSize}
Expand All @@ -37,17 +37,17 @@ public ExecutorServiceExecutionStrategy(ExecutorService executorService) {
}

@Override
public ExecutionResult execute(final ExecutionContext executionContext, final GraphQLObjectType parentType, final Object source, final Map<String, List<Field>> fields) {
public ExecutionResult execute(final ExecutionContext executionContext, final Path currentPath, final GraphQLObjectType parentType, final Object source, final Map<String, List<Field>> fields) {
if (executorService == null)
return new SimpleExecutionStrategy().execute(executionContext, parentType, source, fields);
return new SimpleExecutionStrategy().execute(executionContext, currentPath, parentType, source, fields);

Map<String, Future<ExecutionResult>> futures = new LinkedHashMap<String, Future<ExecutionResult>>();
for (String fieldName : fields.keySet()) {
for (final String fieldName : fields.keySet()) {
final List<Field> fieldList = fields.get(fieldName);
Callable<ExecutionResult> resolveField = new Callable<ExecutionResult>() {
@Override
public ExecutionResult call() throws Exception {
return resolveField(executionContext, parentType, source, fieldList);
return resolveField(executionContext, currentPath.withTrailing(fieldName), parentType, source, fieldList);

}
};
Expand Down
71 changes: 71 additions & 0 deletions src/main/java/graphql/execution/Path.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package graphql.execution;

public class Path {
private final Path parent;
private PathComponent tail;

public static Path root= new Path();

private Path() {
parent = null;
tail = null;
}

public Path(String topLevelField) {
this(null, new StringPathComponent(topLevelField));
}

private Path(Path parent, PathComponent tail) {
this.parent = parent;
this.tail = tail;
}

public Path withTrailing(String trailing) {
return new Path(this, new StringPathComponent(trailing));
}

public Path withTrailing(int index) {
return new Path(this, new IntPathComponent(index));
}

@Override
public String toString() {
if (tail == null) {
return "";
}

if (parent == root) {
return tail.toString().substring(1);
}

return parent.toString() + tail.toString();
}

private interface PathComponent {
}

private static class StringPathComponent implements PathComponent {
private final String value;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please expose value; it can be usable for other things like change tracking (my case)

public StringPathComponent(String value) {
if (value == null || value.isEmpty()) {
throw new IllegalArgumentException("empty path component");
}
this.value = value;
}
@Override
public String toString() {
return '/' + value;
}
}

private static class IntPathComponent implements PathComponent {
private final int value;
public IntPathComponent(int value) {
this.value = value;
}
@Override
public String toString() {
return "[" + value + ']';
}
}
}
4 changes: 2 additions & 2 deletions src/main/java/graphql/execution/SimpleExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@

public class SimpleExecutionStrategy extends ExecutionStrategy {
@Override
public ExecutionResult execute(ExecutionContext executionContext, GraphQLObjectType parentType, Object source, Map<String, List<Field>> fields) {
public ExecutionResult execute(ExecutionContext executionContext, Path currentPath, GraphQLObjectType parentType, Object source, Map<String, List<Field>> fields) {
Map<String, Object> results = new LinkedHashMap<String, Object>();
for (String fieldName : fields.keySet()) {
List<Field> fieldList = fields.get(fieldName);
ExecutionResult resolvedResult = resolveField(executionContext, parentType, source, fieldList);
ExecutionResult resolvedResult = resolveField(executionContext, currentPath.withTrailing(fieldName), parentType, source, fieldList);

results.put(fieldName, resolvedResult != null ? resolvedResult.getData() : null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import graphql.GraphQLException;
import graphql.execution.ExecutionContext;
import graphql.execution.ExecutionStrategy;
import graphql.execution.Path;
import graphql.language.Field;
import graphql.schema.*;
import org.slf4j.Logger;
Expand All @@ -32,13 +33,13 @@ public class BatchedExecutionStrategy extends ExecutionStrategy {
private final BatchedDataFetcherFactory batchingFactory = new BatchedDataFetcherFactory();

@Override
public ExecutionResult execute(ExecutionContext executionContext, GraphQLObjectType parentType, Object source, Map<String, List<Field>> fields) {
public ExecutionResult execute(ExecutionContext executionContext, Path currentPath, GraphQLObjectType parentType, Object source, Map<String, List<Field>> fields) {
GraphQLExecutionNodeDatum data = new GraphQLExecutionNodeDatum(new LinkedHashMap<String, Object>(), source);
GraphQLExecutionNode root = new GraphQLExecutionNode(parentType, fields, singletonList(data));
return execute(executionContext, root);
return execute(executionContext, currentPath, root);
}

private ExecutionResult execute(ExecutionContext executionContext, GraphQLExecutionNode root) {
private ExecutionResult execute(ExecutionContext executionContext, Path currentPath, GraphQLExecutionNode root) {

Queue<GraphQLExecutionNode> nodes = new ArrayDeque<GraphQLExecutionNode>();
nodes.add(root);
Expand All @@ -49,7 +50,7 @@ private ExecutionResult execute(ExecutionContext executionContext, GraphQLExecut

for (String fieldName : node.getFields().keySet()) {
List<Field> fieldList = node.getFields().get(fieldName);
List<GraphQLExecutionNode> childNodes = resolveField(executionContext, node.getParentType(),
List<GraphQLExecutionNode> childNodes = resolveField(executionContext, currentPath.withTrailing(fieldName), node.getParentType(),
node.getData(), fieldName, fieldList);
nodes.addAll(childNodes);
}
Expand All @@ -66,14 +67,14 @@ private GraphQLExecutionNodeDatum getOnlyElement(List<GraphQLExecutionNodeDatum>
// Use the data.parentResult objects to put values into. These are either primitives or empty maps
// If they were empty maps, we need that list of nodes to process

private List<GraphQLExecutionNode> resolveField(ExecutionContext executionContext, GraphQLObjectType parentType,
private List<GraphQLExecutionNode> resolveField(ExecutionContext executionContext, Path currentPath, GraphQLObjectType parentType,
List<GraphQLExecutionNodeDatum> nodeData, String fieldName, List<Field> fields) {

GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, fields.get(0));
if (fieldDef == null) {
return Collections.emptyList();
}
List<GraphQLExecutionNodeValue> values = fetchData(executionContext, parentType, nodeData, fields, fieldDef);
List<GraphQLExecutionNodeValue> values = fetchData(executionContext, currentPath, parentType, nodeData, fields, fieldDef);

return completeValues(executionContext, parentType, values, fieldName, fields, fieldDef.getType());
}
Expand Down Expand Up @@ -242,7 +243,7 @@ private boolean isObject(GraphQLType type) {
}

@SuppressWarnings("unchecked")
private List<GraphQLExecutionNodeValue> fetchData(ExecutionContext executionContext, GraphQLObjectType parentType,
private List<GraphQLExecutionNodeValue> fetchData(ExecutionContext executionContext, Path currentPath, GraphQLObjectType parentType,
List<GraphQLExecutionNodeDatum> nodeData, List<Field> fields, GraphQLFieldDefinition fieldDef) {

Map<String, Object> argumentValues = valuesResolver.getArgumentValues(
Expand Down Expand Up @@ -270,7 +271,7 @@ private List<GraphQLExecutionNodeValue> fetchData(ExecutionContext executionCont
values.add(null);
}
log.warn("Exception while fetching data", e);
executionContext.addError(new ExceptionWhileDataFetching(e));
executionContext.addError(new ExceptionWhileDataFetching(currentPath, e));
}
assert nodeData.size() == values.size();

Expand Down
Loading