Skip to content

Commit d0895c4

Browse files
committed
Fixed a bug where MethodDataFetcher caused NullPointerException
1 parent e120188 commit d0895c4

File tree

3 files changed

+108
-9
lines changed

3 files changed

+108
-9
lines changed

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

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

17-
import graphql.annotations.processor.ProcessingElementsContainer;
1817
import graphql.annotations.annotationTypes.GraphQLInvokeDetached;
1918
import graphql.annotations.annotationTypes.GraphQLName;
19+
import graphql.annotations.processor.ProcessingElementsContainer;
2020
import graphql.annotations.processor.typeFunctions.TypeFunction;
2121
import graphql.schema.*;
22-
import graphql.schema.GraphQLType;
2322

2423
import java.lang.reflect.*;
2524
import java.util.ArrayList;
2625
import java.util.List;
2726
import java.util.Map;
2827

29-
import static graphql.annotations.processor.util.ReflectionKit.constructNewInstance;
3028
import static graphql.annotations.processor.util.NamingKit.toGraphqlName;
29+
import static graphql.annotations.processor.util.ReflectionKit.constructNewInstance;
3130
import static graphql.annotations.processor.util.ReflectionKit.newInstance;
3231

3332
public class MethodDataFetcher<T> implements DataFetcher<T> {
@@ -59,7 +58,13 @@ public T get(DataFetchingEnvironment environment) {
5958
return null;
6059
}
6160
}
62-
return (T)method.invoke(obj, invocationArgs(environment, container));
61+
62+
if (obj == null && environment.getSource() != null) {
63+
Object value = getFieldValue(environment.getSource(), method.getName());
64+
if (value != null) return (T) value;
65+
}
66+
67+
return (T) method.invoke(obj, invocationArgs(environment, container));
6368
} catch (IllegalAccessException | InvocationTargetException e) {
6469
throw new RuntimeException(e);
6570
}
@@ -131,4 +136,14 @@ private Object buildArg(Type p, GraphQLType graphQLType, Object arg) {
131136
return arg;
132137
}
133138
}
139+
140+
private Object getFieldValue(Object source, String fieldName) throws IllegalAccessException {
141+
try {
142+
Field field = source.getClass().getDeclaredField(fieldName);
143+
field.setAccessible(true);
144+
return field.get(source);
145+
} catch (NoSuchFieldException e) {
146+
return null;
147+
}
148+
}
134149
}

src/main/java/graphql/annotations/processor/util/ReflectionKit.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,4 @@ public static <T> T newInstance(Class<T> clazz, Object parameter) {
6969
}
7070
return null;
7171
}
72-
73-
7472
}

src/test/java/graphql/annotations/MethodDataFetcherTest.java

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,27 @@
1414
*/
1515
package graphql.annotations;
1616

17+
import graphql.ExecutionResult;
18+
import graphql.GraphQL;
19+
import graphql.annotations.annotationTypes.GraphQLDataFetcher;
20+
import graphql.annotations.annotationTypes.GraphQLField;
21+
import graphql.annotations.annotationTypes.GraphQLInvokeDetached;
22+
import graphql.annotations.annotationTypes.GraphQLType;
1723
import graphql.annotations.dataFetchers.MethodDataFetcher;
1824
import graphql.annotations.processor.GraphQLAnnotations;
19-
import graphql.schema.DataFetchingEnvironmentImpl;
25+
import graphql.schema.*;
2026
import org.testng.annotations.BeforeMethod;
2127
import org.testng.annotations.Test;
2228

2329
import java.util.ArrayList;
2430
import java.util.HashMap;
31+
import java.util.Map;
2532

