Skip to content

Commit db2a168

Browse files
authored
Merge branch 'master' into custom-paging
2 parents 454e47f + ea31cb2 commit db2a168

File tree

12 files changed

+341
-39
lines changed

12 files changed

+341
-39
lines changed

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,45 @@ GraphQLInterfaceType object = GraphQLAnnotations.iface(SomeInterface.class);
5454
An instance of the type resolver will be created from the specified class. If a `getInstance` method is present on the
5555
class, it will be used instead of the default constructor.
5656

57+
## Defining Unions
58+
59+
To have a union, you must annotate an interface with `@GraphQLUnion`. In the annotation, you must declare all the
60+
possible types of the union, and a type resolver.
61+
If no type resolver is specified, `UnionTypeResovler` is used. It follows this algorithm:
62+
The resolver assumes the the DB entity's name is the same as the API entity's name.
63+
If so, it takes the result from the dataFetcher and decides to which
64+
API entity it should be mapped (according to the name).
65+
Example: If you have a `Pet` union type, and the dataFetcher returns `Dog`, the typeResolver
66+
will check for each API entity if its name is equal to `Dog`, and returns if it finds something
67+
68+
```java
69+
@GraphQLUnion(possibleTypes={Dog.class, Cat.class})
70+
public interface Pet {}
71+
```
72+
and an example with custom `TypeResovler`:
73+
```java
74+
@GraphQLUnion(possibleTypes={DogApi.class, Cat.class}, typeResolver = PetTypeResolver.class)
75+
public interface Pet {}
76+
77+
78+
public class PetTypeResolver implements TypeResolver {
79+
@Override
80+
GraphQLObjectType getType(TypeResolutionEnvironment env) {
81+
Object obj = env.getObject();
82+
if(obj instanceof DogDB) {
83+
return (GraphQLObjectType) env.getSchema().getType("DogApi");
84+
}
85+
else {
86+
return (GraphQLObjectType) env.getSchema().getType("Cat");
87+
}
88+
89+
}
90+
}
91+
```
92+
NOTE: you can have (but not mandatory) a type resolver with constructor that has `Class<?>[]` as the first parameter and
93+
`ProcessingElementsContainer` as the second. the `Class<?>[]` parameter contains the possibleTypes class
94+
and `ProcessingElementsContainer` has all sorts of utils (you can check `UnionTypeResolver` to see how we use it there)
95+
5796
## Fields
5897

