Skip to content

Commit f3393ed

Browse files
authored
Merge pull request #167 from danieltaub96/master
DataFetcherConstructor args constructor as the annotation + tests
2 parents 9c7a3a9 + ae93fe0 commit f3393ed

File tree

4 files changed

+205
-10
lines changed

4 files changed

+205
-10
lines changed

README.md

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ dependencies {
1616
}
1717
```
1818

19+
(Maven syntax)
20+
21+
```groovy
22+
<dependency>
23+
<groupId>io.github.graphql-java</groupId>
24+
<artifactId>graphql-java-annotations</artifactId>
25+
<version>5.2</version>
26+
</dependency>
27+
```
28+
1929

2030
## Defining Objects
2131

@@ -58,7 +68,7 @@ class, it will be used instead of the default constructor.
5868

5969
To have a union, you must annotate an interface with `@GraphQLUnion`. In the annotation, you must declare all the
6070
possible types of the union, and a type resolver.
61-
If no type resolver is specified, `UnionTypeResovler` is used. It follows this algorithm:
71+
If no type resolver is specified, `UnionTypeResolver` is used. It follows this algorithm:
6272
The resolver assumes the the DB entity's name is the same as the API entity's name.
6373
If so, it takes the result from the dataFetcher and decides to which
6474
API entity it should be mapped (according to the name).
@@ -166,9 +176,43 @@ You can specify a custom data fetcher for a field with `@GraphQLDataFetcher`. Th
166176
which will be used as data fetcher.
167177

168178
An instance of the data fetcher will be created. The `args` attribute on the annotation can be used to specify a list of
169-
String arguments to pass to the construcor, allowing to reuse the same class on different fields, with different parameter.
179+
String arguments to pass to the constructor, allowing to reuse the same class on different fields, with different parameter.
170180
The `firstArgIsTargetName` attribute can also be set on `@GraphQLDataFetcher` to pass the field name as a single parameter of the constructor.
171181

182+
Assuming you are using `@GraphQLDataFetcher` this way:
183+
184+
```java
185+
@GraphQLField
186+
@GraphQLDataFetcher(value = HelloWorldDataFetcher.class, args = { "arg1", "arg2" })
187+
public String getHelloWorld(){
188+
return null;
189+
}
190+
```
191+
192+
Then the class that extends from `DataFetcher.class` will get this args to two supported constructors <br>
193+
Or to a constructor that expecting String array that's way (`String[] args` or `String... args`) or for a constructor that expecting the same number of args that you send with in the annotation.<br>
194+
You get to choose which implementation you want.
195+
```java
196+
public class HelloWorldDataFetcher implements DataFetcher<String> {
197+
198+
public HelloWorldDataFetcher(String[] args){
199+
// Do something with your args
200+
}
201+
202+
// Note that you need to expect the same number of args as you send with in the annotation args
203+
public HelloWorldDataFetcher(String arg1, String arg2){
204+
// Do something with your args
205+
}
206+
207+
@Override
208+
public String get(DataFetchingEnvironment environment) {
209+
return "something";
210+
}
211+
}
212+
```
213+
214+
215+
172216
If no argument is needed and a `getInstance` method is present, this method will be called instead of the constructor.
173217

174218
## Type extensions
@@ -218,7 +262,7 @@ public class HumanExtension {
218262
Classes marked as "extensions" will actually not define a new type, but rather set new fields on the class it extends when it will be created.
219263
All GraphQL annotations can be used on extension classes.
220264

221-
Extensions are registered in GraqhQLAnnotationProcessor by using `registerTypeExtension`. Note that extensions must be registered before the type itself is requested with `getObject()` :
265+
Extensions are registered in GraphQLAnnotationProcessor by using `registerTypeExtension`. Note that extensions must be registered before the type itself is requested with `getObject()` :
222266

223267
```
224268
GraphQLAnnotationsProcessor processor = GraphQLAnnotations.getInstance();

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,31 @@ public DataFetcher constructDataFetcher(String fieldName, GraphQLDataFetcher ann
3838
if (args.length == 0) {
3939
return newInstance(annotatedDataFetcher.value());
4040
} else {
41-
try {
42-
final Constructor<? extends DataFetcher> ctr = annotatedDataFetcher.value().getDeclaredConstructor(
43-
stream(args).map(v -> String.class).toArray(Class[]::new));
44-
return constructNewInstance(ctr, (Object[]) args);
45-
} catch (final NoSuchMethodException e) {
46-
throw new GraphQLAnnotationsException("Unable to instantiate DataFetcher via constructor for: " + fieldName, e);
47-
}
41+
return Stream.of(annotatedDataFetcher.value().getConstructors())
42+
// filter only constructor that have the same args as the annotation args
43+
// or that get String[], String... args
44+
.filter(x -> (x.getParameterCount() == args.length) ||
45+
(x.getParameterTypes().length == 1 && x.getParameterTypes()[0] == String[].class))
46+
.map(x -> {
47+
try {
48+
Constructor<? extends DataFetcher> constructor;
49+
if (x.getParameterTypes().length == 1 && x.getParameterTypes()[0] == String[].class) {
50+
constructor = annotatedDataFetcher.value().getDeclaredConstructor(String[].class);
51+
return constructNewInstance(constructor, new Object[]{args});
52+
}
53+
54+
constructor = annotatedDataFetcher.value().getDeclaredConstructor(
55+
stream(args).map(v -> String.class).toArray(Class[]::new));
56+
return constructNewInstance(constructor, (Object[]) args);
57+
} catch (NoSuchMethodException e) {
58+
throw new GraphQLAnnotationsException("Unable to instantiate DataFetcher via constructor for: " + fieldName, e);
59+
}
60+
})
61+
.findFirst()
62+
.orElseThrow(NoArgsConstructorException::new);
4863
}
4964
}
5065

