Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kaqqao parsed directive arguments #1289

Merged
9 changes: 9 additions & 0 deletions src/main/java/graphql/DirectivesUtil.java
Expand Up @@ -23,4 +23,13 @@ public static Optional<GraphQLArgument> directiveWithArg(List<GraphQLDirective>
}
return Optional.ofNullable(argument);
}

public static Optional<GraphQLArgument> directiveWithArg(Map<String,GraphQLDirective> directives, String directiveName, String argumentName) {
GraphQLDirective directive =directives.get(directiveName);
GraphQLArgument argument = null;
if (directive != null) {
argument = directive.getArgument(argumentName);
}
return Optional.ofNullable(argument);
}
}
60 changes: 60 additions & 0 deletions src/main/java/graphql/execution/DirectivesResolver.java
@@ -0,0 +1,60 @@
package graphql.execution;

import graphql.Internal;
import graphql.introspection.Introspection;
import graphql.language.Directive;
import graphql.language.Field;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLDirective;
import graphql.schema.GraphQLSchema;
import graphql.schema.visibility.GraphqlFieldVisibility;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;

@Internal
public class DirectivesResolver {

private final ValuesResolver valuesResolver;

public DirectivesResolver(ValuesResolver valuesResolver) {
this.valuesResolver = valuesResolver;
}

public Map<String, GraphQLDirective> getFieldDirectives(Field field, GraphQLSchema schema, Map<String, Object> variables) {
GraphqlFieldVisibility fieldVisibility = schema.getFieldVisibility();

Map<String, GraphQLDirective> directiveMap = new HashMap<>();
Copy link
Member

Choose a reason for hiding this comment

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

LinkedHasMap


field.getDirectives().forEach(fieldDirective -> {
GraphQLDirective protoType = schema.getDirective(fieldDirective.getName());
if (protoType != null) {
EnumSet<Introspection.DirectiveLocation> validLocations = protoType.validLocations();
//
// we only allow values for directives that exist and are on the right location. Validation before hand
// will have caught most of these but we are being defensive here
//
if (validLocations.contains(Introspection.DirectiveLocation.FIELD)) {
GraphQLDirective newDirective = protoType.transform(builder -> buildArguments(builder, fieldVisibility, protoType, fieldDirective, variables));
directiveMap.put(newDirective.getName(), newDirective);
}
}
});
return directiveMap;
}

private void buildArguments(GraphQLDirective.Builder directiveBuilder, GraphqlFieldVisibility fieldVisibility, GraphQLDirective protoType, Directive fieldDirective, Map<String, Object> variables) {

Map<String, Object> argumentValues = valuesResolver.getArgumentValues(fieldVisibility, protoType.getArguments(), fieldDirective.getArguments(), variables);

directiveBuilder.clearArguments();
protoType.getArguments().forEach(protoArg -> {
if (argumentValues.containsKey(protoArg.getName())) {
Object argValue = argumentValues.get(protoArg.getName());
GraphQLArgument newArgument = protoArg.transform(argBuilder -> argBuilder.value(argValue));
directiveBuilder.argument(newArgument);
}
});
}
}
49 changes: 45 additions & 4 deletions src/main/java/graphql/execution/ExecutionStepInfo.java
@@ -1,7 +1,10 @@
package graphql.execution;

