Skip to content

Commit 6bd6feb

Browse files
author
Thomas Draier
committed
Keep all output types in registry, move type resolution from TypeFunctions to GraphQLAnnotations
1 parent 289a711 commit 6bd6feb

14 files changed

+184
-132
lines changed

src/main/java/graphql/annotations/DefaultTypeFunction.java

Lines changed: 5 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,20 @@
1515
package graphql.annotations;
1616

1717
import graphql.Scalars;
18-
import graphql.schema.GraphQLEnumType;
1918
import graphql.schema.GraphQLList;
2019
import graphql.schema.GraphQLType;
21-
import graphql.schema.GraphQLTypeReference;
22-
import org.osgi.service.component.annotations.Activate;
23-
import org.osgi.service.component.annotations.Component;
24-
import org.osgi.service.component.annotations.Reference;
25-
import org.osgi.service.component.annotations.ReferenceCardinality;
26-
import org.osgi.service.component.annotations.ReferencePolicy;
20+
import org.osgi.service.component.annotations.*;
2721

2822
import java.lang.reflect.AnnotatedParameterizedType;
2923
import java.lang.reflect.AnnotatedType;
30-
import java.lang.reflect.Field;
3124
import java.lang.reflect.ParameterizedType;
32-
import java.util.*;
33-
import java.util.concurrent.ConcurrentHashMap;
25+
import java.util.ArrayList;
26+
import java.util.List;
27+
import java.util.Optional;
3428
import java.util.concurrent.CopyOnWriteArrayList;
3529
import java.util.stream.Stream;
3630

3731
import static graphql.annotations.util.NamingKit.toGraphqlName;
38-
import static graphql.schema.GraphQLEnumType.newEnum;
3932

4033
@Component(property = "type=default")
4134
public class DefaultTypeFunction implements TypeFunction {
@@ -253,69 +246,7 @@ private Class<?> getClass(AnnotatedType annotatedType) {
253246
}
254247
}
255248

