Skip to content

Commit 2fd32ec

Browse files
committed
better way to build fields - with proper builders
1 parent ecc6729 commit 2fd32ec

File tree

13 files changed

+534
-224
lines changed

13 files changed

+534
-224
lines changed

src/main/java/graphql/annotations/dataFetchers/ExtensionDataFetcherWrapper.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ public ExtensionDataFetcherWrapper(Class declaringClass, DataFetcher<T> dataFetc
3232
this.dataFetcher = dataFetcher;
3333
}
3434

35-
@SuppressWarnings("unchecked")
3635
@Override
3736
public T get(DataFetchingEnvironment environment) {
3837
Object source = environment.getSource();

src/main/java/graphql/annotations/processor/retrievers/GraphQLFieldRetriever.java

Lines changed: 78 additions & 223 deletions
Large diffs are not rendered by default.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package graphql.annotations.processor.retrievers.fieldBuilders;
2+
3+
public interface Builder<T> {
4+
public T build();
5+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package graphql.annotations.processor.retrievers.fieldBuilders.field;
2+
3+
import graphql.annotations.annotationTypes.GraphQLConnection;
4+
import graphql.annotations.annotationTypes.GraphQLDataFetcher;
5+
import graphql.annotations.dataFetchers.ExtensionDataFetcherWrapper;
6+
import graphql.annotations.dataFetchers.MethodDataFetcher;
7+
import graphql.annotations.dataFetchers.connection.ConnectionDataFetcher;
8+
import graphql.annotations.processor.ProcessingElementsContainer;
9+
import graphql.annotations.processor.retrievers.fieldBuilders.Builder;
10+
import graphql.annotations.processor.typeFunctions.TypeFunction;
11+
import graphql.annotations.processor.util.DataFetcherConstructor;
12+
import graphql.schema.*;
13+
14+
import java.lang.reflect.Field;
15+
import java.lang.reflect.Method;
16+
17+
import static graphql.Scalars.GraphQLBoolean;
18+
import static java.util.Objects.nonNull;
19+
20+
public class FieldDataFetcherBuilder implements Builder<DataFetcher> {
21+
private Field field;
22+
private DataFetcherConstructor dataFetcherConstructor;
23+
private GraphQLOutputType outputType;
24+
private TypeFunction typeFunction;
25+
private ProcessingElementsContainer container;
26+
private boolean isConnection;
27+
28+
public FieldDataFetcherBuilder(Field field, DataFetcherConstructor dataFetcherConstructor, GraphQLOutputType outputType, TypeFunction typeFunction, ProcessingElementsContainer container, boolean isConnection) {
29+
this.field = field;
30+
this.dataFetcherConstructor = dataFetcherConstructor;
31+
this.outputType = outputType;
32+
this.typeFunction = typeFunction;
33+
this.container = container;
34+
this.isConnection = isConnection;
35+
}
36+
37+
@Override
38+
public DataFetcher build() {
39+
GraphQLDataFetcher dataFetcher = field.getAnnotation(GraphQLDataFetcher.class);
40+
DataFetcher actualDataFetcher = null;
41+
if (nonNull(dataFetcher)) {
42+
actualDataFetcher = dataFetcherConstructor.constructDataFetcher(field.getName(), dataFetcher);
43+
}
44+
45+
if (actualDataFetcher == null) {
46+
actualDataFetcher = handleNullCase(actualDataFetcher);
47+
}
48+
49+
50+
if (isConnection) {
51+
actualDataFetcher = new ConnectionDataFetcher(field.getAnnotation(GraphQLConnection.class).connection(), actualDataFetcher);
52+
}
53+
return actualDataFetcher;
54+
}
55+
56+
private DataFetcher handleNullCase(DataFetcher actualDataFetcher) {
57+
58+
// if there is getter for fields type, use propertyDataFetcher, otherwise use method directly
59+
if (isaBoolean()) {
60+
actualDataFetcher = getBooleanDataFetcher(actualDataFetcher);
61+
} else if (checkIfPrefixGetterExists(field.getDeclaringClass(), "get", field.getName())) {
62+
actualDataFetcher = wrapExtension(new PropertyDataFetcher(field.getName()));
63+
} else{
64+
actualDataFetcher = getDataFetcherWithFluentGetter(actualDataFetcher);
65+
}
66+
67+
if (actualDataFetcher == null) {
68+
actualDataFetcher = wrapExtension(new FieldDataFetcher(field.getName()));
69+
}
70+
return actualDataFetcher;
71+
}
72+
73+
private DataFetcher getDataFetcherWithFluentGetter(DataFetcher actualDataFetcher) {
74+
StringBuilder fluentBuffer = new StringBuilder(field.getName());
75+
fluentBuffer.setCharAt(0, Character.toLowerCase(fluentBuffer.charAt(0)));
76+
String fluentGetter = fluentBuffer.toString();
77+
78+
boolean hasFluentGetter = false;
79+
Method fluentMethod = null;
80+
try {
81+
fluentMethod = field.getDeclaringClass().getMethod(fluentGetter);
82+
hasFluentGetter = true;
83+
} catch (NoSuchMethodException x) {
84+
}
85+
86+
if (hasFluentGetter) {
87+
actualDataFetcher = new MethodDataFetcher(fluentMethod, typeFunction, container);
88+
}
89+
return actualDataFetcher;
90+
}
91+
92+
private ExtensionDataFetcherWrapper wrapExtension(DataFetcher dataFetcher) {
93+
return new ExtensionDataFetcherWrapper(field.getDeclaringClass(), dataFetcher);
94+
}
95+
96+
private DataFetcher getBooleanDataFetcher(DataFetcher actualDataFetcher) {
97+
if (checkIfPrefixGetterExists(field.getDeclaringClass(), "is", field.getName()) ||
98+
checkIfPrefixGetterExists(field.getDeclaringClass(), "get", field.getName())) {
99+
actualDataFetcher = wrapExtension(new PropertyDataFetcher(field.getName()));
100+
}
101+
return actualDataFetcher;
102+
}
103+
104+
private boolean isaBoolean() {
105+
return outputType == GraphQLBoolean || (outputType instanceof GraphQLNonNull && ((GraphQLNonNull) outputType).getWrappedType() == GraphQLBoolean);
106+
}
107+
108+
// check if there is getter for field, basic functionality taken from PropertyDataFetcher
109+
private boolean checkIfPrefixGetterExists(Class c, String prefix, String propertyName) {
110+
String getterName = prefix + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
111+
try {
112+
Method method = c.getMethod(getterName);
113+
} catch (NoSuchMethodException x) {
114+
return false;
115+
}
116+
117+
return true;
118+
}
119+
120+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package graphql.annotations.processor.retrievers.fieldBuilders.field;
2+
3+
import graphql.annotations.annotationTypes.GraphQLName;
4+
import graphql.annotations.processor.retrievers.fieldBuilders.Builder;
5+
6+
import java.lang.reflect.Field;
7+
8+
import static graphql.annotations.processor.util.NamingKit.toGraphqlName;
9+
10+
public class FieldNameBuilder implements Builder<String> {
11+
private Field field;
12+
13+
public FieldNameBuilder(Field field) {
14+
this.field = field;
15+
}
16+
17+
@Override
18+
public String build() {
19+
GraphQLName name = field.getAnnotation(GraphQLName.class);
20+
return toGraphqlName(name == null ? field.getName() : name.value());
21+
}
22+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package graphql.annotations.processor.retrievers.fieldBuilders.method;
2+
3+
import graphql.annotations.annotationTypes.GraphQLDefaultValue;
4+
import graphql.annotations.annotationTypes.GraphQLDescription;
5+
import graphql.annotations.annotationTypes.GraphQLName;
6+
import graphql.annotations.processor.ProcessingElementsContainer;
7+
import graphql.annotations.processor.exceptions.GraphQLAnnotationsException;
8+
import graphql.annotations.processor.retrievers.GraphQLInputObjectRetriever;
9+
import graphql.annotations.processor.retrievers.fieldBuilders.Builder;
10+
import graphql.annotations.processor.typeFunctions.TypeFunction;
11+
import graphql.schema.*;
12+
13+
import java.lang.reflect.Method;
14+
import java.lang.reflect.Parameter;
15+
import java.util.Arrays;
16+
import java.util.List;
17+
import java.util.stream.Collectors;
18+
19+
import static graphql.annotations.processor.util.NamingKit.toGraphqlName;
20+
import static graphql.annotations.processor.util.ReflectionKit.newInstance;
21+
import static graphql.schema.GraphQLArgument.newArgument;
22+
import static graphql.schema.GraphQLInputObjectField.newInputObjectField;
23+
24+
public class ArgumentBuilder implements Builder<List<GraphQLArgument>> {
25+
private static final String DEFAULT_INPUT_PREFIX = "Input";
26+
27+
private Method method;
28+
private TypeFunction typeFunction;
29+
private GraphQLInputObjectRetriever graphQLInputObjectRetriever;
30+
private GraphQLFieldDefinition.Builder builder;
31+
private ProcessingElementsContainer container;
32+
private GraphQLOutputType outputType;
33+
34+
public ArgumentBuilder(Method method, TypeFunction typeFunction, GraphQLInputObjectRetriever graphQLInputObjectRetriever, GraphQLFieldDefinition.Builder builder, ProcessingElementsContainer container, GraphQLOutputType outputType) {
35+
this.method = method;
36+
this.typeFunction = typeFunction;
37+
this.graphQLInputObjectRetriever = graphQLInputObjectRetriever;
38+
this.builder = builder;
39+
this.container = container;
40+
this.outputType = outputType;
41+
}
42+
43+
@Override
44+
public List<GraphQLArgument> build() {
45+
TypeFunction finalTypeFunction = typeFunction;
46+
List<GraphQLArgument> args = Arrays.asList(method.getParameters()).stream().
47+
filter(p -> !DataFetchingEnvironment.class.isAssignableFrom(p.getType())).
48+
map(parameter -> {
49+
Class<?> t = parameter.getType();
50+
graphql.schema.GraphQLInputType graphQLType = graphQLInputObjectRetriever.getInputObject(finalTypeFunction.buildType(t, parameter.getAnnotatedType(), container), DEFAULT_INPUT_PREFIX, container.getTypeRegistry());
51+
return getArgument(parameter, graphQLType);
52+
}).collect(Collectors.toList());
53+
54+
return args;
55+
}
56+
57+
private GraphQLArgument getArgument(Parameter parameter, graphql.schema.GraphQLInputType t) throws
58+
GraphQLAnnotationsException {
59+
GraphQLArgument.Builder builder = newArgument().type(t);
60+
GraphQLDescription description = parameter.getAnnotation(GraphQLDescription.class);
61+
if (description != null) {
62+
builder.description(description.value());
63+
}
64+
GraphQLDefaultValue defaultValue = parameter.getAnnotation(GraphQLDefaultValue.class);
65+
if (defaultValue != null) {
66+
builder.defaultValue(newInstance(defaultValue.value()).get());
67+
}
68+
GraphQLName name = parameter.getAnnotation(GraphQLName.class);
69+
if (name != null) {
70+
builder.name(toGraphqlName(name.value()));
71+
} else {
72+
builder.name(toGraphqlName(parameter.getName()));
73+
}
74+
return builder.build();
75+
}
76+
77+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package graphql.annotations.processor.retrievers.fieldBuilders.method;
2+
3+
import graphql.annotations.annotationTypes.GraphQLBatched;
4+
import graphql.annotations.annotationTypes.GraphQLConnection;
5+
import graphql.annotations.annotationTypes.GraphQLDataFetcher;
6+
import graphql.annotations.annotationTypes.GraphQLRelayMutation;
7+
import graphql.annotations.dataFetchers.BatchedMethodDataFetcher;
8+
import graphql.annotations.dataFetchers.MethodDataFetcher;
9+
import graphql.annotations.dataFetchers.RelayMutationMethodDataFetcher;
10+
import graphql.annotations.dataFetchers.connection.ConnectionDataFetcher;
11+
import graphql.annotations.processor.ProcessingElementsContainer;
12+
import graphql.annotations.processor.retrievers.fieldBuilders.Builder;
13+
import graphql.annotations.processor.typeFunctions.TypeFunction;
14+
import graphql.annotations.processor.util.DataFetcherConstructor;
15+
import graphql.schema.DataFetcher;
16+
import graphql.schema.GraphQLArgument;
17+
import graphql.schema.GraphQLFieldDefinition;
18+
import graphql.schema.GraphQLOutputType;
19+
20+
import java.lang.reflect.Method;
21+
import java.util.List;
22+
23+
public class DataFetcherBuilder implements Builder<DataFetcher> {
24+
private Method method;
25+
private GraphQLOutputType outputType;
26+
private TypeFunction typeFunction;
27+
private ProcessingElementsContainer container;
28+
private GraphQLFieldDefinition relayFieldDefinition;
29+
private List<GraphQLArgument> args;
30+
private DataFetcherConstructor dataFetcherConstructor;
31+
private boolean isConnection;
32+
33+
public DataFetcherBuilder(Method method, GraphQLOutputType outputType, TypeFunction typeFunction,
34+
ProcessingElementsContainer container, GraphQLFieldDefinition relayFieldDefinition,
35+
List<GraphQLArgument> args, DataFetcherConstructor dataFetcherConstructor, boolean isConnection) {
36+
this.method = method;
37+
this.outputType = outputType;
38+
this.typeFunction = typeFunction;
39+
this.container = container;
40+
this.relayFieldDefinition = relayFieldDefinition;
41+
this.args = args;
42+
this.dataFetcherConstructor = dataFetcherConstructor;
43+
this.isConnection = isConnection;
44+
}
45+
46+
@Override
47+
public DataFetcher build() {
48+
GraphQLDataFetcher dataFetcher = method.getAnnotation(GraphQLDataFetcher.class);
49+
DataFetcher actualDataFetcher;
50+
if (dataFetcher == null && method.getAnnotation(GraphQLBatched.class) != null) {
51+
actualDataFetcher = new BatchedMethodDataFetcher(method, typeFunction, container);
52+
} else if (dataFetcher == null) {
53+
actualDataFetcher = new MethodDataFetcher(method, typeFunction, container);
54+
} else {
55+
actualDataFetcher = dataFetcherConstructor.constructDataFetcher(method.getName(), dataFetcher);
56+
}
57+
58+
if (method.isAnnotationPresent(GraphQLRelayMutation.class) && relayFieldDefinition != null) {
59+
actualDataFetcher = new RelayMutationMethodDataFetcher(method, args, relayFieldDefinition.getArgument("input").getType(), relayFieldDefinition.getType());
60+
}
61+
62+
if (isConnection){
63+
actualDataFetcher = new ConnectionDataFetcher(method.getAnnotation(GraphQLConnection.class).connection(), actualDataFetcher);
64+
}
65+
return actualDataFetcher;
66+
}
67+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package graphql.annotations.processor.retrievers.fieldBuilders.method;
2+
3+
import graphql.annotations.annotationTypes.GraphQLDeprecate;
4+
import graphql.annotations.processor.retrievers.fieldBuilders.Builder;
5+
6+
import java.lang.reflect.AccessibleObject;
7+
8+
public class DeprecateBuilder implements Builder<String> {
9+
private AccessibleObject object;
10+
11+
public DeprecateBuilder(AccessibleObject object) {
12+
this.object = object;
13+
}
14+
15+
@Override
16+
public String build() {
17+
GraphQLDeprecate deprecate = object.getAnnotation(GraphQLDeprecate.class);
18+
if (deprecate != null) {
19+
return deprecate.value();
20+
}
21+
if (object.getAnnotation(Deprecated.class) != null) {
22+
return "Deprecated";
23+
}
24+
return null;
25+
}
26+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package graphql.annotations.processor.retrievers.fieldBuilders.method;
2+
3+
import graphql.annotations.annotationTypes.GraphQLDescription;
4+
import graphql.annotations.processor.retrievers.fieldBuilders.Builder;
5+
6+
import java.lang.reflect.AccessibleObject;
7+
8+
public class DescriptionBuilder implements Builder<String> {
9+
private AccessibleObject object;
10+
11+
public DescriptionBuilder(AccessibleObject method) {
12+
this.object = method;
13+
}
14+
15+
@Override
16+
public String build() {
17+
GraphQLDescription description = object.getAnnotation(GraphQLDescription.class);
18+
return description==null? null : description.value();
19+
}
20+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package graphql.annotations.processor.retrievers.fieldBuilders.method;
2+
3+
import graphql.annotations.annotationTypes.GraphQLName;
4+
import graphql.annotations.processor.retrievers.fieldBuilders.Builder;
5+
6+
import java.lang.reflect.Method;
7+
8+
import static graphql.annotations.processor.util.NamingKit.toGraphqlName;
9+
10+
public class NameBuilder implements Builder<String> {
11+
private Method method;
12+
13+
public NameBuilder(Method method) {
14+
this.method = method;
15+
}
16+
17+
@Override
18+
public String build() {
19+
String name = method.getName().replaceFirst("^(is|get|set)(.+)", "$2");
20+
name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
21+
GraphQLName nameAnn = method.getAnnotation(GraphQLName.class);
22+
return toGraphqlName(nameAnn == null ? name : nameAnn.value());
23+
}
24+
}

0 commit comments

Comments
 (0)