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 2020/09/15 13:19:23 UTC

[sling-org-apache-sling-graphql-core] branch master updated (86494f1 -> 4789c4a)

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

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


    from 86494f1  Add Persisted Query Hash section
     new df6e19c  SLING-9720 - Present the persisted queries endpoints using the same extension the GraphQL servlet uses
     new 4789c4a  trivial: reformatted some README.md sections

The 2 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.


Summary of changes:
 README.md                                          | 74 ++++++++++++----------
 .../sling/graphql/core/servlet/GraphQLServlet.java | 50 +++++++++++----
 .../sling/graphql/core/it/GraphQLServletIT.java    | 16 +++--
 3 files changed, 89 insertions(+), 51 deletions(-)


[sling-org-apache-sling-graphql-core] 02/02: trivial: reformatted some README.md sections

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-core.git

commit 4789c4ab2684ac3395ba4e47f578ddcdb4c556fe
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Tue Sep 15 15:18:27 2020 +0200

    trivial: reformatted some README.md sections
---
 README.md | 68 +++++++++++++++++++++++++++++++++++----------------------------
 1 file changed, 38 insertions(+), 30 deletions(-)

diff --git a/README.md b/README.md
index 1419891..ef2f229 100644
--- a/README.md
+++ b/README.md
@@ -43,19 +43,23 @@ See also the _caching_ section later in this file.
 
 Schemas are provided by `SchemaProvider` services:
 
