Skip to content

Commit 5d5c145

Browse files
committed
breaking change: transform directives to work with graphql-java SchemaTransformer, and remove all deprecated methods from GraphQLAnnotations class
1 parent d2af000 commit 5d5c145

17 files changed

+1806
-1680
lines changed

src/main/java/graphql/annotations/AnnotationsSchemaCreator.java

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
11
/**
22
* Copyright 2016 Yurii Rashkovskii
3-
*
3+
* <p>
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
*/
1515
package graphql.annotations;
1616

17+
import graphql.annotations.directives.AnnotationsDirectiveWiring;
18+
import graphql.annotations.directives.DirectiveSchemaVisitor;
19+
import graphql.annotations.processor.DirectiveAndWiring;
1720
import graphql.annotations.processor.GraphQLAnnotations;
1821
import graphql.annotations.processor.typeFunctions.TypeFunction;
1922
import graphql.relay.Relay;
2023
import graphql.schema.GraphQLDirective;
2124
import graphql.schema.GraphQLSchema;
2225
import graphql.schema.GraphQLType;
26+
import graphql.schema.SchemaTransformer;
2327

24-
import java.util.HashSet;
25-
import java.util.Set;
28+
import java.util.*;
2629
import java.util.stream.Collectors;
2730

31+
import static graphql.schema.GraphQLSchema.newSchema;
32+
2833
public class AnnotationsSchemaCreator {
2934

3035
public static Builder newAnnotationsSchema() {
@@ -43,6 +48,7 @@ public static class Builder {
4348
private Boolean shouldAlwaysPrettify = null;
4449
private GraphQLAnnotations graphQLAnnotations;
4550
private GraphQLSchema.Builder graphqlSchemaBuilder;
51+
private SchemaTransformer schemaTransformer = new SchemaTransformer();
4652

4753
/**
4854
* You can set your own schema builder, but its optional
@@ -114,12 +120,18 @@ public Builder directives(Set<Class<?>> directiveClasses) {
114120
return this;
115121
}
116122

123+
public Builder directives(Class<?>... directiveClasses) {
124+
this.directivesObjectList.addAll(Arrays.asList(directiveClasses));
125+
return this;
126+
}
127+
117128
/**
118129
* Add directive declaration class to create directives for the graphql schema
130+
*
119131
* @param directiveContainerClass a directive container class (directives are defined as methods inside the class)
120132
* @return the builder after adding the directive container class to the list of directive container classes
121133
*/
122-
public Builder directives(Class<?> directiveContainerClass){
134+
public Builder directives(Class<?> directiveContainerClass) {
123135
this.directiveContainerClasses.add(directiveContainerClass);
124136
return this;
125137
}
@@ -234,7 +246,7 @@ public GraphQLSchema build() {
234246
}
235247

236248
Set<GraphQLDirective> directives = directivesObjectList.stream().map(dir -> graphQLAnnotations.directive(dir)).collect(Collectors.toSet());
237-
directiveContainerClasses.forEach(dir->directives.addAll(graphQLAnnotations.directives(dir)));
249+
directiveContainerClasses.forEach(dir -> directives.addAll(graphQLAnnotations.directives(dir)));
238250

239251
Set<GraphQLType> additionalTypes = additionalTypesList.stream().map(additionalType ->
240252
additionalType.isInterface() ?
@@ -252,7 +264,25 @@ public GraphQLSchema build() {
252264
}
253265
this.graphqlSchemaBuilder.additionalTypes(additionalTypes).additionalType(Relay.pageInfoType)
254266
.codeRegistry(graphQLAnnotations.getContainer().getCodeRegistryBuilder().build());
255-
return this.graphqlSchemaBuilder.build();
267+
GraphQLSchema schema = this.graphqlSchemaBuilder.build();
268+
// wire with directives
269+
HashMap<String, AnnotationsDirectiveWiring> directiveWiringHashMap = transformDirectiveRegistry(this.graphQLAnnotations.getContainer().getDirectiveRegistry());
270+
DirectiveSchemaVisitor directiveSchemaVisitor = new DirectiveSchemaVisitor(directiveWiringHashMap, graphQLAnnotations.getContainer().getCodeRegistryBuilder());
271+
GraphQLSchema trasnformedSchema = this.schemaTransformer.transform(schema, directiveSchemaVisitor);
272+
return newSchema(trasnformedSchema).codeRegistry(graphQLAnnotations.getContainer().getCodeRegistryBuilder().build()).build();
273+
}
274+
275+
private HashMap<String, AnnotationsDirectiveWiring> transformDirectiveRegistry(Map<String, DirectiveAndWiring> directiveRegistry) {
276+
HashMap<String, AnnotationsDirectiveWiring> map = new HashMap<>();
277+
directiveRegistry.forEach((directiveName, directiveAndWiring) -> {
278+
try {
279+
map.put(directiveName, directiveAndWiring.getWiringClass().newInstance());
280+
} catch (Exception e) {
281+
throw new RuntimeException(e);
282+
}
283+
});
284+
285+
return map;
256286
}
257287
}
258288
}

src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironment.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import graphql.schema.GraphQLCodeRegistry;
1818
import graphql.schema.GraphQLDirective;
1919
import graphql.schema.GraphQLDirectiveContainer;
20+
import graphql.schema.GraphQLSchemaElement;
2021

2122
public interface AnnotationsWiringEnvironment {
2223
/**
@@ -33,7 +34,7 @@ public interface AnnotationsWiringEnvironment {
3334
*
3435
* @return the parent name of the element
3536
*/
36-
String getParentName();
37+
GraphQLSchemaElement getParentElement();
3738

3839

3940
/**

src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironmentImpl.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,19 @@
1717
import graphql.schema.GraphQLCodeRegistry;
1818
import graphql.schema.GraphQLDirective;
1919
import graphql.schema.GraphQLDirectiveContainer;
20+
import graphql.schema.GraphQLSchemaElement;
2021

2122
public class AnnotationsWiringEnvironmentImpl implements AnnotationsWiringEnvironment {
2223
private final GraphQLDirectiveContainer element;
2324
private final GraphQLDirective directive;
24-
private final String parentName;
25+
private final GraphQLSchemaElement parentElement;
2526
private GraphQLCodeRegistry.Builder codeRegistryBuilder;
2627

2728
public AnnotationsWiringEnvironmentImpl(GraphQLDirectiveContainer element, GraphQLDirective directive,
28-
String parentName, GraphQLCodeRegistry.Builder codeRegistryBuilder) {
29+
GraphQLSchemaElement parentELement, GraphQLCodeRegistry.Builder codeRegistryBuilder) {
2930
this.element = element;
3031
this.directive = directive;
31-
this.parentName = parentName;
32+
this.parentElement = parentELement;
3233
this.codeRegistryBuilder = codeRegistryBuilder;
3334
}
3435

@@ -43,8 +44,8 @@ public GraphQLDirective getDirective() {
4344
}
4445

4546
@Override
46-
public String getParentName() {
47-
return parentName;
47+
public GraphQLSchemaElement getParentElement() {
48+
return parentElement;
4849
}
4950

5051
@Override
@@ -60,7 +61,7 @@ public boolean equals(Object o) {
6061
AnnotationsWiringEnvironmentImpl that = (AnnotationsWiringEnvironmentImpl) o;
6162

6263
if (element != null ? !element.equals(that.element) : that.element != null) return false;
63-
if (parentName != null ? !parentName.equals(that.parentName) : that.parentName != null) return false;
64+
if (parentElement != null ? !parentElement.equals(that.parentElement) : that.parentElement != null) return false;
6465
if (codeRegistryBuilder != null ? !codeRegistryBuilder.equals(that.codeRegistryBuilder) : that.codeRegistryBuilder != null)
6566
return false;
6667
return directive != null ? directive.equals(that.directive) : that.directive == null;
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package graphql.annotations.directives;
2+
3+
import graphql.introspection.Introspection;
4+
import graphql.schema.*;
5+
import graphql.util.TraversalControl;
6+
import graphql.util.TraverserContext;
7+
8+
import java.lang.reflect.InvocationTargetException;
9+
import java.util.Arrays;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
import static graphql.util.TreeTransformerUtil.changeNode;
15+
16+
public class DirectiveSchemaVisitor implements GraphQLTypeVisitor {
17+
private HashMap<String, AnnotationsDirectiveWiring> directiveWiringMap;
18+
private GraphQLCodeRegistry.Builder codeRegistryBuilder;
19+
20+
@FunctionalInterface
21+
interface WiringFunction {
22+
GraphQLDirectiveContainer apply(GraphQLDirective a, GraphQLDirectiveContainer b,
23+
AnnotationsDirectiveWiring wiring, GraphQLSchemaElement parentElement)
24+
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
25+
}
26+
27+
private Map<Class, WiringFunction> functionMap;
28+
29+
30+
public DirectiveSchemaVisitor(HashMap<String, AnnotationsDirectiveWiring> directiveWiringMap, GraphQLCodeRegistry.Builder codeRegistryBuilder) {
31+
this.directiveWiringMap = directiveWiringMap;
32+
this.functionMap = createFunctionsMap();
33+
this.codeRegistryBuilder = codeRegistryBuilder;
34+
}
35+
36+
@Override
37+
public TraversalControl visitGraphQLArgument(GraphQLArgument node, TraverserContext<GraphQLSchemaElement> context) {
38+
return this.visitGraphQLType(GraphQLArgument.class, node, context);
39+
}
40+
41+
42+
@Override
43+
public TraversalControl visitGraphQLInterfaceType(GraphQLInterfaceType node, TraverserContext<GraphQLSchemaElement> context) {
44+
return this.visitGraphQLType(GraphQLInterfaceType.class, node, context);
45+
}
46+
47+
@Override
48+
public TraversalControl visitGraphQLEnumType(GraphQLEnumType node, TraverserContext<GraphQLSchemaElement> context) {
49+
return this.visitGraphQLType(GraphQLEnumType.class, node, context);
50+
}
51+
52+
@Override
53+
public TraversalControl visitGraphQLEnumValueDefinition(GraphQLEnumValueDefinition node, TraverserContext<GraphQLSchemaElement> context) {
54+
return this.visitGraphQLType(GraphQLEnumValueDefinition.class, node, context);
55+
}
56+
57+
@Override
58+
public TraversalControl visitGraphQLFieldDefinition(GraphQLFieldDefinition node, TraverserContext<GraphQLSchemaElement> context) {
59+
return this.visitGraphQLType(GraphQLFieldDefinition.class, node, context);
60+
}
61+
62+
@Override
63+
public TraversalControl visitGraphQLDirective(GraphQLDirective node, TraverserContext<GraphQLSchemaElement> context) {
64+
return TraversalControl.CONTINUE;
65+
}
66+
67+
@Override
68+
public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField node, TraverserContext<GraphQLSchemaElement> context) {
69+
return this.visitGraphQLType(GraphQLInputObjectField.class, node, context);
70+
}
71+
72+
@Override
73+
public TraversalControl visitGraphQLInputObjectType(GraphQLInputObjectType node, TraverserContext<GraphQLSchemaElement> context) {
74+
return this.visitGraphQLType(GraphQLInputObjectType.class, node, context);
75+
}
76+
77+
@Override
78+
public TraversalControl visitGraphQLList(GraphQLList node, TraverserContext<GraphQLSchemaElement> context) {
79+
return TraversalControl.CONTINUE;
80+
}
81+
82+
@Override
83+
public TraversalControl visitGraphQLNonNull(GraphQLNonNull node, TraverserContext<GraphQLSchemaElement> context) {
84+
return TraversalControl.CONTINUE;
85+
}
86+
87+
@Override
88+
public TraversalControl visitGraphQLObjectType(GraphQLObjectType node, TraverserContext<GraphQLSchemaElement> context) {
89+
return this.visitGraphQLType(GraphQLObjectType.class, node, context);
90+
}
91+
92+
@Override
93+
public TraversalControl visitGraphQLScalarType(GraphQLScalarType node, TraverserContext<GraphQLSchemaElement> context) {
94+
return this.visitGraphQLType(GraphQLScalarType.class, node, context);
95+
}
96+
97+
@Override
98+
public TraversalControl visitGraphQLTypeReference(GraphQLTypeReference node, TraverserContext<GraphQLSchemaElement> context) {
99+
return TraversalControl.CONTINUE;
100+
}
101+
102+
@Override
103+
public TraversalControl visitGraphQLUnionType(GraphQLUnionType node, TraverserContext<GraphQLSchemaElement> context) {
104+
return this.visitGraphQLType(GraphQLUnionType.class, node, context);
105+
}
106+
107+
private TraversalControl visitGraphQLType(Class<? extends GraphQLDirectiveContainer> typeOfContainer,
108+
GraphQLDirectiveContainer node, TraverserContext<GraphQLSchemaElement> context) {
109+
List<GraphQLDirective> directives = node.getDirectives();
110+
if (directives.size() == 0) {
111+
return TraversalControl.CONTINUE;
112+
}
113+
GraphQLDirectiveContainer newNode = node;
114+
for (GraphQLDirective directive : directives) {
115+
AnnotationsDirectiveWiring wiring = this.directiveWiringMap.get(directive.getName());
116+
if (wiring != null) {
117+
try {
118+
GraphQLSchemaElement parentElement = context.getParentNode();
119+
newNode = functionMap.get(typeOfContainer).apply(directive, newNode,
120+
wiring, parentElement);
121+
} catch (Exception e) {
122+
e.printStackTrace();
123+
return TraversalControl.CONTINUE;
124+
}
125+
}
126+
}
127+
return changeNode(context, newNode);
128+
}
129+
130+
private void putInMap(Map<Class, WiringFunction> map, Class clazz, String functionName,
131+
Introspection.DirectiveLocation... locations) {
132+
map.put(clazz, (d, e, wiring, parentElement) -> {
133+
assertLocation(d, e, locations);
134+
AnnotationsWiringEnvironmentImpl environment =
135+
new AnnotationsWiringEnvironmentImpl(e, e.getDirective(d.getName()), parentElement, codeRegistryBuilder);
136+
return (GraphQLDirectiveContainer) wiring.getClass().getMethod(functionName, AnnotationsWiringEnvironment.class)
137+
.invoke(wiring, environment);
138+
});
139+
}
140+
141+
private Map<Class, WiringFunction> createFunctionsMap() {
142+
Map<Class, WiringFunction> functionMap = new HashMap<>();
143+
putInMap(functionMap, GraphQLFieldDefinition.class, "onField", Introspection.DirectiveLocation.FIELD, Introspection.DirectiveLocation.FIELD_DEFINITION);
144+
putInMap(functionMap, GraphQLObjectType.class, "onObject", Introspection.DirectiveLocation.OBJECT);
145+
putInMap(functionMap, GraphQLArgument.class, "onArgument", Introspection.DirectiveLocation.ARGUMENT_DEFINITION);
146+
putInMap(functionMap, GraphQLInterfaceType.class, "onInterface", Introspection.DirectiveLocation.INTERFACE);
147+
putInMap(functionMap, GraphQLUnionType.class, "onUnion", Introspection.DirectiveLocation.UNION);
148+
putInMap(functionMap, GraphQLEnumType.class, "onEnum", Introspection.DirectiveLocation.ENUM);
149+
putInMap(functionMap, GraphQLEnumValueDefinition.class, "onEnumValue", Introspection.DirectiveLocation.ENUM_VALUE);
150+
putInMap(functionMap, GraphQLScalarType.class, "onScalar", Introspection.DirectiveLocation.SCALAR);
151+
putInMap(functionMap, GraphQLInputObjectType.class, "onInputObjectType", Introspection.DirectiveLocation.INPUT_OBJECT);
152+
putInMap(functionMap, GraphQLInputObjectField.class, "onInputObjectField", Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION);
153+
154+
return functionMap;
155+
}
156+
157+
private void assertLocation(GraphQLDirective graphQLDirective, GraphQLDirectiveContainer element, Introspection.DirectiveLocation... validLocations) {
158+
boolean isSupported = false;
159+
for (Introspection.DirectiveLocation validLocation : validLocations) {
160+
if (graphQLDirective.validLocations().contains(validLocation)) {
161+
isSupported = true;
162+
}
163+
}
164+
if (!isSupported) {
165+
throw getInvalidDirectiveLocationException(element, graphQLDirective, validLocations);
166+
}
167+
}
168+
169+
private InvalidDirectiveLocationException getInvalidDirectiveLocationException(GraphQLDirectiveContainer element, GraphQLDirective graphQLDirective, Introspection.DirectiveLocation... validLocations) {
170+
return new InvalidDirectiveLocationException("The element: '" + element.getName() + "' is annotated with the directive: '"
171+
+ graphQLDirective.getName() + "' which is not valid on the element location: '" + Arrays.toString(Arrays.stream(validLocations).map(Enum::name).toArray()) + "'", null);
172+
}
173+
}

0 commit comments

Comments
 (0)