256-
private class EnumFunction implements TypeFunction {
257-
private final Map<String, GraphQLTypeReference> processing = new ConcurrentHashMap<>();
258-
private final Map<String, GraphQLType> types = new ConcurrentHashMap<>();
259-
260-
@Override
261-
public String getTypeName(Class<?> aClass, AnnotatedType annotatedType) {
262-
GraphQLName name = aClass.getAnnotation(GraphQLName.class);
263-
return toGraphqlName(name == null ? aClass.getSimpleName() : name.value());
264-
}
265-
266-
@Override
267-
public boolean canBuildType(Class<?> aClass, AnnotatedType annotatedType) {
268-
return Enum.class.isAssignableFrom(aClass);
269-
}
270-
271-
@Override
272-
public GraphQLType buildType(String typeName, Class<?> aClass, AnnotatedType annotatedType) {
273-
if (types.containsKey(typeName)) {
274-
return types.get(typeName);
275-
} else if (processing.containsKey(typeName)) {
276-
return processing.getOrDefault(typeName, new GraphQLTypeReference(typeName));
277-
} else {
278-
279-
processing.put(typeName, new GraphQLTypeReference(typeName));
280-
281-
//noinspection unchecked
282-
Class<? extends Enum> enumClass = (Class<? extends Enum>) aClass;
283-
GraphQLEnumType.Builder builder = newEnum();
284-
builder.name(typeName);
285-
286-
GraphQLDescription description = aClass.getAnnotation(GraphQLDescription.class);
287-
if (description != null) {
288-
builder.description(description.value());
289-
}
290-
291-
List<Enum> constants = Arrays.asList(enumClass.getEnumConstants());
292-
293-
Arrays.stream(enumClass.getEnumConstants()).map(Enum::name).forEachOrdered(n -> {
294-
try {
295-
Field field = aClass.getField(n);
296-
GraphQLName fieldName = field.getAnnotation(GraphQLName.class);
297-
GraphQLDescription fieldDescription = field.getAnnotation(GraphQLDescription.class);
298-
Enum constant = constants.stream().filter(c -> c.name().contentEquals(n)).findFirst().get();
299-
String name_ = fieldName == null ? n : fieldName.value();
300-
builder.value(name_, constant, fieldDescription == null ? name_ : fieldDescription.value());
301-
} catch (NoSuchFieldException ignore) {
302-
}
303-
});
304-
305-
final GraphQLEnumType type = builder.build();
306-
types.put(typeName, type);
307-
//noinspection SuspiciousMethodCalls
308-
processing.remove(type);
309-
return type;
310-
}
311-
}
312-
}
313-
314249
private class ObjectFunction implements TypeFunction {
315-
316-
private final Map<String, GraphQLTypeReference> processing = new ConcurrentHashMap<>();
317-
private final Map<String, GraphQLType> types = new ConcurrentHashMap<>();
318-
319250
@Override
320251
public String getTypeName(Class<?> aClass, AnnotatedType annotatedType) {
321252
GraphQLName name = aClass.getAnnotation(GraphQLName.class);
@@ -329,22 +260,7 @@ public boolean canBuildType(Class<?> aClass, AnnotatedType annotatedType) {
329260

330261
@Override
331262
public GraphQLType buildType(String typeName, Class<?> aClass, AnnotatedType annotatedType) {
332-
if (types.containsKey(typeName)) {
333-
return types.get(typeName);
334-
} else if (processing.containsKey(typeName)) {
335-
return processing.getOrDefault(typeName, new GraphQLTypeReference(typeName));
336-
} else {
337-
processing.put(typeName, new GraphQLTypeReference(typeName));
338-
GraphQLType type;
339-
if (aClass.isInterface()) {
340-
type = annotationsProcessor.getInterface(aClass);
341-
} else {
342-
type = annotationsProcessor.getObjectOrRef(aClass);
343-
}
344-
types.put(typeName, type);
345-
processing.remove(typeName);
346-
return type;
347-
}
263+
return annotationsProcessor.getOutputTypeOrRef(aClass);
348264
}
349265
}
350266

@@ -362,8 +278,6 @@ public DefaultTypeFunction() {
362278
typeFunctions.add(new IterableFunction());
363279
typeFunctions.add(new StreamFunction());
364280

365-
typeFunctions.add(new EnumFunction());
366-
367281
typeFunctions.add(new OptionalFunction());
368282

369283
typeFunctions.add(new ObjectFunction());

src/main/java/graphql/annotations/GraphQLAnnotations.java

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import static graphql.annotations.ReflectionKit.newInstance;
3434
import static graphql.annotations.util.NamingKit.toGraphqlName;
3535
import static graphql.schema.GraphQLArgument.newArgument;
36+
import static graphql.schema.GraphQLEnumType.newEnum;
3637
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
3738
import static graphql.schema.GraphQLInputObjectField.newInputObjectField;
3839
import static graphql.schema.GraphQLInterfaceType.newInterface;
@@ -50,7 +51,7 @@ public class GraphQLAnnotations implements GraphQLAnnotationsProcessor {
5051

5152
private static final List<Class> TYPES_FOR_CONNECTION = Arrays.asList(GraphQLObjectType.class, GraphQLInterfaceType.class, GraphQLUnionType.class, GraphQLTypeReference.class);
5253

53-
private Map<String, graphql.schema.GraphQLType> typeRegistry = new HashMap<>();
54+
private Map<String, graphql.schema.GraphQLOutputType> typeRegistry = new HashMap<>();
5455
private Map<Class<?>, Set<Class<?>>> extensionsTypeRegistry = new HashMap<>();
5556
private final Stack<String> processing = new Stack<>();
5657
private Relay relay = new Relay();
@@ -75,21 +76,8 @@ public void setRelay(Relay relay) {
7576
}
7677

7778
@Override
78-
public graphql.schema.GraphQLType getInterface(Class<?> iface) throws GraphQLAnnotationsException {
79-
String typeName = getTypeName(iface);
80-
graphql.schema.GraphQLType type = typeRegistry.get(typeName);
81-
if (type != null) { // type already exists, do not build a new new one
82-
return type;
83-
}
84-
if (iface.getAnnotation(GraphQLUnion.class) != null) {
85-
type = getUnionBuilder(iface).build();
86-
} else if (!iface.isAnnotationPresent(GraphQLTypeResolver.class)) {
87-
type = getObject(iface);
88-
} else {
89-
type = getIfaceBuilder(iface).build();
90-
}
91-
typeRegistry.put(typeName, type);
92-
return type;
79+
public graphql.schema.GraphQLOutputType getInterface(Class<?> iface) throws GraphQLAnnotationsException {
80+
return getOutputType(iface);
9381
}
9482

9583
public static graphql.schema.GraphQLType iface(Class<?> iface) throws GraphQLAnnotationsException {
@@ -252,26 +240,92 @@ private boolean parentalSearch(Field field) {
252240

253241
@Override
254242
public GraphQLObjectType getObject(Class<?> object) throws GraphQLAnnotationsException {
243+
GraphQLOutputType type = getOutputType(object);
244+
if (type instanceof GraphQLObjectType) {
245+
return (GraphQLObjectType) type;
246+
} else {
247+
throw new IllegalArgumentException("Object resolve to a "+type.getClass().getSimpleName());
248+
}
249+
}
250+
251+
@Override
252+
public GraphQLOutputType getOutputType(Class<?> object) throws GraphQLAnnotationsException {
255253
// because the TypeFunction can call back to this processor and
256254
// Java classes can be circular, we need to protect against
257255
// building the same type twice because graphql-java 3.x requires
258256
// all type instances to be unique singletons
259257
String typeName = getTypeName(object);
260-
processing.push(typeName);
261258

262-
GraphQLObjectType.Builder builder = getObjectBuilder(object);
259+
GraphQLOutputType type = typeRegistry.get(typeName);
260+
if (type != null) { // type already exists, do not build a new new one
261+
return type;
262+
}
263263

264+
processing.push(typeName);
265+
if (object.getAnnotation(GraphQLUnion.class) != null) {
266+
type = getUnionBuilder(object).build();
267+
} else if (object.isAnnotationPresent(GraphQLTypeResolver.class)) {
268+
type = getIfaceBuilder(object).build();
269+
} else if (Enum.class.isAssignableFrom(object)) {
270+
type = getEnumBuilder(object).build();
271+
} else {
272+
type = new GraphQLObjectTypeWrapper(object, getObjectBuilder(object).build());
273+
}
274+
275+
typeRegistry.put(typeName, type);
264276
processing.pop();
265-
return new GraphQLObjectTypeWrapper(object, builder.build());
277+
278+
return type;
279+
}
280+
281+
public static GraphQLOutputType outputType(Class<?> object) {
282+
return getInstance().getOutputType(object);
283+
}
284+
285+
public GraphQLEnumType.Builder getEnumBuilder(Class<?> aClass) {
286+
String typeName = getTypeName(aClass);
287+
//noinspection unchecked
288+
Class<? extends Enum> enumClass = (Class<? extends Enum>) aClass;
289+
GraphQLEnumType.Builder builder = newEnum();
290+
builder.name(typeName);
291+
292+
GraphQLDescription description = aClass.getAnnotation(GraphQLDescription.class);
293+
if (description != null) {
294+
builder.description(description.value());
295+
}
296+
297+
List<Enum> constants = Arrays.asList(enumClass.getEnumConstants());
298+
299+
Arrays.stream(enumClass.getEnumConstants()).map(Enum::name).forEachOrdered(n -> {
300+
try {
301+
Field field = aClass.getField(n);
302+
GraphQLName fieldName = field.getAnnotation(GraphQLName.class);
303+
GraphQLDescription fieldDescription = field.getAnnotation(GraphQLDescription.class);
304+
Enum constant = constants.stream().filter(c -> c.name().contentEquals(n)).findFirst().get();
305+
String name_ = fieldName == null ? n : fieldName.value();
306+
builder.value(name_, constant, fieldDescription == null ? name_ : fieldDescription.value());
307+
} catch (NoSuchFieldException ignore) {
308+
}
309+
});
310+
return builder;
311+
}
312+
313+
public static GraphQLEnumType.Builder enumBuilder(Class<?> object) throws GraphQLAnnotationsException {
314+
return getInstance().getEnumBuilder(object);
266315
}
267316

268-
@Override
269317
public GraphQLOutputType getObjectOrRef(Class<?> object) throws GraphQLAnnotationsException {
318+
return getOutputTypeOrRef(object);
319+
}
320+
321+
@Override
322+
public GraphQLOutputType getOutputTypeOrRef(Class<?> object) throws GraphQLAnnotationsException {
270323
String typeName = getTypeName(object);
271324
if (processing.contains(typeName)) {
272325
return new GraphQLTypeReference(typeName);
273326
}
274-
return getObject(object);
327+
328+
return getOutputType(object);
275329
}
276330

277331
public static GraphQLObjectType object(Class<?> object) throws GraphQLAnnotationsException {
@@ -726,7 +780,7 @@ public static void register(TypeFunction typeFunction) {
726780
getInstance().registerType(typeFunction);
727781
}
728782

729-
public Map<String, graphql.schema.GraphQLType> getTypeRegistry() {
783+
public Map<String, graphql.schema.GraphQLOutputType> getTypeRegistry() {
730784
return typeRegistry;
731785
}
732786

src/main/java/graphql/annotations/GraphQLAnnotationsProcessor.java

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,11 @@
1414
*/
1515
package graphql.annotations;
1616

17-
import graphql.schema.GraphQLInputObjectType;
18-
import graphql.schema.GraphQLInterfaceType;
19-
import graphql.schema.GraphQLObjectType;
20-
import graphql.schema.GraphQLOutputType;
21-
import graphql.schema.GraphQLUnionType;
17+
import graphql.schema.*;
2218

2319
public interface GraphQLAnnotationsProcessor {
2420
/**
25-
* This will examine the class and if its annotated with {@link GraphQLUnion} it will return
26-
* a {@link GraphQLUnionType.Builder}, if its annotated with {@link GraphQLTypeResolver} it will return
27-
* a {@link GraphQLObjectType} otherwise it will return a {@link GraphQLInterfaceType.Builder}.
28-
*
29-
* @param iface interface to examine
30-
*
31-
* @return a GraphQLType that represents that interface
32-
*
33-
* @throws GraphQLAnnotationsException if the interface cannot be examined
34-
* @throws IllegalArgumentException if <code>iface</code> is not an interface
21+
* @deprecated See {@link #getOutputType(Class)}
3522
*/
3623
graphql.schema.GraphQLType getInterface(Class<?> iface) throws GraphQLAnnotationsException;
3724

@@ -60,28 +47,53 @@ public interface GraphQLAnnotationsProcessor {
6047
GraphQLInterfaceType.Builder getIfaceBuilder(Class<?> iface) throws GraphQLAnnotationsException, IllegalArgumentException;
6148

6249
/**
63-
* This will examine the object class and return a {@link GraphQLObjectType} representation
50+
* This will examine the object class and return a {@link GraphQLEnumType.Builder} ready for further definition
6451
*
6552
* @param object the object class to examine
6653
*
67-
* @return a {@link GraphQLObjectType} that represents that object class
54+
* @return a {@link GraphQLEnumType.Builder} that represents that object class
6855
*
6956
* @throws GraphQLAnnotationsException if the object class cannot be examined
7057
*/
58+
GraphQLEnumType.Builder getEnumBuilder(Class<?> object) throws GraphQLAnnotationsException;
59+
60+
/**
61+
* @deprecated See {@link #getOutputType(Class)}
62+
*/
7163
GraphQLObjectType getObject(Class<?> object) throws GraphQLAnnotationsException;
7264

7365
/**
74-
* This will examine the object class and return a {@link GraphQLOutputType} representation
75-
* which may be a {@link GraphQLObjectType} or a {@link graphql.schema.GraphQLTypeReference}
66+
* This will examine the object and will return a {@link GraphQLOutputType} based on the class type and annotations.
67+
* - If its annotated with {@link GraphQLUnion} it will return a {@link GraphQLUnionType}
68+
* - If its annotated with {@link GraphQLTypeResolver} it will return a {@link GraphQLInterfaceType}
69+
* - It it's an Enum it will return a {@link GraphQLEnumType},
70+
* otherwise it will return a {@link GraphQLObjectType}.
7671
*
7772
* @param object the object class to examine
7873
*
7974
* @return a {@link GraphQLOutputType} that represents that object class
8075
*
8176
* @throws GraphQLAnnotationsException if the object class cannot be examined
8277
*/
78+
GraphQLOutputType getOutputType(Class<?> object) throws GraphQLAnnotationsException;
79+
80+
/**
81+
* @deprecated See {@link #getOutputTypeOrRef(Class)}
82+
*/
8383
GraphQLOutputType getObjectOrRef(Class<?> object) throws GraphQLAnnotationsException;
8484

85+
/**
86+
* This will examine the object class and return a {@link GraphQLOutputType} representation
87+
* which may be a {@link GraphQLOutputType} or a {@link graphql.schema.GraphQLTypeReference}
88+
*
89+
* @param object the object class to examine
90+
*
91+
* @return a {@link GraphQLOutputType} that represents that object class
92+
*
93+
* @throws GraphQLAnnotationsException if the object class cannot be examined
94+
*/
95+
GraphQLOutputType getOutputTypeOrRef(Class<?> object) throws GraphQLAnnotationsException;
96+
8597
/**
8698
* This will examine the object class and return a {@link GraphQLObjectType.Builder} ready for further definition
8799
*

src/test/java/graphql/annotations/DefaultTypeFunctionTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import graphql.schema.*;
1818
import graphql.schema.GraphQLType;
19+
import org.testng.annotations.BeforeMethod;
20+
import org.testng.annotations.BeforeTest;
1921
import org.testng.annotations.Test;
2022

2123
import java.lang.reflect.Field;
@@ -30,6 +32,11 @@
3032

3133
public class DefaultTypeFunctionTest {
3234

35+
@BeforeMethod
36+
public void init() {
37+
GraphQLAnnotations.getInstance().getTypeRegistry().clear();
38+
}
39+
3340
private enum A {
3441
@GraphQLName("someA") @GraphQLDescription("a") A, B
3542
}

0 commit comments

Comments
 (0)