-    public interface SchemaProvider {
+```java
+@ProviderType
+public interface SchemaProvider {
   
-      /** Get a GraphQL Schema definition for the given resource and optional selectors
-       *
-       *  @param r The Resource to which the schema applies
-       *  @param selectors Optional set of Request Selectors that can influence the schema selection
-       *  @return a GraphQL schema that can be annotated to define the data fetchers to use, see
-       *      this module's documentation. Can return null if a schema cannot be provided, in which
-       *      case a different provider should be used.
-       */
-      @Nullable
-      String getSchema(@NotNull Resource r, @Nullable String [] selectors) throws IOException;
-    }
+    /** Get a GraphQL Schema definition for the given resource and optional selectors
+     *
+     *  @param r The Resource to which the schema applies
+     *  @param selectors Optional set of Request Selectors that can influence the schema selection
+     *  @return a GraphQL schema that can be annotated to define the data fetchers to use, see
+     *      this module's documentation. Can return null if a schema cannot be provided, in which
+     *      case a different provider should be used.
+     *  @throws java.io.IOException if the schema cannot be retrieved
+     */
+    @Nullable
+    String getSchema(@NotNull Resource r, @Nullable String [] selectors) throws IOException;
+}
+```
 
 The default provider makes an internal Sling request with for the current Resource with a `.GQLschema` extension.
 
@@ -105,22 +109,22 @@ The `<options>` and `<source>` arguments of the directive can be used by the
 
 Besides Java, `SlingDataFetcher` scripts can be written in any scripting language that supported by the Sling instance's configuration.
 
-Here's an example from the test code.
-
-The schema contains the following statement:
+Here's an example from the test code. The schema contains the following statement:
 
     scriptedFetcher (testing : String) : Test @fetcher(name:"scripted/example")
 
 And here's the data fetcher code:
 
-    var result = { 
-        boolValue: true,
-        resourcePath: "From the test script: " + resource.path,
-        testingArgument: environment.getArgument("testing"),
-        anotherValue: 450 + 1
-    };
+```javascript
+var result = { 
+    boolValue: true,
+    resourcePath: "From the test script: " + resource.path,
+    testingArgument: environment.getArgument("testing"),
+    anotherValue: 450 + 1
+};
 
-    result;
+result;
+```
     
 The data fetcher provider then looks for a script that handles the `graphql/fetchers/scripted/example` resource type with a `fetcher`script name. `graphql/fetchers`is a prefix (hardcoded for now) and `scripted/example` comes from the above schema's `@fetcher` directive.
 
@@ -150,17 +154,21 @@ the parameter becoming too large for HTTP services and intermediates.
 
 #### Persisted Query Hash
 
-The hash that's part of the `persisted` URL is computed on the POSTed GraphQL query by the active `GraphQLCacheProvider` service. By 
-default this is the `SimpleGraphQLCacheProvider` which computes it as follows:
+The hash that's part of the `persisted` URL is computed on the POSTed GraphQL query by the
+active `GraphQLCacheProvider` service. By default, this is the `SimpleGraphQLCacheProvider`
+which computes it as follows:
 
-    MessageDigest digest = MessageDigest.getInstance("SHA-256");
-    byte[] hash = digest.digest(query.getBytes(StandardCharsets.UTF_8));
+```java
+MessageDigest digest = MessageDigest.getInstance("SHA-256");
+byte[] hash = digest.digest(query.getBytes(StandardCharsets.UTF_8));
+```
     
-And encodes it in hex to build the persisted query's path.
+and encodes it in hex to build the persisted query's path.
 
-This means that, if desired, an optimistic client can compute the hash itself and try a GET to the `persisted/<hash>` URL without doing
-a POST first. If the query is already cache this saves the POST request, and if not the client gets a 404 status and has to POST the
-query first.
+This means that, if desired, an optimistic client can compute the hash itself and try a GET to
+the `persisted/<hash>` URL without doing a POST first. If the query already exists in the cache
+this saves the POST request, and if not the client gets a 404 status and has to POST the query
+first.
 
 #### Example HTTP interactions with persisted queries enabled
 


[sling-org-apache-sling-graphql-core] 01/02: SLING-9720 - Present the persisted queries endpoints using the same extension the GraphQL servlet uses

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-core.git

commit df6e19cfb021ed7c23667e367c3d8922ca15a39d
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Tue Sep 15 15:10:16 2020 +0200

    SLING-9720 - Present the persisted queries endpoints using the same extension the GraphQL servlet uses
    
    * validate the persisted queries suffix against the request's current extension
---
 README.md                                          |  6 +--
 .../sling/graphql/core/servlet/GraphQLServlet.java | 50 ++++++++++++++++------
 .../sling/graphql/core/it/GraphQLServletIT.java    | 16 +++++--
 3 files changed, 51 insertions(+), 21 deletions(-)

diff --git a/README.md b/README.md
index 129c4c5..1419891 100644
--- a/README.md
+++ b/README.md
@@ -183,13 +183,13 @@ query first.
     < Date: Mon, 31 Aug 2020 16:33:48 GMT
     < X-Content-Type-Options: nosniff
     < X-Frame-Options: SAMEORIGIN
-    < Location: http://localhost:8080/graphql.json/persisted/e1ce2e205e1dfb3969627c6f417860cadab696e0e87b1c44de1438848661b62f
+    < Location: http://localhost:8080/graphql.json/persisted/e1ce2e205e1dfb3969627c6f417860cadab696e0e87b1c44de1438848661b62f.json
     < Content-Length: 0
     ```
 2. Running a persisted query
 ```bash
-curl -v http://localhost:8080/graphql.json/persisted/e1ce2e205e1dfb3969627c6f417860cadab696e0e87b1c44de1438848661b62f
-> GET /graphql.json/persisted/e1ce2e205e1dfb3969627c6f417860cadab696e0e87b1c44de1438848661b62f HTTP/1.1
+curl -v http://localhost:8080/graphql.json/persisted/e1ce2e205e1dfb3969627c6f417860cadab696e0e87b1c44de1438848661b62f.json
+> GET /graphql.json/persisted/e1ce2e205e1dfb3969627c6f417860cadab696e0e87b1c44de1438848661b62f.json HTTP/1.1
 > Host: localhost:8080
 > User-Agent: curl/7.64.1
 > Accept: */*
diff --git a/src/main/java/org/apache/sling/graphql/core/servlet/GraphQLServlet.java b/src/main/java/org/apache/sling/graphql/core/servlet/GraphQLServlet.java
index d346468..775bb33 100644
--- a/src/main/java/org/apache/sling/graphql/core/servlet/GraphQLServlet.java
+++ b/src/main/java/org/apache/sling/graphql/core/servlet/GraphQLServlet.java
@@ -135,11 +135,24 @@ public class GraphQLServlet extends SlingAllMethodsServlet {
 
     @Activate
     private void activate(Config config) {
+        String[] extensions = config.sling_servlet_extensions();
+        StringBuilder extensionsPattern = new StringBuilder();
+        for (String extension : extensions) {
+            if (extensionsPattern.length() > 0) {
+                extensionsPattern.append("|");
+            }
+            extensionsPattern.append(extension);
+        }
+        if (extensionsPattern.length() > 0) {
+            extensionsPattern.insert(0, "(");
+            extensionsPattern.append(")");
+        }
         cacheControlMaxAge = config.cache$_$control_max$_$age() >= 0 ? config.cache$_$control_max$_$age() : 0;
         String suffix = config.persistedQueries_suffix();
         if (StringUtils.isNotEmpty(suffix) && suffix.startsWith("/")) {
             suffixPersisted = suffix;
-            patternGetPersistedQuery = Pattern.compile("^" + suffixPersisted + "/([a-f0-9]{64})$");
+            patternGetPersistedQuery = Pattern.compile("^" + suffixPersisted + "/([a-f0-9]{64})" + (extensionsPattern.length() > 0 ?
+                    "\\." + extensionsPattern.toString()  + "$" : "$"));
         } else {
             suffixPersisted = null;
             patternGetPersistedQuery = null;
@@ -154,20 +167,27 @@ public class GraphQLServlet extends SlingAllMethodsServlet {
                 Matcher matcher = patternGetPersistedQuery.matcher(suffix);
                 if (matcher.matches()) {
                     String queryHash = matcher.group(1);
-                    if (StringUtils.isNotEmpty(queryHash)) {
-                        String query = cacheProvider.getQuery(queryHash, request.getResource().getResourceType(),
-                                request.getRequestPathInfo().getSelectorString());
-                        if (query != null) {
-                            boolean isAuthenticated = request.getHeaders("Authorization").hasMoreElements();
-                            StringBuilder cacheControlValue = new StringBuilder("max-age=").append(cacheControlMaxAge);
-                            if (isAuthenticated) {
-                                cacheControlValue.append(",private");
+                    String extension = matcher.group(2);
+                    String requestExtension = request.getRequestPathInfo().getExtension();
+                    if (requestExtension != null && requestExtension.equals(extension)) {
+                        if (StringUtils.isNotEmpty(queryHash)) {
+                            String query = cacheProvider.getQuery(queryHash, request.getResource().getResourceType(),
+                                    request.getRequestPathInfo().getSelectorString());
+                            if (query != null) {
+                                boolean isAuthenticated = request.getHeaders("Authorization").hasMoreElements();
+                                StringBuilder cacheControlValue = new StringBuilder("max-age=").append(cacheControlMaxAge);
+                                if (isAuthenticated) {
+                                    cacheControlValue.append(",private");
+                                }
+                                response.addHeader("Cache-Control", cacheControlValue.toString());
+                                execute(query, request, response);
+                            } else {
+                                response.sendError(HttpServletResponse.SC_NOT_FOUND, "Cannot find persisted query " + queryHash);
                             }
-                            response.addHeader("Cache-Control", cacheControlValue.toString());
-                            execute(query, request, response);
-                        } else {
-                            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Cannot find persisted query " + queryHash);
                         }
+                    } else {
+                        response.sendError(HttpServletResponse.SC_BAD_REQUEST, "The persisted query's extension does not match the " +
+                                "servlet extension.");
                     }
                 } else {
                     response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unexpected hash.");
@@ -245,7 +265,9 @@ public class GraphQLServlet extends SlingAllMethodsServlet {
         if (localPort != 80 && localPort != 443) {
             location.append(":").append(localPort);
         }
-        location.append(request.getContextPath()).append(request.getPathInfo()).append("/").append(hash);
+        String extension = request.getRequestPathInfo().getExtension();
+        location.append(request.getContextPath()).append(request.getPathInfo()).append("/").append(hash)
+                .append(StringUtils.isNotEmpty(extension) ? "." + extension : "");
         return location.toString();
     }
 
diff --git a/src/test/java/org/apache/sling/graphql/core/it/GraphQLServletIT.java b/src/test/java/org/apache/sling/graphql/core/it/GraphQLServletIT.java
index fc6356c..ced345b 100644
--- a/src/test/java/org/apache/sling/graphql/core/it/GraphQLServletIT.java
+++ b/src/test/java/org/apache/sling/graphql/core/it/GraphQLServletIT.java
@@ -125,9 +125,14 @@ public class GraphQLServletIT extends GraphQLCoreTestSupport {
     public void testPersistedQueriesBasic() throws Exception {
         String queryHash = "a16982712f6ecdeba5d950d42e3c13df0fc26d008c497f6bf012701b57e02a51";
         MockSlingHttpServletResponse response = persistQuery("/graphql/two.gql", "{ currentResource { resourceType name } }", null);
-        assertEquals("http://localhost/graphql/two.gql/persisted/" + queryHash, response.getHeader("Location"));
-
-        response = executeRequest("GET", "/graphql/two.gql/persisted/" + queryHash, null, "application/json", new StringReader(""),200);
+        assertEquals("Expected to have stored a persisted query.", 201, response.getStatus());
+        assertEquals("The value of the Location header does not look correct.",
+                "http://localhost/graphql/two.gql/persisted/" + queryHash + ".gql",
+                response.getHeader("Location"));
+
+        response =
+                executeRequest("GET", "/graphql/two.gql/persisted/" + queryHash + ".gql", null, "application/json", new StringReader(""),
+                        200);
         assertEquals("max-age=60", response.getHeader("Cache-Control"));
         final String json = response.getOutputAsString();
         assertThat(json, hasJsonPath("$.data.currentResource.resourceType", equalTo("graphql/test/two")));
@@ -161,12 +166,15 @@ public class GraphQLServletIT extends GraphQLCoreTestSupport {
             post.setEntity(new ByteArrayEntity(json.getBytes(), ContentType.APPLICATION_JSON));
 
             try (CloseableHttpResponse postResponse = client.execute(targetHost, post, context)) {
+                assertEquals("Expected to have stored a persisted query.", 201, postResponse.getStatusLine().getStatusCode());
                 Header locationHeader = postResponse.getFirstHeader(HttpHeaders.LOCATION);
                 assertNotNull(locationHeader);
                 String location = locationHeader.getValue();
                 HttpGet get = new HttpGet(location);
-                try (CloseableHttpResponse getResponse = client.execute(targetHost, get, context)){
+                try (CloseableHttpResponse getResponse = client.execute(targetHost, get, context)) {
+                    assertEquals("Expected to find a persisted query.", 200, getResponse.getStatusLine().getStatusCode());
                     Header cacheControl = getResponse.getFirstHeader("Cache-Control");
+                    assertNotNull("Expected a Cache-Control header.", cacheControl);
                     assertEquals("max-age=60,private", cacheControl.getValue());
                     String getJson = IOUtils.toString(getResponse.getEntity().getContent());
                     assertThat(getJson, hasJsonPath("$.data.currentResource.resourceType", equalTo("graphql/test/two")));