From 8d68ec2544d12424227fabaee43a3ac0cd3a62f4 Mon Sep 17 00:00:00 2001 From: Jiayu Liu Date: Thu, 6 Apr 2017 23:31:05 +0800 Subject: [PATCH 1/2] type param and misc. --- src/main/java/graphql/relay/Connection.java | 4 +- .../java/graphql/relay/ConnectionCursor.java | 2 +- .../java/graphql/relay/DefaultConnection.java | 46 ++++++++++-- src/main/java/graphql/relay/DefaultEdge.java | 33 +++++++-- .../java/graphql/relay/DefaultPageInfo.java | 30 ++++++++ src/main/java/graphql/relay/Edge.java | 4 +- src/main/java/graphql/relay/PageInfo.java | 13 +++- src/main/java/graphql/relay/Relay.java | 32 +++++---- .../graphql/relay/SimpleListConnection.java | 70 ++++++++++--------- 9 files changed, 170 insertions(+), 64 deletions(-) diff --git a/src/main/java/graphql/relay/Connection.java b/src/main/java/graphql/relay/Connection.java index b448b2f63b..8c1f11750f 100644 --- a/src/main/java/graphql/relay/Connection.java +++ b/src/main/java/graphql/relay/Connection.java @@ -5,9 +5,9 @@ /** * represents a connection in relay. */ -public interface Connection { +public interface Connection { - List getEdges(); + List> getEdges(); PageInfo getPageInfo(); diff --git a/src/main/java/graphql/relay/ConnectionCursor.java b/src/main/java/graphql/relay/ConnectionCursor.java index fe5678662e..745e091884 100644 --- a/src/main/java/graphql/relay/ConnectionCursor.java +++ b/src/main/java/graphql/relay/ConnectionCursor.java @@ -1,7 +1,7 @@ package graphql.relay; /** - * represents a connection cursor in relay. + * represents a {@link Connection connection} cursor in relay. */ public interface ConnectionCursor { diff --git a/src/main/java/graphql/relay/DefaultConnection.java b/src/main/java/graphql/relay/DefaultConnection.java index e3c3c2cf3c..6c340ba712 100644 --- a/src/main/java/graphql/relay/DefaultConnection.java +++ b/src/main/java/graphql/relay/DefaultConnection.java @@ -1,20 +1,52 @@ package graphql.relay; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -public class DefaultConnection implements Connection { +public class DefaultConnection implements Connection { - private List edges = new ArrayList(); + private List> edges = new ArrayList>(); private PageInfo pageInfo; + /** + * @deprecated prefer {@link #DefaultConnection(List, PageInfo)} + */ + @Deprecated + public DefaultConnection() { + } + + /** + * @param edges edges + * @param pageInfo page info + * @throws IllegalArgumentException if edges or page info is null. use {@link Collections#emptyList()} for empty edges. + */ + public DefaultConnection(List> edges, PageInfo pageInfo) { + if (edges == null) { + throw new IllegalArgumentException("edges cannot be empty"); + } + if (pageInfo == null) { + throw new IllegalArgumentException("page info cannot be null"); + } + // TODO make defensive copy + this.edges = edges; + this.pageInfo = pageInfo; + } + @Override - public List getEdges() { - return edges; + public List> getEdges() { + return Collections.unmodifiableList(edges); } - public void setEdges(List edges) { + /** + * @deprecated prefer {@link #DefaultConnection(List, PageInfo)} and avoid mutation + */ + @Deprecated + public void setEdges(List> edges) { + if (edges == null) { // TODO remove setter + edges = Collections.emptyList(); + } this.edges = edges; } @@ -23,6 +55,10 @@ public PageInfo getPageInfo() { return pageInfo; } + /** + * @deprecated prefer {@link #DefaultConnection(List, PageInfo)} and avoid mutation + */ + @Deprecated public void setPageInfo(PageInfo pageInfo) { this.pageInfo = pageInfo; } diff --git a/src/main/java/graphql/relay/DefaultEdge.java b/src/main/java/graphql/relay/DefaultEdge.java index 4dd2965593..70cecea31f 100644 --- a/src/main/java/graphql/relay/DefaultEdge.java +++ b/src/main/java/graphql/relay/DefaultEdge.java @@ -1,21 +1,38 @@ package graphql.relay; -public class DefaultEdge implements Edge { - - public DefaultEdge(Object node, DefaultConnectionCursor cursor) { +public class DefaultEdge implements Edge { + + public DefaultEdge(T node, ConnectionCursor cursor) { + if (node == null) { + throw new IllegalArgumentException("node cannot be null"); + } + if (cursor == null) { + throw new IllegalArgumentException("cursor cannot be null"); + } this.node = node; this.cursor = cursor; } - private Object node; + /** + * @deprecated prefer {@link #DefaultEdge(Object, ConnectionCursor)} + */ + @Deprecated + public DefaultEdge() { + } + + private T node; private ConnectionCursor cursor; @Override - public Object getNode() { + public T getNode() { return node; } - public void setNode(Object node) { + /** + * @deprecated prefer {@link #DefaultEdge(Object, ConnectionCursor)} and avoid mutation. + */ + @Deprecated + public void setNode(T node) { this.node = node; } @@ -24,6 +41,10 @@ public ConnectionCursor getCursor() { return cursor; } + /** + * @deprecated prefer {@link #DefaultEdge(Object, ConnectionCursor)} and avoid mutation. + */ + @Deprecated public void setCursor(ConnectionCursor cursor) { this.cursor = cursor; } diff --git a/src/main/java/graphql/relay/DefaultPageInfo.java b/src/main/java/graphql/relay/DefaultPageInfo.java index d2e06e2e67..5ee2587646 100644 --- a/src/main/java/graphql/relay/DefaultPageInfo.java +++ b/src/main/java/graphql/relay/DefaultPageInfo.java @@ -7,11 +7,29 @@ public class DefaultPageInfo implements PageInfo { private boolean hasPreviousPage; private boolean hasNextPage; + /** + * @deprecated prefer {@link #DefaultPageInfo(ConnectionCursor, ConnectionCursor, boolean, boolean)} + */ + @Deprecated + public DefaultPageInfo() { + } + + public DefaultPageInfo(ConnectionCursor startCursor, ConnectionCursor endCursor, boolean hasPreviousPage, boolean hasNextPage) { + this.startCursor = startCursor; + this.endCursor = endCursor; + this.hasPreviousPage = hasPreviousPage; + this.hasNextPage = hasNextPage; + } + @Override public ConnectionCursor getStartCursor() { return startCursor; } + /** + * @deprecated prefer {@link #DefaultPageInfo(ConnectionCursor, ConnectionCursor, boolean, boolean)} and avoid mutation + */ + @Deprecated public void setStartCursor(ConnectionCursor startCursor) { this.startCursor = startCursor; } @@ -21,6 +39,10 @@ public ConnectionCursor getEndCursor() { return endCursor; } + /** + * @deprecated prefer {@link #DefaultPageInfo(ConnectionCursor, ConnectionCursor, boolean, boolean)} and avoid mutation + */ + @Deprecated public void setEndCursor(ConnectionCursor endCursor) { this.endCursor = endCursor; } @@ -30,6 +52,10 @@ public boolean isHasPreviousPage() { return hasPreviousPage; } + /** + * @deprecated prefer {@link #DefaultPageInfo(ConnectionCursor, ConnectionCursor, boolean, boolean)} and avoid mutation + */ + @Deprecated public void setHasPreviousPage(boolean hasPreviousPage) { this.hasPreviousPage = hasPreviousPage; } @@ -39,6 +65,10 @@ public boolean isHasNextPage() { return hasNextPage; } + /** + * @deprecated prefer {@link #DefaultPageInfo(ConnectionCursor, ConnectionCursor, boolean, boolean)} + */ + @Deprecated public void setHasNextPage(boolean hasNextPage) { this.hasNextPage = hasNextPage; } diff --git a/src/main/java/graphql/relay/Edge.java b/src/main/java/graphql/relay/Edge.java index 507ea69398..979daf077e 100644 --- a/src/main/java/graphql/relay/Edge.java +++ b/src/main/java/graphql/relay/Edge.java @@ -3,9 +3,9 @@ /** * represents an edge in relay. */ -public interface Edge { +public interface Edge { - Object getNode(); + T getNode(); ConnectionCursor getCursor(); diff --git a/src/main/java/graphql/relay/PageInfo.java b/src/main/java/graphql/relay/PageInfo.java index 11e66d0e7e..70ec31192b 100644 --- a/src/main/java/graphql/relay/PageInfo.java +++ b/src/main/java/graphql/relay/PageInfo.java @@ -5,12 +5,23 @@ */ public interface PageInfo { + /** + * @return cursor to the first edge, or null if this page is empty. + */ ConnectionCursor getStartCursor(); + /** + * @return cursor to the last edge, or null if this page is empty. + */ ConnectionCursor getEndCursor(); + /** + * @return true if and only if this page is not the first page. only meaningful when you gave {@code last} argument. + */ boolean isHasPreviousPage(); + /** + * @return true if and only if this page is not the last page. only meaningful when you gave {@code first} argument. + */ boolean isHasNextPage(); - } diff --git a/src/main/java/graphql/relay/Relay.java b/src/main/java/graphql/relay/Relay.java index 54f4dacc5c..a0ece9743a 100644 --- a/src/main/java/graphql/relay/Relay.java +++ b/src/main/java/graphql/relay/Relay.java @@ -17,6 +17,7 @@ public class Relay { public static final String NODE = "Node"; + private GraphQLObjectType pageInfoType = newObject() .name("PageInfo") .description("Information about pagination in a connection.") @@ -39,7 +40,7 @@ public class Relay { .build(); public GraphQLInterfaceType nodeInterface(TypeResolver typeResolver) { - GraphQLInterfaceType node = newInterface() + return newInterface() .name(NODE) .description("An object with an ID") .typeResolver(typeResolver) @@ -48,11 +49,10 @@ public GraphQLInterfaceType nodeInterface(TypeResolver typeResolver) { .description("The ID of an object") .type(new GraphQLNonNull(GraphQLID))) .build(); - return node; } public GraphQLFieldDefinition nodeField(GraphQLInterfaceType nodeInterface, DataFetcher nodeDataFetcher) { - GraphQLFieldDefinition fieldDefinition = newFieldDefinition() + return newFieldDefinition() .name("node") .description("Fetches an object given its ID") .type(nodeInterface) @@ -62,26 +62,28 @@ public GraphQLFieldDefinition nodeField(GraphQLInterfaceType nodeInterface, Data .description("The ID of an object") .type(new GraphQLNonNull(GraphQLID))) .build(); - return fieldDefinition; } public List getConnectionFieldArguments() { List args = new ArrayList(); - args.add(newArgument() .name("before") + .description("fetching only nodes before this node (exclusive)") .type(GraphQLString) .build()); args.add(newArgument() .name("after") + .description("fetching only nodes after this node (exclusive)") .type(GraphQLString) .build()); args.add(newArgument() .name("first") + .description("fetching only the first certain number of nodes") .type(GraphQLInt) .build()); args.add(newArgument() .name("last") + .description("fetching only the last certain number of nodes") .type(GraphQLInt) .build()); return args; @@ -89,13 +91,14 @@ public List getConnectionFieldArguments() { public List getBackwardPaginationConnectionFieldArguments() { List args = new ArrayList(); - args.add(newArgument() .name("before") + .description("fetching only nodes before this node (exclusive)") .type(GraphQLString) .build()); args.add(newArgument() .name("last") + .description("fetching only the last certain number of nodes") .type(GraphQLInt) .build()); return args; @@ -103,23 +106,23 @@ public List getBackwardPaginationConnectionFieldArguments() { public List getForwardPaginationConnectionFieldArguments() { List args = new ArrayList(); - args.add(newArgument() .name("after") + .description("fetching only nodes after this node (exclusive)") .type(GraphQLString) .build()); args.add(newArgument() .name("first") + .description("fetching only the first certain number of nodes") .type(GraphQLInt) .build()); return args; } public GraphQLObjectType edgeType(String name, GraphQLOutputType nodeType, GraphQLInterfaceType nodeInterface, List edgeFields) { - - GraphQLObjectType edgeType = newObject() + return newObject() .name(name + "Edge") - .description("An edge in a connection.") + .description("An edge in a connection") .field(newFieldDefinition() .name("node") .type(nodeType) @@ -127,26 +130,25 @@ public GraphQLObjectType edgeType(String name, GraphQLOutputType nodeType, Graph .field(newFieldDefinition() .name("cursor") .type(new GraphQLNonNull(GraphQLString)) - .description("")) + .description("cursor marks a unique position or index into the connection")) .fields(edgeFields) .build(); - return edgeType; } public GraphQLObjectType connectionType(String name, GraphQLObjectType edgeType, List connectionFields) { - - GraphQLObjectType connectionType = newObject() + return newObject() .name(name + "Connection") .description("A connection to a list of items.") .field(newFieldDefinition() .name("edges") + .description("a list of edges") .type(new GraphQLList(edgeType))) .field(newFieldDefinition() .name("pageInfo") + .description("details about this specific page") .type(new GraphQLNonNull(pageInfoType))) .fields(connectionFields) .build(); - return connectionType; } diff --git a/src/main/java/graphql/relay/SimpleListConnection.java b/src/main/java/graphql/relay/SimpleListConnection.java index 07f0e16521..1f752b13f9 100644 --- a/src/main/java/graphql/relay/SimpleListConnection.java +++ b/src/main/java/graphql/relay/SimpleListConnection.java @@ -2,17 +2,17 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; - import java.util.ArrayList; +import java.util.Collections; import java.util.List; -public class SimpleListConnection implements DataFetcher { +public class SimpleListConnection implements DataFetcher> { private static final String DUMMY_CURSOR_PREFIX = "simple-cursor"; private final String prefix; - private final List data; + private final List data; - public SimpleListConnection(List data, String prefix) { + public SimpleListConnection(List data, String prefix) { if (prefix == null || prefix.length() == 0) { throw new IllegalArgumentException("prefix cannot be null or empty"); } @@ -20,23 +20,23 @@ public SimpleListConnection(List data, String prefix) { this.data = data; } - public SimpleListConnection(List data) { + public SimpleListConnection(List data) { this(data, DUMMY_CURSOR_PREFIX); } - private List buildEdges() { - List edges = new ArrayList(); + private List> buildEdges() { + List> edges = new ArrayList>(); int ix = 0; - for (Object object : data) { - edges.add(new DefaultEdge(object, new DefaultConnectionCursor(createCursor(ix++)))); + for (T object : data) { + edges.add(new DefaultEdge(object, new DefaultConnectionCursor(createCursor(ix++)))); } return edges; } @Override - public Object get(DataFetchingEnvironment environment) { + public Connection get(DataFetchingEnvironment environment) { - List edges = buildEdges(); + List> edges = buildEdges(); int afterOffset = getOffsetFromCursor(environment.getArgument("after"), -1); int begin = Math.max(afterOffset, -1) + 1; @@ -61,46 +61,52 @@ public Object get(DataFetchingEnvironment environment) { edges = edges.subList(last > edges.size() ? 0 : edges.size() - last, edges.size()); } - if (edges.size() == 0) { + if (edges.isEmpty()) { return emptyConnection(); } - Edge firstEdge = edges.get(0); - Edge lastEdge = edges.get(edges.size() - 1); - - DefaultPageInfo pageInfo = new DefaultPageInfo(); - pageInfo.setStartCursor(firstEdge.getCursor()); - pageInfo.setEndCursor(lastEdge.getCursor()); - pageInfo.setHasPreviousPage(!firstEdge.getCursor().equals(firstPresliceCursor)); - pageInfo.setHasNextPage(!lastEdge.getCursor().equals(lastPresliceCursor)); + Edge firstEdge = edges.get(0); + Edge lastEdge = edges.get(edges.size() - 1); - DefaultConnection connection = new DefaultConnection(); - connection.setEdges(edges); - connection.setPageInfo(pageInfo); + PageInfo pageInfo = new DefaultPageInfo( + firstEdge.getCursor(), + lastEdge.getCursor(), + !firstEdge.getCursor().equals(firstPresliceCursor), + !lastEdge.getCursor().equals(lastPresliceCursor) + ); - return connection; + return new DefaultConnection( + edges, + pageInfo + ); } - private Connection emptyConnection() { - DefaultConnection connection = new DefaultConnection(); - connection.setPageInfo(new DefaultPageInfo()); - return connection; + private Connection emptyConnection() { + PageInfo pageInfo = new DefaultPageInfo(null, null, false, false); + return new DefaultConnection(Collections.>emptyList(), pageInfo); } - public ConnectionCursor cursorForObjectInConnection(Object object) { + /** + * find the object's cursor, or null if the object is not in this connection. + */ + public ConnectionCursor cursorForObjectInConnection(T object) { int index = data.indexOf(object); + if (index == -1) { + return null; + } String cursor = createCursor(index); return new DefaultConnectionCursor(cursor); } private int getOffsetFromCursor(String cursor, int defaultValue) { - if (cursor == null) return defaultValue; + if (cursor == null) { + return defaultValue; + } String string = Base64.fromBase64(cursor); return Integer.parseInt(string.substring(prefix.length())); } private String createCursor(int offset) { - String string = Base64.toBase64(prefix + Integer.toString(offset)); - return string; + return Base64.toBase64(prefix + Integer.toString(offset)); } } \ No newline at end of file From f68679657754f682935bc233a3972c32c84a603a Mon Sep 17 00:00:00 2001 From: Jiayu Liu Date: Fri, 7 Apr 2017 09:34:19 +0800 Subject: [PATCH 2/2] fix with java8 --- src/main/java/graphql/relay/DefaultConnection.java | 2 +- src/main/java/graphql/relay/SimpleListConnection.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/graphql/relay/DefaultConnection.java b/src/main/java/graphql/relay/DefaultConnection.java index 6c340ba712..d3b463c217 100644 --- a/src/main/java/graphql/relay/DefaultConnection.java +++ b/src/main/java/graphql/relay/DefaultConnection.java @@ -6,7 +6,7 @@ public class DefaultConnection implements Connection { - private List> edges = new ArrayList>(); + private List> edges = new ArrayList<>(); private PageInfo pageInfo; diff --git a/src/main/java/graphql/relay/SimpleListConnection.java b/src/main/java/graphql/relay/SimpleListConnection.java index 1f752b13f9..8267b6ba36 100644 --- a/src/main/java/graphql/relay/SimpleListConnection.java +++ b/src/main/java/graphql/relay/SimpleListConnection.java @@ -25,10 +25,10 @@ public SimpleListConnection(List data) { } private List> buildEdges() { - List> edges = new ArrayList>(); + List> edges = new ArrayList<>(); int ix = 0; for (T object : data) { - edges.add(new DefaultEdge(object, new DefaultConnectionCursor(createCursor(ix++)))); + edges.add(new DefaultEdge<>(object, new DefaultConnectionCursor(createCursor(ix++)))); } return edges; } @@ -83,7 +83,7 @@ public Connection get(DataFetchingEnvironment environment) { private Connection emptyConnection() { PageInfo pageInfo = new DefaultPageInfo(null, null, false, false); - return new DefaultConnection(Collections.>emptyList(), pageInfo); + return new DefaultConnection<>(Collections.emptyList(), pageInfo); } /**