Skip to content
Merged
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
9 changes: 5 additions & 4 deletions src/main/java/graphql/GraphQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import graphql.execution.Execution;
import graphql.execution.ExecutionStrategy;
import graphql.language.Document;
import graphql.language.SourceLocation;
import graphql.parser.Parser;
Expand All @@ -24,7 +25,7 @@ public class GraphQL {


private final GraphQLSchema graphQLSchema;
private final ExecutorService executorService;
private final ExecutionStrategy executionStrategy;

private static final Logger log = LoggerFactory.getLogger(GraphQL.class);

Expand All @@ -33,9 +34,9 @@ public GraphQL(GraphQLSchema graphQLSchema) {
}


public GraphQL(GraphQLSchema graphQLSchema, ExecutorService executorService) {
public GraphQL(GraphQLSchema graphQLSchema, ExecutionStrategy executionStrategy) {
this.graphQLSchema = graphQLSchema;
this.executorService = executorService;
this.executionStrategy = executionStrategy;
}

public ExecutionResult execute(String requestString) {
Expand Down Expand Up @@ -72,7 +73,7 @@ public ExecutionResult execute(String requestString, String operationName, Objec
if (validationErrors.size() > 0) {
return new ExecutionResultImpl(validationErrors);
}
Execution execution = new Execution(executorService);
Execution execution = new Execution(executionStrategy);
return execution.execute(graphQLSchema, context, document, operationName, arguments);
}

Expand Down
174 changes: 10 additions & 164 deletions src/main/java/graphql/execution/Execution.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,20 @@

public class Execution {

private FieldCollector fieldCollector;
private ValuesResolver valuesResolver;
private ExecutorService executorService;
private FieldCollector fieldCollector = new FieldCollector();
private ExecutionStrategy strategy;

public Execution(ExecutionStrategy strategy) {
this.strategy = strategy;

private static final Logger log = LoggerFactory.getLogger(Execution.class);

public Execution(ExecutorService executorService) {
fieldCollector = new FieldCollector();
valuesResolver = new ValuesResolver();
this.executorService = executorService;
if (this.strategy == null) {
this.strategy = new SimpleExecutionStrategy();
}
}

public ExecutionResult execute(GraphQLSchema graphQLSchema, Object root, Document document, String operationName, Map<String, Object> args) {
ExecutionContextBuilder executionContextBuilder = new ExecutionContextBuilder(new ValuesResolver());
ExecutionContext executionContext = executionContextBuilder.build(graphQLSchema, root, document, operationName, args);
ExecutionContext executionContext = executionContextBuilder.build(graphQLSchema, strategy, root, document, operationName, args);
return executeOperation(executionContext, root, executionContext.getOperationDefinition());
}

Expand All @@ -65,162 +63,10 @@ private ExecutionResult executeOperation(
Map<String, List<Field>> fields = new LinkedHashMap<>();
fieldCollector.collectFields(executionContext, operationRootType, operationDefinition.getSelectionSet(), new ArrayList<String>(), fields);

Map<String, Object> result;
if (operationDefinition.getOperation() == OperationDefinition.Operation.MUTATION) {
result = executeFieldsSerially(executionContext, operationRootType, root, fields);
return new SimpleExecutionStrategy().execute(executionContext, operationRootType, root, fields);
} else {
result = executeFieldsParallel(executionContext, operationRootType, root, fields);
return strategy.execute(executionContext, operationRootType, root, fields);
}
return new ExecutionResultImpl(result, executionContext.getErrors());
}

private Map<String, Object> executeFieldsSerially(ExecutionContext executionContext, GraphQLObjectType parentType, Object source, Map<String, List<Field>> fields) {
Map<String, Object> results = new LinkedHashMap<>();
for (String fieldName : fields.keySet()) {
List<Field> fieldList = fields.get(fieldName);
Object resolvedResult = resolveField(executionContext, parentType, source, fieldList);
results.put(fieldName, resolvedResult);
}
return results;
}


private Map<String, Object> executeFieldsParallel(final ExecutionContext executionContext, final GraphQLObjectType parentType, final Object source, Map<String, List<Field>> fields) {
if (executorService == null) return executeFieldsSerially(executionContext, parentType, source, fields);

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

}
};
futures.put(fieldName, executorService.submit(resolveField));
}
try {
Map<String, Object> results = new LinkedHashMap<>();
for (String fieldName : futures.keySet()) {
results.put(fieldName, futures.get(fieldName).get());
}
return results;
} catch (InterruptedException | ExecutionException e) {
throw new GraphQLException(e);
}

}

private Object resolveField(ExecutionContext executionContext, GraphQLObjectType parentType, Object source, List<Field> fields) {
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, fields.get(0));
if (fieldDef == null) return null;

Map<String, Object> argumentValues = valuesResolver.getArgumentValues(fieldDef.getArguments(), fields.get(0).getArguments(), executionContext.getVariables());
DataFetchingEnvironment environment = new DataFetchingEnvironment(
source,
argumentValues,
executionContext.getRoot(),
fields,
fieldDef.getType(),
parentType,
executionContext.getGraphQLSchema()
);

Object resolvedValue = null;
try {
resolvedValue = fieldDef.getDataFetcher().get(environment);
} catch (Exception e) {
log.info("Exception while fetching data", e);
executionContext.addError(new ExceptionWhileDataFetching(e));
}


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

private Object completeValue(ExecutionContext executionContext, GraphQLType fieldType, List<Field> fields, Object result) {
if (fieldType instanceof GraphQLNonNull) {
GraphQLNonNull graphQLNonNull = (GraphQLNonNull) fieldType;
Object completed = completeValue(executionContext, graphQLNonNull.getWrappedType(), fields, result);
if (completed == null) throw new GraphQLException("Cannot return null for non-nullable type: " + fields);
return completed;

} else if (result == null) {
return null;
} else if (fieldType instanceof GraphQLList) {
return completeValueForList(executionContext, (GraphQLList) fieldType, fields, (List<Object>) result);
} else if (fieldType instanceof GraphQLScalarType) {
return completeValueForScalar((GraphQLScalarType) fieldType, result);
} else if (fieldType instanceof GraphQLEnumType) {
return completeValueForEnum((GraphQLEnumType) fieldType, result);
}


GraphQLObjectType resolvedType;
if (fieldType instanceof GraphQLInterfaceType) {
resolvedType = resolveType((GraphQLInterfaceType) fieldType, result);
} else if (fieldType instanceof GraphQLUnionType) {
resolvedType = resolveType((GraphQLUnionType) fieldType, result);
} else {
resolvedType = (GraphQLObjectType) fieldType;
}

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

private GraphQLObjectType resolveType(GraphQLInterfaceType graphQLInterfaceType, Object value) {
GraphQLObjectType result = graphQLInterfaceType.getTypeResolver().getType(value);
if (result == null) throw new GraphQLException("could not determine type");
return result;
}

private GraphQLObjectType resolveType(GraphQLUnionType graphQLUnionType, Object value) {
GraphQLObjectType result = graphQLUnionType.getTypeResolver().getType(value);
if (result == null) throw new GraphQLException("could not determine type");
return result;
}


private Object completeValueForEnum(GraphQLEnumType enumType, Object result) {
return enumType.getCoercing().coerce(result);
}

private Object completeValueForScalar(GraphQLScalarType scalarType, Object result) {
return scalarType.getCoercing().coerce(result);
}

private Object completeValueForList(ExecutionContext executionContext, GraphQLList fieldType, List<Field> fields, List<Object> result) {
List<Object> completedResults = new ArrayList<>();
for (Object item : result) {
completedResults.add(completeValue(executionContext, fieldType.getWrappedType(), fields, item));
}
return completedResults;
}

private GraphQLFieldDefinition getFieldDef(GraphQLSchema schema, GraphQLObjectType parentType, Field field) {
if (schema.getQueryType() == parentType) {
if (field.getName().equals(SchemaMetaFieldDef.getName())) {
return SchemaMetaFieldDef;
}
if (field.getName().equals(TypeMetaFieldDef.getName())) {
return TypeMetaFieldDef;
}
}
if (field.getName().equals(TypeNameMetaFieldDef.getName())) {
return TypeNameMetaFieldDef;
}

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


}
9 changes: 9 additions & 0 deletions src/main/java/graphql/execution/ExecutionContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
public class ExecutionContext {

private GraphQLSchema graphQLSchema;
private ExecutionStrategy executionStrategy;
private Map<String, FragmentDefinition> fragmentsByName = new LinkedHashMap<>();
private OperationDefinition operationDefinition;
private Map<String, Object> variables = new LinkedHashMap<>();
Expand Down Expand Up @@ -71,4 +72,12 @@ public void addError(GraphQLError error) {
public List<GraphQLError> getErrors(){
return errors;
}

public ExecutionStrategy getExecutionStrategy() {
return executionStrategy;
}

public void setExecutionStrategy(ExecutionStrategy executionStrategy) {
this.executionStrategy = executionStrategy;
}
}
3 changes: 2 additions & 1 deletion src/main/java/graphql/execution/ExecutionContextBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public ExecutionContextBuilder(ValuesResolver valuesResolver) {
this.valuesResolver = valuesResolver;
}

public ExecutionContext build(GraphQLSchema graphQLSchema, Object root, Document document, String operationName, Map<String, Object> args) {
public ExecutionContext build(GraphQLSchema graphQLSchema, ExecutionStrategy executionStrategy, Object root, Document document, String operationName, Map<String, Object> args) {
Map<String, FragmentDefinition> fragmentsByName = new LinkedHashMap<>();
Map<String, OperationDefinition> operationsByName = new LinkedHashMap<>();

Expand Down Expand Up @@ -48,6 +48,7 @@ public ExecutionContext build(GraphQLSchema graphQLSchema, Object root, Document

ExecutionContext executionContext = new ExecutionContext();
executionContext.setGraphQLSchema(graphQLSchema);
executionContext.setExecutionStrategy(executionStrategy);
executionContext.setOperationDefinition(operation);
executionContext.setRoot(root);
executionContext.setFragmentsByName(fragmentsByName);
Expand Down
Loading