Skip to content

Commit e54fd89

Browse files
committed
Handle repeated query directives
1 parent fa8cf1b commit e54fd89

4 files changed

Lines changed: 34 additions & 11 deletions

File tree

src/main/java/graphql/execution/directives/DirectivesResolver.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import graphql.schema.GraphQLDirective;
1212
import graphql.schema.GraphQLSchema;
1313

14+
import java.util.ArrayList;
1415
import java.util.LinkedHashMap;
1516
import java.util.List;
1617
import java.util.Locale;
@@ -25,14 +26,14 @@ public class DirectivesResolver {
2526
public DirectivesResolver() {
2627
}
2728

28-
public Map<String, GraphQLDirective> resolveDirectives(List<Directive> directives, GraphQLSchema schema, Map<String, Object> variables, GraphQLContext graphQLContext, Locale locale) {
29+
public Map<String, List<GraphQLDirective>> resolveDirectives(List<Directive> directives, GraphQLSchema schema, Map<String, Object> variables, GraphQLContext graphQLContext, Locale locale) {
2930
GraphQLCodeRegistry codeRegistry = schema.getCodeRegistry();
30-
Map<String, GraphQLDirective> directiveMap = new LinkedHashMap<>();
31+
Map<String, List<GraphQLDirective>> directiveMap = new LinkedHashMap<>();
3132
directives.forEach(directive -> {
3233
GraphQLDirective protoType = schema.getDirective(directive.getName());
3334
if (protoType != null) {
3435
GraphQLDirective newDirective = protoType.transform(builder -> buildArguments(builder, codeRegistry, protoType, directive, variables, graphQLContext, locale));
35-
directiveMap.put(newDirective.getName(), newDirective);
36+
directiveMap.computeIfAbsent(newDirective.getName(), k -> new ArrayList<>()).add(newDirective);
3637
}
3738
});
3839
return ImmutableMap.copyOf(directiveMap);
@@ -60,4 +61,4 @@ private void buildArguments(GraphQLDirective.Builder directiveBuilder,
6061
}
6162
});
6263
}
63-
}
64+
}

src/main/java/graphql/execution/directives/QueryDirectivesImpl.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import graphql.schema.GraphQLArgument;
1212
import graphql.schema.GraphQLDirective;
1313
import graphql.schema.GraphQLSchema;
14+
import graphql.util.FpKit;
1415

1516
import java.util.ArrayList;
1617
import java.util.LinkedHashMap;
@@ -22,7 +23,7 @@
2223

2324
/**
2425
* These objects are ALWAYS in the context of a single MergedField
25-
*
26+
* <p>
2627
* Also note we compute these values lazily
2728
*/
2829
@Internal
@@ -57,11 +58,11 @@ private void computeValuesLazily() {
5758
final Map<Field, List<QueryAppliedDirective>> byFieldApplied = new LinkedHashMap<>();
5859
mergedField.getFields().forEach(field -> {
5960
List<Directive> directives = field.getDirectives();
60-
ImmutableList<GraphQLDirective> resolvedDirectives = ImmutableList.copyOf(
61+
ImmutableList<GraphQLDirective> resolvedDirectives = ImmutableList.copyOf(FpKit.flatList(
6162
directivesResolver
6263
.resolveDirectives(directives, schema, variables, graphQLContext, locale)
6364
.values()
64-
);
65+
));
6566
byField.put(field, resolvedDirectives);
6667
// at some point we will only use applied
6768
byFieldApplied.put(field, ImmutableKit.map(resolvedDirectives, this::toAppliedDirective));

src/main/java/graphql/util/FpKit.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ public static <T> CompletableFuture<List<T>> flatList(CompletableFuture<List<Lis
265265
return cf.thenApply(FpKit::flatList);
266266
}
267267

268-
public static <T> List<T> flatList(List<List<T>> listLists) {
268+
public static <T> List<T> flatList(Collection<List<T>> listLists) {
269269
return listLists.stream()
270270
.flatMap(List::stream)
271271
.collect(ImmutableList.toImmutableList());

src/test/groovy/graphql/execution/directives/QueryDirectivesImplTest.groovy

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class QueryDirectivesImplTest extends Specification {
1616
directive @cached(forMillis : Int = 99) on FIELD | QUERY
1717
1818
directive @upper(place : String) on FIELD
19+
20+
directive @rep(place : String) repeatable on FIELD
1921
2022
type Query {
2123
f : String
@@ -24,9 +26,7 @@ class QueryDirectivesImplTest extends Specification {
2426

2527
def schema = TestUtil.schema(sdl)
2628

27-
2829
def "can get immediate directives"() {
29-
3030
def f1 = TestUtil.parseField("f1 @cached @upper")
3131
def f2 = TestUtil.parseField("f2 @cached(forMillis : \$var) @timeout")
3232

@@ -68,7 +68,6 @@ class QueryDirectivesImplTest extends Specification {
6868
}
6969

7070
def "builder works as expected"() {
71-
7271
def f1 = TestUtil.parseField("f1 @cached @upper")
7372
def f2 = TestUtil.parseField("f2 @cached(forMillis : \$var) @timeout")
7473

@@ -87,6 +86,28 @@ class QueryDirectivesImplTest extends Specification {
8786

8887
then:
8988
appliedDirectivesByName.keySet().sort() == ["cached", "timeout", "upper"]
89+
}
90+
91+
def "gets repeated definitions"() {
92+
def f1 = TestUtil.parseField("f1 @rep(place: \$var) @rep(place: \"HELLO\")")
9093

94+
def mergedField = MergedField.newMergedField([f1]).build()
95+
96+
def queryDirectives = QueryDirectives.newQueryDirectives()
97+
.mergedField(mergedField)
98+
.schema(schema)
99+
.coercedVariables(CoercedVariables.of([var: "ABC"]))
100+
.graphQLContext(GraphQLContext.getDefault())
101+
.locale(Locale.getDefault())
102+
.build()
103+
104+
when:
105+
def appliedDirectivesByName = queryDirectives.getImmediateAppliedDirectivesByName()
106+
107+
then:
108+
appliedDirectivesByName.keySet() == ["rep"] as Set
109+
appliedDirectivesByName["rep"].size() == 2
110+
// Groovy is a pathway to many abilities some consider to be unnatural
111+
appliedDirectivesByName["rep"].arguments.value.flatten().sort() == ["ABC", "HELLO"]
91112
}
92113
}

0 commit comments

Comments
 (0)