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
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import com.google.common.collect.ImmutableMap;
import graphql.Assert;
import graphql.PublicApi;
import graphql.execution.directives.QueryDirectives;
import graphql.introspection.Introspection;
import graphql.language.Argument;
import graphql.language.ArrayValue;
import graphql.language.Directive;
import graphql.language.Document;
import graphql.language.Field;
import graphql.language.InlineFragment;
Expand All @@ -30,6 +32,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static graphql.collect.ImmutableKit.emptyList;
import static graphql.collect.ImmutableKit.map;
Expand Down Expand Up @@ -78,7 +81,7 @@ public Map<String, Object> getVariables() {
}

/**
* This will compile a operation text {@link Document} with possibly variables from the given {@link ExecutableNormalizedField}s
* This will compile an operation text {@link Document} with possibly variables from the given {@link ExecutableNormalizedField}s
*
* The {@link VariablePredicate} is used called to decide if the given argument values should be made into a variable
* OR inlined into the operation text as a graphql literal.
Expand All @@ -96,10 +99,34 @@ public static CompilerResult compileToDocument(@NotNull GraphQLSchema schema,
@Nullable String operationName,
@NotNull List<ExecutableNormalizedField> topLevelFields,
@Nullable VariablePredicate variablePredicate) {
return compileToDocument(schema,operationKind,operationName,topLevelFields,Map.of(),variablePredicate);
}

/**
* This will compile an operation text {@link Document} with possibly variables from the given {@link ExecutableNormalizedField}s
*
* The {@link VariablePredicate} is used called to decide if the given argument values should be made into a variable
* OR inlined into the operation text as a graphql literal.
*
* @param schema the graphql schema to use
* @param operationKind the kind of operation
* @param operationName the name of the operation to use
* @param topLevelFields the top level {@link ExecutableNormalizedField}s to start from
* @param normalizedFieldToQueryDirectives the map of normalized field to query directives
* @param variablePredicate the variable predicate that decides if arguments turn into variables or not during compilation
*
* @return a {@link CompilerResult} object
*/
public static CompilerResult compileToDocument(@NotNull GraphQLSchema schema,
@NotNull OperationDefinition.Operation operationKind,
@Nullable String operationName,
@NotNull List<ExecutableNormalizedField> topLevelFields,
@NotNull Map<ExecutableNormalizedField, QueryDirectives> normalizedFieldToQueryDirectives,
@Nullable VariablePredicate variablePredicate) {
GraphQLObjectType operationType = getOperationType(schema, operationKind);

VariableAccumulator variableAccumulator = new VariableAccumulator(variablePredicate);
List<Selection<?>> selections = subselectionsForNormalizedField(schema, operationType.getName(), topLevelFields, variableAccumulator);
List<Selection<?>> selections = subselectionsForNormalizedField(schema, operationType.getName(), topLevelFields, normalizedFieldToQueryDirectives, variableAccumulator);
SelectionSet selectionSet = new SelectionSet(selections);

OperationDefinition.Builder definitionBuilder = OperationDefinition.newOperationDefinition()
Expand All @@ -120,6 +147,7 @@ public static CompilerResult compileToDocument(@NotNull GraphQLSchema schema,
private static List<Selection<?>> subselectionsForNormalizedField(GraphQLSchema schema,
@NotNull String parentOutputType,
List<ExecutableNormalizedField> executableNormalizedFields,
@NotNull Map<ExecutableNormalizedField, QueryDirectives> normalizedFieldToQueryDirectives,
VariableAccumulator variableAccumulator) {
ImmutableList.Builder<Selection<?>> selections = ImmutableList.builder();

Expand All @@ -129,13 +157,13 @@ private static List<Selection<?>> subselectionsForNormalizedField(GraphQLSchema

for (ExecutableNormalizedField nf : executableNormalizedFields) {
if (nf.isConditional(schema)) {
selectionForNormalizedField(schema, nf, variableAccumulator)
selectionForNormalizedField(schema, nf, normalizedFieldToQueryDirectives, variableAccumulator)
.forEach((objectTypeName, field) ->
fieldsByTypeCondition
.computeIfAbsent(objectTypeName, ignored -> new ArrayList<>())
.add(field));
} else {
selections.add(selectionForNormalizedField(schema, parentOutputType, nf, variableAccumulator));
selections.add(selectionForNormalizedField(schema, parentOutputType, nf, normalizedFieldToQueryDirectives,variableAccumulator));
}
}

Expand All @@ -156,11 +184,12 @@ private static List<Selection<?>> subselectionsForNormalizedField(GraphQLSchema
*/
private static Map<String, Field> selectionForNormalizedField(GraphQLSchema schema,
ExecutableNormalizedField executableNormalizedField,
@NotNull Map<ExecutableNormalizedField, QueryDirectives> normalizedFieldToQueryDirectives,
VariableAccumulator variableAccumulator) {
Map<String, Field> groupedFields = new LinkedHashMap<>();

for (String objectTypeName : executableNormalizedField.getObjectTypeNames()) {
groupedFields.put(objectTypeName, selectionForNormalizedField(schema, objectTypeName, executableNormalizedField, variableAccumulator));
groupedFields.put(objectTypeName, selectionForNormalizedField(schema, objectTypeName, executableNormalizedField,normalizedFieldToQueryDirectives, variableAccumulator));
}

return groupedFields;
Expand All @@ -172,6 +201,7 @@ private static Map<String, Field> selectionForNormalizedField(GraphQLSchema sche
private static Field selectionForNormalizedField(GraphQLSchema schema,
String objectTypeName,
ExecutableNormalizedField executableNormalizedField,
@NotNull Map<ExecutableNormalizedField, QueryDirectives> normalizedFieldToQueryDirectives,
VariableAccumulator variableAccumulator) {
final List<Selection<?>> subSelections;
if (executableNormalizedField.getChildren().isEmpty()) {
Expand All @@ -184,19 +214,30 @@ private static Field selectionForNormalizedField(GraphQLSchema schema,
schema,
fieldOutputType.getName(),
executableNormalizedField.getChildren(),
normalizedFieldToQueryDirectives,
variableAccumulator
);
}

SelectionSet selectionSet = selectionSetOrNullIfEmpty(subSelections);
List<Argument> arguments = createArguments(executableNormalizedField, variableAccumulator);

return newField()
QueryDirectives queryDirectives = normalizedFieldToQueryDirectives.get(executableNormalizedField);


Field.Builder builder = newField()
.name(executableNormalizedField.getFieldName())
.alias(executableNormalizedField.getAlias())
.selectionSet(selectionSet)
.arguments(arguments)
.build();
.arguments(arguments);
if(queryDirectives == null || queryDirectives.getImmediateAppliedDirectivesByField().isEmpty() ){
return builder.build();
}else {
List<Directive> directives = queryDirectives.getImmediateAppliedDirectivesByField().keySet().stream().flatMap(field -> field.getDirectives().stream()).collect(Collectors.toList());
return builder
.directives(directives)
.build();
}
}

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import graphql.GraphQL
import graphql.TestUtil
import graphql.execution.RawVariables
import graphql.language.AstPrinter
import graphql.language.Field
import graphql.language.OperationDefinition
import graphql.language.AstSorter
import graphql.language.Document
import graphql.language.IntValue
import graphql.language.StringValue
import graphql.parser.Parser
import graphql.schema.GraphQLSchema
import graphql.schema.idl.RuntimeWiring
import graphql.schema.idl.TestLiveMockedWiringFactory
Expand Down Expand Up @@ -1238,6 +1241,62 @@ class ExecutableNormalizedOperationToAstCompilerTest extends Specification {
'''
}



def "test query directive"() {
def sdl = '''
type Query {
foo1(arg: I): String

}
type Subscription {
foo1(arg: I): DevOps

}
input I {
arg1: String
}

type DevOps{
name: String
}

directive @optIn(to : [String!]!) repeatable on FIELD
'''
def query = '''subscription {
foo1 (arg: {
arg1: "Subscription"
}) @optIn(to: "foo") {
name @optIn(to: "devOps")
}


}
'''
GraphQLSchema schema = mkSchema(sdl)
Document document = new Parser().parse(query)
ExecutableNormalizedOperation eno = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema,document, null,RawVariables.emptyVariables())


when:
def result = compileToDocument(schema, SUBSCRIPTION, null, eno.topLevelFields, eno.normalizedFieldToQueryDirectives, noVariables)
OperationDefinition operationDefinition = result.document.getDefinitionsOfType(OperationDefinition.class)[0]
def fooField = (Field)operationDefinition.selectionSet.children[0]
def nameField = (Field)fooField.selectionSet.children[0]
def documentPrinted = AstPrinter.printAst(new AstSorter().sort(result.document))

then:

fooField.directives.size() == 1
nameField.directives.size() == 1
documentPrinted == '''subscription {
foo1(arg: {arg1 : "Subscription"}) @optIn(to: "foo") {
name @optIn(to: "devOps")
}
}
'''
}

def "test redundant inline fragments specified in original query"() {
def sdl = '''
type Query {
Expand Down