5998
In addition to specifying a field over a Java class field, a field can be defined over a method:

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ gradle.projectsEvaluated {
6969

7070
dependencies {
7171
compile 'javax.validation:validation-api:1.1.0.Final'
72-
compile 'com.graphql-java:graphql-java:7.0'
72+
compile 'com.graphql-java:graphql-java:8.0'
7373

7474
// OSGi
7575
compileOnly 'org.osgi:org.osgi.core:6.0.0'
@@ -78,7 +78,7 @@ dependencies {
7878
compileOnly 'biz.aQute.bnd:biz.aQute.bndlib:3.2.0'
7979

8080
testCompile 'org.testng:testng:6.9.10'
81-
testCompile 'org.hamcrest:hamcrest-core:1.3'
81+
testCompile 'org.hamcrest:hamcrest-all:1.3'
8282
}
8383

8484
test.useTestNG()

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ org.gradle.jvmargs=-Dfile.encoding=UTF-8
77

88
bintray.user=DUMMY_USER
99
bintray.key=DUMMY_KEY
10+
version = 5.2

src/main/java/graphql/annotations/GraphQLFieldDefinitionWrapper.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
import graphql.schema.DataFetcherFactories;
1919
import graphql.schema.GraphQLFieldDefinition;
2020

21-
public class GraphQLFieldDefinitionWrapper extends GraphQLFieldDefinition {
21+
public class GraphQLFieldDefinitionWrapper extends GraphQLFieldDefinition {
2222

2323
public GraphQLFieldDefinitionWrapper(GraphQLFieldDefinition fieldDefinition) {
2424
super(fieldDefinition.getName(), fieldDefinition.getDescription(), fieldDefinition.getType(),
25-
DataFetcherFactories.useDataFetcher((DataFetcher<?>)fieldDefinition.getDataFetcher()), fieldDefinition.getArguments(), fieldDefinition.getDeprecationReason(),
25+
DataFetcherFactories.useDataFetcher((DataFetcher<?>) fieldDefinition.getDataFetcher()),
26+
fieldDefinition.getArguments(), fieldDefinition.getDeprecationReason(),
27+
fieldDefinition.getDirectives(),
2628
fieldDefinition.getDefinition());
2729
}
2830

src/main/java/graphql/annotations/annotationTypes/GraphQLUnion.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
*/
1515
package graphql.annotations.annotationTypes;
1616

17+
import graphql.annotations.typeResolvers.UnionTypeResolver;
18+
import graphql.schema.TypeResolver;
19+
1720
import java.lang.annotation.ElementType;
1821
import java.lang.annotation.Retention;
1922
import java.lang.annotation.RetentionPolicy;
@@ -23,4 +26,6 @@
2326
@Retention(RetentionPolicy.RUNTIME)
2427
public @interface GraphQLUnion {
2528
Class<?>[] possibleTypes();
29+
30+
Class<? extends TypeResolver> typeResolver() default UnionTypeResolver.class;
2631
}

src/main/java/graphql/annotations/processor/typeBuilders/UnionBuilder.java

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,20 @@
1919
import graphql.annotations.annotationTypes.GraphQLType;
2020
import graphql.annotations.annotationTypes.GraphQLUnion;
2121
import graphql.annotations.processor.ProcessingElementsContainer;
22-
import graphql.annotations.processor.util.ReflectionKit;
2322
import graphql.annotations.processor.exceptions.GraphQLAnnotationsException;
24-
import graphql.annotations.processor.typeFunctions.TypeFunction;
2523
import graphql.annotations.processor.retrievers.GraphQLObjectInfoRetriever;
24+
import graphql.annotations.processor.typeFunctions.TypeFunction;
2625
import graphql.annotations.typeResolvers.UnionTypeResolver;
2726
import graphql.schema.GraphQLObjectType;
28-
import graphql.schema.GraphQLUnionType;
27+
import graphql.schema.GraphQLUnionType.Builder;
28+
import graphql.schema.TypeResolver;
2929

30+
import java.lang.reflect.Constructor;
3031
import java.util.Arrays;
31-
import java.util.function.Function;
32+
import java.util.Optional;
3233

34+
import static graphql.annotations.processor.util.ReflectionKit.constructNewInstance;
35+
import static graphql.annotations.processor.util.ReflectionKit.newInstance;
3336
import static graphql.schema.GraphQLUnionType.newUnionType;
3437

3538
public class UnionBuilder {
@@ -41,18 +44,18 @@ public UnionBuilder(GraphQLObjectInfoRetriever graphQLObjectInfoRetriever) {
4144
}
4245

4346
/**
44-
* This will examine the class and return a {@link GraphQLUnionType.Builder} ready for further definition
47+
* This will examine the class and return a {@link Builder} ready for further definition
4548
* @param container a class that hold several members that are required in order to build schema
4649
* @param iface interface to examine
47-
* @return a {@link GraphQLUnionType.Builder}
50+
* @return a {@link Builder}
4851
* @throws GraphQLAnnotationsException if the class cannot be examined
4952
*/
5053

51-
public GraphQLUnionType.Builder getUnionBuilder(Class<?> iface, ProcessingElementsContainer container) throws GraphQLAnnotationsException, IllegalArgumentException {
54+
public Builder getUnionBuilder(Class<?> iface, ProcessingElementsContainer container) throws GraphQLAnnotationsException, IllegalArgumentException {
5255
if (!iface.isInterface()) {
5356
throw new IllegalArgumentException(iface + " is not an interface");
5457
}
55-
GraphQLUnionType.Builder builder = newUnionType();
58+
Builder builder = newUnionType();
5659

5760
GraphQLUnion unionAnnotation = iface.getAnnotation(GraphQLUnion.class);
5861
builder.name(graphQLObjectInfoRetriever.getTypeName(iface));
@@ -65,21 +68,41 @@ public GraphQLUnionType.Builder getUnionBuilder(Class<?> iface, ProcessingElemen
6568
TypeFunction typeFunction = container.getDefaultTypeFunction();
6669

6770
if (typeAnnotation != null) {
68-
typeFunction = ReflectionKit.newInstance(typeAnnotation.value());
71+
typeFunction = newInstance(typeAnnotation.value());
6972
}
7073

7174
TypeFunction finalTypeFunction = typeFunction;
72-
Arrays.asList(unionAnnotation.possibleTypes()).stream()
73-
.map(new Function<Class<?>, graphql.schema.GraphQLType>() {
74-
@Override
75-
public graphql.schema.GraphQLType apply(Class<?> aClass) {
76-
return finalTypeFunction.buildType(aClass, null, container);
77-
}
78-
})
75+
Arrays.stream(unionAnnotation.possibleTypes())
76+
.map(aClass -> finalTypeFunction.buildType(aClass, null, container))
7977
.map(v -> (GraphQLObjectType) v)
8078
.forEach(builder::possibleType);
8179

82-
builder.typeResolver(new UnionTypeResolver(unionAnnotation.possibleTypes(), container));
80+
TypeResolver typeResolver = getTypeResolver(container, unionAnnotation);
81+
82+
builder.typeResolver(typeResolver);
8383
return builder;
8484
}
85+
86+
private TypeResolver getTypeResolver(ProcessingElementsContainer container, GraphQLUnion unionAnnotation) {
87+
Optional<Constructor<?>> typeResolverConstructorOptional = Arrays.stream(unionAnnotation.typeResolver().getConstructors())
88+
.filter(constructor -> constructor.getParameterCount() == 0 || constructorHasPossibleTypesAndContainerAsParameters(container, constructor))
89+
.findFirst();
90+
91+
return typeResolverConstructorOptional
92+
.map(constructor -> {
93+
if(constructor.getParameterCount() == 0) {
94+
return (TypeResolver) constructNewInstance(constructor);
95+
}
96+
else {
97+
return (TypeResolver) constructNewInstance(constructor, unionAnnotation.possibleTypes(), container);
98+
}
99+
})
100+
.orElseGet(() -> new UnionTypeResolver(unionAnnotation.possibleTypes(), container));
101+
}
102+
103+
private boolean constructorHasPossibleTypesAndContainerAsParameters(ProcessingElementsContainer container, Constructor<?> constructor) {
104+
return constructor.getParameterCount() == 2 &&
105+
Class[].class.isAssignableFrom(constructor.getParameterTypes()[0]) &&
106+
container.getClass().isAssignableFrom(constructor.getParameterTypes()[1]);
107+
}
85108
}

src/main/java/graphql/annotations/processor/typeFunctions/FloatFunction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public String getTypeName(Class<?> aClass, AnnotatedType annotatedType) {
2929

3030
@Override
3131
public boolean canBuildType(Class<?> aClass, AnnotatedType annotatedType) {
32-
return aClass == Float.class || aClass == float.class || aClass == Double.class || aClass == double.class;
32+
return aClass == Double.class || aClass == double.class;
3333
}
3434

3535
@Override

src/main/java/graphql/annotations/strategies/EnhancedExecutionStrategy.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public class EnhancedExecutionStrategy extends AsyncSerialExecutionStrategy {
3131

3232
@Override
3333
protected CompletableFuture<ExecutionResult> resolveField(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
34-
GraphQLObjectType parentType = (GraphQLObjectType) parameters.typeInfo().getType();
35-
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, parameters.field().get(0));
34+
GraphQLObjectType parentType = (GraphQLObjectType) parameters.getTypeInfo().getType();
35+
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, parameters.getField().get(0));
3636
if (fieldDef == null) return null;
3737

3838
if (fieldDef.getName().contentEquals(CLIENT_MUTATION_ID)) {
@@ -52,11 +52,11 @@ protected CompletableFuture<ExecutionResult> resolveField(ExecutionContext execu
5252
clientMutationId = clientMutationIdVal.getValue();
5353
}
5454

55-
ExecutionTypeInfo fieldTypeInfo = ExecutionTypeInfo.newTypeInfo().type(fieldDef.getType()).parentInfo(parameters.typeInfo()).build();
55+
ExecutionTypeInfo fieldTypeInfo = ExecutionTypeInfo.newTypeInfo().type(fieldDef.getType()).parentInfo(parameters.getTypeInfo()).build();
5656
ExecutionStrategyParameters newParameters = ExecutionStrategyParameters.newParameters()
57-
.arguments(parameters.arguments())
58-
.fields(parameters.fields())
59-
.nonNullFieldValidator(parameters.nonNullFieldValidator())
57+
.arguments(parameters.getArguments())
58+
.fields(parameters.getFields())
59+
.nonNullFieldValidator(parameters.getNonNullFieldValidator())
6060
.typeInfo(fieldTypeInfo)
6161
.source(clientMutationId)
6262
.build();
@@ -70,8 +70,8 @@ protected CompletableFuture<ExecutionResult> resolveField(ExecutionContext execu
7070

7171
@Override
7272
protected CompletableFuture<ExecutionResult> completeValue(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException {
73-
graphql.schema.GraphQLType fieldType = parameters.typeInfo().getType();
74-
Object result = parameters.source();
73+
graphql.schema.GraphQLType fieldType = parameters.getTypeInfo().getType();
74+
Object result = parameters.getSource();
7575
if (result instanceof Enum && fieldType instanceof GraphQLEnumType) {
7676
Object value = ((GraphQLEnumType) fieldType).getCoercing().parseValue(((Enum) result).name());
7777
return super.completeValue(executionContext, withSource(parameters, value));
@@ -88,10 +88,10 @@ protected CompletableFuture<ExecutionResult> completeValue(ExecutionContext exec
8888
*/
8989
private ExecutionStrategyParameters withSource(ExecutionStrategyParameters parameters, Object source) {
9090
return ExecutionStrategyParameters.newParameters()
91-
.arguments(parameters.arguments())
92-
.fields(parameters.fields())
93-
.nonNullFieldValidator(parameters.nonNullFieldValidator())
94-
.typeInfo(parameters.typeInfo())
91+
.arguments(parameters.getArguments())
92+
.fields(parameters.getFields())
93+
.nonNullFieldValidator(parameters.getNonNullFieldValidator())
94+
.typeInfo(parameters.getTypeInfo())
9595
.source(source)
9696
.build();
9797
}

src/main/java/graphql/annotations/typeResolvers/UnionTypeResolver.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@
2323
import java.util.Arrays;
2424
import java.util.HashMap;
2525
import java.util.Map;
26+
import java.util.Map.Entry;
2627
import java.util.Optional;
2728

2829
public class UnionTypeResolver implements TypeResolver {
29-
private final Map<Class<?>, graphql.schema.GraphQLType> types = new HashMap<>();
30+
private final Map<Class<?>, GraphQLType> types = new HashMap<>();
3031

3132
public UnionTypeResolver(Class<?>[] classes, ProcessingElementsContainer container) {
3233
Arrays.stream(classes).
@@ -36,8 +37,10 @@ public UnionTypeResolver(Class<?>[] classes, ProcessingElementsContainer contain
3637
@Override
3738
public GraphQLObjectType getType(TypeResolutionEnvironment env) {
3839
Object object = env.getObject();
39-
Optional<Map.Entry<Class<?>, GraphQLType>> maybeType = types.entrySet().
40-
stream().filter(e -> e.getKey().isAssignableFrom(object.getClass())).findFirst();
40+
Optional<Entry<Class<?>, GraphQLType>> maybeType = types.entrySet().
41+
stream().filter(e -> object.getClass().getSimpleName()
42+
.equals(e.getKey().getSimpleName())).findFirst();
43+
4144
if (maybeType.isPresent()) {
4245
return (GraphQLObjectType) maybeType.get().getValue();
4346
} else {

src/test/java/graphql/annotations/GraphQLObjectTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public String getZ() {
125125
public void setZ(String z) {
126126
this.z = z;
127127
}
128+
128129
}
129130

130131
private static class TestDefaults {
@@ -825,6 +826,7 @@ public String getOn() {
825826
public String getOff() {
826827
return "off";
827828
}
829+
828830
}
829831

830832
@Test
@@ -836,4 +838,5 @@ public void inheritGraphQLField() {
836838
assertNull(object.getFieldDefinition("forcedOff"));
837839
}
838840

841+
839842
}

0 commit comments

Comments
 (0)