Skip to content

Commit 44c9683

Browse files
authored
Merge pull request #22 from nfl/better-connection-creation
adding a name registry to allow for reusable connections as long as the underlying GraphQL types are equal
2 parents 3bc1b86 + 0b022b2 commit 44c9683

File tree

10 files changed

+379
-92
lines changed

10 files changed

+379
-92
lines changed

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ maven.central.sync=false
55
sonatype.username=DUMMY_SONATYPE_USER
66
sonatype.password=DUMMY_SONATYPE_PASSWORD
77

8-
PROJECT_VERSION=1.1.6
8+
PROJECT_VERSION=1.2.1
99
PROJECT_GITHUB_REPO_URL=https://github.com/nfl/glitr
1010
PROJECT_LICENSE_URL=https://github.com/nfl/glitr/blob/master/LICENSE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package com.nfl.glitr.registry;
2+
3+
import com.nfl.glitr.exception.GlitrException;
4+
import graphql.schema.GraphQLType;
5+
6+
import java.util.Collection;
7+
import java.util.Iterator;
8+
import java.util.Map;
9+
import java.util.Set;
10+
import java.util.concurrent.ConcurrentHashMap;
11+
import java.util.concurrent.ConcurrentMap;
12+
13+
/**
14+
* A map implementation to sync the two kinds of registries currently in {@link com.nfl.glitr.registry.TypeRegistry}.
15+
* Syncs happen dynamically, keeping the nameRegistry appraised of classRegistry additions to ensure unique GraphQLTypes
16+
*/
17+
public class GlitrTypeMap implements ConcurrentMap {
18+
19+
private final Map<Class, GraphQLType> classRegistry = new ConcurrentHashMap<>();
20+
private final Map<String, GraphQLType> nameRegistry = new ConcurrentHashMap<>();
21+
22+
23+
@Override
24+
public Object getOrDefault(Object key, Object defaultValue) {
25+
if (isClass(key)) {
26+
return classRegistry.getOrDefault(key, (GraphQLType) defaultValue);
27+
} else if (isString(key)) {
28+
return nameRegistry.getOrDefault(key, (GraphQLType) defaultValue);
29+
}
30+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
31+
}
32+
33+
@Override
34+
public Object putIfAbsent(Object key, Object value) {
35+
if (isClass(key)) {
36+
nameRegistry.putIfAbsent(((Class) key).getSimpleName(), (GraphQLType) value);
37+
return classRegistry.putIfAbsent((Class) key, (GraphQLType) value);
38+
} else if (isString(key)) {
39+
return nameRegistry.putIfAbsent((String) key, (GraphQLType) value);
40+
}
41+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
42+
}
43+
44+
@Override
45+
public boolean remove(Object key, Object value) {
46+
if (isClass(key)) {
47+
nameRegistry.remove(((Class) key).getSimpleName(), value);
48+
return classRegistry.remove(key, value);
49+
} else if (isString(key)) {
50+
return nameRegistry.remove(key, value);
51+
}
52+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
53+
}
54+
55+
@Override
56+
public boolean replace(Object key, Object oldValue, Object newValue) {
57+
if (isClass(key)) {
58+
nameRegistry.replace(((Class) key).getSimpleName(), (GraphQLType) oldValue, (GraphQLType) newValue);
59+
return classRegistry.replace((Class) key, (GraphQLType) oldValue, (GraphQLType) newValue);
60+
} else if (isString(key)) {
61+
return nameRegistry.replace((String) key, (GraphQLType) oldValue, (GraphQLType) newValue);
62+
}
63+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
64+
}
65+
66+
@Override
67+
public Object replace(Object key, Object value) {
68+
if (isClass(key)) {
69+
nameRegistry.replace(((Class) key).getSimpleName(), (GraphQLType) value);
70+
return classRegistry.replace((Class) key, (GraphQLType) value);
71+
} else if (isString(key)) {
72+
return nameRegistry.replace((String) key, (GraphQLType) value);
73+
}
74+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
75+
}
76+
77+
@Override
78+
public boolean isEmpty() {
79+
return nameRegistry.isEmpty();
80+
}
81+
82+
@Override
83+
public boolean containsKey(Object key) {
84+
if (isClass(key)) {
85+
return classRegistry.containsKey(key);
86+
} else if (isString(key)) {
87+
return nameRegistry.containsKey(key);
88+
}
89+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
90+
}
91+
92+
@Override
93+
public boolean containsValue(Object value) {
94+
return nameRegistry.containsValue(value);
95+
}
96+
97+
@Override
98+
public Object get(Object key) {
99+
if (isClass(key)) {
100+
return classRegistry.get(key);
101+
} else if (isString(key)) {
102+
return nameRegistry.get(key);
103+
}
104+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
105+
}
106+
107+
@Override
108+
public Object put(Object key, Object value) {
109+
if (isClass(key)) {
110+
nameRegistry.put(((Class) key).getSimpleName(), (GraphQLType) value);
111+
return classRegistry.put((Class) key, (GraphQLType) value);
112+
} else if (isString(key)) {
113+
return nameRegistry.put((String) key, (GraphQLType) value);
114+
}
115+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
116+
}
117+
118+
@Override
119+
public Object remove(Object key) {
120+
if (isClass(key)) {
121+
nameRegistry.remove(((Class) key).getSimpleName());
122+
return classRegistry.remove(key);
123+
} else if (isString(key)) {
124+
return nameRegistry.remove(key);
125+
}
126+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
127+
}
128+
129+
@Override
130+
public void clear() {
131+
classRegistry.clear();
132+
nameRegistry.clear();
133+
}
134+
135+
@Override
136+
public int size() {
137+
return nameRegistry.size();
138+
}
139+
140+
@Override
141+
public void putAll(Map m) {
142+
Set set = m.keySet();
143+
Object next = set.iterator().next();
144+
if (isClass(next)) {
145+
for (Object o : m.entrySet()) {
146+
Entry pair = (Entry) o;
147+
nameRegistry.put(((Class) pair.getKey()).getSimpleName(), (GraphQLType) pair.getValue());
148+
classRegistry.put((Class) pair.getKey(), (GraphQLType) pair.getValue());
149+
}
150+
} else if (isString(next)) {
151+
nameRegistry.putAll(m);
152+
}
153+
throw new GlitrException("Unsupported type passed as key to GlitrTypeMap");
154+
}
155+
156+
@Override
157+
public Collection values() {
158+
return nameRegistry.values();
159+
}
160+
161+
@Override
162+
public Set keySet() {
163+
throw new GlitrException("Unsupported method, GlitrTypeMap does not support this method to ensure expected return types");
164+
}
165+
166+
@Override
167+
public Set<Entry> entrySet() {
168+
throw new GlitrException("Unsupported method, GlitrTypeMap does not support this method to ensure expected return types");
169+
}
170+
171+
public Set ClassKeySet() {
172+
return classRegistry.keySet();
173+
}
174+
175+
public Set NameKeySet() {
176+
return nameRegistry.keySet();
177+
}
178+
179+
public Set<Entry<Class, GraphQLType>> ClassEntrySet() {
180+
return classRegistry.entrySet();
181+
}
182+
183+
public Set<Entry<String, GraphQLType>> NameEntrySet() {
184+
return nameRegistry.entrySet();
185+
}
186+
187+
private boolean isClass(Object obj) {
188+
return obj instanceof Class;
189+
}
190+
191+
private boolean isString(Object obj) {
192+
return obj instanceof String;
193+
}
194+
}

