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-whiteboard.git
The following commit(s) were added to refs/heads/master by this push: new 04c602a GraphiQL client-side queries work 04c602a is described below commit 04c602ab741d6905aec4afdd64e000a824dc066f Author: Bertrand Delacretaz <bdelacre...@apache.org> AuthorDate: Thu May 28 16:11:13 2020 +0200 GraphiQL client-side queries work --- org.apache.sling.graphql.samples.website/README.md | 57 ++++++++++++++++---- .../datafetchers/ArticlesWithTextFetcher.java | 63 ++++++++++++++++++++++ .../samples/website/datafetchers/FetcherUtil.java | 48 +++++++++++++++++ .../datafetchers/SamplesDataFetcherProvider.java | 2 + .../website/datafetchers/SeeAlsoDataFetcher.java | 14 ++++- .../apps/samples/servlet/GQLschema.jsp | 25 +++++++++ .../features/feature-graphql-example-website.json | 4 +- 7 files changed, 200 insertions(+), 13 deletions(-) diff --git a/org.apache.sling.graphql.samples.website/README.md b/org.apache.sling.graphql.samples.website/README.md index c8b4b94..a353235 100644 --- a/org.apache.sling.graphql.samples.website/README.md +++ b/org.apache.sling.graphql.samples.website/README.md @@ -3,7 +3,17 @@ Apache Sling GraphQL demo website This is a work in progress demo of the Sling GraphQL Core. -## Status +It demonstrates both server-side GraphQL queries, used for content aggregation, and the +more traditional client-side queries, using the same GraphQL schemas and data fetching +Java components for both variants. + +Besides the page rendering code there's not much: GraphQL schema definitions and a few +Java classes used for aggregating or enhancing content and for content queries. + +For now there's no pagination of query results, just arbitrary limits on the number +of results returned. + +## Demo Website A website with rich navigation is implemented with server-side GraphQL queries and client-side Handlebars templates for HTML rendering. @@ -12,16 +22,45 @@ http://localhost:8080/content/graphql-website-demo.html is the entry point, afte this as described below. The rendering is based on JSON content that's aggregated server-side using GraphQL queries -to provide all the page content, navigation etc. in a single request.</p> +to provide all the page content, navigation etc. in a single request. + +This is just an initial prototype. As a next step I'd like to render the article pages using +server-side Handlebars templates and implement a few single-page applications for search +and browsing. While keeping the articles rendering server-side (so that they make sense +for Web search engines for example) and ideally using the same languages (GraphQL and +Handlebars) either server- or client-side. + +## Client-side GraphQL queries + +Client-side queries work using an external GraphiQL client (or any suitable client) that +talks to http://localhost:8080/graphql.json + +For now only simple queries are supported, like: + + { + article(withText: "jacobi") { + path + title + tags + seeAlso { + title + path + } + } + } -This is just an initial prototype. I'd like to move closer to -[Progressive Web Apps](https://en.wikipedia.org/wiki/Progressive_web_application) techniques, -generating a basic rendering server-side and enhancing it with CSS and JavaScript -client-side. +Besides fixing the `DataFetcher`s to use the correct context Resource, setting this up +only required activating the `GraphQLServlet` (in the Feature Model that starts this demo) +and adding the below schema file. Everything else is shared between the server-side and +client-side query variants. -The GraphQL schemas and `DataFetchers` that have been implemented will be usable for more -traditional client-side GraphQL queries as well, once that's setup. As that side of things has -less unknowns, I'm focusing on the less usual server-side GraphQL concepts for now. + # /apps/samples/servlet/GQLschema.jsp + type Query { + ## fetch:samples/articlesWithText + article (withText : String) : [Article] + } + + <%@include file="/apps/samples/common/GQLschema.jsp" %> ## How to run this diff --git a/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/ArticlesWithTextFetcher.java b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/ArticlesWithTextFetcher.java new file mode 100644 index 0000000..8005a0e --- /dev/null +++ b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/ArticlesWithTextFetcher.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +package org.apache.sling.graphql.samples.website.datafetchers; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.graphql.samples.website.models.SlingWrappers; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; + +class ArticlesWithTextFetcher implements DataFetcher<Object> { + + public static final String NAME = "articlesWithText"; + public static final String P_WITH_TEXT = "withText"; + + private final Resource resource; + + ArticlesWithTextFetcher(Resource resource) { + this.resource = resource; + } + + @Override + public Object get(DataFetchingEnvironment environment) throws Exception { + final String expectedText = environment.getArgument(P_WITH_TEXT); + final String jcrQuery = String.format( + "/jcr:root/content/articles//*[jcr:contains(@text, '%s') or jcr:contains(@title, '%s')]", + expectedText, expectedText); + + final List<Map<String, Object>> result = new ArrayList<>(); + final Iterator<Resource> it = resource.getResourceResolver().findResources(jcrQuery, "xpath"); + // TODO should use pagination + int counter = 451; + while(it.hasNext()) { + if(--counter <= 0) { + break; + } + result.add(SlingWrappers.resourceWrapper(it.next())); + } + return result; + } +} \ No newline at end of file diff --git a/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/FetcherUtil.java b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/FetcherUtil.java new file mode 100644 index 0000000..514feba --- /dev/null +++ b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/FetcherUtil.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +package org.apache.sling.graphql.samples.website.datafetchers; + +import java.util.Map; + +import org.apache.sling.api.resource.Resource; +import graphql.schema.DataFetchingEnvironment; + +class FetcherUtil { + + /** Return the "source" Resource to use, preferably the one provided + * by the DataFetchingEnvironment, otherwise the supplied base Resource. + */ + static Resource getSourceResource(DataFetchingEnvironment env, Resource base) { + Resource result = base; + String path = null; + final Object o = env.getSource(); + if(o instanceof Map) { + final Map<?, ?> m = (Map)o; + path = String.valueOf(m.get("path")); + } + if(path != null) { + final Resource r = base.getResourceResolver().getResource(base, path); + if(r != null) { + result = r; + } + } + return result; + } +} \ No newline at end of file diff --git a/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/SamplesDataFetcherProvider.java b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/SamplesDataFetcherProvider.java index b6b6739..850efac 100644 --- a/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/SamplesDataFetcherProvider.java +++ b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/SamplesDataFetcherProvider.java @@ -46,6 +46,8 @@ public class SamplesDataFetcherProvider implements DataFetcherProvider { return new ArticlesBySectionFetcher(r); } else if(NavigationDataFetcher.NAME.equals(name)) { return new NavigationDataFetcher(r); + } else if(ArticlesWithTextFetcher.NAME.equals(name)) { + return new ArticlesWithTextFetcher(r); } return null; } diff --git a/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/SeeAlsoDataFetcher.java b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/SeeAlsoDataFetcher.java index e863761..ecd9722 100644 --- a/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/SeeAlsoDataFetcher.java +++ b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/SeeAlsoDataFetcher.java @@ -54,13 +54,23 @@ class SeeAlsoDataFetcher implements DataFetcher<Object> { return result; } + private String getSourcePath(DataFetchingEnvironment env) { + String result = null; + final Object o = env.getSource(); + if(o instanceof Map) { + final Map<?, ?> m = (Map)o; + result = String.valueOf(m.get("path")); + } + return result; + } + @Override - public Object get(DataFetchingEnvironment environment) throws Exception { + public Object get(DataFetchingEnvironment env) throws Exception { // Our "see also" field only contains node names - this demonstrates // using a query to get their full paths // - final ValueMap vm = resource.adaptTo(ValueMap.class); + final ValueMap vm = FetcherUtil.getSourceResource(env, resource).adaptTo(ValueMap.class); if(vm != null) { return Arrays .stream(vm.get(NAME, String[].class)) diff --git a/org.apache.sling.graphql.samples.website/src/main/resources/SLING-INF/initial-content/apps/samples/servlet/GQLschema.jsp b/org.apache.sling.graphql.samples.website/src/main/resources/SLING-INF/initial-content/apps/samples/servlet/GQLschema.jsp new file mode 100644 index 0000000..2c8e0de --- /dev/null +++ b/org.apache.sling.graphql.samples.website/src/main/resources/SLING-INF/initial-content/apps/samples/servlet/GQLschema.jsp @@ -0,0 +1,25 @@ +<%-- +* 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. +--%> + +type Query { + ## fetch:samples/articlesWithText + article (withText : String) : [Article] +} + +<%@include file="/apps/samples/common/GQLschema.jsp" %> \ No newline at end of file diff --git a/org.apache.sling.graphql.samples.website/src/main/resources/features/feature-graphql-example-website.json b/org.apache.sling.graphql.samples.website/src/main/resources/features/feature-graphql-example-website.json index de1ad43..816323a 100644 --- a/org.apache.sling.graphql.samples.website/src/main/resources/features/feature-graphql-example-website.json +++ b/org.apache.sling.graphql.samples.website/src/main/resources/features/feature-graphql-example-website.json @@ -10,7 +10,7 @@ "id" : "org.apache.sling:org.apache.sling.graphql.core:0.0.1-SNAPSHOT", "configurations" : { "org.apache.sling.graphql.core.GraphQLServlet~default" : { - "sling.servlet.resourceTypes" : "graphql/servlet", + "sling.servlet.resourceTypes" : "samples/servlet", "sling.servlet.extensions": "json", "sling.servlet.methods": [ "GET", "POST" ] } @@ -41,7 +41,7 @@ "allow jcr:read on /graphql", "end", "set properties on /graphql", - "set sling:resourceType{String} to graphql/servlet", + "set sling:resourceType{String} to samples/servlet", "end" ] } \ No newline at end of file