You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2020/05/12 15:07:59 UTC

[sling-org-apache-sling-graphql-core] branch master updated: Take request selectors into account for schema generation

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

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


The following commit(s) were added to refs/heads/master by this push:
     new d3b8c21  Take request selectors into account for schema generation
d3b8c21 is described below

commit d3b8c21e571fb453e8ef301f438ab8674932d92f
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Tue May 12 17:07:43 2020 +0200

    Take request selectors into account for schema generation
---
 .../graphql/core/engine/GraphQLResourceQuery.java  | 12 ++++---
 .../sling/graphql/core/json/JsonSerializer.java    | 15 ++++++++-
 .../graphql/core/schema/DefaultSchemaProvider.java | 11 ++++++-
 .../core/scripting/GraphQLScriptEngine.java        |  2 +-
 .../sling/graphql/core/servlet/GraphQLServlet.java |  3 +-
 .../core/engine/GraphQLResourceQueryTest.java      | 24 +++++++++++---
 .../core/it/GraphQLScriptingTestSupport.java       | 17 ----------
 .../sling/graphql/core/it/GraphQLServletIT.java    | 18 ++++++----
 .../graphql/core/it/PipeDataFetcherProvider.java   | 20 +++++-------
 .../graphql/core/mocks/MockSchemaProvider.java     | 38 +++++++++++++++-------
 .../apps/graphql/test/two/testing.GQLschema.jsp    | 35 ++++++++++++++++++++
 src/test/resources/test-schema-selected-foryou.txt | 32 ++++++++++++++++++
 12 files changed, 169 insertions(+), 58 deletions(-)