import graphql.DirectivesUtil;
import graphql.PublicApi;
import graphql.language.Field;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLDirective;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLNonNull;
Expand Down Expand Up @@ -31,16 +34,18 @@ public class ExecutionStepInfo {
private final GraphQLFieldDefinition fieldDefinition;
private final ExecutionPath path;
private final Map<String, Object> arguments;
private final Map<String, GraphQLDirective> directives;
private final ExecutionStepInfo parent;

private ExecutionStepInfo(GraphQLType type, GraphQLFieldDefinition fieldDefinition, Field field, ExecutionPath path, ExecutionStepInfo parent, Map<String, Object> arguments) {
private ExecutionStepInfo(GraphQLType type, GraphQLFieldDefinition fieldDefinition, Field field, ExecutionPath path, ExecutionStepInfo parent, Map<String, Object> arguments, Map<String, GraphQLDirective> directives) {
this.fieldDefinition = fieldDefinition;
this.field = field;
this.path = path;
this.parent = parent;
this.type = assertNotNull(type, "you must provide a graphql type");
this.arguments = arguments;

this.directives = directives;
}

/**
Expand Down Expand Up @@ -121,6 +126,36 @@ public <T> T getArgument(String name) {
return (T) arguments.get(name);
}

/**
* @return the resolved directives that have been passed to this field
*/
public Map<String, GraphQLDirective> getDirectives() {
return new LinkedHashMap<>(directives);
}

/**
* Returns a named directive or null if it is not present
*
* @param directiveName the name of the directive
*
* @return a named directive or null if it is not present
*/
public GraphQLDirective getDirective(String directiveName) {
return directives.get(directiveName);
}

/**
* Returns a named argument for a named directive or null if there is not one present
*
* @param directiveName the name of the directive
* @param argumentName the name of the argument
*
* @return a named argument for a named directive or null if there is not one present
*/
public GraphQLArgument getDirectiveArgument(String directiveName, String argumentName) {
return DirectivesUtil.directiveWithArg(directives, directiveName, argumentName).orElse(null);
}

/**
* @return the parent type information
*/
Expand Down Expand Up @@ -148,9 +183,9 @@ public boolean hasParent() {
public ExecutionStepInfo changeTypeWithPreservedNonNull(GraphQLType newType) {
assertTrue(!GraphQLTypeUtil.isNonNull(newType), "newType can't be non null");
if (isNonNullType()) {
return new ExecutionStepInfo(GraphQLNonNull.nonNull(newType), fieldDefinition, field, path, this.parent, arguments);
return new ExecutionStepInfo(GraphQLNonNull.nonNull(newType), fieldDefinition, field, path, this.parent, arguments, directives);
} else {
return new ExecutionStepInfo(newType, fieldDefinition, field, path, this.parent, arguments);
return new ExecutionStepInfo(newType, fieldDefinition, field, path, this.parent, arguments, directives);
}
}

Expand Down Expand Up @@ -187,6 +222,7 @@ public static class Builder {
Field field;
ExecutionPath executionPath;
Map<String, Object> arguments = new LinkedHashMap<>();
Map<String, GraphQLDirective> directives = new LinkedHashMap<>();

/**
* @see ExecutionStepInfo#newExecutionStepInfo()
Expand Down Expand Up @@ -224,8 +260,13 @@ public Builder arguments(Map<String, Object> arguments) {
return this;
}

public Builder directives(Map<String, GraphQLDirective> directives) {
this.directives = new LinkedHashMap<>(directives == null ? Collections.emptyMap() : directives);
return this;
}

public ExecutionStepInfo build() {
return new ExecutionStepInfo(type, fieldDefinition, field, executionPath, parentInfo, arguments);
return new ExecutionStepInfo(type, fieldDefinition, field, executionPath, parentInfo, arguments, directives);
}
}
}
21 changes: 18 additions & 3 deletions src/main/java/graphql/execution/ExecutionStrategy.java
Expand Up @@ -9,6 +9,7 @@
import graphql.TypeMismatchError;
import graphql.TypeResolutionEnvironment;
import graphql.UnresolvedTypeError;
import graphql.VisibleForTesting;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.InstrumentationContext;
import graphql.execution.instrumentation.parameters.InstrumentationFieldCompleteParameters;
Expand All @@ -22,6 +23,7 @@
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.DataFetchingFieldSelectionSet;
import graphql.schema.DataFetchingFieldSelectionSetImpl;
import graphql.schema.GraphQLDirective;
import graphql.schema.GraphQLEnumType;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLInterfaceType;
Expand Down Expand Up @@ -120,8 +122,12 @@ public abstract class ExecutionStrategy {

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

protected final ValuesResolver valuesResolver = new ValuesResolver();
protected final FieldCollector fieldCollector = new FieldCollector();
@VisibleForTesting
protected ValuesResolver valuesResolver = new ValuesResolver();
@VisibleForTesting
protected FieldCollector fieldCollector = new FieldCollector();
@VisibleForTesting
protected DirectivesResolver directivesResolver = new DirectivesResolver(valuesResolver);

protected final DataFetcherExceptionHandler dataFetcherExceptionHandler;

Expand Down Expand Up @@ -230,15 +236,19 @@ protected CompletableFuture<Object> fetchField(ExecutionContext executionContext
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, field);

GraphqlFieldVisibility fieldVisibility = executionContext.getGraphQLSchema().getFieldVisibility();

Map<String, Object> argumentValues = valuesResolver.getArgumentValues(fieldVisibility, fieldDef.getArguments(), field.getArguments(), executionContext.getVariables());

Map<String, GraphQLDirective> directivesMap = directivesResolver.getFieldDirectives(field, executionContext.getGraphQLSchema(), executionContext.getVariables());

GraphQLOutputType fieldType = fieldDef.getType();
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, parameters.getField());
ExecutionStepInfo executionStepInfo = createExecutionStepInfo(executionContext, parameters, fieldDef);

DataFetchingEnvironment environment = newDataFetchingEnvironment(executionContext)
.source(parameters.getSource())
.arguments(argumentValues)
.directives(directivesMap)
.fieldDefinition(fieldDef)
.fields(parameters.getField())
.fieldType(fieldType)
Expand Down Expand Up @@ -894,15 +904,20 @@ protected ExecutionStepInfo createExecutionStepInfo(ExecutionContext executionCo
GraphqlFieldVisibility fieldVisibility = executionContext.getGraphQLSchema().getFieldVisibility();
Map<String, Object> argumentValues = valuesResolver.getArgumentValues(fieldVisibility, fieldDefinition.getArguments(), fieldArgs, executionContext.getVariables());

Map<String, GraphQLDirective> directivesMap = Collections.emptyMap();
if (field != null) {
directivesMap = directivesResolver.getFieldDirectives(field, executionContext.getGraphQLSchema(), executionContext.getVariables());
}

return newExecutionStepInfo()
.type(fieldType)
.fieldDefinition(fieldDefinition)
.field(field)
.path(parameters.getPath())
.parentInfo(parameters.getExecutionStepInfo())
.arguments(argumentValues)
.directives(directivesMap)
.build();

}


Expand Down
Expand Up @@ -7,6 +7,7 @@
import graphql.execution.Async;
import graphql.execution.DataFetcherExceptionHandler;
import graphql.execution.DataFetcherExceptionHandlerParameters;
import graphql.execution.DirectivesResolver;
import graphql.execution.ExecutionContext;
import graphql.execution.ExecutionPath;
import graphql.execution.ExecutionStepInfo;
Expand All @@ -26,6 +27,7 @@
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.DataFetchingFieldSelectionSet;
import graphql.schema.DataFetchingFieldSelectionSetImpl;
import graphql.schema.GraphQLDirective;
import graphql.schema.GraphQLEnumType;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLInterfaceType;
Expand Down Expand Up @@ -236,19 +238,23 @@ private CompletableFuture<FetchedValues> fetchData(ExecutionContext executionCon
GraphQLFieldDefinition fieldDef) {
GraphQLObjectType parentType = node.getType();
List<Field> fields = node.getFields().get(fieldName);
Field field = fields.get(0);
List<MapOrList> parentResults = node.getParentResults();

GraphqlFieldVisibility fieldVisibility = executionContext.getGraphQLSchema().getFieldVisibility();
Map<String, Object> argumentValues = valuesResolver.getArgumentValues(
fieldVisibility,
fieldDef.getArguments(), fields.get(0).getArguments(), executionContext.getVariables());

Map<String, GraphQLDirective> directivesMap = directivesResolver.getFieldDirectives(field, executionContext.getGraphQLSchema(), executionContext.getVariables());

GraphQLOutputType fieldType = fieldDef.getType();
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, fields);

DataFetchingEnvironment environment = newDataFetchingEnvironment(executionContext)
.source(node.getSources())
.arguments(argumentValues)
.directives(directivesMap)
.fieldDefinition(fieldDef)
.fields(fields)
.fieldType(fieldDef.getType())
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/graphql/schema/DataFetchingEnvironment.java
Expand Up @@ -56,6 +56,32 @@ public interface DataFetchingEnvironment {
*/
<T> T getArgument(String name);

/**
* Returns a map of the directives that were present on the field
*
* @return a map of the directives that were present on the field
*/
Map<String, GraphQLDirective> getDirectives();

/**
* Returns a named directive that might present on the field
*
* @param directiveName the name of the directive to retrieve
*
* @return a named directive that might present on the field or null if not present
*/
GraphQLDirective getDirective(String directiveName);

/**
* Returns a named argument of a named directive that might present on the field
*
* @param directiveName the name of the directive to retrieve
* @param argumentName the name of the directive argument
*
* @return a named argument that might present on a directive on the field or null if not present
*/
GraphQLArgument getDirectiveArgument(String directiveName, String argumentName);

/**
* Returns a context argument that is set up when the {@link graphql.GraphQL#execute} method
* is invoked.
Expand Down
Expand Up @@ -29,6 +29,7 @@ public static DataFetchingEnvironmentBuilder newDataFetchingEnvironment(DataFetc
return new DataFetchingEnvironmentBuilder()
.source(environment.getSource())
.arguments(environment.getArguments())
.directives(environment.getDirectives())
.context(environment.getContext())
.root(environment.getRoot())
.fieldDefinition(environment.getFieldDefinition())
Expand Down Expand Up @@ -58,6 +59,7 @@ public static DataFetchingEnvironmentBuilder newDataFetchingEnvironment(Executio

private Object source;
private Map<String, Object> arguments = Collections.emptyMap();
private Map<String, GraphQLDirective> directives = Collections.emptyMap();
private Object context;
private Object root;
private GraphQLFieldDefinition fieldDefinition;
Expand All @@ -81,6 +83,11 @@ public DataFetchingEnvironmentBuilder arguments(Map<String, Object> arguments) {
return this;
}

public DataFetchingEnvironmentBuilder directives(Map<String, GraphQLDirective> directives) {
this.directives = directives;
return this;
}

public DataFetchingEnvironmentBuilder context(Object context) {
this.context = context;
return this;
Expand Down Expand Up @@ -142,7 +149,7 @@ public DataFetchingEnvironmentBuilder executionContext(ExecutionContext executio
}

public DataFetchingEnvironment build() {
return new DataFetchingEnvironmentImpl(source, arguments, context, root,
return new DataFetchingEnvironmentImpl(source, arguments, directives, context, root,
fieldDefinition, fields, fieldType, parentType, graphQLSchema, fragmentsByName, executionId, selectionSet,
executionStepInfo,
executionContext);
Expand Down