diff --git a/README.md b/README.md index c2314779..c674d8e7 100644 --- a/README.md +++ b/README.md @@ -34,18 +34,26 @@ GraphQLObjectType object = GraphQLAnnotations.object(SomeObject.class); ## Defining Interfaces -This is very similar to defining objects: +This is very similar to defining objects, with the addition of type resolver : ```java +@GraphQLTypeResolver(MyTypeResolver.class) public interface SomeInterface { @GraphQLField String field(); } +public class MyTypeResolver implements TypeResolver { + GraphQLObjectType getType(TypeResolutionEnvironment env) { ... } +} + // ... GraphQLInterfaceType object = GraphQLAnnotations.iface(SomeInterface.class); ``` +An instance of the type resolver will be created from the specified class. If a `getInstance` method is present on the +class, it will be used instead of the default constructor. + ## Fields In addition to specifying a field over a Java class field, a field can be defined over a method: @@ -108,10 +116,21 @@ public String field(@GraphQLDefaultValue(DefaultValue.class) String value) { } ``` +The `DefaultValue` class can define a `getInstance` method that will be called instead of the default constructor. + `@GraphQLDeprecate` and Java's `@Deprecated` can be used to specify a deprecated field. -You can specify a custom data fetcher for a field with `@GraphQLDataFetcher` +### Custom data fetcher + +You can specify a custom data fetcher for a field with `@GraphQLDataFetcher`. The annotation will reference a class name, +which will be used as data fetcher. + +An instance of the data fetcher will be created. The `args` attribute on the annotation can be used to specify a list of +String arguments to pass to the construcor, allowing to reuse the same class on different fields, with different parameter. +The `firstArgIsTargetName` attribute can also be set on `@GraphQLDataFetcher` to pass the field name as a single parameter of the constructor. + +If no argument is needed and a `getInstance` method is present, this method will be called instead of the constructor. ## Type extensions diff --git a/src/main/java/graphql/annotations/processor/util/ReflectionKit.java b/src/main/java/graphql/annotations/processor/util/ReflectionKit.java index b62a692f..d4eef6a8 100644 --- a/src/main/java/graphql/annotations/processor/util/ReflectionKit.java +++ b/src/main/java/graphql/annotations/processor/util/ReflectionKit.java @@ -18,6 +18,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; /** * A package level helper in calling reflective methods and turning them into @@ -26,8 +28,16 @@ public class ReflectionKit { public static T newInstance(Class clazz) throws GraphQLAnnotationsException { try { + try { + Method getInstance = clazz.getMethod("getInstance", new Class[0]); + if (Modifier.isStatic(getInstance.getModifiers()) && clazz.isAssignableFrom(getInstance.getReturnType())) { + return (T) getInstance.invoke(null); + } + } catch (NoSuchMethodException e) { + // ignore, just call the constructor + } return clazz.newInstance(); - } catch (InstantiationException | IllegalAccessException e) { + } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { throw new GraphQLAnnotationsException("Unable to instantiate class : " + clazz, e); } } diff --git a/src/test/java/graphql/annotations/GraphQLInterfaceTest.java b/src/test/java/graphql/annotations/GraphQLInterfaceTest.java index 66f57844..94b3eb44 100644 --- a/src/test/java/graphql/annotations/GraphQLInterfaceTest.java +++ b/src/test/java/graphql/annotations/GraphQLInterfaceTest.java @@ -69,6 +69,10 @@ public void noResolver() { public static class Resolver implements TypeResolver { + public static Resolver getInstance() { + return new Resolver(); + } + @Override public GraphQLObjectType getType(TypeResolutionEnvironment env) { try {