diff --git a/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java b/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java
index 6c43d22..81daff4 100644
--- a/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java
+++ b/src/main/java/org/apache/sling/graphql/core/engine/GraphQLResourceQuery.java
@@ -52,12 +52,14 @@ public class GraphQLResourceQuery {
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     public ExecutionResult executeQuery(SchemaProvider schemaProvider, DataFetcherSelector fetchers,
-                                        Resource r, String query) throws ScriptException {
-        return executeQuery(schemaProvider, fetchers, r, query, Collections.emptyMap());
+                                        Resource r, String [] requestSelectors, String query) throws ScriptException {
+        return executeQuery(schemaProvider, fetchers, r, requestSelectors, query, Collections.emptyMap());
     }
 
     public ExecutionResult executeQuery(SchemaProvider schemaProvider, DataFetcherSelector fetchersSelector,
-                                        Resource r, String query, Map<String, Object> variables) throws ScriptException {
+                                        Resource r,
+                                        String [] requestSelectors,
+                                        String query, Map<String, Object> variables) throws ScriptException {
         if(r == null) {
             throw new ScriptException("Resource is null");
         }
@@ -73,7 +75,7 @@ public class GraphQLResourceQuery {
 
         String schemaDef = null;
         try {
-            schemaDef = schemaProvider.getSchema(r, null);
+            schemaDef = schemaProvider.getSchema(r, requestSelectors);
         } catch(Exception e) {
             final ScriptException up = new ScriptException("Schema provider failed");
             up.initCause(e);
@@ -146,7 +148,7 @@ public class GraphQLResourceQuery {
                         log.warn("No data fetcher registered for {}", def.toString());
                     }
                 } catch (IllegalArgumentException iae) {
-                    log.warn("Invalid fetcher definition", iae);
+                    throw new IOException("Invalid fetcher definition", iae);
                 }
             }
         }
diff --git a/src/main/java/org/apache/sling/graphql/core/json/JsonSerializer.java b/src/main/java/org/apache/sling/graphql/core/json/JsonSerializer.java
index ca618d3..8c57816 100644
--- a/src/main/java/org/apache/sling/graphql/core/json/JsonSerializer.java
+++ b/src/main/java/org/apache/sling/graphql/core/json/JsonSerializer.java
@@ -22,6 +22,8 @@ package org.apache.sling.graphql.core.json;
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
 
 import com.cedarsoftware.util.io.JsonWriter;
 
@@ -31,13 +33,24 @@ import graphql.ExecutionResult;
 
 public class JsonSerializer {
 
+    public static final Map<String, Object> WRITER_OPTIONS = new HashMap<String, Object>() {
+        private static final long serialVersionUID = 1L;
+        {
+            put(JsonWriter.TYPE, false);
+        }
+    };
+
     public void sendJSON(PrintWriter out, ExecutionResult result) throws IOException {
         final Object data = result.toSpecification();
         if (data == null) {
             throw new IOException("No data");
         }
-        try(JsonWriter w = new JsonWriter(new WriterOutputStream(out))) {
+        try(JsonWriter w = new JsonWriter(new WriterOutputStream(out), WRITER_OPTIONS)) {
             w.write(data);
         }
     }
+
+    public String toJSON(Object data) {
+        return JsonWriter.objectToJson(data, JsonSerializer.WRITER_OPTIONS);
+    }
 }
diff --git a/src/main/java/org/apache/sling/graphql/core/schema/DefaultSchemaProvider.java b/src/main/java/org/apache/sling/graphql/core/schema/DefaultSchemaProvider.java
index bc323ac..05017d2 100644
--- a/src/main/java/org/apache/sling/graphql/core/schema/DefaultSchemaProvider.java
+++ b/src/main/java/org/apache/sling/graphql/core/schema/DefaultSchemaProvider.java
@@ -59,7 +59,16 @@ public class DefaultSchemaProvider implements SchemaProvider {
     public String getSchema(Resource r, String [] selectors) throws SchemaProviderException {
         final ResourceResolver resourceResolver = r.getResourceResolver();
         final MockSlingHttpServletRequest request = new MockSlingHttpServletRequest(resourceResolver);
-        final String path = r.getPath() + SCHEMA_EXTENSION;
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(r.getPath());
+        if(selectors != null) {
+            for(String s : selectors) {
+                sb.append(".").append(s);
+            }
+        }
+        sb.append(SCHEMA_EXTENSION);
+        final String path = sb.toString();
         request.setPathInfo(path);
         final MockSlingHttpServletResponse response = new MockSlingHttpServletResponse();
         final int status = response.getStatus();
diff --git a/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngine.java b/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngine.java
index ed8f33f..60a2126 100644
--- a/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngine.java
+++ b/src/main/java/org/apache/sling/graphql/core/scripting/GraphQLScriptEngine.java
@@ -61,7 +61,7 @@ public class GraphQLScriptEngine extends AbstractScriptEngine {
             final Resource resource = (Resource) context.getBindings(ScriptContext.ENGINE_SCOPE)
                     .get(SlingBindings.RESOURCE);
             final ExecutionResult result = q.executeQuery(factory.getSchemaProvider(), factory.getdataFetcherSelector(),
-                    resource, IOUtils.toString(reader));
+                    resource, null, IOUtils.toString(reader));
             final PrintWriter out = (PrintWriter) context.getBindings(ScriptContext.ENGINE_SCOPE).get(SlingBindings.OUT);
             jsonSerializer.sendJSON(out, result);
         } catch(Exception e) {
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 ebb99cc..8a8499e 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
@@ -128,7 +128,8 @@ public class GraphQLServlet extends SlingAllMethodsServlet {
 
         try {
             final GraphQLResourceQuery q = new GraphQLResourceQuery();
-            final ExecutionResult result = q.executeQuery(schemaProvider, dataFetcherSelector, resource, query, parser.getVariables());
+            final ExecutionResult result = q.executeQuery(schemaProvider, dataFetcherSelector,
+                resource, request.getRequestPathInfo().getSelectors(), query, parser.getVariables());
             jsonSerializer.sendJSON(response.getWriter(), result);
         } catch(Exception ex) {
             throw new IOException(ex);
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java b/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java
index 916bedd..3c5a0dd 100644
--- a/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/engine/GraphQLResourceQueryTest.java
@@ -37,6 +37,7 @@ import org.apache.sling.graphql.api.SchemaProvider;
 import org.apache.sling.graphql.api.graphqljava.DataFetcherProvider;
 import org.apache.sling.graphql.core.schema.DataFetcherSelector;
 import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.apache.sling.graphql.core.json.JsonSerializer;
 import org.apache.sling.graphql.core.mocks.DigestDataFetcher;
 import org.apache.sling.graphql.core.mocks.DigestDataFetcherProvider;
 import org.apache.sling.graphql.core.mocks.EchoDataFetcherProvider;
@@ -50,7 +51,7 @@ import org.mockito.Mockito;
 import graphql.ExecutionResult;
 
 public class GraphQLResourceQueryTest {
-    private final SchemaProvider schemaProvider = new MockSchemaProvider();
+    private SchemaProvider schemaProvider = new MockSchemaProvider("test-schema");
     private DataFetcherSelector dataFetchersSelector;
     private Resource resource;
 
@@ -79,15 +80,20 @@ public class GraphQLResourceQueryTest {
         registerDataFetcherProvider("test", new FailingDataFetcherProvider("echo"));
         registerDataFetcherProvider("failure", new FailingDataFetcherProvider("fail"));
         registerDataFetcherProvider("test", new EchoDataFetcherProvider("static", staticData));
+        registerDataFetcherProvider("test", new EchoDataFetcherProvider("fortyTwo", 42));
         registerDataFetcherProvider("test", new DigestDataFetcherProvider());
 
         dataFetchersSelector = new DataFetcherSelector(context.bundleContext());
     }
 
     private String queryJSON(String stmt) throws Exception {
-        final ExecutionResult result = new GraphQLResourceQuery().executeQuery(schemaProvider, dataFetchersSelector, resource, stmt);
+        return queryJSON(stmt, null);
+    }
+
+    private String queryJSON(String stmt, String [] selectors) throws Exception {
+        final ExecutionResult result = new GraphQLResourceQuery().executeQuery(schemaProvider, dataFetchersSelector, resource, selectors, stmt);
         assertTrue("Expecting no errors: " + result.getErrors(), result.getErrors().isEmpty());
-        return JsonWriter.objectToJson(result);
+        return new JsonSerializer().toJSON(result);
     }
 
     @Test
@@ -131,9 +137,19 @@ public class GraphQLResourceQueryTest {
     public void dataFetcherFailureTest() throws Exception {
         try {
             final String stmt = "{ currentResource { failure } }";
-            new GraphQLResourceQuery().executeQuery(schemaProvider, dataFetchersSelector, resource, stmt);
+            new GraphQLResourceQuery().executeQuery(schemaProvider, dataFetchersSelector, resource, null, stmt);
         } catch(RuntimeException rex) {
             assertThat(rex.getMessage(), equalTo("FailureDataFetcher"));
         }
     }
+
+    @Test
+    public void schemaSelectorsTest() throws Exception {
+        final String [] selectors = { "selected", "foryou" };
+        final String json = queryJSON("{ currentResource { path fortyTwo } }", selectors);
+
+        assertThat(json, hasJsonPath("$.data.currentResource"));
+        assertThat(json, hasJsonPath("$.data.currentResource.path", equalTo(42)));
+        assertThat(json, hasJsonPath("$.data.currentResource.fortyTwo", equalTo(42)));
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/graphql/core/it/GraphQLScriptingTestSupport.java b/src/test/java/org/apache/sling/graphql/core/it/GraphQLScriptingTestSupport.java
index dadcbba..9fc889b 100644
--- a/src/test/java/org/apache/sling/graphql/core/it/GraphQLScriptingTestSupport.java
+++ b/src/test/java/org/apache/sling/graphql/core/it/GraphQLScriptingTestSupport.java
@@ -70,23 +70,6 @@ public abstract class GraphQLScriptingTestSupport extends TestSupport {
     @Inject
     protected SlingRequestProcessor requestProcessor;
 
-    protected ServiceRegistration<DataFetcherProvider> dataFetcherFactoryRegistration;
-
-    @Inject
-    private BundleContext bundleContext;
-
-    @Before
-    public void registerFetchers() {
-        PipeDataFetcherProvider pipeDataFetcherFactory = new PipeDataFetcherProvider();
-        dataFetcherFactoryRegistration =
-                bundleContext.registerService(DataFetcherProvider.class, pipeDataFetcherFactory, null);
-    }
-
-    @After
-    public void unregisterFetchers() {
-        dataFetcherFactoryRegistration.unregister();
-    }
-
     public ModifiableCompositeOption baseConfiguration() {
         final String vmOpt = System.getProperty("pax.vm.options");
 
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 08c27c2..d325c9c 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
@@ -64,7 +64,7 @@ public class GraphQLServletIT extends GraphQLScriptingTestSupport {
                 .asOption(),
             factoryConfiguration(GRAPHQL_SERVLET_CONFIG_PID)
                 .put("sling.servlet.resourceTypes", "graphql/test/two")
-                .put("sling.servlet.selectors", "testing")
+                .put("sling.servlet.selectors", new String[] { "testing", "another" })
                 .put("sling.servlet.extensions", "otherExt")
                 .asOption(),
         };
@@ -87,12 +87,18 @@ public class GraphQLServletIT extends GraphQLScriptingTestSupport {
     }
 
     @Test
-    public void testOtherExt() throws Exception {
-        final String json = getContent("/graphql/two.testing.otherExt", "query", "{ currentResource { path name } }");
-        assertThat(json, hasJsonPath("$.data.currentResource.path", equalTo("/content/graphql/two")));
-        assertThat(json, hasJsonPath("$.data.currentResource.name", equalTo("two")));
-        assertThat(json, hasNoJsonPath("$.data.currentResource.resourceType"));
+    public void testOtherExtAndTestingSelector() throws Exception {
         executeRequest("GET", "/graphql/two.otherExt", null, 404);
+        final String json = getContent("/graphql/two.testing.otherExt", "query", "{ withTestingSelector { farenheit } }");
+        assertThat(json, hasJsonPath("$.data.withTestingSelector.farenheit", equalTo(451)));
+    }
+
+    @Test
+    public void testOtherExtAndOtherSelector() throws Exception {
+        final String json = getContent("/graphql/two.another.otherExt", "query", "{ currentResource { resourceType name } }");
+        assertThat(json, hasJsonPath("$.data.currentResource.resourceType", equalTo("graphql/test/two")));
+        assertThat(json, hasJsonPath("$.data.currentResource.name", equalTo("two")));
+        assertThat(json, hasNoJsonPath("$.data.currentResource.path"));
     }
 
     @Test
diff --git a/src/test/java/org/apache/sling/graphql/core/it/PipeDataFetcherProvider.java b/src/test/java/org/apache/sling/graphql/core/it/PipeDataFetcherProvider.java
index a0d0a24..a134918 100644
--- a/src/test/java/org/apache/sling/graphql/core/it/PipeDataFetcherProvider.java
+++ b/src/test/java/org/apache/sling/graphql/core/it/PipeDataFetcherProvider.java
@@ -30,21 +30,15 @@ public class PipeDataFetcherProvider implements DataFetcherProvider {
 
     static class PipeDataFetcher implements DataFetcher<Object> {
 
-        private final Resource r;
-        private final String options;
+        private final Object value;
 
-        PipeDataFetcher(Resource r, String name, String options, String source) {
-            this.r = r;
-            this.options = options;
+        PipeDataFetcher(Object value) {
+            this.value = value;
         }
 
         @Override
         public Object get(DataFetchingEnvironment environment) throws Exception {
-            if (!options.isEmpty() && !options.equals("$")) {
-                throw new IllegalArgumentException("Invalid fetcher options: " + options);
-            }
-
-            return r;
+            return value;
         }
 
     }
@@ -52,7 +46,11 @@ public class PipeDataFetcherProvider implements DataFetcherProvider {
     @Override
     public DataFetcher<Object> createDataFetcher(Resource r, String name, String options, String source) {
         if("pipe".equals(name)) {
-            return new PipeDataFetcher(r, name, options, source);
+            if("farenheit".equals(options)) {
+                return new PipeDataFetcher(451);
+            } else {
+                return new PipeDataFetcher(r);
+            }
         } else {
             return null;
         }
diff --git a/src/test/java/org/apache/sling/graphql/core/mocks/MockSchemaProvider.java b/src/test/java/org/apache/sling/graphql/core/mocks/MockSchemaProvider.java
index d58a668..10f3a66 100644
--- a/src/test/java/org/apache/sling/graphql/core/mocks/MockSchemaProvider.java
+++ b/src/test/java/org/apache/sling/graphql/core/mocks/MockSchemaProvider.java
@@ -18,6 +18,8 @@
  */
 package org.apache.sling.graphql.core.mocks;
 
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringWriter;
@@ -28,22 +30,36 @@ import org.apache.sling.graphql.api.SchemaProvider;
 
 public class MockSchemaProvider implements SchemaProvider {
 
-    private static final String MOCK_SCHEMA_PATH = "/test-schema.txt";
-    private static final String MOCK_SCHEMA;
+    private final String basename;
 
-    static {
-        try (InputStream is = MockSchemaProvider.class.getResourceAsStream(MOCK_SCHEMA_PATH)) {
-            final StringWriter w = new StringWriter();
-            IOUtils.copy(is, w);
-            MOCK_SCHEMA = w.toString();
-        } catch(IOException ioe) {
-            throw new RuntimeException("Error reading " + MOCK_SCHEMA_PATH, ioe);
-        }
+    public MockSchemaProvider(String basename) {
+        this.basename = basename;
     }
 
     @Override
     public String getSchema(Resource r, String[] selectors) {
-        return MOCK_SCHEMA;
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append("/").append(basename);
+        if(selectors != null) {
+            for(String s : selectors) {
+                sb.append("-").append(s);
+            }
+        }
+        sb.append(".txt");
+
+        final String path = sb.toString();
+
+        try (InputStream is = MockSchemaProvider.class.getResourceAsStream(path)) {
+            if(is == null) {
+                fail("Test schema not found: " + path);
+            }
+            final StringWriter w = new StringWriter();
+            IOUtils.copy(is, w);
+            return w.toString();
+        } catch(IOException ioe) {
+            throw new RuntimeException("Error reading " + path, ioe);
+        }
     }
 
 }
diff --git a/src/test/resources/initial-content/apps/graphql/test/two/testing.GQLschema.jsp b/src/test/resources/initial-content/apps/graphql/test/two/testing.GQLschema.jsp
new file mode 100644
index 0000000..61633a0
--- /dev/null
+++ b/src/test/resources/initial-content/apps/graphql/test/two/testing.GQLschema.jsp
@@ -0,0 +1,35 @@
+<%-- 
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+--%>
+
+<%--
+Generating the schemas in JSP might not be the best way
+but it works for these initial tests - we might create
+a "passthrough" script engine, or one that extracts the
+additional DataFetcher information that we need.
+--%>
+
+type Query {
+  ## fetch:test/pipe
+  withTestingSelector : TestData
+}
+
+type TestData {
+  ## fetch:test/pipe/farenheit
+  farenheit: Int
+}
\ No newline at end of file
diff --git a/src/test/resources/test-schema-selected-foryou.txt b/src/test/resources/test-schema-selected-foryou.txt
new file mode 100644
index 0000000..0f00020
--- /dev/null
+++ b/src/test/resources/test-schema-selected-foryou.txt
@@ -0,0 +1,32 @@
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements.  See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership.  The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License.  You may obtain a copy of the License at
+# *
+# *   http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied.  See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+
+# GraphQL Schema used for our tests
+# annotated with our fetch: definitions
+
+type Query {
+    ## fetch:echoNS/echo
+    currentResource : SlingResource
+}
+
+type SlingResource { 
+    ## fetch:test/fortyTwo
+    fortyTwo: Int
+
+    ## fetch:test/fortyTwo
+    path: Int
+}
\ No newline at end of file