src/main/java/com/nfl/glitr/registry/TypeRegistry.java

+9
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public class TypeRegistry implements TypeResolver {
7575
private static final Logger logger = LoggerFactory.getLogger(TypeRegistry.class);
7676

7777
private final Map<Class, GraphQLType> registry = new ConcurrentHashMap<>();
78+
private final Map<String, GraphQLType> nameRegistry = new ConcurrentHashMap<>();
7879
private final Map<Class, List<Object>> overrides;
7980

8081
private final Map<Class<? extends Annotation>, Func4<Field, Method, Class, Annotation, List<GraphQLArgument>>> annotationToArgumentsProviderMap;
@@ -108,6 +109,7 @@ public class TypeRegistry implements TypeResolver {
108109
this.nodeInterface = relay.nodeInterface(this);
109110
// register Node so we don't inadvertently recreate it later
110111
this.registry.put(Node.class, this.nodeInterface);
112+
this.nameRegistry.put(Node.class.getSimpleName(), this.nodeInterface);
111113
}
112114
this.explicitRelayNodeScanEnabled = explicitRelayNodeScanEnabled;
113115
}
@@ -175,11 +177,13 @@ public GraphQLType lookupOutput(Class clazz) {
175177

176178
// put a type reference in while building the type to work around circular references
177179
registry.put(clazz, new GraphQLTypeReference(clazz.getSimpleName()));
180+
nameRegistry.put(clazz.getSimpleName(), new GraphQLTypeReference(clazz.getSimpleName()));
178181

179182
GraphQLOutputType type = graphQLTypeFactory.createGraphQLOutputType(clazz);
180183

181184
if (type != null) {
182185
registry.put(clazz, type);
186+
nameRegistry.put(clazz.getSimpleName(), type);
183187
} else {
184188
throw new IllegalArgumentException("Unable to create GraphQLOutputType for: " + clazz.getCanonicalName());
185189
}
@@ -203,6 +207,7 @@ public GraphQLType lookupInput(Class clazz) {
203207

204208
if (type != null) {
205209
registry.put(clazz, type);
210+
nameRegistry.put(clazz.getSimpleName(), type);
206211
} else {
207212
throw new IllegalArgumentException("Unable to create GraphQLInputType for: " + clazz.getCanonicalName());
208213
}
@@ -644,4 +649,8 @@ private GraphQLType getGraphQLTypeForInputParameterizedType(Type type) {
644649
throw new IllegalArgumentException("Unable to convert type " + type.getTypeName() + " to GraphQLInputType");
645650
}
646651
}
652+
653+
public Map<String, GraphQLType> getNameRegistry() {
654+
return nameRegistry;
655+
}
647656
}