33+
import static graphql.schema.GraphQLSchema.newSchema;
34+
import static org.testng.Assert.assertEquals;
35+
import static org.testng.Assert.assertTrue;
36+
37+
@SuppressWarnings("unchecked")
2638
public class MethodDataFetcherTest {
2739

2840
@BeforeMethod
@@ -41,10 +53,84 @@ public String method() throws TestException {
4153
@Test(expectedExceptions = RuntimeException.class)
4254
public void exceptionRethrowing() {
4355
try {
44-
MethodDataFetcher methodDataFetcher = new MethodDataFetcher(getClass().getMethod("method"),null,null);
45-
methodDataFetcher.get(new DataFetchingEnvironmentImpl(this, new HashMap<String,Object>(), null, null, null, new ArrayList<>(), null, null, null, null, null, null, null));
56+
MethodDataFetcher methodDataFetcher = new MethodDataFetcher(getClass().getMethod("method"), null, null);
57+
methodDataFetcher.get(new DataFetchingEnvironmentImpl(this, new HashMap<>(), null, null, null, new ArrayList<>(), null, null, null, null, null, null, null));
4658
} catch (NoSuchMethodException e) {
4759
e.printStackTrace();
4860
}
4961
}
62+
63+
64+
@GraphQLType
65+
public static class ApiType {
66+
@GraphQLField
67+
public int a() {
68+
return 1;
69+
}
70+
71+
@GraphQLField
72+
@GraphQLInvokeDetached
73+
public int b() {
74+
return 2;
75+
}
76+
}
77+
78+
public static class InternalType {
79+
public int a = 123;
80+
public int b;
81+
}
82+
83+
@GraphQLType
84+
public static class Query {
85+
@GraphQLField
86+
@GraphQLDataFetcher(MyFetcher.class)
87+
public ApiType field;
88+
89+
@GraphQLField
90+
@GraphQLDataFetcher(MyApiFetcher.class)
91+
public ApiType apiField;
92+
}
93+
94+
public static class MyFetcher implements DataFetcher<InternalType> {
95+
public InternalType get(DataFetchingEnvironment environment) {
96+
return new InternalType();
97+
}
98+
}
99+
100+
public static class MyApiFetcher implements DataFetcher<ApiType> {
101+
public ApiType get(DataFetchingEnvironment environment) {
102+
return new ApiType();
103+
}
104+
}
105+
106+
@Test
107+
public void queryingOneFieldNotAnnotatedWithGraphQLInvokeDetached_valueIsDeterminedByEntity() {
108+
GraphQLObjectType object = GraphQLAnnotations.object(Query.class);
109+
GraphQLSchema schema = newSchema().query(object).build();
110+
111+
ExecutionResult result = GraphQL.newGraphQL(schema).build().execute("query { field { a } }");
112+
assertTrue(result.getErrors().isEmpty());
113+
assertEquals(((Map<String, Map<String, Integer>>) result.getData()).get("field").get("a").toString(), "123");
114+
}
115+
116+
@Test
117+
public void queryingOneFieldAnnotatedWithGraphQLInvokeDetached_valueIsDeterminedByApiEntity() {
118+
GraphQLObjectType object = GraphQLAnnotations.object(Query.class);
119+
GraphQLSchema schema = newSchema().query(object).build();
120+
121+
ExecutionResult result = GraphQL.newGraphQL(schema).build().execute("query { field { b } }");
122+
assertTrue(result.getErrors().isEmpty());
123+
assertEquals(((Map<String, Map<String, Integer>>) result.getData()).get("field").get("b").toString(), "2");
124+
}
125+
126+
@Test
127+
public void queryingFieldsFromApiEntityFetcher_valueIsDeterminedByApiEntity() {
128+
GraphQLObjectType object = GraphQLAnnotations.object(Query.class);
129+
GraphQLSchema schema = newSchema().query(object).build();
130+
131+
ExecutionResult result = GraphQL.newGraphQL(schema).build().execute("query { apiField { a b } }");
132+
assertTrue(result.getErrors().isEmpty());
133+
assertEquals(((Map<String, Map<String, Integer>>) result.getData()).get("apiField").get("a").toString(), "1");
134+
assertEquals(((Map<String, Map<String, Integer>>) result.getData()).get("apiField").get("b").toString(), "2");
135+
}
50136
}

0 commit comments

Comments
 (0)