66+
public static class NoArgsConstructorException extends RuntimeException {
67+
}
5168
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* Copyright 2016 Yurii Rashkovskii
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
*/
15+
package graphql.annotations.processor.util;
16+
17+
import graphql.annotations.annotationTypes.GraphQLDataFetcher;
18+
import graphql.schema.DataFetcher;
19+
import org.testng.annotations.Test;
20+
21+
import java.lang.annotation.Annotation;
22+
23+
import static org.testng.Assert.assertEquals;
24+
import static org.testng.Assert.assertNull;
25+
26+
/**
27+
* @author danieltaub on 07/05/2018.
28+
*/
29+
public class DataFetcherConstructorTest {
30+
private DataFetcherConstructor constructor = new DataFetcherConstructor();
31+
32+
@Test
33+
public void graphQLDataFetcherWithArrayCtorTest() {
34+
GraphQLDataFetcher graphQLDataFetcher = getGraphQLDataFetcher(DataFetcherMock.class, false, "Arg1", "Arg2");
35+
DataFetcherMock dataFetcher = (DataFetcherMock) constructor.constructDataFetcher(null, graphQLDataFetcher);
36+
37+
assertEquals(dataFetcher.getArgs().length, 2);
38+
assertEquals(dataFetcher.getArgs()[0], "Arg1");
39+
assertEquals(dataFetcher.getArgs()[1], "Arg2");
40+
}
41+
42+
@Test
43+
public void graphQLDataFetcherWithParamsCtorTest() {
44+
GraphQLDataFetcher graphQLDataFetcher = getGraphQLDataFetcher(DataFetcherMock.class, false, "Arg1", "Arg2", "Arg3");
45+
DataFetcherMock dataFetcher = (DataFetcherMock) constructor.constructDataFetcher(null, graphQLDataFetcher);
46+
47+
assertEquals(dataFetcher.getArgs().length, 3);
48+
assertEquals(dataFetcher.getArgs()[0], "Arg1");
49+
assertEquals(dataFetcher.getArgs()[1], "Arg2");
50+
assertEquals(dataFetcher.getArgs()[2], "Arg3");
51+
}
52+
53+
@Test
54+
public void graphQLDataFetcherDefaultCtorTest() {
55+
GraphQLDataFetcher graphQLDataFetcher = getGraphQLDataFetcher(DataFetcherMock.class, false);
56+
DataFetcherMock dataFetcher = (DataFetcherMock) constructor.constructDataFetcher(null, graphQLDataFetcher);
57+
58+
assertNull(dataFetcher.getArgs());
59+
}
60+
61+
private GraphQLDataFetcher getGraphQLDataFetcher(final Class<? extends DataFetcher> value,
62+
boolean argsInTarget, String... args) {
63+
GraphQLDataFetcher annotation = new GraphQLDataFetcher() {
64+
@Override
65+
public Class<? extends Annotation> annotationType() {
66+
return GraphQLDataFetcher.class;
67+
}
68+
69+
@Override
70+
public Class<? extends DataFetcher> value() {
71+
return value;
72+
}
73+
74+
@Override
75+
public String[] args() {
76+
return args;
77+
}
78+
79+
@Override
80+
public boolean firstArgIsTargetName() {
81+
return argsInTarget;
82+
}
83+
};
84+
85+
return annotation;
86+
}
87+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Copyright 2016 Yurii Rashkovskii
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
*/
15+
package graphql.annotations.processor.util;
16+
17+
import graphql.schema.DataFetcher;
18+
import graphql.schema.DataFetchingEnvironment;
19+
20+
/**
21+
* @author danieltaub on 07/05/2018.
22+
*/
23+
public class DataFetcherMock implements DataFetcher {
24+
private String[] args;
25+
26+
public DataFetcherMock(String... args) {
27+
this.args = args;
28+
}
29+
30+
public DataFetcherMock(String arg1, String arg2, String arg3) {
31+
this.args = new String[]{
32+
arg1, arg2, arg3
33+
};
34+
}
35+
36+
public DataFetcherMock() {
37+
}
38+
39+
@Override
40+
public Object get(DataFetchingEnvironment environment) {
41+
return null;
42+
}
43+
44+
public String[] getArgs() {
45+
return args;
46+
}
47+
}

0 commit comments

Comments
 (0)