src/main/java/com/nfl/glitr/relay/type/PagingOutputTypeConverter.java

+21-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.google.common.collect.Lists;
44
import com.googlecode.gentyref.GenericTypeReflector;
5+
import com.nfl.glitr.exception.GlitrException;
56
import com.nfl.glitr.relay.RelayHelper;
67
import com.nfl.glitr.util.ReflectionUtil;
78
import com.nfl.glitr.annotation.GlitrForwardPagingArguments;
@@ -10,6 +11,7 @@
1011
import graphql.schema.GraphQLNonNull;
1112
import graphql.schema.GraphQLObjectType;
1213
import graphql.schema.GraphQLOutputType;
14+
import graphql.schema.GraphQLType;
1315
import rx.functions.Func4;
1416

1517
import javax.annotation.Nullable;
@@ -19,6 +21,7 @@
1921
import java.lang.reflect.ParameterizedType;
2022
import java.lang.reflect.Type;
2123
import java.util.Collections;
24+
import java.util.Map;
2225

2326
/**
2427
* Output type converter function for paging arguments annotations.
@@ -64,8 +67,24 @@ public GraphQLOutputType call(@Nullable Field field, Method method, Class declar
6467
edgeGraphQLOutputType,
6568
relayHelper.getNodeInterface(),
6669
Collections.emptyList());
67-
// last build the relay connection!
68-
return relayHelper.connectionType(endEdgeClass.getSimpleName(), edgeType, Lists.newArrayList());
70+
// build the relay connection
71+
GraphQLObjectType connectionType = relayHelper.connectionType(endEdgeClass.getSimpleName(), edgeType, Lists.newArrayList());
72+
73+
// check if a connection with this name already exists
74+
Map<String, GraphQLType> nameRegistry = typeRegistry.getNameRegistry();
75+
GraphQLObjectType qlObjectType = (GraphQLObjectType) nameRegistry.get(connectionType.getName());
76+
if (qlObjectType != null) {
77+
// TODO: better equality function
78+
if (!qlObjectType.toString().equals(connectionType.toString())) {
79+
throw new GlitrException("Attempting to create two types with the same name. All types within a GraphQL schema must have unique names. " +
80+
"You have defined the type [" + connectionType.getName() + "] as both [" + qlObjectType + "] and [" + connectionType + "]");
81+
}
82+
return qlObjectType;
83+
}
84+
85+
// add the connection to the registry and return the connection
86+
nameRegistry.put(connectionType.getName(), connectionType);
87+
return connectionType;
6988
}
7089

7190
public PagingOutputTypeConverter setTypeRegistry(TypeRegistry typeRegistry) {

src/test/groovy/com/nfl/glitr/data/query/QueryType.java

+21-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,27 @@ public Video getVideo() {
1818
}
1919

2020
@GlitrArgument(name = "id", type = String.class, nullable = false)
21-
public com.nfl.glitr.relay.Node getNode() { return null; }
21+
public com.nfl.glitr.relay.Node getNode() {
22+
return null;
23+
}
2224

2325
@GlitrArgument(name = "ids", type = String[].class, nullable = false)
24-
public List<com.nfl.glitr.relay.Node> getNodes() { return null; }
26+
public List<com.nfl.glitr.relay.Node> getNodes() {
27+
return null;
28+
}
29+
30+
@GlitrForwardPagingArguments
31+
public List<Video> getOtherVideos() {
32+
return null;
33+
}
34+
35+
@GlitrArgument(name = "ids", type = String[].class, nullable = false)
36+
public List<com.nfl.glitr.relay.Node> getZZZNodes() {
37+
return null;
38+
}
39+
40+
// no arguments
41+
public List<Video> getZZZVideos() {
42+
return null;
43+
}
2544
}
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
11
package com.nfl.glitr.data.query.additionalTypes;
22

33
public class Cyborg implements Person {
4-
public String getId() {
5-
return id;
6-
}
7-
8-
public String getCodeName() {
9-
return codeName;
10-
}
11-
12-
@Override
13-
public String getAge() {
14-
return this.age;
15-
}
16-
17-
public void setId(String id) {
18-
this.id = id;
19-
}
20-
21-
public void setCodeName(String codeName) {
22-
this.codeName = codeName;
23-
}
24-
25-
public void setAge(String age) {
26-
this.age = age;
27-
}
28-
29-
private String id;
30-
private String codeName;
31-
private String age;
32-
}
4+
5+
private String id;
6+
private String codeName;
7+
private String age;
8+
9+
10+
public String getId() {
11+
return id;
12+
}
13+
14+
public String getCodeName() {
15+
return codeName;
16+
}
17+
18+
@Override
19+
public String getAge() {
20+
return this.age;
21+
}
22+
23+
public void setId(String id) {
24+
this.id = id;
25+
}
26+
27+
public void setCodeName(String codeName) {
28+
this.codeName = codeName;
29+
}
30+
31+
public void setAge(String age) {
32+
this.age = age;
33+
}
34+
}

0 commit comments

Comments
 (0)