Skip to content

Commit 0d0aa22

Browse files
authored
Support for inherited interfaces (#65) (#298)
1 parent 4fc9ac8 commit 0d0aa22

File tree

4 files changed

+103
-3
lines changed

4 files changed

+103
-3
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import graphql.annotations.processor.retrievers.GraphQLInterfaceRetriever;
2424
import graphql.annotations.processor.retrievers.GraphQLObjectInfoRetriever;
2525
import graphql.annotations.processor.searchAlgorithms.SearchAlgorithm;
26+
import graphql.annotations.processor.util.ClassUtils;
2627
import graphql.schema.GraphQLFieldDefinition;
2728
import graphql.schema.GraphQLInterfaceType;
2829
import graphql.schema.GraphQLObjectType;
@@ -95,7 +96,7 @@ public GraphQLObjectType.Builder getOutputObjectBuilder(Class<?> object, Process
9596
}
9697
}
9798

98-
for (Class<?> iface : object.getInterfaces()) {
99+
for (Class<?> iface : ClassUtils.getAllInterfaces(object)) {
99100
if (iface.getAnnotation(GraphQLTypeResolver.class) != null) {
100101
String ifaceName = graphQLObjectInfoRetriever.getTypeName(iface);
101102
if (container.getProcessing().contains(ifaceName)) {
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
*/
13+
14+
package graphql.annotations.processor.util;
15+
16+
import java.util.ArrayList;
17+
import java.util.HashSet;
18+
import java.util.LinkedHashSet;
19+
import java.util.List;
20+
import java.util.Set;
21+
22+
/**
23+
* Operates on classes without using reflection.
24+
*
25+
* <p>
26+
* This class handles invalid {@code null} inputs as best it can. Each method documents its behavior in more detail.
27+
* </p>
28+
*
29+
* <p>
30+
* The notion of a {@code canonical name} includes the human readable name for the type, for example {@code int[]}. The
31+
* non-canonical method variants work with the JVM names, such as {@code [I}.
32+
* </p>
33+
*
34+
* <p>
35+
* This class and the functions contained within are from the Apache commons-lang project.
36+
* </p>
37+
*
38+
*/
39+
public class ClassUtils {
40+
41+
/**
42+
* Gets a {@link List} of all interfaces implemented by the given class and its superclasses.
43+
*
44+
* <p>
45+
* The order is determined by looking through each interface in turn as declared in the source file and following its
46+
* hierarchy up. Then each superclass is considered in the same way. Later duplicates are ignored, so the order is
47+
* maintained.
48+
* </p>
49+
*
50+
* @param cls the class to look up, may be {@code null}
51+
* @return the {@link List} of interfaces in order, {@code null} if null input
52+
*/
53+
public static List<Class<?>> getAllInterfaces(final Class<?> cls) {
54+
if (cls == null) {
55+
return null;
56+
}
57+
58+
final LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<>();
59+
getAllInterfaces(cls, interfacesFound);
60+
61+
return new ArrayList<>(interfacesFound);
62+
}
63+
64+
/**
65+
* Gets the interfaces for the specified class.
66+
*
67+
* @param cls the class to look up, may be {@code null}
68+
* @param interfacesFound the {@link Set} of interfaces for the class
69+
*/
70+
private static void getAllInterfaces(Class<?> cls, final HashSet<Class<?>> interfacesFound) {
71+
while (cls != null) {
72+
final Class<?>[] interfaces = cls.getInterfaces();
73+
74+
for (final Class<?> i : interfaces) {
75+
if (interfacesFound.add(i)) {
76+
getAllInterfaces(i, interfacesFound);
77+
}
78+
}
79+
80+
cls = cls.getSuperclass();
81+
}
82+
}
83+
84+
/**
85+
* ClassUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
86+
* {@code ClassUtils.getShortClassName(cls)}.
87+
*
88+
* <p>
89+
* This constructor is public to permit tools that require a JavaBean instance to operate.
90+
* </p>
91+
*/
92+
public ClassUtils() {
93+
}
94+
95+
}

src/test/java/graphql/annotations/GraphQLFragmentTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import graphql.ExecutionResult;
1818
import graphql.GraphQL;
1919
import graphql.TypeResolutionEnvironment;
20+
import graphql.annotations.GraphQLFragmentTest.MyInterface;
2021
import graphql.annotations.annotationTypes.GraphQLField;
2122
import graphql.annotations.annotationTypes.GraphQLTypeResolver;
2223
import graphql.annotations.processor.GraphQLAnnotations;
@@ -95,7 +96,10 @@ public MyObject2 getMy() {
9596
}
9697
}
9798

98-
public static class MyObject2 implements MyInterface {
99+
public static class MyObject2 extends SuperClass {
100+
}
101+
102+
public static class SuperClass implements MyInterface {
99103
public String getA() {
100104
return "a2";
101105
}

src/test/java/graphql/annotations/GraphQLInterfaceTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public void testUnion() {
128128
public void testInterfaces() {
129129
GraphQLObjectType object = this.graphQLAnnotations.object(TestObject.class);
130130
List<GraphQLNamedOutputType> ifaces = object.getInterfaces();
131-
assertEquals(ifaces.size(), 1);
131+
assertEquals(ifaces.size(), 2);
132132
assertEquals(ifaces.get(0).getName(), "TestIface");
133133
}
134134

0 commit comments

Comments
 (0)