You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2021/05/12 14:38:25 UTC
[sling-org-apache-sling-graphql-core] 01/01: SLING-10309 - GraphQL
results pagination
This is an automated email from the ASF dual-hosted git repository.
radu pushed a commit to branch issue/SLING-10309
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-core.git
commit d22fac658e088244a6f3e5ff1f12df2591cb0f83
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Wed May 12 16:37:58 2021 +0200
SLING-10309 - GraphQL results pagination
* added basic support for the connection directive
---
.../graphql/core/engine/DefaultQueryExecutor.java | 50 +++++++++++++++++++++-
src/test/resources/paginated-humans-schema.txt | 30 ++++---------
2 files changed, 57 insertions(+), 23 deletions(-)
diff --git a/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java b/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java
index c4f70e2..843f281 100644
--- a/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java
+++ b/src/main/java/org/apache/sling/graphql/core/engine/DefaultQueryExecutor.java
@@ -23,6 +23,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -62,10 +63,13 @@ import graphql.language.Argument;
import graphql.language.Directive;
import graphql.language.FieldDefinition;
import graphql.language.InterfaceTypeDefinition;
+import graphql.language.ListType;
+import graphql.language.NonNullType;
import graphql.language.ObjectTypeDefinition;
import graphql.language.SourceLocation;
import graphql.language.StringValue;
import graphql.language.TypeDefinition;
+import graphql.language.TypeName;
import graphql.language.UnionTypeDefinition;
import graphql.schema.DataFetcher;
import graphql.schema.GraphQLScalarType;
@@ -94,6 +98,12 @@ public class DefaultQueryExecutor implements QueryExecutor {
public static final String RESOLVER_OPTIONS = "options";
public static final String RESOLVER_SOURCE = "source";
+ public static final String CONNECTION_FOR = "for";
+ public static final String CONNECTION_FETCHER = "fetcher";
+ public static final String TYPE_STRING = "String";
+ public static final String TYPE_BOOLEAN = "Boolean";
+ public static final String TYPE_PAGE_INFO = "PageInfo";
+
private static final LogSanitizer cleanLog = new LogSanitizer();
private Map<String, String> resourceToHashMap;
@@ -239,6 +249,7 @@ public class DefaultQueryExecutor implements QueryExecutor {
throw new SlingGraphQLException("Exception while building wiring.", e);
}
}
+ handleConnectionTypes(type, typeRegistry);
return typeWiring;
});
}
@@ -251,7 +262,6 @@ public class DefaultQueryExecutor implements QueryExecutor {
for (InterfaceTypeDefinition type : interfaceTypes) {
wireTypeResolver(builder, type, r);
}
-
return builder.build();
}
@@ -380,6 +390,44 @@ public class DefaultQueryExecutor implements QueryExecutor {
return resource.getPath() + ":" + String.join(".", selectors);
}
+ private void handleConnectionTypes(ObjectTypeDefinition typeDefinition, TypeDefinitionRegistry typeRegistry) {
+ for (FieldDefinition fieldDefinition : typeDefinition.getFieldDefinitions()) {
+ Directive directive = fieldDefinition.getDirective("connection");
+ if (directive != null) {
+ if (directive.getArgument(CONNECTION_FOR) != null) {
+ String forType = ((StringValue) directive.getArgument(CONNECTION_FOR).getValue()).getValue();
+ Optional<TypeDefinition> forTypeDefinition = typeRegistry.getType(forType);
+ if (!forTypeDefinition.isPresent()) {
+ throw new SlingGraphQLException("Type '" + forType + "' has not been defined.");
+ }
+ ObjectTypeDefinition forOTD = (ObjectTypeDefinition) forTypeDefinition.get();
+ ObjectTypeDefinition edge = ObjectTypeDefinition.newObjectTypeDefinition().name(forOTD.getName() + "Edge")
+ .fieldDefinition(new FieldDefinition("cursor", new TypeName(TYPE_STRING)))
+ .fieldDefinition(new FieldDefinition("node", new TypeName(forOTD.getName())))
+ .build();
+ ObjectTypeDefinition connection = ObjectTypeDefinition.newObjectTypeDefinition().name(forOTD.getName() +
+ "Connection")
+ .fieldDefinition(new FieldDefinition("edges", new ListType(new TypeName(forType + "Edge"))))
+ .fieldDefinition(new FieldDefinition("pageInfo", new TypeName(TYPE_PAGE_INFO)))
+ .build();
+ if (!typeRegistry.getType(TYPE_PAGE_INFO).isPresent()) {
+ ObjectTypeDefinition pageInfo = ObjectTypeDefinition.newObjectTypeDefinition().name(TYPE_PAGE_INFO)
+ .fieldDefinition(new FieldDefinition("hasPreviousPage", new NonNullType(new TypeName(TYPE_BOOLEAN))))
+ .fieldDefinition(new FieldDefinition("hasNextPage", new NonNullType(new TypeName(TYPE_BOOLEAN))))
+ .fieldDefinition(new FieldDefinition("startCursor", new TypeName(TYPE_STRING)))
+ .fieldDefinition(new FieldDefinition("endCursor", new TypeName(TYPE_STRING)))
+ .build();
+ typeRegistry.add(pageInfo);
+ }
+ typeRegistry.add(edge);
+ typeRegistry.add(connection);
+ } else {
+ throw new SlingGraphQLException("The connection directive requires a 'for' argument.");
+ }
+ }
+ }
+ }
+
private static class LRUCache<T> extends LinkedHashMap<String, T> {
private final int capacity;
diff --git a/src/test/resources/paginated-humans-schema.txt b/src/test/resources/paginated-humans-schema.txt
index 62c1e40..3fecc3d 100644
--- a/src/test/resources/paginated-humans-schema.txt
+++ b/src/test/resources/paginated-humans-schema.txt
@@ -22,8 +22,15 @@ directive @fetcher(
source : String = ""
) on FIELD_DEFINITION
+# This directive will generate the additional types for a Connection,
+# according to the Relay Connections specification from
+# https://relay.dev/graphql/connections.htm
+directive @connection(
+ for : String
+) on FIELD_DEFINITION
+
type Query {
- paginatedHumans (after : String, limit : Int) : HumanConnection @fetcher(name:"humans/connection")
+ paginatedHumans (after : String, limit : Int) : HumanConnection @connection(for: "Human") @fetcher(name:"humans/connection")
}
type Human {
@@ -31,24 +38,3 @@ type Human {
name: String!
address: String
}
-
-# The connection-specific parts of the schema might be
-# generated based on a schema directive later, but
-# writing them "by hand" also works
-
-type PageInfo {
- startCursor : String
- endCursor : String
- hasPreviousPage : Boolean
- hasNextPage : Boolean
-}
-
-type HumanEdge {
- cursor: String
- node: Human
-}
-
-type HumanConnection {
- edges : [HumanEdge]
- pageInfo : PageInfo
-}
\ No newline at end of file