Skip to content

Commit 58b6f39

Browse files
committed
first basic support for relay
1 parent e3a0022 commit 58b6f39

File tree

8 files changed

+469
-0
lines changed

8 files changed

+469
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package graphql.relay;
2+
3+
import javax.xml.bind.DatatypeConverter;
4+
import java.io.UnsupportedEncodingException;
5+
6+
7+
public class Base64 {
8+
9+
public static String toBase64(String string) {
10+
try {
11+
return DatatypeConverter.printBase64Binary(string.getBytes("utf-8"));
12+
} catch (UnsupportedEncodingException e) {
13+
throw new RuntimeException(e);
14+
}
15+
}
16+
17+
public static String fromeBase64(String string) {
18+
return new String(DatatypeConverter.parseBase64Binary(string));
19+
}
20+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package graphql.relay;
2+
3+
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
public class Connection {
8+
private List<Edge> edges = new ArrayList<>();
9+
10+
private PageInfo pageInfo;
11+
12+
public List<Edge> getEdges() {
13+
return edges;
14+
}
15+
16+
public void setEdges(List<Edge> edges) {
17+
this.edges = edges;
18+
}
19+
20+
public PageInfo getPageInfo() {
21+
return pageInfo;
22+
}
23+
24+
public void setPageInfo(PageInfo pageInfo) {
25+
this.pageInfo = pageInfo;
26+
}
27+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package graphql.relay;
2+
3+
4+
public class ConnectionCursor {
5+
6+
private final String value;
7+
8+
public ConnectionCursor(String value) {
9+
this.value = value;
10+
}
11+
12+
public String getValue() {
13+
return value;
14+
}
15+
16+
@Override
17+
public boolean equals(Object o) {
18+
19+
20+
if (this == o) return true;
21+
if (o == null || getClass() != o.getClass()) return false;
22+
23+
ConnectionCursor that = (ConnectionCursor) o;
24+
25+
if (value != null ? !value.equals(that.value) : that.value != null) return false;
26+
27+
return true;
28+
}
29+
30+
@Override
31+
public int hashCode() {
32+
return value != null ? value.hashCode() : 0;
33+
}
34+
35+
@Override
36+
public String toString() {
37+
return "ConnectionCursor{" +
38+
"value='" + value + '\'' +
39+
'}';
40+
}
41+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package graphql.relay;
2+
3+
4+
public class Edge {
5+
6+
public Edge(Object node, ConnectionCursor cursor) {
7+
this.node = node;
8+
this.cursor = cursor;
9+
}
10+
11+
Object node;
12+
ConnectionCursor cursor;
13+
14+
public Object getNode() {
15+
return node;
16+
}
17+
18+
public void setNode(Object node) {
19+
this.node = node;
20+
}
21+
22+
public ConnectionCursor getCursor() {
23+
return cursor;
24+
}
25+
26+
public void setCursor(ConnectionCursor cursor) {
27+
this.cursor = cursor;
28+
}
29+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package graphql.relay;
2+
3+
4+
public class PageInfo {
5+
private ConnectionCursor startCursor;
6+
private ConnectionCursor endCursor;
7+
private boolean hasPreviousPage;
8+
private boolean hasNextPage;
9+
10+
public ConnectionCursor getStartCursor() {
11+
return startCursor;
12+
}
13+
14+
public void setStartCursor(ConnectionCursor startCursor) {
15+
this.startCursor = startCursor;
16+
}
17+
18+
public ConnectionCursor getEndCursor() {
19+
return endCursor;
20+
}
21+
22+
public void setEndCursor(ConnectionCursor endCursor) {
23+
this.endCursor = endCursor;
24+
}
25+
26+
public boolean isHasPreviousPage() {
27+
return hasPreviousPage;
28+
}
29+
30+
public void setHasPreviousPage(boolean hasPreviousPage) {
31+
this.hasPreviousPage = hasPreviousPage;
32+
}
33+
34+
public boolean isHasNextPage() {
35+
return hasNextPage;
36+
}
37+
38+
public void setHasNextPage(boolean hasNextPage) {
39+
this.hasNextPage = hasNextPage;
40+
}
41+
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package graphql.relay;
2+
3+
4+
import graphql.schema.*;
5+
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
9+
import static graphql.Scalars.*;
10+
import static graphql.schema.GraphQLArgument.newArgument;
11+
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
12+
import static graphql.schema.GraphQLInputObjectField.newInputObjectField;
13+
import static graphql.schema.GraphQLInputObjectType.newInputObject;
14+
import static graphql.schema.GraphQLInterfaceType.newInterface;
15+
import static graphql.schema.GraphQLObjectType.newObject;
16+
17+
public class Relay {
18+
19+
public static final String NODE = "Node";
20+
private GraphQLObjectType pageInfoType = newObject()
21+
.name("PageInfo")
22+
.description("Information about pagination in a connection.")
23+
.field(newFieldDefinition()
24+
.name("hasNextPage")
25+
.type(new GraphQLNonNull(GraphQLBoolean))
26+
.description("When paginating forwards, are there more items?")
27+
.build())
28+
.field(newFieldDefinition()
29+
.name("hasPreviousPage")
30+
.type(new GraphQLNonNull(GraphQLBoolean))
31+
.description("When paginating backwards, are there more items?")
32+
.build())
33+
.field(newFieldDefinition()
34+
.name("startCursor")
35+
.type(GraphQLString)
36+
.description("When paginating backwards, the cursor to continue.")
37+
.build())
38+
.field(newFieldDefinition()
39+
.name("endCursor")
40+
.type(GraphQLString)
41+
.description("When paginating forwards, the cursor to continue.")
42+
.build())
43+
.build();
44+
45+
public GraphQLInterfaceType nodeInterface(TypeResolver typeResolver) {
46+
GraphQLInterfaceType node = newInterface()
47+
.name(NODE)
48+
.description("An object with an ID")
49+
.typeResolver(typeResolver)
50+
.field(newFieldDefinition()
51+
.name("id")
52+
.type(new GraphQLNonNull(GraphQLID))
53+
.build())
54+
.build();
55+
return node;
56+
}
57+
58+
public GraphQLFieldDefinition nodeField(GraphQLInterfaceType nodeInterface, DataFetcher nodeDataFetcher) {
59+
GraphQLFieldDefinition fieldDefinition = newFieldDefinition()
60+
.name("node")
61+
.description("Fetches an object given its ID")
62+
.type(nodeInterface)
63+
.dataFetcher(nodeDataFetcher)
64+
.argument(newArgument()
65+
.name("id")
66+
.description("The ID of an object")
67+
.type(new GraphQLNonNull(GraphQLID))
68+
.build())
69+
.build();
70+
return fieldDefinition;
71+
}
72+
73+
public List<GraphQLArgument> getConnectionFieldArguments() {
74+
List<GraphQLArgument> args = new ArrayList<>();
75+
76+
args.add(newArgument()
77+
.name("before")
78+
.type(GraphQLString)
79+
.build());
80+
args.add(newArgument()
81+
.name("after")
82+
.type(GraphQLString)
83+
.build());
84+
args.add(newArgument()
85+
.name("first")
86+
.type(GraphQLInt)
87+
.build());
88+
args.add(newArgument()
89+
.name("last")
90+
.type(GraphQLInt)
91+
.build());
92+
return args;
93+
}
94+
95+
public GraphQLObjectType edgeType(String name, GraphQLOutputType nodeType, GraphQLInterfaceType nodeInterface, List<GraphQLFieldDefinition> edgeFields) {
96+
97+
GraphQLObjectType edgeType = newObject()
98+
.name(name + "Edge")
99+
.description("An edge in a connection.")
100+
.field(newFieldDefinition()
101+
.name("node")
102+
.type(nodeType)
103+
.description("The item at the end of the edge")
104+
.build())
105+
.field(newFieldDefinition()
106+
.name("cursor")
107+
.type(new GraphQLNonNull(GraphQLString))
108+
.description("")
109+
.build())
110+
.fields(edgeFields)
111+
.build();
112+
return edgeType;
113+
}
114+
115+
public GraphQLObjectType connectionType(String name, GraphQLObjectType edgeType, List<GraphQLFieldDefinition> connectionFields) {
116+
117+
GraphQLObjectType connectionType = newObject()
118+
.name(name + "Connection")
119+
.description("A connection to a list of items.")
120+
.field(newFieldDefinition()
121+
.name("edges")
122+
.type(new GraphQLList(edgeType))
123+
.build())
124+
.field(newFieldDefinition()
125+
.name("pageInfo")
126+
.type(pageInfoType)
127+
.build())
128+
.fields(connectionFields)
129+
.build();
130+
return connectionType;
131+
}
132+
133+
134+
public GraphQLFieldDefinition mutationWithClientMutationId(String name, String fieldName,
135+
List<GraphQLInputObjectField> inputFields,
136+
List<GraphQLFieldDefinition> outputFields,
137+
DataFetcher dataFetcher) {
138+
GraphQLInputObjectType inputObjectType = newInputObject()
139+
.name(name + "Input")
140+
.field(newInputObjectField()
141+
.name("clientMutationId")
142+
.type(new GraphQLNonNull(GraphQLString))
143+
.build())
144+
.fields(inputFields)
145+
.build();
146+
GraphQLObjectType outputType = newObject()
147+
.name(name + "Payload")
148+
.field(newFieldDefinition()
149+
.name("clientMutationId")
150+
.type(new GraphQLNonNull(GraphQLString))
151+
.build())
152+
.fields(outputFields)
153+
.build();
154+
155+
return newFieldDefinition()
156+
.name(fieldName)
157+
.type(outputType)
158+
.argument(newArgument()
159+
.name("input")
160+
.type(new GraphQLNonNull(inputObjectType))
161+
.build())
162+
.dataFetcher(dataFetcher)
163+
.build();
164+
}
165+
166+
public static class ResolvedGlobalId {
167+
public ResolvedGlobalId(String type, String id) {
168+
this.type = type;
169+
this.id = id;
170+
}
171+
172+
public String type;
173+
public String id;
174+
}
175+
176+
public String toGlobalId(String type, String id) {
177+
return Base64.toBase64(type + ":" + id);
178+
}
179+
180+
public ResolvedGlobalId fromGlobalId(String globalId) {
181+
String[] split = Base64.fromeBase64(globalId).split(":", 2);
182+
return new ResolvedGlobalId(split[0], split[1]);
183+
}
184+
}

0 commit comments

Comments
 (0)