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:24 UTC

[sling-org-apache-sling-graphql-core] branch issue/SLING-10309 created (now d22fac6)

This is an automated email from the ASF dual-hosted git repository.

radu pushed a change to branch issue/SLING-10309
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-core.git.


      at d22fac6  SLING-10309 - GraphQL results pagination

This branch includes the following new commits:

     new d22fac6  SLING-10309 - GraphQL results pagination

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[sling-org-apache-sling-graphql-core] 01/01: SLING-10309 - GraphQL results pagination

Posted by ra...@apache.org.
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